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 }