icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

indexed.go (3289B)


      1 package icns
      2 
      3 import (
      4 	"fmt"
      5 	"image"
      6 	"image/color"
      7 )
      8 
      9 // The icons that predate Mac OS 8.5 store an index per pixel rather than a
     10 // colour, against a table the system fixed, and keep their alpha as a one bit
     11 // mask in the element named "#" for their size.
     12 
     13 // macPalette4 is the sixteen colour table: white, the bright hues, the
     14 // muted ones, then the greys down to black.
     15 var macPalette4 = [16]color.NRGBA{
     16 	{0xFF, 0xFF, 0xFF, 0xFF}, // white
     17 	{0xFC, 0xF3, 0x05, 0xFF}, // yellow
     18 	{0xFF, 0x64, 0x03, 0xFF}, // orange
     19 	{0xDD, 0x09, 0x07, 0xFF}, // red
     20 	{0xF2, 0x08, 0x84, 0xFF}, // magenta
     21 	{0x47, 0x00, 0xA5, 0xFF}, // purple
     22 	{0x00, 0x00, 0xD3, 0xFF}, // blue
     23 	{0x02, 0xAB, 0xEA, 0xFF}, // cyan
     24 	{0x1F, 0xB7, 0x14, 0xFF}, // green
     25 	{0x00, 0x64, 0x12, 0xFF}, // dark green
     26 	{0x56, 0x2C, 0x05, 0xFF}, // brown
     27 	{0x90, 0x71, 0x3A, 0xFF}, // tan
     28 	{0xC0, 0xC0, 0xC0, 0xFF}, // light grey
     29 	{0x80, 0x80, 0x80, 0xFF}, // medium grey
     30 	{0x40, 0x40, 0x40, 0xFF}, // dark grey
     31 	{0x00, 0x00, 0x00, 0xFF}, // black
     32 }
     33 
     34 // macPalette8 is the 256 colour table: a six level colour cube with black
     35 // left out, then ten shade ramps of red, green, blue and grey, and black
     36 // last. White is index 0 and black is index 255.
     37 var macPalette8 = buildPalette8()
     38 
     39 func buildPalette8() [256]color.NRGBA {
     40 	var (
     41 		palette [256]color.NRGBA
     42 		levels  = [6]uint8{0xFF, 0xCC, 0x99, 0x66, 0x33, 0x00}
     43 		// The shades between the cube's levels, darkest last.
     44 		shades = [10]uint8{0xEE, 0xDD, 0xBB, 0xAA, 0x88, 0x77, 0x55, 0x44, 0x22, 0x11}
     45 		at     int
     46 	)
     47 	for _, r := range levels {
     48 		for _, g := range levels {
     49 			for _, b := range levels {
     50 				if r == 0 && g == 0 && b == 0 {
     51 					continue // Black is held back for the last index.
     52 				}
     53 				palette[at] = color.NRGBA{r, g, b, 0xFF}
     54 				at++
     55 			}
     56 		}
     57 	}
     58 	for _, v := range shades {
     59 		palette[at] = color.NRGBA{v, 0, 0, 0xFF}
     60 		at++
     61 	}
     62 	for _, v := range shades {
     63 		palette[at] = color.NRGBA{0, v, 0, 0xFF}
     64 		at++
     65 	}
     66 	for _, v := range shades {
     67 		palette[at] = color.NRGBA{0, 0, v, 0xFF}
     68 		at++
     69 	}
     70 	for _, v := range shades {
     71 		palette[at] = color.NRGBA{v, v, v, 0xFF}
     72 		at++
     73 	}
     74 	palette[at] = color.NRGBA{0x00, 0x00, 0x00, 0xFF}
     75 	return palette
     76 }
     77 
     78 // decodeIndexed builds an image from bits-per-pixel indices and a one bit
     79 // mask. bits is 1, 4 or 8, and a nil mask leaves the icon opaque.
     80 func decodeIndexed(data, mask []byte, w, h, bits int) (image.Image, error) {
     81 	var (
     82 		pixels = w * h
     83 		need   = pixels * bits / 8
     84 		bitmap = pixels / 8
     85 	)
     86 	if len(data) < need {
     87 		return nil, fmt.Errorf("%w: holds %d bytes, want %d for %dx%d at %d bits", ErrMalformed, len(data), need, w, h, bits)
     88 	}
     89 	if mask != nil && len(mask) < bitmap {
     90 		return nil, fmt.Errorf("%w: mask holds %d bytes, want %d", ErrMalformed, len(mask), bitmap)
     91 	}
     92 	img := image.NewNRGBA(image.Rect(0, 0, w, h))
     93 	for i := 0; i < pixels; i++ {
     94 		var c color.NRGBA
     95 		switch bits {
     96 		case 1:
     97 			// A set bit is ink, which is black on white.
     98 			c = macPalette4[0]
     99 			if data[i/8]&(0x80>>(i%8)) != 0 {
    100 				c = macPalette4[15]
    101 			}
    102 		case 4:
    103 			index := data[i/2] >> 4
    104 			if i%2 == 1 {
    105 				index = data[i/2] & 0x0F
    106 			}
    107 			c = macPalette4[index]
    108 		case 8:
    109 			c = macPalette8[data[i]]
    110 		}
    111 		if mask != nil && mask[i/8]&(0x80>>(i%8)) == 0 {
    112 			c.A = 0
    113 		}
    114 		img.SetNRGBA(i%w, i/w, c)
    115 	}
    116 	return img, nil
    117 }