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  }