#define PAGE_X_SHIFT 3
#define PAGE_Y_SHIFT 3
#define PAGE_X_BITS (7 - PAGE_X_SHIFT)
#define PAGE_Y_BITS (6 - PAGE_Y_SHIFT)
#if PAGE_X_SHIFT == 0
#define PAGENO(x, y) ((y) >> PAGE_Y_SHIFT)
#elif PAGE_X_SHIFT <= PAGE_Y_SHIFT
#define PAGENO(x, y) (((x) >> PAGE_X_BITS) + (((y) & ~(_BV(PAGE_Y_SHIFT) - 1)) >> (PAGE_Y_SHIFT - PAGE_X_SHIFT)))
#else
#define PAGENO(x, y) (((x) >> PAGE_X_BITS) + (((y) & ~(_BV(PAGE_Y_SHIFT) - 1)) << (PAGE_X_SHIFT - PAGE_Y_SHIFT)))
#endif
typedef struct
{
int x, y;
} POINT;
typedef struct
{
int left, top, right, bottom;
} RECT;
uint8_t _g_buf[_BV(PAGE_X_BITS + PAGE_Y_BITS ) >> 3];
uint8_t _g_msk[_BV(PAGE_X_SHIFT + PAGE_Y_SHIFT) >> 3];
uint8_t _g_flg[_BV(PAGE_X_SHIFT + PAGE_Y_SHIFT) >> 3];
uint8_t _g_pno;
POINT _g_org;
RECT _g_cur;
bool intersectRect(int x0, int y0, int x1, int y1)
{
return (x0 < x1 ? (x0 >= _g_cur.right ) || (x1 <= _g_cur.left) : (x0 < _g_cur.left) || (x1 >= _g_cur.right ))
|| (y0 < y1 ? (y0 >= _g_cur.bottom) || (y1 <= _g_cur.top ) : (y0 < _g_cur.top ) || (y1 >= _g_cur.bottom))
? false : true;
}
void setCurPage(uint8_t no)
{
_g_pno = no;
_g_msk[no >> 3] |= _BV(no & 7);
#if PAGE_X_SHIFT == 0
_g_cur.left = 0;
_g_cur.top = no << PAGE_Y_BITS;
#else
_g_cur.left = (no & (_BV(PAGE_X_SHIFT) - 1)) << PAGE_X_BITS;
#if PAGE_X_SHIFT <= PAGE_Y_BITS
_g_cur.top = (no & ~(_BV(PAGE_X_SHIFT) - 1)) << (PAGE_Y_BITS - PAGE_X_SHIFT);
#else
_g_cur.top = (no & ~(_BV(PAGE_X_SHIFT) - 1)) >> (PAGE_X_SHIFT - PAGE_Y_BITS);
#endif
#endif
_g_cur.right = _g_cur.left + _BV(PAGE_X_BITS);
_g_cur.bottom = _g_cur.top + _BV(PAGE_Y_BITS);
memset(_g_buf, 0, sizeof(_g_buf));
}
void beginPaint()
{
memset(_g_msk, 0, sizeof(_g_msk));
_g_pno = 0xFF;
}
bool endPaint()
{
if (_g_pno != 0xFF)
{
//
// flush page
//
cursor(_g_cur.left >> 3, _g_cur.top >> 3);
ctrl(SSD1306_GDDRAM, 1);
uint8_t *p = _g_buf;
for (uint8_t x = 0; x < _BV(PAGE_X_BITS); ++x)
data(*p++);
//
// select page
//
for (uint8_t i = 0; i < _BV(PAGE_X_SHIFT + PAGE_Y_SHIFT); ++i)
{
if (~_g_msk[i >> 3] & _g_flg[i >> 3] & _BV(i & 7))
{
setCurPage(i);
return true;
}
}
memcpy(_g_flg, _g_msk, sizeof(_g_flg));
_g_pno = 0xFF;
}
return false;
}
bool setPixel(int x, int y)
{
if ((x >= 0) && (x < SSD1306_MAX_WIDTH) && (y >= 0) && (y < SSD1306_MAX_HEIGHT))
{
uint8_t no = PAGENO(x, y);
if (_g_pno == 0xFF)
setCurPage(no);
if (_g_pno == no)
{
_g_buf[x & (_BV(PAGE_X_BITS) - 1)] |= _BV(y & 7);
return true;
}
_g_flg[no >> 3] |= _BV(no & 7);
}
return false;
}
void moveto(int x, int y)
{
_g_org.x = x;
_g_org.y = y;
}
void lineto(int x, int y)
{
int x0 = _g_org.x;
int y0 = _g_org.y;
int dx = abs(x - x0);
int dy = abs(y - y0);
int sx = (x0 < x ? 1 : -1);
int sy = (y0 < y ? 1 : -1);
int er = dx - dy;
while (dx >= dy ? x0 != x : y0 != y)
{
if (!setPixel(x0, y0))
{
if (!intersectRect(x0, y0, x, y))
break;
}
int e2 = er + er;
if (e2 > -dy)
{
er -= dy;
x0 += sx;
}
if (e2 < dx)
{
er += dx;
y0 += sy;
}
}
_g_org.x = x;
_g_org.y = y;
}