C API 1.9.1: How to use an LCDPattern-based LCDColor?

The C API documentation 6.5 Graphics > Supporting types lists LCDColor as "Either an LCDSolidColor or an LCDPattern*."

Using SDK version 1.9.1 C API on Mac, I am having trouble passing patterns into functions that accept LCDColor.

src/main.c

#include "pd_api.h"

LCDPattern gray50_data = {
  0, 1, 0 ,1,
  1, 0, 1, 0,
  0, 1, 0 ,1,
  1, 0, 1, 0,
};
LCDPattern *gray50 = &gray50_data;

static int update(void* userdata)
{
  PlaydateAPI* pd = userdata;
  pd->graphics->fillRect(0, 0, LCD_COLUMNS, LCD_ROWS, gray50);
  return 1;
}

// eventHandler() setup omitted
warning: incompatible pointer to integer conversion passing 'LCDPattern *' (aka 'uint8_t (*)[16]') to parameter of type 'LCDColor' (aka 'unsigned long') [-Wint-conversion]

If I am confused as to how to call something that should be obvious, an example of using an LCDPattern-based LCDColor would be appreciated. If this is a documentation oversight, it should be fixed.

Note that setColorToPattern() looks promising, but it appears to work with LCDBitmap *. Therefore, I simply decided to post because the C API LCDPattern documentation is unclear.

EDIT: Also, LCDColor does not appear to be a tagged union. To the best of my recollection, implicitly casting pointers to integer types and vice versa is not generally considered a best practice in C. Low values could, however, be used as an implicit tag based on the assumption that user allocated patterns will not be located at a memory address less that 5 (or whatever the value of the final LCDSolidColor is). Having said that, an explicit cast eliminates the warning without solving the problem.

1 Like

mmmh interesting question.

I never used LCDPattern but like LCDBitmap, the data format is not really explained in the doc.

1 bit is basically 1 pixel so one byte is a row of 8 pixels.
For LCDPattern there is two parts: the first one is the black and white bitmap and the second one is the mask (1 is opaque, 0 is transparent)

LCDPattern grey50 = {
	0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // Bitmap, each byte is a row of pixel
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Mask, here fully opaque
};

pd->graphics->fillRect(0, 0, LCD_COLUMNS, LCD_ROWS, grey50);

But if you use binary representation this is a bit easier to edit

LCDPattern grey50 = {
	// Bitmap
	0b10101010,
	0b01010101,
	0b10101010,
	0b01010101,
	0b10101010,
	0b01010101,
	0b10101010,
	0b01010101,

	// Mask
	0b11111111,
	0b11111111,
	0b11111111,
	0b11111111,
	0b11111111,
	0b11111111,
	0b11111111,
	0b11111111,
};
3 Likes

The example works, but a warning is still issued. Is an explicit cast recommended to get rid of the warning?

pd->graphics->fillRect(0, 0, LCD_COLUMNS, LCD_ROWS, (LCDColor)grey50);

While we are at it, how is an LCDBitmap declared? Say I want to declare the following 11x3 pseudocode bitmap. Can that be done, or do bitmap dimensions need be a specific power of two?

// 11-bit "bytes"
LCDBitmap oddBitmap = {
  // Bitmap
  0b01110000100,
  0b00101110000,
  0b00001001110,

  // Mask
  0b11111111100,
  0b01111111110,
  0b00111111111,
};

For the warning I would just cast it, yes. There might be a smarter/cleaner way...

For LCD Bitmap you can allocate them with:
playdate->graphics->newBitmap(int width, int height, LCDColor bgcolor);

and to get information about it:
playdate->graphics->getBitmapData(LCDBitmap* bitmap, int* width, int* height, int* rowbytes, int* hasmask, uint8_t** data)

Just to be clear, LCDPattern needs to be declared as a literal- there is no API function to create it. LCDBitmap is the opposite- it must be created via the API and not declared as a literal. Is this correct?

Is it generally acceptable to directly overwrite the data returned by getBitmapData()? For example, this code works.

  // 11x3 bitmap
  char dartBitmapData[] = {
    0b11000001, 0b100 << 5, 0, 0,
    0b00101110, 0b011 << 5, 0, 0,
    0b11000001, 0b100 << 5, 0, 0,
  };
  LCDBitmap *bitmap = pd->graphics->newBitmap(11, 3, (LCDColor)gray50);
  int width, height, rowbytes, hasmask;
  uint8_t *data;
  pd->graphics->getBitmapData(bitmap, &width, &height, &rowbytes, &hasmask, &data);
  for (int i = 0; i < height * rowbytes && i < sizeof(dartBitmapData); i++) {
    data[i] = dartBitmapData[i];
  }

I also wrote some code to write information about the bitmap to the screen. Evidently 1 is white and 0 is black. Based on this test, rows seems to be 32-bit aligned

Also, the SDK/API are closed source, correct? If so, there is no way to dive into the source code, so questions need to go through the developer forums. (Feedback is good.)

Edit: BitmapInfo demo attached. It can also serve as an input handling demo.

BitmapInfo.zip (12.8 KB)

1 Like

Just to be clear, LCDPattern needs to be declared as a literal- there is no API function to create it. LCDBitmap is the opposite- it must be created via the API and not declared as a literal. Is this correct?

yes this is correct. This is mainly because LCDBitmap had to deal with different size which mean different stride etc. LCDPattern is much simple just a 8 by 8 with a mask.

Is it generally acceptable to directly overwrite the data returned by getBitmapData() ?

yes that data is provided so that you can write into it. Same for playdate->graphics->getFrame() which returns the frame buffer and let render the way you want in there.

Also, the SDK/API are closed source, correct?

yes. But usually you can ask questions on the forum and the panic staff is pretty open discussing any question you have.

2 Likes