You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
atmega-geiger/lib/libssd1306/src/nano_engine/canvas.cpp

1477 lines
38 KiB

/*
MIT License
Copyright (c) 2018-2020, Alexey Dynda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "canvas.h"
#include "lcd/lcd_common.h"
#include "ssd1306.h"
extern const uint8_t *s_font6x8;
extern "C" SFixedFontInfo s_fixedFont;
#ifdef CONFIG_SSD1306_UNICODE_ENABLE
extern "C" uint8_t g_ssd1306_unicode;
#endif
/////////////////////////////////////////////////////////////////////////////////
//
// COMMON GRAPHICS
//
/////////////////////////////////////////////////////////////////////////////////
template <uint8_t BPP>
void NanoCanvasOps<BPP>::putPixel(const NanoPoint &p)
{
putPixel(p.x, p.y);
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::drawRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
{
drawHLine(x1, y1, x2);
drawHLine(x1, y2, x2);
drawVLine(x1, y1, y2);
drawVLine(x2, y1, y2);
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::drawRect(const NanoRect &rect)
{
drawRect(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::drawLine(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
{
lcduint_t dx = x1 > x2 ? (x1 - x2): (x2 - x1);
lcduint_t dy = y1 > y2 ? (y1 - y2): (y2 - y1);
lcduint_t err = 0;
if (dy > dx)
{
if (y1 > y2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
ssd1306_swap_data(y1, y2, lcdint_t);
}
for(; y1<=y2; y1++)
{
err += dx;
if (err >= dy)
{
err -= dy;
x1 < x2 ? x1++: x1--;
}
putPixel( x1, y1 );
}
}
else
{
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
ssd1306_swap_data(y1, y2, lcdint_t);
}
for(; x1<=x2; x1++)
{
err += dy;
if (err >= dx)
{
err -= dx;
if (y1 < y2) y1++; else y1--;
}
putPixel( x1, y1 );
}
}
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::drawLine(const NanoRect &rect)
{
drawLine(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::fillRect(const NanoRect &rect)
{
fillRect(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
}
template <uint8_t BPP>
uint8_t NanoCanvasOps<BPP>::printChar(uint8_t c)
{
uint16_t unicode = ssd1306_unicode16FromUtf8(c);
if (unicode == SSD1306_MORE_CHARS_REQUIRED) return 0;
SCharInfo char_info;
ssd1306_getCharBitmap(unicode, &char_info);
uint8_t mode = m_textMode;
for (uint8_t i = 0; i<(m_fontStyle == STYLE_BOLD ? 2: 1); i++)
{
drawBitmap1(m_cursorX + i,
m_cursorY,
char_info.width,
char_info.height,
char_info.glyph );
m_textMode |= CANVAS_MODE_TRANSPARENT;
}
m_textMode = mode;
m_cursorX += (lcdint_t)(char_info.width + char_info.spacing);
if ( ( (m_textMode & CANVAS_TEXT_WRAP_LOCAL) && (m_cursorX > ((lcdint_t)m_w - (lcdint_t)s_fixedFont.h.width) ) )
|| ( (m_textMode & CANVAS_TEXT_WRAP) && (m_cursorX > ((lcdint_t)ssd1306_lcd.width - (lcdint_t)s_fixedFont.h.width)) ) )
{
m_cursorY += (lcdint_t)s_fixedFont.h.height;
m_cursorX = 0;
if ( (m_textMode & CANVAS_TEXT_WRAP_LOCAL) && (m_cursorY > ((lcdint_t)m_h - (lcdint_t)s_fixedFont.h.height)) )
{
m_cursorY = 0;
}
}
return 1;
}
template <uint8_t BPP>
size_t NanoCanvasOps<BPP>::write(uint8_t c)
{
if (c == '\n')
{
m_cursorY += (lcdint_t)s_fixedFont.h.height;
m_cursorX = 0;
}
else if (c == '\r')
{
// skip non-printed char
}
else
{
return printChar( c );
}
return 1;
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::printFixed(lcdint_t xpos, lcdint_t y, const char *ch, EFontStyle style)
{
m_fontStyle = style;
m_cursorX = xpos;
m_cursorY = y;
while (*ch)
{
write(*ch);
ch++;
}
}
template <uint8_t BPP>
void NanoCanvasOps<BPP>::printFixedPgm(lcdint_t xpos, lcdint_t y, const char *ch, EFontStyle style)
{
m_fontStyle = style;
m_cursorX = xpos;
m_cursorY = y;
for (;;)
{
char c = pgm_read_byte(ch);
if (!c) break;
write(c);
ch++;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// 1-BIT GRAPHICS
//
/////////////////////////////////////////////////////////////////////////////////
#ifdef CONFIG_MULTIPLICATION_NOT_SUPPORTED
#define YADDR1(y) (static_cast<uint16_t>((y) >> 3) << m_p)
#define BANK_ADDR1(b) ((b) << m_p)
#else
#define YADDR1(y) (static_cast<uint16_t>((y) >> 3) * m_w)
#define BANK_ADDR1(b) ((b) * m_w)
#endif
template <>
void NanoCanvasOps<1>::putPixel(lcdint_t x, lcdint_t y)
{
x -= offset.x;
y -= offset.y;
if ((x<0) || (y<0)) return;
if (( x >= (lcdint_t)m_w ) || ( y >= (lcdint_t)m_h)) return;
if (m_color)
{
m_buf[YADDR1(y) + x] |= (1 << (y & 0x7));
}
else
{
m_buf[YADDR1(y) + x] &= ~(1 << (y & 0x7));
}
}
template <>
void NanoCanvasOps<1>::drawHLine(lcdint_t x1, lcdint_t y1, lcdint_t x2)
{
if (x2 < x1) ssd1306_swap_data(x2, x1, lcdint_t);
x1 -= offset.x;
x2 -= offset.x;
y1 -= offset.y;
if ((y1 >= (lcdint_t)m_h) || (y1 < 0)) return;
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
x1 = max(0, x1);
x2 = min(x2, (lcdint_t)(m_w -1));
uint16_t addr = YADDR1(y1) + x1;
uint8_t mask = (1 << (y1 & 0x7));
if (m_color)
{
do { m_buf[addr++] |= mask; } while (x2>x1++);
}
else
{
do { m_buf[addr++] &= ~mask; } while (x2>x1++);
}
}
template <>
void NanoCanvasOps<1>::drawVLine(lcdint_t x1, lcdint_t y1, lcdint_t y2)
{
if (y2 < y1) ssd1306_swap_data(y2, y1, lcdint_t);
x1 -= offset.x;
y1 -= offset.y;
y2 -= offset.y;
if ((x1 >= (lcdint_t)m_w) || (x1 < 0)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
y1 = max(0, y1);
y2 = min(y2, (lcdint_t)(m_h -1));
uint16_t addr = YADDR1(y1) + x1;
if ((y1 & 0xFFF8) == (y2 & 0xFFF8))
{
uint8_t mask = ((0xFF >> (0x07 + y1 - y2)) << (y1 & 0x07));
if (m_color)
m_buf[addr] |= mask;
else
m_buf[addr] &= ~mask;
return;
}
if (m_color)
{
m_buf[addr] |= (0xFF << (y1 & 0x07));
addr += m_w;
while (addr<YADDR1(y2) + x1)
{
m_buf[addr] |= 0xFF;
addr += m_w;
}
m_buf[addr] |= (0xFF >> (0x07 - (y2 & 0x07)));
}
else
{
m_buf[addr] &= ~(0xFF << (y1 & 0x07));
addr += m_w;
while (addr<YADDR1(y2) + x1)
{
m_buf[addr] &= 0;
addr += m_w;
}
m_buf[addr] &= ~(0xFF >> (0x07 - (y2 & 0x07)));
}
}
template <>
void NanoCanvasOps<1>::fillRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
{
if (x2 < x1) ssd1306_swap_data(x2, x1, lcdint_t);
if (y2 < y1) ssd1306_swap_data(y2, y1, lcdint_t);
x1 -= offset.x;
x2 -= offset.x;
y1 -= offset.y;
y2 -= offset.y;
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(0, x1);
x2 = min(x2, (lcdint_t)(m_w - 1));
y1 = max(0, y1);
y2 = min(y2, (lcdint_t)(m_h - 1));
uint8_t bank1 = (y1 >> 3);
uint8_t bank2 = (y2 >> 3);
for (uint8_t bank = bank1; bank<=bank2; bank++)
{
uint8_t mask = 0xFF;
if (bank1 == bank2)
{
mask = (mask >> ((y1 & 7) + 7 - (y2 & 7))) << (y1 & 7);
}
else if (bank1 == bank)
{
mask = (mask << (y1 & 7));
}
else if (bank2 == bank)
{
mask = (mask >> (7 - (y2 & 7)));
}
for (uint8_t x=x1; x<=x2; x++)
{
if (m_color)
{
m_buf[BANK_ADDR1(bank) + x] |= mask;
}
else
{
m_buf[BANK_ADDR1(bank) + x] &= ~mask;
}
}
}
};
template <>
void NanoCanvasOps<1>::clear()
{
memset(m_buf, 0, YADDR1(m_h));
}
// TODO: Not so fast implementation. needs to be optimized
template <>
void NanoCanvasOps<1>::drawBitmap1(lcdint_t x, lcdint_t y, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
x -= offset.x;
y -= offset.y;
lcduint_t origin_width = w;
uint8_t offs = y & 0x07;
uint8_t complexFlag = 0;
uint8_t mainFlag = 1;
if (y + (lcdint_t)h <= 0) return;
if (y >= (lcdint_t)m_h) return;
if ((int16_t)x + (int16_t)w <= 0) return;
if ((int16_t)x >= (int16_t)m_w) return;
if (y < 0)
{
bitmap += ((lcduint_t)((-y) + 7) >> 3) * w;
h += y;
y = 0;
complexFlag = 1;
}
if (x < 0)
{
bitmap += -x;
w += x;
x = 0;
}
uint8_t max_pages = (lcduint_t)(h + 15 - offs) >> 3;
if ((lcduint_t)(y + (lcdint_t)h) > (lcduint_t)m_h)
{
h = (lcduint_t)(m_h - (lcduint_t)y);
}
if ((lcduint_t)(x + (lcdint_t)w) > (lcduint_t)m_w)
{
w = (lcduint_t)(m_w - (lcduint_t)x);
}
uint8_t pages = ((y + h - 1) >> 3) - (y >> 3) + 1;
uint8_t j;
lcduint_t i;
for(j=0; j < pages; j++)
{
uint16_t addr = YADDR1(y + ((uint16_t)j<<3)) + x;
if ( j == max_pages - 1 ) mainFlag = !offs;
for( i=w; i > 0; i--)
{
uint8_t data = 0;
uint8_t mask = 0;
if ( mainFlag ) { data |= (pgm_read_byte(bitmap) << offs); mask |= (0xFF << offs); }
if ( complexFlag ) { data |= (pgm_read_byte(bitmap - origin_width) >> (8 - offs)); mask |= (0xFF >> (8 - offs)); }
if (CANVAS_MODE_TRANSPARENT != (m_textMode & CANVAS_MODE_TRANSPARENT))
{
m_buf[addr] &= ~mask;
m_buf[addr] |= m_color == BLACK ? ~data: data;
}
else
{
if (m_color == BLACK)
m_buf[addr] &= ~data;
else
m_buf[addr] |= data;
}
bitmap++;
addr++;
}
bitmap += origin_width - w;
complexFlag = offs;
}
}
template <>
void NanoCanvasOps<1>::drawXBitmap1(lcdint_t x, lcdint_t y, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
x -= offset.x;
y -= offset.y;
lcduint_t origin_width = w;
if (y + (lcdint_t)h <= 0) return;
if (y >= (lcdint_t)m_h) return;
if (x + (lcdint_t)w <= 0) return;
if (x >= (lcdint_t)m_w) return;
delay(100);
uint8_t start_bit = 0;
lcduint_t pitch_delta = 0;
if (y < 0)
{
bitmap += /*pitch*/((origin_width + 7) >> 3) * (lcduint_t)(-y);
h += y;
y = 0;
}
if (x < 0)
{
bitmap += ((lcduint_t)(-x)) / 8;
start_bit = ((lcduint_t)(-x)) & 0x07;
w += x;
x = 0;
}
if ((lcduint_t)(y + (lcdint_t)h) > (lcduint_t)m_h)
{
h = (lcduint_t)(m_h - (lcduint_t)y);
}
if ((lcduint_t)(x + (lcdint_t)w) > (lcduint_t)m_w)
{
w = (lcduint_t)(m_w - (lcduint_t)x);
}
pitch_delta = ((origin_width + 7 - start_bit) >> 3) - ((w + 7) >> 3);
for(lcduint_t j = 0; j < h; j++)
{
uint8_t bit = start_bit;
for(lcduint_t i = 0; i < w; i++)
{
uint8_t data = 0;
data = (pgm_read_byte(bitmap) >> bit) & 0x01;
if (data)
{
if (m_color == BLACK)
m_buf[YADDR1(y + j) + (x + i)] &= ~(1 << ((y + j) & 0x7));
else
m_buf[YADDR1(y + j) + (x + i)] |= (1 << ((y + j) & 0x7));
}
else if (CANVAS_MODE_TRANSPARENT != (m_textMode & CANVAS_MODE_TRANSPARENT))
{
if (m_color == BLACK)
m_buf[YADDR1(y + j) + (x + i)] |= (1 << ((y + j) & 0x7));
else
m_buf[YADDR1(y + j) + (x + i)] &= ~(1 << ((y + j) & 0x7));
}
bit++;
if (bit >= 8)
{
bitmap++;
bit=0;
}
}
if (bit)
{
bitmap++;
}
bitmap += pitch_delta;
}
}
template <>
void NanoCanvasOps<1>::begin(lcdint_t w, lcdint_t h, uint8_t *bytes)
{
m_w = w;
m_h = h;
offset.x = 0;
offset.y = 0;
m_cursorX = 0;
m_cursorY = 0;
m_color = WHITE;
m_textMode = 0;
m_p = 3;
while (w >> (m_p+1)) { m_p++; };
m_buf = bytes;
clear();
}
// NANO CANVAS 1
void NanoCanvas1::blt(lcdint_t x, lcdint_t y)
{
ssd1306_drawBufferFast(x, y, m_w, m_h, m_buf);
}
void NanoCanvas1::blt()
{
ssd1306_drawBufferFast(offset.x, offset.y, m_w, m_h, m_buf);
}
void NanoCanvas1::blt(const NanoRect &rect)
{
// TODO: NOT IMPLEMENTED
}
// NANO CANVAS 1_8
void NanoCanvas1_8::blt(lcdint_t x, lcdint_t y)
{
ssd1306_drawMonoBuffer8(x, y, m_w, m_h, m_buf);
}
void NanoCanvas1_8::blt()
{
ssd1306_drawMonoBuffer8(offset.x, offset.y, m_w, m_h, m_buf);
}
void NanoCanvas1_8::blt(const NanoRect &rect)
{
// TODO: NOT IMPLEMENTED
}
// NANO CANVAS 1_16
void NanoCanvas1_16::blt(lcdint_t x, lcdint_t y)
{
ssd1306_drawMonoBuffer16(x, y, m_w, m_h, m_buf);
}
void NanoCanvas1_16::blt()
{
ssd1306_drawMonoBuffer16(offset.x, offset.y, m_w, m_h, m_buf);
}
void NanoCanvas1_16::blt(const NanoRect &rect)
{
// TODO: NOT IMPLEMENTED
}
/////////////////////////////////////////////////////////////////////////////////
//
// 4-BIT GRAY GRAPHICS
//
/////////////////////////////////////////////////////////////////////////////////
/* We need to use multiply operation, because there are displays on the market *
* with resolution different from 2^N (160x128, 96x64, etc.) */
#define YADDR4(y) (static_cast<uint32_t>(y) * m_w / 2)
#define BITS_SHIFT4(x) ((x & 1) ? 4 : 0)
template <>
void NanoCanvasOps<4>::putPixel(lcdint_t x, lcdint_t y)
{
x -= offset.x;
y -= offset.y;
if ((x >= 0) && (y >= 0) && (x < (lcdint_t)m_w) && (y < (lcdint_t)m_h))
{
m_buf[YADDR4(y) + x / 2] &= ~(0x0F << BITS_SHIFT4(x));
m_buf[YADDR4(y) + x / 2] |= (m_color & 0x0F) << BITS_SHIFT4(x);
}
}
template <>
void NanoCanvasOps<4>::drawVLine(lcdint_t x1, lcdint_t y1, lcdint_t y2)
{
x1 -= offset.x;
y1 -= offset.y;
y2 -= offset.y;
if (y1 > y2)
{
ssd1306_swap_data(y1, y2, lcdint_t);
}
if ((x1 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
y1 = max(y1,0);
y2 = min(y2,(lcdint_t)m_h-1) - y1;
uint8_t *buf = m_buf + YADDR4(y1) + x1 / 2;
do
{
*buf &= ~(0x0F << BITS_SHIFT4(x1));
*buf |= ((m_color & 0x0F) << BITS_SHIFT4(x1));
buf += m_w / 2;
}
while (y2--);
}
template <>
void NanoCanvasOps<4>::drawHLine(lcdint_t x1, lcdint_t y1, lcdint_t x2)
{
x1 -= offset.x;
y1 -= offset.y;
x2 -= offset.x;
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
}
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y1 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(x1,0);
x2 = min(x2,(lcdint_t)m_w-1);
uint8_t *buf = m_buf + YADDR4(y1) + x1 / 2;
for (lcdint_t x = x1; x <= x2; x++)
{
*buf &= ~(0x0F << BITS_SHIFT4(x));
*buf |= ((m_color & 0x0F) << BITS_SHIFT4(x));
if ( x & 1 )
{
buf++;
}
}
}
template <>
void NanoCanvasOps<4>::fillRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
{
if (y1 > y2)
{
ssd1306_swap_data(y1, y2, lcdint_t);
}
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
}
x1 -= offset.x;
y1 -= offset.y;
x2 -= offset.x;
y2 -= offset.y;
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(x1,0);
x2 = min(x2,(lcdint_t)m_w-1);
y1 = max(y1,0);
y2 = min(y2,(lcdint_t)m_h-1);
uint8_t *buf = m_buf + YADDR4(y1) + x1 / 2;
for (lcdint_t y = y1; y <= y2; y++)
{
for (lcdint_t x = x1; x <= x2; x++)
{
*buf &= ~(0x0F << BITS_SHIFT4(x));
*buf |= ((m_color & 0xF) << BITS_SHIFT4(x));
if ( x & 1 )
{
buf++;
}
}
buf += ((lcdint_t)(m_w) - (x2 - x1 + ((x2 - x1) & 0x1))) / 2;
}
}
template <>
void NanoCanvasOps<4>::drawBitmap1(lcdint_t xpos, lcdint_t ypos, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
lcdint_t xb1 = 0;
lcdint_t yb1 = 0;
lcdint_t xb2 = (lcdint_t)w - 1;
lcdint_t yb2 = (lcdint_t)h - 1;
/* calculate char rectangle */
lcdint_t x1 = xpos - offset.x;
lcdint_t y1 = ypos - offset.y;
lcdint_t x2 = x1 + xb2;
lcdint_t y2 = y1 + yb2;
/* clip bitmap */
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
if (x1 < 0)
{
xb1 -= x1;
x1 = 0;
}
if (y1 < 0)
{
yb1 -= y1;
y1 = 0;
}
if (y2 >= (lcdint_t)m_h)
{
// yb2 -= (y2 - (lcdint_t)m_h + 1);
y2 = (lcdint_t)m_h - 1;
}
if (x2 >= (lcdint_t)m_w)
{
// xb2 -= (x2 - (lcdint_t)m_w + 1);
x2 = (lcdint_t)m_w - 1;
}
for ( lcdint_t y = y1; y <= y2; y++ )
{
for ( lcdint_t x = x1; x <= x2; x++ )
{
uint16_t src_addr1 = xb1 + x - x1 + ((yb1 + y - y1) / 8) * w;
uint8_t src_bit1 = (yb1 + y - y1) & 0x07;
uint8_t data = pgm_read_byte( &bitmap[ src_addr1 ] );
uint16_t addr = YADDR4(y) + x / 2;
if (data & (1 << src_bit1))
{
data = m_color & 0x0F;
m_buf[ addr ] &= ~(0x0F << BITS_SHIFT4(x));
m_buf[ addr ] |= data << BITS_SHIFT4(x);
}
else if (!(m_textMode & CANVAS_MODE_TRANSPARENT))
{
data = 0x00;
m_buf[ addr ] &= ~(0x0F << BITS_SHIFT4(x));
}
}
}
}
template <>
void NanoCanvasOps<4>::drawBitmap8(lcdint_t xpos, lcdint_t ypos, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
lcdint_t xb1 = 0;
lcdint_t yb1 = 0;
lcdint_t xb2 = (lcdint_t)w - 1;
lcdint_t yb2 = (lcdint_t)h - 1;
/* calculate char rectangle */
lcdint_t x1 = xpos - offset.x;
lcdint_t y1 = ypos - offset.y;
lcdint_t x2 = x1 + xb2;
lcdint_t y2 = y1 + yb2;
/* clip bitmap */
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
if (x1 < 0)
{
xb1 -= x1;
x1 = 0;
}
if (y1 < 0)
{
yb1 -= y1;
y1 = 0;
}
if (y2 >= (lcdint_t)m_h)
{
//yb2 -= (y2 - (lcdint_t)m_h + 1);
y2 = (lcdint_t)m_h - 1;
}
if (x2 >= (lcdint_t)m_w)
{
//xb2 -= (x2 - (lcdint_t)m_w + 1);
x2 = (lcdint_t)m_w - 1;
}
for ( lcdint_t y = y1; y <= y2; y++ )
{
for ( lcdint_t x = x1; x <= x2; x++ )
{
uint16_t src_addr8 = xb1 + x - x1 + ((yb1 + y - y1)) * w;
uint8_t data = pgm_read_byte( &bitmap[ src_addr8 ] );
uint16_t addr = YADDR4(y) + x / 2;
if ( (data) || (!(m_textMode & CANVAS_MODE_TRANSPARENT)) )
{
data = RGB8_TO_GRAY4(data);
m_buf[ addr ] &= ~(0x0F << BITS_SHIFT4(x));
m_buf[ addr ] |= (data & 0x0F) << BITS_SHIFT4(x);
}
}
}
}
template <>
void NanoCanvasOps<4>::clear()
{
memset(m_buf, 0, YADDR4(m_h));
}
/* This method must be implemented always after clear() */
template <>
void NanoCanvasOps<4>::begin(lcdint_t w, lcdint_t h, uint8_t *bytes)
{
m_w = w;
m_h = h;
offset.x = 0;
offset.y = 0;
m_cursorX = 0;
m_cursorY = 0;
m_color = 0xFF; // white color by default
m_textMode = 0;
m_buf = bytes;
clear();
}
// NANO CANVAS 1_8
void NanoCanvas1_4::blt(lcdint_t x, lcdint_t y)
{
ssd1306_drawBuffer1_4(x, y, m_w, m_h, m_buf);
}
void NanoCanvas1_4::blt()
{
ssd1306_drawBuffer1_4(offset.x, offset.y, m_w, m_h, m_buf);
}
void NanoCanvas1_4::blt(const NanoRect &rect)
{
// TODO: NOT IMPLEMENTED
}
/////////////////////////////////////////////////////////////////////////////////
//
// 8-BIT GRAPHICS
//
/////////////////////////////////////////////////////////////////////////////////
/* We need to use multiply operation, because there are displays on the market *
* with resolution different from 2^N (160x128, 96x64, etc.) */
#define YADDR8(y) (static_cast<uint32_t>(y) * m_w)
template <>
void NanoCanvasOps<8>::putPixel(lcdint_t x, lcdint_t y)
{
x -= offset.x;
y -= offset.y;
if ((x >= 0) && (y >= 0) && (x < (lcdint_t)m_w) && (y < (lcdint_t)m_h))
{
m_buf[YADDR8(y) + x] = m_color;
}
}
template <>
void NanoCanvasOps<8>::drawVLine(lcdint_t x1, lcdint_t y1, lcdint_t y2)
{
x1 -= offset.x;
y1 -= offset.y;
y2 -= offset.y;
if (y1 > y2)
{
ssd1306_swap_data(y1, y2, lcdint_t);
}
if ((x1 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
y1 = max(y1,0);
uint8_t *buf = m_buf + YADDR8(y1) + x1;
y2 = min(y2,(lcdint_t)m_h-1) - y1;
do
{
*buf = m_color;
buf += m_w;
}
while (y2--);
}
template <>
void NanoCanvasOps<8>::drawHLine(lcdint_t x1, lcdint_t y1, lcdint_t x2)
{
x1 -= offset.x;
y1 -= offset.y;
x2 -= offset.x;
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
}
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y1 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(x1,0);
x2 = min(x2,(lcdint_t)m_w-1);
uint8_t *buf = m_buf + YADDR8(y1) + x1;
for (lcdint_t x = 0; x <= x2 - x1; x++)
{
*buf = m_color;
buf++;
}
}
template <>
void NanoCanvasOps<8>::fillRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
{
if (y1 > y2)
{
ssd1306_swap_data(y1, y2, lcdint_t);
}
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
}
x1 -= offset.x;
y1 -= offset.y;
x2 -= offset.x;
y2 -= offset.y;
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(x1,0);
x2 = min(x2,(lcdint_t)m_w-1);
y1 = max(y1,0);
y2 = min(y2,(lcdint_t)m_h-1);
uint8_t *buf = m_buf + YADDR8(y1) + x1;
for (lcdint_t y = y1; y <= y2; y++)
{
for (lcdint_t x = x1; x <= x2; x++)
{
*buf = m_color;
buf++;
}
buf += ((lcdint_t)(m_w) - (x2 - x1 + 1));
}
}
template <>
void NanoCanvasOps<8>::drawBitmap1(lcdint_t xpos, lcdint_t ypos, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
uint8_t offs = 0;
/* calculate char rectangle */
lcdint_t x1 = xpos - offset.x;
lcdint_t y1 = ypos - offset.y;
lcdint_t x2 = x1 + (lcdint_t)w - 1;
lcdint_t y2 = y1 + (lcdint_t)h - 1;
/* clip bitmap */
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
if (x1 < 0)
{
bitmap -= x1;
x1 = 0;
}
if (y1 < 0)
{
bitmap += ((lcduint_t)(-y1) >> 3) * w;
offs = ((-y1) & 0x07);
y1 = 0;
}
if (y2 >= (lcdint_t)m_h)
{
y2 = (lcdint_t)m_h - 1;
}
if (x2 >= (lcdint_t)m_w)
{
x2 = (lcdint_t)m_w - 1;
}
uint8_t offs2 = 8 - offs;
lcdint_t y = y1;
while ( y <= y2)
{
for ( lcdint_t x = x1; x <= x2; x++ )
{
uint8_t data = pgm_read_byte( bitmap );
uint16_t addr = YADDR8(y) + x;
for (uint8_t n = 0; n < min(y2 - y + 1, 8); n++)
{
if ( data & (1<<(n + offs)) )
m_buf[addr] = m_color;
else if (!(m_textMode & CANVAS_MODE_TRANSPARENT))
m_buf[addr] = 0x00;
addr += (lcduint_t)m_w;
}
bitmap++;
}
bitmap += (w - (x2 - x1 + 1));
y = y + offs2;
offs = 0;
offs2 = 8;
}
}
template <>
void NanoCanvasOps<8>::drawXBitmap1(lcdint_t x, lcdint_t y, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
x -= offset.x;
y -= offset.y;
lcduint_t origin_width = w;
if (y + (lcdint_t)h <= 0) return;
if (y >= (lcdint_t)m_h) return;
if (x + (lcdint_t)w <= 0) return;
if (x >= (lcdint_t)m_w) return;
uint8_t start_bit = 0;
lcduint_t pitch_delta = 0;
if (y < 0)
{
bitmap += /*pitch*/((origin_width + 7) >> 3) * (lcduint_t)(-y);
h += y;
y = 0;
}
if (x < 0)
{
bitmap += ((lcduint_t)(-x)) / 8;
start_bit = ((lcduint_t)(-x)) & 0x07;
w += x;
x = 0;
}
if ((lcduint_t)(y + (lcdint_t)h) > (lcduint_t)m_h)
{
h = (lcduint_t)(m_h - (lcduint_t)y);
}
if ((lcduint_t)(x + (lcdint_t)w) > (lcduint_t)m_w)
{
w = (lcduint_t)(m_w - (lcduint_t)x);
}
pitch_delta = ((origin_width + 7 - start_bit) >> 3) - ((w + 7) >> 3);
for(lcduint_t j = 0; j < h; j++)
{
uint8_t bit = start_bit;
for(lcduint_t i = 0; i < w; i++)
{
uint8_t data = 0;
data = (pgm_read_byte(bitmap) >> bit) & 0x01;
if (data)
{
m_buf[YADDR8(y + j) + (x + i)] = m_color;
}
else if (CANVAS_MODE_TRANSPARENT != (m_textMode & CANVAS_MODE_TRANSPARENT))
{
m_buf[YADDR8(y + j) + (x + i)] = 0x00;
}
bit++;
if (bit >= 8)
{
bitmap++;
bit=0;
}
}
if (bit)
{
bitmap++;
}
bitmap += pitch_delta;
}
}
template <>
void NanoCanvasOps<8>::drawBitmap8(lcdint_t xpos, lcdint_t ypos, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
/* calculate char rectangle */
lcdint_t x1 = xpos - offset.x;
lcdint_t y1 = ypos - offset.y;
lcdint_t x2 = x1 + (lcdint_t)w - 1;
lcdint_t y2 = y1 + (lcdint_t)h - 1;
/* clip bitmap */
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
if (x1 < 0)
{
bitmap -= x1;
x1 = 0;
}
if (y1 < 0)
{
bitmap += (lcduint_t)(-y1) * w;
y1 = 0;
}
if (y2 >= (lcdint_t)m_h)
{
y2 = (lcdint_t)m_h - 1;
}
if (x2 >= (lcdint_t)m_w)
{
x2 = (lcdint_t)m_w - 1;
}
lcdint_t y = y1;
while ( y <= y2 )
{
for ( lcdint_t x = x1; x <= x2; x++ )
{
uint8_t data = pgm_read_byte( bitmap );
if ( (data) || (!(m_textMode & CANVAS_MODE_TRANSPARENT)) )
{
m_buf[YADDR8(y) + x] = data;
}
bitmap++;
}
bitmap += (w - (x2 - x1 + 1));
y++;
}
}
template <>
void NanoCanvasOps<8u>::clear()
{
memset(m_buf, 0, YADDR8(m_h));
}
/* This method must be implemented always after clear() */
template <>
void NanoCanvasOps<8>::begin(lcdint_t w, lcdint_t h, uint8_t *bytes)
{
m_w = w;
m_h = h;
offset.x = 0;
offset.y = 0;
m_cursorX = 0;
m_cursorY = 0;
m_color = 0xFF; // white color by default
m_textMode = 0;
m_p = 3;
while (w >> (m_p+1)) { m_p++; };
m_buf = bytes;
clear();
}
// NANO CANVAS 8
void NanoCanvas8::blt(lcdint_t x, lcdint_t y)
{
ssd1306_drawBufferFast8(x, y, m_w, m_h, m_buf);
}
void NanoCanvas8::blt()
{
ssd1306_drawBufferFast8(offset.x, offset.y, m_w, m_h, m_buf);
}
void NanoCanvas8::blt(const NanoRect &rect)
{
ssd1306_drawBufferEx8(offset.x + rect.p1.x,
offset.y + rect.p1.y,
rect.width(),
rect.height(),
m_w,
m_buf + rect.p1.x + rect.p1.y * m_w );
}
/////////////////////////////////////////////////////////////////////////////////
//
// 16-BIT GRAPHICS
//
/////////////////////////////////////////////////////////////////////////////////
/* We need to use multiply operation, because there are displays on the market *
* with resolution different from 2^N (160x128, 96x64, etc.) */
#define YADDR16(y) (static_cast<uint32_t>(y) * (m_w << 1))
template <>
void NanoCanvasOps<16>::putPixel(lcdint_t x, lcdint_t y)
{
x -= offset.x;
y -= offset.y;
if ((x >= 0) && (y >= 0) && (x < (lcdint_t)m_w) && (y < (lcdint_t)m_h))
{
m_buf[YADDR16(y) + (x<<1)] = m_color >> 8;
m_buf[YADDR16(y) + (x<<1) + 1] = m_color & 0xFF;
}
}
template <>
void NanoCanvasOps<16>::drawVLine(lcdint_t x1, lcdint_t y1, lcdint_t y2)
{
x1 -= offset.x;
y1 -= offset.y;
y2 -= offset.y;
if (y1 > y2)
{
ssd1306_swap_data(y1, y2, lcdint_t);
}
if ((x1 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
y1 = max(y1,0);
uint8_t *buf = m_buf + YADDR16(y1) + (x1 << 1);
y2 = min(y2,(lcdint_t)m_h-1) - y1;
do
{
buf[0] = m_color >> 8;
buf[1] = m_color & 0xFF;
buf += (m_w<<1);
}
while (y2--);
}
template <>
void NanoCanvasOps<16>::drawHLine(lcdint_t x1, lcdint_t y1, lcdint_t x2)
{
x1 -= offset.x;
y1 -= offset.y;
x2 -= offset.x;
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
}
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y1 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(x1,0);
x2 = min(x2,(lcdint_t)m_w-1);
uint8_t *buf = m_buf + YADDR16(y1) + (x1<<1);
for (lcdint_t x = 0; x <= x2 - x1; x++)
{
buf[0] = m_color >> 8;
buf[1] = m_color & 0xFF;
buf+=2;
}
}
template <>
void NanoCanvasOps<16>::fillRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
{
if (y1 > y2)
{
ssd1306_swap_data(y1, y2, lcdint_t);
}
if (x1 > x2)
{
ssd1306_swap_data(x1, x2, lcdint_t);
}
x1 -= offset.x;
y1 -= offset.y;
x2 -= offset.x;
y2 -= offset.y;
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
x1 = max(x1,0);
x2 = min(x2,(lcdint_t)m_w-1);
y1 = max(y1,0);
y2 = min(y2,(lcdint_t)m_h-1);
uint8_t *buf = m_buf + YADDR16(y1) + (x1<<1);
for (lcdint_t y = y1; y <= y2; y++)
{
for (lcdint_t x = x1; x <= x2; x++)
{
buf[0] = m_color >> 8;
buf[1] = m_color & 0xFF;
buf+=2;
}
buf += ( ((lcdint_t)(m_w) - (x2 - x1 + 1)) <<1 );
}
}
template <>
void NanoCanvasOps<16>::drawBitmap1(lcdint_t xpos, lcdint_t ypos, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
uint8_t offs = 0;
/* calculate char rectangle */
lcdint_t x1 = xpos - offset.x;
lcdint_t y1 = ypos - offset.y;
lcdint_t x2 = x1 + (lcdint_t)w - 1;
lcdint_t y2 = y1 + (lcdint_t)h - 1;
/* clip bitmap */
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
if (x1 < 0)
{
bitmap -= x1;
x1 = 0;
}
if (y1 < 0)
{
bitmap += ((lcduint_t)(-y1) >> 3) * w;
offs = ((-y1) & 0x07);
y1 = 0;
}
if (y2 >= (lcdint_t)m_h)
{
y2 = (lcdint_t)m_h - 1;
}
if (x2 >= (lcdint_t)m_w)
{
x2 = (lcdint_t)m_w - 1;
}
uint8_t offs2 = 8 - offs;
lcdint_t y = y1;
while ( y <= y2)
{
for ( lcdint_t x = x1; x <= x2; x++ )
{
uint8_t data = pgm_read_byte( bitmap );
uint16_t addr = YADDR16(y) + (x<<1);
for (uint8_t n = 0; n < min(y2 - y + 1, 8); n++)
{
if ( data & (1<<(n + offs)) )
{
m_buf[addr] = m_color >> 8;
m_buf[addr+1] = m_color & 0xFF;
}
else if (!(m_textMode & CANVAS_MODE_TRANSPARENT))
{
m_buf[addr] = 0x00;
m_buf[addr+1] = 0x00;
}
addr += ((lcduint_t)m_w << 1);
}
bitmap++;
}
bitmap += (w - (x2 - x1 + 1));
y = y + offs2;
offs = 0;
offs2 = 8;
}
}
template <>
void NanoCanvasOps<16>::drawXBitmap1(lcdint_t x, lcdint_t y, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
x -= offset.x;
y -= offset.y;
lcduint_t origin_width = w;
if (y + (lcdint_t)h <= 0) return;
if (y >= (lcdint_t)m_h) return;
if (x + (lcdint_t)w <= 0) return;
if (x >= (lcdint_t)m_w) return;
uint8_t start_bit = 0;
lcduint_t pitch_delta = 0;
if (y < 0)
{
bitmap += /*pitch*/((origin_width + 7) >> 3) * (lcduint_t)(-y);
h += y;
y = 0;
}
if (x < 0)
{
bitmap += ((lcduint_t)(-x)) / 8;
start_bit = ((lcduint_t)(-x)) & 0x07;
w += x;
x = 0;
}
if ((lcduint_t)(y + (lcdint_t)h) > (lcduint_t)m_h)
{
h = (lcduint_t)(m_h - (lcduint_t)y);
}
if ((lcduint_t)(x + (lcdint_t)w) > (lcduint_t)m_w)
{
w = (lcduint_t)(m_w - (lcduint_t)x);
}
pitch_delta = ((origin_width + 7 - start_bit) >> 3) - ((w + 7) >> 3);
for(lcduint_t j = 0; j < h; j++)
{
uint8_t bit = start_bit;
for(lcduint_t i = 0; i < w; i++)
{
uint8_t data = 0;
data = (pgm_read_byte(bitmap) >> bit) & 0x01;
if (data)
{
m_buf[YADDR16(y + j) + ((x + i)<<1)] = m_color >> 8;
m_buf[YADDR16(y + j) + ((x + i)<<1) + 1] = m_color & 0xFF;
}
else if (CANVAS_MODE_TRANSPARENT != (m_textMode & CANVAS_MODE_TRANSPARENT))
{
m_buf[YADDR16(y + j) + ((x + i)<<1)] = 0x00;
m_buf[YADDR16(y + j) + ((x + i)<<1) + 1] = 0x00;
}
bit++;
if (bit >= 8)
{
bitmap++;
bit=0;
}
}
if (bit)
{
bitmap++;
}
bitmap += pitch_delta;
}
}
template <>
void NanoCanvasOps<16>::drawBitmap8(lcdint_t xpos, lcdint_t ypos, lcduint_t w, lcduint_t h, const uint8_t *bitmap)
{
/* calculate char rectangle */
lcdint_t x1 = xpos - offset.x;
lcdint_t y1 = ypos - offset.y;
lcdint_t x2 = x1 + (lcdint_t)w - 1;
lcdint_t y2 = y1 + (lcdint_t)h - 1;
/* clip bitmap */
if ((x2 < 0) || (x1 >= (lcdint_t)m_w)) return;
if ((y2 < 0) || (y1 >= (lcdint_t)m_h)) return;
if (x1 < 0)
{
bitmap -= x1;
x1 = 0;
}
if (y1 < 0)
{
bitmap += (lcduint_t)(-y1) * w;
y1 = 0;
}
if (y2 >= (lcdint_t)m_h)
{
y2 = (lcdint_t)m_h - 1;
}
if (x2 >= (lcdint_t)m_w)
{
x2 = (lcdint_t)m_w - 1;
}
lcdint_t y = y1;
while ( y <= y2 )
{
for ( lcdint_t x = x1; x <= x2; x++ )
{
uint8_t data = pgm_read_byte( bitmap );
if ( (data) || (!(m_textMode & CANVAS_MODE_TRANSPARENT)) )
{
uint16_t color = (((uint16_t)data & 0b11100000) << 8) |
(((uint16_t)data & 0b00011100) << 6) |
(((uint16_t)data & 0b00000011) << 3);
m_buf[YADDR16(y) + (x<<1)] = color;
}
bitmap++;
}
bitmap += (w - (x2 - x1 + 1));
y++;
}
}
template <>
void NanoCanvasOps<16>::clear()
{
memset(m_buf, 0, YADDR16(m_h));
}
template <>
void NanoCanvasOps<16>::begin(lcdint_t w, lcdint_t h, uint8_t *bytes)
{
m_w = w;
m_h = h;
offset.x = 0;
offset.y = 0;
m_cursorX = 0;
m_cursorY = 0;
m_color = 0xFFFF; // white color by default
m_textMode = 0;
m_p = 3;
while (w >> (m_p+1)) { m_p++; };
m_p++;
m_buf = bytes;
clear();
}
/////////////////////////////////////////////////////////////////////////////////
//
// NanoCanvasOps class initiation
//
/////////////////////////////////////////////////////////////////////////////////
template class NanoCanvasOps<1>;
template class NanoCanvasOps<4>;
template class NanoCanvasOps<8>;
template class NanoCanvasOps<16>;
// NANO CANVAS 16
void NanoCanvas16::blt(lcdint_t x, lcdint_t y)
{
ssd1306_drawBufferFast16(x, y, m_w, m_h, m_buf);
}
void NanoCanvas16::blt()
{
ssd1306_drawBufferFast16(offset.x, offset.y, m_w, m_h, m_buf);
}
void NanoCanvas16::blt(const NanoRect &rect)
{
ssd1306_drawBufferEx16(offset.x + rect.p1.x,
offset.y + rect.p1.y,
rect.width(),
rect.height(),
m_w<<1,
m_buf + (rect.p1.x<<1) + rect.p1.y * (m_w<<1) );
}