lovyan03 / LovyanGFX

SPI LCD graphics library for ESP32 (ESP-IDF/ArduinoESP32) / ESP8266 (ArduinoESP8266) / SAMD51(Seeed ArduinoSAMD51)
Other
1.13k stars 200 forks source link

pushImage() does not work for 4bit images #308

Closed ESPboy-edu closed 1 year ago

ESPboy-edu commented 1 year ago

Environment ( 実行環境 )

Problem Description ( 問題の内容 )

pushImage() for 4bit images does not work

Expected Behavior ( 期待される動作 )

displaying 4bit image on the LCD or copying 4bit image to the 4bit sprite

Actual Behavior ( 実際の動作 )

got corrupted picture the picture decoded as 8bit (1 byte per pixel) not 4bit (1 byte per 2 pixels)

Steps to reproduce ( 再現のための前提条件 )

  1. create 4bit sprite2
  2. pushImage() from 4bit image array to the LCD or to the 4bit sprite3.
  3. you will see corrupted picrure.4.

try to display this 4bit spaceship but it works fine with my own custom function of pushing image to the 4bit sprite

PROGMEM const uint8_t spaceShip[] = {
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x02, 0x00, 0x11, 0x10, 0x02, 0x00, 0x00,
    0x00, 0x02, 0x00, 0x11, 0x10, 0x02, 0x00, 0x00,
    0x00, 0x01, 0x01, 0x11, 0x11, 0x01, 0x00, 0x00,
    0x20, 0x01, 0x31, 0x12, 0x11, 0x31, 0x00, 0x20,
    0x20, 0x03, 0x11, 0x22, 0x21, 0x13, 0x00, 0x20,
    0x10, 0x01, 0x11, 0x21, 0x21, 0x11, 0x00, 0x10,
    0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10,
    0x11, 0x11, 0x12, 0x11, 0x12, 0x11, 0x11, 0x10,
    0x11, 0x10, 0x22, 0x11, 0x12, 0x20, 0x11, 0x10,
    0x11, 0x00, 0x22, 0x01, 0x02, 0x20, 0x01, 0x10,
    0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10
 };

it's rather sad because the most commonly used color images for Indie games are 16 color 4-bit.

lovyan03 commented 1 year ago

Please provide buildable code that you have executed.

m1cr0lab commented 1 year ago

Hi @lovyan03,

Sorry to bother you with this...

I tested on my side with a 16x16 color indexed sprite:

coin@8x

#include <ESPboy.h>

uint16_t const constexpr PALETTE[] PROGMEM = {

    0xa719, // #1C363E => 0x0
    0x0000, // #000000 => 0x1
    0xffff, // #FFFFFF => 0x2
    0x80b3, // #B67000 => 0x3
    0xa0fd, // #FFB400 => 0x4
    0x2aff, // #FFE456 => 0x5
    0xe007  // #00FF00 => 0x6 (transparency color)

};

uint8_t const constexpr COIN[] PROGMEM = {

    0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
    0x6, 0x6, 0x6, 0x6, 0x1, 0x1, 0x4, 0x4, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x6, 0x6,
    0x6, 0x6, 0x6, 0x1, 0x4, 0x4, 0x2, 0x5, 0x5, 0x5, 0x3, 0x3, 0x1, 0x6, 0x6, 0x6,
    0x6, 0x6, 0x1, 0x4, 0x2, 0x2, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x1, 0x6, 0x6,
    0x6, 0x1, 0x4, 0x2, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x5, 0x5, 0x5, 0x3, 0x1, 0x6,
    0x6, 0x1, 0x4, 0x2, 0x5, 0x3, 0x4, 0x4, 0x4, 0x4, 0x3, 0x5, 0x5, 0x3, 0x1, 0x6,
    0x1, 0x4, 0x2, 0x5, 0x3, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x4, 0x5, 0x5, 0x3, 0x1,
    0x1, 0x4, 0x5, 0x5, 0x3, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x4, 0x5, 0x5, 0x3, 0x1,
    0x1, 0x4, 0x5, 0x5, 0x3, 0x4, 0x4, 0x5, 0x5, 0x5, 0x2, 0x4, 0x5, 0x5, 0x3, 0x1,
    0x1, 0x4, 0x5, 0x5, 0x3, 0x4, 0x5, 0x5, 0x5, 0x2, 0x2, 0x4, 0x5, 0x5, 0x3, 0x1,
    0x6, 0x1, 0x4, 0x5, 0x5, 0x3, 0x5, 0x5, 0x2, 0x2, 0x4, 0x5, 0x5, 0x3, 0x1, 0x6,
    0x6, 0x1, 0x4, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x3, 0x1, 0x6,
    0x6, 0x6, 0x1, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x1, 0x6, 0x6,
    0x6, 0x6, 0x6, 0x1, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x1, 0x6, 0x6, 0x6,
    0x6, 0x6, 0x6, 0x6, 0x1, 0x1, 0x4, 0x4, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x6, 0x6,
    0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6

};

LGFX_Sprite framebuffer(&espboy.tft);

void setup() {

    espboy.begin();

    framebuffer.setColorDepth(4);
    framebuffer.createSprite(TFT_WIDTH, TFT_HEIGHT);
    framebuffer.createPalette();
    for (uint8_t i = 0; i < 7; ++i) {
        uint16_t c = pgm_read_word(PALETTE + i);
        c = ((c & 0xff) << 8) | (c >> 8);
        framebuffer.setPaletteColor(i, (c & 0xf800) >> 8, (c & 0x7e0) >> 3, (c & 0x1f) << 3);
    }

    framebuffer.clear();
    framebuffer.pushImage(56, 56, 16, 16, COIN, 0x6);
    framebuffer.pushSprite(0, 0);

}

void loop() {

    espboy.update();

}

The rendering is consistent on the screen.

However, with a 4-bit depth image, it would be more advantageous to group 2 adjacent pixels on a single byte... but I can't find how to do it with LovyanGFX. I haven't found an example that shows how to do this.

The idea would be to have a colormap coded like this:

uint8_t const constexpr COIN[] PROGMEM = {

    0x66, 0x66, 0x66, 0x11, 0x11, 0x66, 0x66, 0x66,
    0x66, 0x66, 0x11, 0x44, 0x44, 0x11, 0x66, 0x66,
    0x66, 0x61, 0x44, 0x25, 0x55, 0x33, 0x16, 0x66,
    0x66, 0x14, 0x22, 0x55, 0x55, 0x55, 0x31, 0x66,
    0x61, 0x42, 0x55, 0x33, 0x33, 0x55, 0x53, 0x16,
    0x61, 0x42, 0x53, 0x44, 0x44, 0x35, 0x53, 0x16,
    0x14, 0x25, 0x34, 0x44, 0x45, 0x54, 0x55, 0x31,
    0x14, 0x55, 0x34, 0x44, 0x55, 0x54, 0x55, 0x31,
    0x14, 0x55, 0x34, 0x45, 0x55, 0x24, 0x55, 0x31,
    0x14, 0x55, 0x34, 0x55, 0x52, 0x24, 0x55, 0x31,
    0x61, 0x45, 0x53, 0x55, 0x22, 0x45, 0x53, 0x16,
    0x61, 0x45, 0x55, 0x44, 0x44, 0x55, 0x53, 0x16,
    0x66, 0x14, 0x55, 0x55, 0x55, 0x55, 0x31, 0x66,
    0x66, 0x61, 0x44, 0x55, 0x55, 0x33, 0x16, 0x66,
    0x66, 0x66, 0x11, 0x44, 0x44, 0x11, 0x66, 0x66,
    0x66, 0x66, 0x66, 0x11, 0x11, 0x66, 0x66, 0x66

};

Is it possible?

Thanks for your time @lovyan03.

lovyan03 commented 1 year ago
#define LGFX_AUTODETECT
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <LGFX_AUTODETECT.hpp>

LGFX lcd;

lgfx::swap565_t const constexpr PALETTE[] PROGMEM = {

    0xa719, // #1C363E => 0x0
    0x0000, // #000000 => 0x1
    0xffff, // #FFFFFF => 0x2
    0x80b3, // #B67000 => 0x3
    0xa0fd, // #FFB400 => 0x4
    0x2aff, // #FFE456 => 0x5
    0xe007  // #00FF00 => 0x6 (transparency color)

};

uint8_t const constexpr COIN[] PROGMEM = {

    0x66, 0x66, 0x66, 0x11, 0x11, 0x66, 0x66, 0x66,
    0x66, 0x66, 0x11, 0x44, 0x44, 0x11, 0x66, 0x66,
    0x66, 0x61, 0x44, 0x25, 0x55, 0x33, 0x16, 0x66,
    0x66, 0x14, 0x22, 0x55, 0x55, 0x55, 0x31, 0x66,
    0x61, 0x42, 0x55, 0x33, 0x33, 0x55, 0x53, 0x16,
    0x61, 0x42, 0x53, 0x44, 0x44, 0x35, 0x53, 0x16,
    0x14, 0x25, 0x34, 0x44, 0x45, 0x54, 0x55, 0x31,
    0x14, 0x55, 0x34, 0x44, 0x55, 0x54, 0x55, 0x31,
    0x14, 0x55, 0x34, 0x45, 0x55, 0x24, 0x55, 0x31,
    0x14, 0x55, 0x34, 0x55, 0x52, 0x24, 0x55, 0x31,
    0x61, 0x45, 0x53, 0x55, 0x22, 0x45, 0x53, 0x16,
    0x61, 0x45, 0x55, 0x44, 0x44, 0x55, 0x53, 0x16,
    0x66, 0x14, 0x55, 0x55, 0x55, 0x55, 0x31, 0x66,
    0x66, 0x61, 0x44, 0x55, 0x55, 0x33, 0x16, 0x66,
    0x66, 0x66, 0x11, 0x44, 0x44, 0x11, 0x66, 0x66,
    0x66, 0x66, 0x66, 0x11, 0x11, 0x66, 0x66, 0x66
};

void setup()
{
  lcd.init();
}

void loop() {

  lgfx::delay(10);

  lcd.pushImage(rand() & 127, rand() & 127, 16, 16, COIN, 6, lgfx::color_depth_t::palette_4bit, PALETTE);
}
ESPboy-edu commented 1 year ago

Yeah, so it's about lgfx::color_depth_t::palette_4bit! Еhanks a lot for the tip! A delightful library, very detailed. But it's already pretty big and I don't have the knowledge to figure out in detail how everything works. Great work on your part and very helpful.

m1cr0lab commented 1 year ago

Thank you so much @lovyan03. ❤️

It was really nice of you to take the time to provide an example. LovyanGFX is by far the best graphics library!