tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/pixel/pixel.go (about) 1 // Package pixel contains pixel format definitions used in various displays and 2 // fast operations on them. 3 // 4 // This package is just a base for pixel operations, it is _not_ a graphics 5 // library. It doesn't define circles, lines, etc - just the bare minimum 6 // graphics operations needed plus the ones that need to be specialized per 7 // pixel format. 8 package pixel 9 10 import ( 11 "image/color" 12 "math/bits" 13 ) 14 15 // Pixel with a particular color, matching the underlying hardware of a 16 // particular display. Each pixel is at least 1 byte in size. 17 // The color format is sRGB (or close to it) in all cases except for 1-bit. 18 type Color interface { 19 RGB888 | RGB565BE | RGB555 | RGB444BE | Monochrome 20 21 BaseColor 22 } 23 24 // BaseColor contains all the methods needed in a color format. This can be used 25 // in display drivers that want to define their own Color type with just the 26 // pixel formats the display supports. 27 type BaseColor interface { 28 // The number of bits when stored. 29 // This means for example that RGB555 (which is still stored as a 16-bit 30 // integer) returns 16, while RGB444 returns 12. 31 BitsPerPixel() int 32 33 // Return the given color in color.RGBA format, which is always sRGB. The 34 // alpha channel is always 255. 35 RGBA() color.RGBA 36 } 37 38 // NewColor returns the given color based on the RGB values passed in the 39 // parameters. The input value is assumed to be in sRGB color space. 40 func NewColor[T Color](r, g, b uint8) T { 41 // Ugly cast from color.RGBA to T. The type switch and interface casts are 42 // trivially optimized away after instantiation. 43 var value T 44 switch any(value).(type) { 45 case RGB888: 46 return any(NewRGB888(r, g, b)).(T) 47 case RGB565BE: 48 return any(NewRGB565BE(r, g, b)).(T) 49 case RGB555: 50 return any(NewRGB555(r, g, b)).(T) 51 case RGB444BE: 52 return any(NewRGB444BE(r, g, b)).(T) 53 case Monochrome: 54 return any(NewMonochrome(r, g, b)).(T) 55 default: 56 panic("unknown color format") 57 } 58 } 59 60 // NewLinearColor returns the given color based on the linear RGB values passed 61 // in the parameters. Use this if the RGB values are actually linear colors 62 // (like those that are used in most RGB LEDs) and not when it is in the usual 63 // sRGB color space (which is not linear). 64 // 65 // The input is assumed to be in the linear sRGB color space. 66 func NewLinearColor[T Color](r, g, b uint8) T { 67 r = gammaEncodeTable[r] 68 g = gammaEncodeTable[g] 69 b = gammaEncodeTable[b] 70 return NewColor[T](r, g, b) 71 } 72 73 // RGB888 format, more commonly used in other places (desktop PC displays, CSS, 74 // etc). Less commonly used on embedded displays due to the higher memory usage. 75 type RGB888 struct { 76 R, G, B uint8 77 } 78 79 func NewRGB888(r, g, b uint8) RGB888 { 80 return RGB888{r, g, b} 81 } 82 83 func (c RGB888) BitsPerPixel() int { 84 return 24 85 } 86 87 func (c RGB888) RGBA() color.RGBA { 88 return color.RGBA{ 89 R: c.R, 90 G: c.G, 91 B: c.B, 92 A: 255, 93 } 94 } 95 96 // RGB565 as used in many SPI displays. Stored as a big endian value. 97 // 98 // The color format in integer form is gggbbbbb_rrrrrggg on little endian 99 // systems, which is the standard RGB565 format but with the top and bottom 100 // bytes swapped. 101 // 102 // There are a few alternatives to this weird big-endian format, but they're not 103 // great: 104 // - Storing the value in two 8-bit stores (to make the code endian-agnostic) 105 // incurs too much of a performance penalty. 106 // - Swapping the upper and lower bits just before storing. This is still less 107 // efficient than it could be, since colors are usually constructed once and 108 // then reused in many store operations. Doing the swap once instead of many 109 // times for each store is a performance win. 110 type RGB565BE uint16 111 112 func NewRGB565BE(r, g, b uint8) RGB565BE { 113 val := uint16(r&0xF8)<<8 + 114 uint16(g&0xFC)<<3 + 115 uint16(b&0xF8)>>3 116 // Swap endianness (make big endian). 117 // This is done using a single instruction on ARM (rev16). 118 // TODO: this should only be done on little endian systems, but TinyGo 119 // doesn't currently (2023) support big endian systems so it's difficult to 120 // test. Also, big endian systems don't seem fasionable these days. 121 val = bits.ReverseBytes16(val) 122 return RGB565BE(val) 123 } 124 125 func (c RGB565BE) BitsPerPixel() int { 126 return 16 127 } 128 129 func (c RGB565BE) RGBA() color.RGBA { 130 // Note: on ARM, the compiler uses a rev instruction instead of a rev16 131 // instruction. I wonder whether this can be optimized further to use rev16 132 // instead? 133 c = c<<8 | c>>8 134 color := color.RGBA{ 135 R: uint8(c>>11) << 3, 136 G: uint8(c>>5) << 2, 137 B: uint8(c) << 3, 138 A: 255, 139 } 140 // Correct color rounding, so that 0xff roundtrips back to 0xff. 141 color.R |= color.R >> 5 142 color.G |= color.G >> 6 143 color.B |= color.B >> 5 144 return color 145 } 146 147 // Color format used on the GameBoy Advance among others. 148 // 149 // Colors are stored as native endian values, with bits 0bbbbbgg_gggrrrrr (red 150 // is least significant, blue is most significant). 151 type RGB555 uint16 152 153 func NewRGB555(r, g, b uint8) RGB555 { 154 return RGB555(r)>>3 | (RGB555(g)>>3)<<5 | (RGB555(b)>>3)<<10 155 } 156 157 func (c RGB555) BitsPerPixel() int { 158 // 15 bits per pixel, but there are 16 bits when stored 159 return 16 160 } 161 162 func (c RGB555) RGBA() color.RGBA { 163 color := color.RGBA{ 164 R: uint8(c>>10) << 3, 165 G: uint8(c>>5) << 3, 166 B: uint8(c) << 3, 167 A: 255, 168 } 169 // Correct color rounding, so that 0xff roundtrips back to 0xff. 170 color.R |= color.R >> 5 171 color.G |= color.G >> 5 172 color.B |= color.B >> 5 173 return color 174 } 175 176 // Color format that is supported by the ST7789 for example. 177 // It may be a bit faster to use than RGB565BE on very slow SPI buses. 178 // 179 // The color format is native endian as a uint16 (0000rrrr_ggggbbbb), not big 180 // endian which you might expect. I tried swapping the bytes, but it didn't have 181 // much of a performance impact and made the code harder to read. It is stored 182 // as a 12-bit big endian value in Image[RGB444BE] though. 183 type RGB444BE uint16 184 185 func NewRGB444BE(r, g, b uint8) RGB444BE { 186 return RGB444BE(r>>4)<<8 | RGB444BE(g>>4)<<4 | RGB444BE(b>>4) 187 } 188 189 func (c RGB444BE) BitsPerPixel() int { 190 return 12 191 } 192 193 func (c RGB444BE) RGBA() color.RGBA { 194 color := color.RGBA{ 195 R: uint8(c>>8) << 4, 196 G: uint8(c>>4) << 4, 197 B: uint8(c>>0) << 4, 198 A: 255, 199 } 200 // Correct color rounding, so that 0xff roundtrips back to 0xff. 201 color.R |= color.R >> 4 202 color.G |= color.G >> 4 203 color.B |= color.B >> 4 204 return color 205 } 206 207 type Monochrome bool 208 209 func NewMonochrome(r, g, b uint8) Monochrome { 210 // Very simple black/white split. 211 // This isn't very accurate (especially for sRGB colors) but is close enough. 212 if int(r)+int(g)+int(b) > 128*3 { // light, convert to white 213 return Monochrome(true) 214 } 215 // dark, convert to black 216 return Monochrome(false) 217 } 218 219 func (c Monochrome) BitsPerPixel() int { 220 return 1 221 } 222 223 func (c Monochrome) RGBA() color.RGBA { 224 value := uint8(0) 225 if c { 226 value = 255 227 } 228 return color.RGBA{ 229 R: value, 230 G: value, 231 B: value, 232 A: 255, 233 } 234 } 235 236 // Gamma brightness lookup table: 237 // https://victornpb.github.io/gamma-table-generator 238 // gamma = 0.45 steps = 256 range = 0-255 239 var gammaEncodeTable = [256]uint8{ 240 0, 21, 28, 34, 39, 43, 46, 50, 53, 56, 59, 61, 64, 66, 68, 70, 241 72, 74, 76, 78, 80, 82, 84, 85, 87, 89, 90, 92, 93, 95, 96, 98, 242 99, 101, 102, 103, 105, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 243 119, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 244 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 245 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 160, 160, 161, 162, 163, 246 164, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 247 175, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 184, 185, 186, 248 186, 187, 188, 188, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 195, 196, 249 197, 197, 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 250 206, 207, 207, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 251 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 252 224, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 253 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239, 240, 254 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 255 248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 256 }