ThingPulse / esp8266-oled-ssd1306

Driver for the SSD1306 and SH1106 based 128x64, 128x32, 64x48 pixel OLED display running on ESP8266/ESP32
https://thingpulse.com
Other
2.01k stars 638 forks source link

String in negative #372

Open dobraMorda opened 2 years ago

dobraMorda commented 2 years ago

Hello!

Did you think anytime about add new feature to this library which can make draw string in negative like in below picture? bez nazwy I think it will be great feature to making multi menus with scrolling screen to easy showing selected position in menu.I think it should be method like drawNegativeString(x, y, message). Maybe I can help with that for you?

Best Regards!

dobraMorda commented 2 years ago

I made this feature in my local code. To avoid potential damage to the rest of the code, I wrote two separate methods. If someone want to use this feature you can add below methods to OLEDDisplay.cpp file:

void OLEDDisplay::drawStringInternalNegative( int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth ) {
    uint8_t textHeight       = pgm_read_byte( fontData + HEIGHT_POS );
    uint8_t firstChar        = pgm_read_byte( fontData + FIRST_CHAR_POS );
    uint16_t sizeOfJumpTable = pgm_read_byte( fontData + CHAR_NUM_POS ) * JUMPTABLE_BYTES;

    uint16_t cursorX = 0;
    uint16_t cursorY = 0;

    switch ( textAlignment ) {
        case TEXT_ALIGN_CENTER_BOTH:
            yMove -= textHeight >> 1;
        // Fallthrough
        case TEXT_ALIGN_CENTER:
            xMove -= textWidth >> 1;  // divide by 2
            break;
        case TEXT_ALIGN_RIGHT:
            xMove -= textWidth;
            break;
        case TEXT_ALIGN_LEFT:
            break;
    }

    // Don't draw anything if it is not on the screen.
    if ( xMove + textWidth < 0 || xMove > this->width( ) ) {
        return;
    }
    if ( yMove + textHeight < 0 || yMove > this->width( ) ) {
        return;
    }

    // inverse color again
    setColor( WHITE );

    // fill rect under text
    fillRect( xMove, yMove + 2, textWidth, textHeight - 4 );

    // inverse color
    setColor( BLACK );

    for ( uint16_t j = 0; j < textLength; j++ ) {
        int16_t xPos = xMove + cursorX;
        int16_t yPos = yMove + cursorY;

        uint8_t code = text[ j ];
        if ( code >= firstChar ) {
            uint8_t charCode = code - firstChar;

            // 4 Bytes per char code
            uint8_t msbJumpToChar    = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES );                    // MSB  \ JumpAddress
            uint8_t lsbJumpToChar    = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB );    // LSB /
            uint8_t charByteSize     = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE );   // Size
            uint8_t currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH );  // Width

            // Test if the char is drawable
            if ( !( msbJumpToChar == 255 && lsbJumpToChar == 255 ) ) {
                // Get the position of the char data
                uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ( ( msbJumpToChar << 8 ) + lsbJumpToChar );
                drawInternal( xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize );
            }

            cursorX += currentCharWidth;
        }
    }

    // inverse color again to back to normal mode
    setColor( WHITE );
}

void OLEDDisplay::drawStringNegative( int16_t xMove, int16_t yMove, const String& strUser ) {
    uint16_t lineHeight = pgm_read_byte( fontData + HEIGHT_POS );

    // char* text must be freed!
    char* text = utf8ascii( strUser );

    uint16_t yOffset = 0;
    // If the string should be centered vertically too
    // we need to now how heigh the string is.
    if ( textAlignment == TEXT_ALIGN_CENTER_BOTH ) {
        uint16_t lb = 0;
        // Find number of linebreaks in text
        for ( uint16_t i = 0; text[ i ] != 0; i++ ) {
            lb += ( text[ i ] == 10 );
        }
        // Calculate center
        yOffset = ( lb * lineHeight ) / 2;
    }

    uint16_t line  = 0;
    char* textPart = strtok( text, "\n" );
    while ( textPart != NULL ) {
        uint16_t length = strlen( textPart );
        drawStringInternalNegative( xMove, yMove - yOffset + ( line++ ) * lineHeight, textPart, length, getStringWidth( textPart, length ) );
        textPart = strtok( NULL, "\n" );
    }

    free( text );
}

And then you need to add declare of methods in .h file: in public section: void drawStringNegative( int16_t xMove, int16_t yMove, const String& strUser ); in protected section: void drawStringInternalNegative( int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth );

@marcelstoer If You accept this feature and the code I wrote is correct for you, feel free to add it to your library!

marcelstoer commented 2 years ago

Thanks for the proposal and your willingness to help the community. I like the idea, it sounds sane. However, I am not happy with the proposed implementation for two primary reasons:

Alternative I:

Alternative II: