lexus2k / lcdgfx

Driver for LCD displays running on Arduino/Avr/ESP32/Linux (including Rasperry) platforms
MIT License
356 stars 51 forks source link

Rotate SH1107 to landscape #72

Open guhland opened 2 years ago

guhland commented 2 years ago

I want to rotate the display 90 degrees.

https://github.com/lexus2k/lcdgfx/issues/48

This issue is about the same topic. It mentions that I could use canvas and write my own method to rotate the display. How would I go about doing that?

There has to be a way to do this.

Pedrohpcavalcante commented 2 years ago

I have the same problem

lexus2k commented 2 years ago

The 1-bit canvas has the associated buffer with it, where pixels are represented the same way, as described in display controllers datasheets.

For example, if canvas size is 128x64, then each row consists of 128 bytes and each byte row describes 8 rows of pixels on the display. Because each byte is 8 vertical pixels (1 bit per pixel). This is done for speed.

So, the address of each pixel can be described as:

buffer_index = x + (y / 8) * width;
bit_index = y & 0x07;

In your case, since you want to have vertical orientation you need have 1bitx64x128 canvas. Then you draw what you need in the canvas, and the rotate the content. The rotation must result in the different buffer 1bitx128x64. So, you need something like this:

new_width = height;
new_height = width;
for(x=0; x<64; x++)
    for(y=0; y<128; y++)
    {
        uint8_t pixel = (m_buffer[x + (y / 8) * width] >> (y & 0x07)) & 0x01; // width is 64 here
        new_buffer[y + (x / 8) * new_width] |= pixel << (x & 0x07); // width is 128 here
    }

Another way is to rewrite all display 1-bit functions - a bit time consuming development.

guhland commented 2 years ago

Okay I sort of follow what is going on here. I am not 100 percent on it, but it looks like you are getting the pixel from the buffer and then writing it to a new location in a new buffer. Here is what I wrote for a rotate function for in canvas.cpp:

template <> void NanoCanvasOps<1>::rotate()
{
    lcdint_t new_width = height();
    lcdint_t new_height = width();
    uint8_t new_buffer[new_width * new_height * 1];
    for ( int x = 0; x < 64; x++ )
    {
        for ( int y = 0; y < 128; y++ )
        {
            uint8_t pixel = (m_buf[x + (y / 8) * width()] >> (y & 0x07)) & 0x01; // width is 64 here
            new_buffer[y + (x / 8) * new_width] |= pixel << (x & 0x07);          // width is 128 here
        }
    }
m_buf = new_buffer;
}

This does not work. What am I doing wrong? Let me know if you need more info or a picture...

lexus2k commented 2 years ago

The code doesn't work because you assign a pointer to the locally allocated array: new_buffer is defined inside the function.

lexus2k commented 2 years ago

I added enhancement label. Once I have free time, this feature will be implemented.

lexus2k commented 2 years ago

Hi @guhland

Good news that I implemented rotate method for 1-bit displays.

NanoCanvas<64,32,1> canvas;
NanoCanvas<32,64,1> rotatedCanvas;

void setup()
{
    /* Initialize and clear display */
    display.begin();
    display.clear();
    canvas.setMode( CANVAS_MODE_TRANSPARENT );
    canvas.clear();
    canvas.drawBitmap1( 5, 8, 8, 8, heartImage );
    canvas.drawRect(0,0,63,31);
    canvas.rotateCW(rotatedCanvas);
}

void loop()
{
    lcd_delay(40);

    /* Now, draw original canvas on the display */
    display.drawCanvas( 0, 0, canvas );
    /* Now, draw rotated canvas on the display */
    display.drawCanvas( 80, 0, rotatedCanvas );
}

The band news is that such rotation requires 2X SRAM memory. But as long as you're using powerful microcontrollers that doesn't matter.

guhland commented 2 years ago

Thanks a bunch for your support. I had something working similarly with the pseudocode you provided before. This is working better. However, I have a issue a ran into before where the only the rotated "left" half of the screen will print anything. I am using a 64x128 display. Let me know if you need more info.

lexus2k commented 2 years ago

Hi

It would be nice to have full sketch example (as simple as possible), that doesn't work in your case. So I can run tests and figure out what's wrong.

guhland commented 2 years ago
#include "lcdgfx.h"

NanoCanvas<64,128,1> canvas;
NanoCanvas<128,64,1> rotatedCanvas;

int main(){

    /* Initialize and clear display */
    DisplaySH1107_64x128_I2C display(-1,{-1, 0x3C, -1, -1 , 0});
    display.begin();
    display.clear();
    canvas.setMode( CANVAS_MODE_TRANSPARENT );
    canvas.clear();
    canvas.setFixedFont(ssd1306xled_font5x7);
    canvas.printFixed(5,5,"test1!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    canvas.rotateCW(rotatedCanvas);
    lcd_delay(40);

    /* Now, draw original canvas on the display */
    //display.drawCanvas( 0, 0, canvas );
    /* Now, draw rotated canvas on the display */
    display.drawCanvas( 63, 0, rotatedCanvas );

    display.end();
    return 0;

}

The added "!" are just to make sure it would normally fit the entire screen.

lexus2k commented 2 years ago

Hi,

I slightly modified your example to run in Arduino IDE. Please, find my comments below

#include "lcdgfx.h"

DisplaySH1107_64x128_I2C display(-1,{-1, 0x3C, -1, -1 , 0});

NanoCanvas<128,64,1> canvas; // <<< Change (64,128) to (128,64) as you would like to have it in landscape mode
NanoCanvas<64,128,1> rotatedCanvas; // <<< Change (128,64) to (64,128) as you need to rotate canvas before displaying

void setup()
{
    /* Initialize and clear display */
    display.begin();
    display.clear();
    canvas.setMode( CANVAS_MODE_TRANSPARENT | CANVAS_TEXT_WRAP_LOCAL ); // <<< Set text wrap local mode
    canvas.clear();
    canvas.setFixedFont(ssd1306xled_font5x7);
    canvas.printFixed(5,5,"test1!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    canvas.rotateCW(rotatedCanvas);
}

void loop()
{
    lcd_delay(40);
    /* Now, draw original canvas on the display */
    //display.drawCanvas( 0, 0, canvas );
    /* Now, draw rotated canvas on the display */
    display.drawCanvas( 0, 0, rotatedCanvas ); // <<< change (63,0) to (0,0) since you need to draw canvas on the entire display without any shift
}
guhland commented 2 years ago

So following what you wrote here. I am still only able to print to half of the screen. Only this time being the top half instead of the right. However, if I remove the offset like you suggested, Nothing prints. Right now I am at a offset of 95.... Thoughts?

lexus2k commented 2 years ago

This is how it looks like if to run the example provided by you: image

This is how the display looks like, if to apply my recommendations: image

If the issue relates to some specific hardware you have, I need much more information on the setup, including the display hardware part number, datasheet for your display, CPU/MCU hardware part number (maybe board description), etc.

guhland commented 2 years ago

I am using a raspberry pi 4 4GB right now. I have plans to use the display with other things. Testing with the pi right now. I am able to get your second image. However, I cannot print to the bottom half. oledissue

lexus2k commented 2 years ago

изображение

If the issue relates to some specific hardware you have, I need much more information on the setup, including the display hardware part number, datasheet for your display, CPU/MCU hardware part number (maybe board description), etc.

Without details on your setup I can do nothing. I know that you have Raspberry Pi, and since it's 64-bit, the data types, used by the library is not the issue in your case.