From 87c0f1748868e5728d842d75490a707c8b6863ac Mon Sep 17 00:00:00 2001 From: PLSCO Date: Wed, 2 Mar 2016 14:52:59 -0800 Subject: [PATCH] Added SPI transactions support for ILI9341 Current version of the library malfunctions when another device (besides the LCD) shares the SPI bus. Implementing the modern SPI transactions method seems to have resolved it. --- Pixels.cpp | 4017 +++++++++++++++++++++++----------------------- Pixels.h | 1883 +++++++++++----------- Pixels_ILI9341.h | 522 +++--- Pixels_SPIhw.h | 853 ++++++---- 4 files changed, 3797 insertions(+), 3478 deletions(-) mode change 100644 => 100755 Pixels_ILI9341.h mode change 100644 => 100755 Pixels_SPIhw.h diff --git a/Pixels.cpp b/Pixels.cpp index a36f299..cb9803c 100755 --- a/Pixels.cpp +++ b/Pixels.cpp @@ -1,2005 +1,2012 @@ -/* - * Pixels. Graphics library for TFT displays. - * - * Copyright (C) 2012-2015 Igor Repinetski - * - * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. - * - * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister - * - * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ - * - * This library includes some code portions and algoritmic ideas derived from works of - * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) - * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) - */ - -#include "Pixels.h" - -RGB::RGB(uint8_t r, uint8_t g, uint8_t b) { - setColor(r, g, b); -} - -RGB::RGB() { -} - -void RGB::setColor(int32_t r, int32_t g, int32_t b) { - red = r; - green = g; - blue = b; - col = (((uint16_t)red / 8) << 11) | ((green / 4) << 5) | (blue / 8); -} - - -RGB RGB::convert565toRGB(uint16_t color) { - uint8_t r = ((0xf800 & color)>>11) * 255 / 31; - uint8_t g = ((0x7e0 & color)>>5) * 255 / 63; - uint8_t b = (0x1f & color) * 255 / 31; - return RGB(r, g, b); -} - -uint16_t RGB::convertRGBto565(RGB color) { - return ((color.red / 8) << 11) | ((color.green / 4) << 5) | (color.blue / 8); -} - -uint16_t RGB::convertTo565() { - return col; // ((red / 8) << 11) | ((green / 4) << 5) | (blue / 8); -} - -regtype *registerCS; // chip select -regsize bitmaskCS; - -PixelsBase::PixelsBase(uint16_t width, uint16_t height) { - deviceWidth = width < height ? width : height; - deviceHeight = width > height ? width : height; - this->width = width; - this->height = height; - setOrientation( width > height ? LANDSCAPE : PORTRAIT ); - - relativeOrigin = true; - - currentScroll = 0; - scrollSupported = true; - scrollEnabled = true; - extraScrollDelay = 0; - - lineWidth = 1; - fillDirection = 0; - - computedBgColor = new RGB(0, 0, 0); - computedFgColor = new RGB(0, 0, 0); - bgBuffer = new RGB(0, 0, 0); - fgBuffer = new RGB(0, 0, 0); - - gfxOpNestingDepth = 0; - - setBackground(0,0,0); - setColor(0xFF,0xFF,0xFF); -} - -void PixelsBase::setOrientation( uint8_t direction ){ - - if ( (orientation < 2 && direction > 1) || (orientation > 1 && direction < 2) ) { - currentScroll = 2 * deviceHeight - currentScroll; - currentScroll %= deviceHeight; - } - orientation = direction; - - landscape = false; - - switch ( orientation ) { - case LANDSCAPE_FLIP: - case LANDSCAPE: - width = deviceHeight; - height = deviceWidth; - landscape = true; - break; - case PORTRAIT_FLIP: - width = deviceWidth; - height = deviceHeight; - break; - default: - width = deviceWidth; - height = deviceHeight; - orientation = PORTRAIT; - break; - } -} - -/* Graphic primitives */ - -void PixelsBase::clear() { - boolean s = relativeOrigin; - relativeOrigin = false; - RGB* sav = getColor(); - setColor(background); - fillRectangle(0, 0, width, height); - setColor(sav); - relativeOrigin = s; -} - -RGB* PixelsBase::getPixel(int16_t x, int16_t y) { - return getBackground(); -} - -void PixelsBase::drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { - - beginGfxOperation(); - - if (y1 == y2 && lineWidth == 1) { - hLine(x1, y1, x2); - } else if (x1 == x2 && lineWidth == 1) { - vLine(x1, y1, y2); - } else { - if ( lineWidth == 1 ) { - if ( antialiasing ) { - drawLineAntialiased(x1, y1, x2, y2); - } else { - int16_t dx; - int16_t dy; - int16_t sx; - int16_t sy; - - if ( x2 > x1 ) { - dx = x2 - x1; - sx = 1; - } else { - dx = x1 - x2; - sx = -1; - } - - if ( y2 > y1 ) { - dy = y2 - y1; - sy = 1; - } else { - dy = y1 - y2; - sy = -1; - } - - int16_t x = x1; - int16_t y = y1; - int16_t err = dx - dy; - int16_t e2; - while (true) { - drawPixel(x, y); - if (x == x2 && y == y2) { - break; - } - e2 = err << 1; - if (e2 > -dy) { - err = err - dy; - x = x + sx; - } - if (e2 < dx) { - err = err + dx; - y = y + sy; - } - } - } - } else { - drawFatLineAntialiased(x1, y1, x2, y2); - } - } - - endGfxOperation(); -} - -void PixelsBase::drawRectangle(int16_t x, int16_t y, int16_t width, int16_t height) { - beginGfxOperation(); - hLine(x, y, x+width-2); - vLine(x+width-1, y, y+height-2); - hLine(x+1, y+height-1, x+width-1); - vLine(x, y+1, y+height-1); - endGfxOperation(); -} - -void PixelsBase::fillRectangle(int16_t x, int16_t y, int16_t width, int16_t height) { - beginGfxOperation(); - fill(foreground->convertTo565(), x, y, x+width-1, y+height-1); - endGfxOperation(); -} - -void PixelsBase::drawRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t radius) { - - if ( radius < 1 ) { - drawRectangle(x, y, width, height); - return; - } - - if ( radius > height >> 2 ) { - radius = height >> 2; - } - if ( radius > width >> 1 ) { - radius = width >> 1; - } - - beginGfxOperation(); - - if ( antialiasing ) { - drawRoundRectangleAntialiased(x, y, width, height, radius, radius, 0); - } else { - height--; - width--; - - hLine(x + radius, y + height, x + width - radius); - hLine(x + radius, y, x + width - radius ); - vLine(x + width, y + radius, y + height - radius); - vLine(x, y + radius, y + height - radius); - - int16_t shiftX = width - (radius << 1); - int16_t shiftY = height - (radius << 1); - int16_t f = 1 - radius; - int16_t ddF_x = 1; - int16_t ddF_y = - (radius << 1); - int16_t x1 = 0; - int16_t y1 = radius; - - int16_t xx = x + radius; - int16_t yy = y + radius; - - while (x1 < y1) { - if (f >= 0) { - y1--; - ddF_y += 2; - f += ddF_y; - } - x1++; - ddF_x += 2; - f += ddF_x; - - drawPixel(xx + x1 + shiftX, yy + y1 + shiftY); - drawPixel(xx - x1, yy + y1 + shiftY); - drawPixel(xx + x1 + shiftX, yy - y1); - drawPixel(xx - x1, yy - y1); - drawPixel(xx + y1 + shiftX, yy + x1 + shiftY); - drawPixel(xx - y1, yy + x1 + shiftY); - drawPixel(xx + y1 + shiftX, yy - x1); - drawPixel(xx - y1, yy - x1); - } - } - - endGfxOperation(); -} - -void PixelsBase::fillRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t radius) { - - if ( radius < 1 ) { - fillRectangle(x, y, width, height); - return; - } - - if ( radius > height >> 1 ) { - radius = height >> 1; - } - if ( radius > width >> 1 ) { - radius = width >> 1; - } - - beginGfxOperation(); - - if ( antialiasing ) { - drawRoundRectangleAntialiased(x, y, width-1, height-1, radius, radius, true); - } - - fillRectangle(x + radius, y + height - radius, width - (radius << 1), radius); - fillRectangle(x, y + radius, width, height - (radius << 1)); - fillRectangle(x + radius, y, width - (radius << 1), radius); - - height--; - width--; - - int16_t shiftX = width - (radius << 1); - int16_t shiftY = height - (radius << 1); - int16_t f = 1 - radius; - int16_t ddF_x = 1; - int16_t ddF_y = -(radius << 1); - int16_t x1 = 0; - int16_t y1 = radius; - - int16_t xx = x + radius; - int16_t yy = y + radius; - - while (x1 < y1) { - if (f >= 0) { - y1--; - ddF_y += 2; - f += ddF_y; - } - x1++; - ddF_x += 2; - f += ddF_x; - - hLine(xx + shiftX, yy - y1, xx + shiftX + x1); - hLine(xx - x1, yy - y1, xx); - hLine(xx + shiftX, yy - x1, xx + shiftX + y1); - hLine(xx - y1, yy - x1, xx); - - hLine(xx + shiftX, yy + y1 + shiftY, xx + x1 + shiftX); - hLine(xx + shiftX, yy + x1 + shiftY, xx + shiftX + y1); - hLine(xx - x1, yy + y1 + shiftY, xx); - hLine(xx - y1, yy + x1 + shiftY, xx); - } - - endGfxOperation(); -} - -void PixelsBase::drawCircle(int16_t x, int16_t y, int16_t r) { - - drawOval(x-r, y-r, r<<1, r<<1); - -// if ( antialiasing ) { -// drawCircleAntialiaced(x, y, r, false); -// } else { -// int16_t f = 1 - r; -// int16_t ddF_x = 1; -// int16_t ddF_y = -2 * r; -// int16_t x1 = 0; -// int16_t y1 = r; - -// drawPixel(x, y + r); -// drawPixel(x, y - r); -// drawPixel(x + r, y); -// drawPixel(x - r, y); - -// while (x1 < y1) { -// if (f >= 0) { -// y1--; -// ddF_y += 2; -// f += ddF_y; -// } -// x1++; -// ddF_x += 2; -// f += ddF_x; -// drawPixel(x + x1, y + y1); -// drawPixel(x - x1, y + y1); -// drawPixel(x + x1, y - y1); -// drawPixel(x - x1, y - y1); -// drawPixel(x + y1, y + x1); -// drawPixel(x - y1, y + x1); -// drawPixel(x + y1, y - x1); -// drawPixel(x - y1, y - x1); -// } -// } -} - -void PixelsBase::fillCircle(int16_t x, int16_t y, int16_t r) { -// int16_t yy; -// int16_t xx; - - fillOval(x-r, y-r, r<<1, r<<1); - -// if ( antialiasing ) { -// drawCircleAntialiaced(x, y, r, true); -// } - -// for (yy = -r; yy <= r; yy++) { -// for (xx = -r; xx <= r; xx++) { -// if ((xx * xx) + (yy * yy) <= (r * r)) { -// drawPixel(x+xx, y+yy); -// } -// } -// } -} - -void PixelsBase::drawOval(int16_t x, int16_t y, int16_t width, int16_t height) { - - if ((width <= 0) || (height <= 0)) { - return; - } - - beginGfxOperation(); - - if ( antialiasing ) { - drawRoundRectangleAntialiased(x, y, width, height, width/2, height/2, 0); - } else { - height--; - width--; - - int16_t ix, iy; - int16_t h, i, j, k; - int16_t oh, oi, oj, ok; - int16_t xmh, xph, ypk, ymk; - int16_t xmi, xpi, ymj, ypj; - int16_t xmj, xpj, ymi, ypi; - int16_t xmk, xpk, ymh, yph; - - int16_t rx = width / 2; - int16_t ry = height / 2; - - int16_t xx = x + rx; - int16_t yy = y + ry; - - if (width == 1) { - vLine(xx, yy, yy + height - 1); - endGfxOperation(); - return; - } - if (height == 1) { - hLine(xx, yy, xx + width - 1); - endGfxOperation(); - return; - } - - oh = oi = oj = ok = 0xFFFF; - - if (width > height) { - ix = 0; - iy = rx << 6; - - do { - h = (ix + 32) >> 6; - i = (iy + 32) >> 6; - j = (h * ry) / rx; - k = (i * ry) / rx; - - if (((ok != k) && (oj != k)) || ((oj != j) && (ok != j)) || (k != j)) { - xph = xx + h; - xmh = xx - h; - if (k > 0) { - ypk = yy + k; - ymk = yy - k; - drawPixel(xmh, ypk); - drawPixel(xph, ypk); - drawPixel(xmh, ymk); - drawPixel(xph, ymk); - } else { - drawPixel(xmh, yy); - drawPixel(xph, yy); - } - ok = k; - xpi = xx + i; - xmi = xx - i; - if (j > 0) { - ypj = yy + j; - ymj = yy - j; - drawPixel(xmi, ypj); - drawPixel(xpi, ypj); - drawPixel(xmi, ymj); - drawPixel(xpi, ymj); - } else { - drawPixel(xmi, yy); - drawPixel(xpi, yy); - } - oj = j; - } - - ix = ix + iy / rx; - iy = iy - ix / rx; - - } while (i > h); - } else { - ix = 0; - iy = ry << 6; - - do { - h = (ix + 32) >> 6; - i = (iy + 32) >> 6; - j = (h * rx) / ry; - k = (i * rx) / ry; - - if (((oi != i) && (oh != i)) || ((oh != h) && (oi != h) && (i != h))) { - xmj = xx - j; - xpj = xx + j; - if (i > 0) { - ypi = yy + i; - ymi = yy - i; - drawPixel(xmj, ypi); - drawPixel(xpj, ypi); - drawPixel(xmj, ymi); - drawPixel(xpj, ymi); - } else { - drawPixel(xmj, yy); - drawPixel(xpj, yy); - } - oi = i; - xmk = xx - k; - xpk = xx + k; - if (h > 0) { - yph = yy + h; - ymh = yy - h; - drawPixel(xmk, yph); - drawPixel(xpk, yph); - drawPixel(xmk, ymh); - drawPixel(xpk, ymh); - } else { - drawPixel(xmk, yy); - drawPixel(xpk, yy); - } - oh = h; - } - - ix = ix + iy / ry; - iy = iy - ix / ry; - - } while (i > h); - } - } - - endGfxOperation(); -} - -void PixelsBase::fillOval(int16_t xx, int16_t yy, int16_t width, int16_t height) { - - height--; - width--; - - int16_t rx = width / 2; - int16_t ry = height / 2; - - int16_t x = xx + rx; - int16_t y = yy + ry; - - int16_t ix, iy; - int16_t h, i, j, k; - int16_t oh, oi, oj, ok; - int16_t xmh, xph; - int16_t xmi, xpi; - int16_t xmj, xpj; - int16_t xmk, xpk; - - if ((rx < 0) || (ry < 0)) { - return; - } - - if (width < 2) { - vLine(xx, yy, yy + height); - return; - } - - if (height < 2) { - hLine(xx, yy, xx + width); - return; - } - - beginGfxOperation(); - - if ( antialiasing ) { - drawRoundRectangleAntialiased(x-rx, y-ry, rx<<1, ry<<1, rx, ry, true); - } - - oh = oi = oj = ok = 0xFFFF; - - if (rx > ry) { - ix = 0; - iy = rx << 6; - - do { - h = (ix + 32) >> 6; - i = (iy + 32) >> 6; - j = (h * ry) / rx; - k = (i * ry) / rx; - - if ((ok != k) && (oj != k)) { - xph = x + h; - xmh = x - h; - if (k > 0) { - hLine(xmh, y + k, xph); - hLine(xmh, y - k, xph); - } else { - hLine(xmh, y, xph); - } - ok = k; - } - if ((oj != j) && (ok != j) && (k != j)) { - xmi = x - i; - xpi = x + i; - if (j > 0) { - hLine(xmi, y + j, xpi); - hLine(xmi, y - j, xpi); - } else { - hLine(xmi, y, xpi); - } - oj = j; - } - - ix = ix + iy / rx; - iy = iy - ix / rx; - - } while (i > h); - } else { - ix = 0; - iy = ry << 6; - - do { - h = (ix + 32) >> 6; - i = (iy + 32) >> 6; - j = (h * rx) / ry; - k = (i * rx) / ry; - - if ((oi != i) && (oh != i)) { - xmj = x - j; - xpj = x + j; - if (i > 0) { - hLine(xmj, y + i, xpj); - hLine(xmj, y - i, xpj); - } else { - hLine(xmj, y, xpj); - } - oi = i; - } - if ((oh != h) && (oi != h) && (i != h)) { - xmk = x - k; - xpk = x + k; - if (h > 0) { - hLine(xmk, y + h, xpk); - hLine(xmk, y - h, xpk); - } else { - hLine(xmk, y, xpk); - } - oh = h; - } - - ix = ix + iy / ry; - iy = iy - ix / ry; - - } while (i > h); - } - - endGfxOperation(); -} - -void PixelsBase::drawIcon(int16_t xx, int16_t yy, prog_uchar* data) { - - int16_t fontType = BITMASK_FONT; - if ( pgm_read_byte_near(data + 1) == 'a' ) { - fontType = ANTIALIASED_FONT; - } - - int16_t length = ((0xFF & (int32_t)pgm_read_byte_near(data + 2)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 3)); - - int16_t height = pgm_read_byte_near(data + 4); - - beginGfxOperation(); - drawGlyph(fontType, false, xx, yy, height, data + 1, length-1); - endGfxOperation(); -} - -void PixelsBase::cleanIcon(int16_t xx, int16_t yy, prog_uchar* data) { - - int16_t fontType = BITMASK_FONT; - if ( pgm_read_byte_near(data + 1) == 'a' ) { - fontType = ANTIALIASED_FONT; - } - - int16_t length = ((0xFF & (int32_t)pgm_read_byte_near(data + 2)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 3)); - - int16_t height = pgm_read_byte_near(data + 4); - - beginGfxOperation(); - drawGlyph(fontType, true, xx, yy, height, data + 1, length-1); - endGfxOperation(); -} - -int8_t PixelsBase::drawBitmap(int16_t x, int16_t y, int16_t width, int16_t height, prog_uint16_t* data) { - - Bounds bb(x, y, x+width-1, y+height-1); - if( !transformBounds(bb) ) { - return 1; - } - - if( !checkBounds(bb) ) { - return 1; - } - - beginGfxOperation(); - setRegion(bb.x1, bb.y1, bb.x2, bb.y2); - - int sc = currentScroll; - if ( sc == 0 ) { - sc = deviceHeight; - } - - switch( orientation ) { - case PORTRAIT: - { - for ( int16_t j = bb.y1; j <= bb.y2; j++ ) { - for ( int16_t i = bb.x1; i <= bb.x2; i++ ) { - int16_t px = pgm_read_word_near(data + (j - y) * width + i - x); - setCurrentPixel(px); - } - } - } - break; - case LANDSCAPE: - { - int h1 = height - max(0, (y + height) - deviceWidth) - 1; - int w1 = width - max(0, (x + width) - sc) - 1; - int w = bb.x2 - bb.x1 + 1; - int h = bb.y2 - bb.y1 + 1; - for ( int16_t j = h - 1; j >= 0; j-- ) { - for ( int16_t i = 0; i < w; i++ ) { - int16_t px = pgm_read_word_near(data + (h1 - i) * width + (w1 - j)); - setCurrentPixel(px); - } - } - } - break; - case PORTRAIT_FLIP: - { - int h = bb.y2 - bb.y1 + 1; - int w = bb.x2 - bb.x1 + 1; - int cutH = y < 0 ? 0 : height - h; - int cutW = x < 0 ? 0 : width - w; - for ( int16_t j = 0; j < h; j++ ) { - for ( int16_t i = 0; i < w; i++ ) { - int16_t px = pgm_read_word_near(data + (height - j - 1 - cutH) * width + (width - i - 1 - cutW)); - setCurrentPixel(px); - } - } - } - break; - case LANDSCAPE_FLIP: - { - int h1 = height - max(0, (y + height) - deviceWidth) - 1; - int w1 = width - max(0, (x + width) - sc) - 1; - int w = bb.x2 - bb.x1 + 1; - int h = bb.y2 - bb.y1 + 1; - for ( int16_t j = 0; j < h; j++ ) { - for ( int16_t i = w - 1; i >= 0; i-- ) { - int16_t px = pgm_read_word_near(data + (h1 - i) * width + (w1 - j)); - setCurrentPixel(px); - } - } - } - break; - } - - endGfxOperation(); - return 0; -} - -int8_t PixelsBase::drawCompressedBitmap(int16_t x, int16_t y, prog_uchar* data) { - - if ( data == NULL ) { - return -1; - } - - if ( pgm_read_byte_near(data + 0) != 'Z' ) { - // Unknown compression method - return -2; - } - - int32_t compressedLen = ((0xFF & (int32_t)pgm_read_byte_near(data + 1)) << 16) + ((0xFF & (int32_t)pgm_read_byte_near(data + 2)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 3)); - if ( compressedLen < 0 ) { - // Unknown compression method or compressed data inconsistence - return -3; - } - - int32_t resultLen = ((0xFF & (int32_t)pgm_read_byte_near(data + 4)) << 16) + ((0xFF & (int32_t)pgm_read_byte_near(data + 5)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 6)); - if ( resultLen < 0 ) { - // Unknown compression method or compression format error - return resultLen; - } - - uint8_t windowLen = 0xFF & (int16_t)pgm_read_byte_near(data + 7); - if ( windowLen < 0 || windowLen > 254 ) { - // corrupted content - return -5; - } - - int16_t width = ((0xFF & (int32_t)pgm_read_byte_near(data + 8)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 9)); - if ( width < 0 ) { - // Unknown compression method or compression format error (width parameter is invalid) - return -6; - } - - int16_t height = ((0xFF & (int32_t)pgm_read_byte_near(data + 10)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 11)); - if ( height < 0 ) { - // Unknown compression method or compression format error (height parameter is invalid) - return -7; - } - - uint8_t window[windowLen]; - int16_t wptr = 0; - - int32_t ctr = 0; - - uint8_t buf; - bool bufEmpty = true; - - int* raster = NULL; - int rasterPtr = 0; - int rasterLine = y; - - raster = new int[width]; - - beginGfxOperation(); - - BitStream bs( data, compressedLen, 96 ); - while ( true ) { - - uint8_t bit = bs.readBit(); - if ( bit == 0 ) { // literal - uint8_t bits = bs.readBits(8); - if ( bufEmpty ) { - buf = bits; - bufEmpty = false; - } else { - uint16_t px = buf; - px <<= 8; - px |= bits; - raster[rasterPtr++] = px; - if ( rasterPtr == width ) { - Bounds bb(x, rasterLine, x + width - 1, rasterLine); - if( transformBounds(bb) && checkBounds(bb) ) { - setRegion(bb.x1, bb.y1, bb.x2, bb.y2); - - int ww = width; - int corr = 0; - if (bb.x1 == bb.x2) { - ww = bb.y2 - bb.y1 + 1; - } else { - ww = bb.x2 - bb.x1 + 1; - } - - if (x < 0) { - corr = -x; - ww += corr; - } - - if ( orientation < 2 ) { - for ( int i = corr; i < ww; i++ ) { - setCurrentPixel(raster[i]); - } - } else { - for ( int i = min(width, ww - 1); i >= corr; i-- ) { - setCurrentPixel(raster[i]); - } - } - } - - rasterLine++; - rasterPtr = 0; - } - bufEmpty = true; - } - ctr++; - window[wptr++] = bits; - if ( wptr >= windowLen ) { - wptr -= windowLen; - } - } else { - uint8_t offset = (uint8_t)bs.readNumber() - 1; - uint8_t matchCount = (uint8_t)bs.readNumber() - 1; - - while( matchCount-- > 0 ) { - int16_t p1 = wptr - offset; - while ( p1 < 0 ) { - p1 += windowLen; - } - while ( p1 >= windowLen ) { - p1 -= windowLen; - } - int16_t p2 = wptr; - while ( p2 >= windowLen ) { - p2 -= windowLen; - } - wptr++; - ctr++; - - if ( bufEmpty ) { - buf = window[p1]; - bufEmpty = false; - } else { - uint16_t px = buf; - px <<= 8; - px |= window[p1]; - - raster[rasterPtr++] = px; - if ( rasterPtr == width ) { - Bounds bb(x, rasterLine, x + width - 1, rasterLine); - if( transformBounds(bb) && checkBounds(bb) ) { - setRegion(bb.x1, bb.y1, bb.x2, bb.y2); - - int corr = 0; - int ww; - if (bb.x1 == bb.x2) { - ww = bb.y2 - bb.y1 + 1; - } else { - ww = bb.x2 - bb.x1 + 1; - } - - if (x < 0) { - corr = -x; - ww += corr; - } - - - if ( orientation < 2 ) { - for ( int i = corr; i < ww; i++ ) { - setCurrentPixel(raster[i]); - } - } else { - for ( int i = min(width, ww - 1); i >= corr; i-- ) { - setCurrentPixel(raster[i]); - } - } - } - - rasterLine++; - rasterPtr = 0; - } - bufEmpty = true; - } - window[p2] = window[p1]; - } - - while ( wptr >= windowLen ) { - wptr -= windowLen; - } - } - if ( ctr > resultLen ) { - break; - } - } - - delete raster; - - endGfxOperation(); - - return 0; -} - - -int8_t PixelsBase::loadBitmap(int16_t x, int16_t y, int16_t sx, int16_t sy, String path) { -// int16_t* data = loadFileBytes( path ); - return 0; // drawBitmap(x, y, sx, sy, data); -} - -/* ------- Antialiasing ------- */ -/* To be overriden with Pixels_Antialiasing.h */ - -void PixelsBase::drawLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2) {} - -void PixelsBase::drawFatLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2) {} - -void PixelsBase::drawRoundRectangleAntialiased(int16_t x, int16_t y, int16_t width, int16_t height, int16_t rx, int16_t ry, boolean bordermode) {} - -void PixelsBase::drawCircleAntialiaced( int16_t x, int16_t y, int16_t radius, boolean bordermode ) {} - -/* TEXT */ - - -int PixelsBase::setFont(prog_uchar font[]) { - int16_t p1 = pgm_read_byte_near(font + 0); - int16_t p2 = pgm_read_byte_near(font + 1); - if ( p1 != 'Z' || p2 != 'F' ) { -// Serial.print("Invalid font prefix "); -// Serial.print( p1 ); -// Serial.print( " " ); -// Serial.println( p2 ); - currentFont = NULL; - return -1; - } - int16_t fontType = pgm_read_byte_near(font + 2); - if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { -// Serial.println("Unsupported font type"); - currentFont = NULL; - return -1; - } - currentFont = font; - return 0; -} - -void PixelsBase::print(int16_t xx, int16_t yy, String text, int8_t kerning[]) { - beginGfxOperation(); - printString(xx, yy, text, 0, kerning); - endGfxOperation(); -} - - -#ifndef NO_TEXT_WRAP -int16_t PixelsBase::computeBreakPos(String text, int16_t t) { - int16_t breakPos = -1; - String s = t == 0 ? text : text.substring(t, text.length()); - int16_t w = getTextWidth(s); - if ( w + caretX > width - textWrapMarginRight || text.indexOf('\n') >= 0 ) { - char prev = 0; - w = 0; - for ( uint16_t j = t; j < text.length(); j++ ) { - char cc = text.charAt(j); - w += getCharWidth(cc); - if ( (cc == ' ' && prev != ' ') || (cc == '\n' && breakPos >= 0) ) { - if ( caretX + w > width - textWrapMarginRight ) { - break; - } else { - breakPos = j; - if ( cc == '\n' ) { - break; - } - } - } else if ( cc == '\n' ) { - breakPos = j; - break; - } - prev = cc; - } - } - return breakPos; -} -#endif - -void PixelsBase::cleanText(int16_t xx, int16_t yy, String text, int8_t kerning[]) { - beginGfxOperation(); - printString(xx, yy, text, 1, kerning); - endGfxOperation(); -} - -void PixelsBase::printString(int16_t xx, int16_t yy, String text, boolean clean, int8_t kerning[]) { - - if ( currentFont == NULL ) { - return; - } - - int16_t fontType = pgm_read_byte_near(currentFont + 2); - if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { - return; - } - - RGB* fg = foreground; - - int16_t kernPtr = 0; - int16_t kern = -100; // no kerning - - int16_t glyphHeight = pgm_read_byte_near(currentFont + 3); - - caretX = xx; - caretY = yy; - - int16_t glyphWidth = 0; - int breakPos = -1; - -#ifndef NO_TEXT_WRAP - boolean relOrigin = isOriginRelative(); - if ( wrapText ) { - breakPos = computeBreakPos(text, 0); - } -#endif - - for (uint16_t t = 0; t < text.length(); t++) { - char c = text.charAt(t); - -#ifndef NO_TEXT_WRAP - if ( t == breakPos ) { - if ( c == ' ' || c == '\n' ) { - breakPos++; - continue; - } - caretX = textWrapMarginLeft; - caretY = caretY + glyphHeight + textWrapLineGap; - if ( textWrapScroll && (orientation == PORTRAIT_FLIP || orientation == PORTRAIT) && - caretY + glyphHeight + textWrapMarginBottom > height ) { - - RGB* sav = NULL; - if ( textWrapScrollFill != NULL ) { - sav = getBackground(); - setBackground(textWrapScrollFill); - } - scroll(-(height - caretY - glyphHeight - textWrapMarginBottom), SCROLL_SMOOTH | SCROLL_CLEAN); - if ( sav != NULL ) { - setBackground(sav); - } - setOriginAbsolute(); - - caretY = height - glyphHeight - textWrapMarginBottom; - } - breakPos = computeBreakPos(text, t); - } - - boolean repeat = false; -#endif - - boolean found = false; - int16_t ptr = HEADER_LENGTH; - while ( 1 ) { - char cx = (char)(((int)pgm_read_byte_near(currentFont + ptr + 0) << 8) + pgm_read_byte_near(currentFont + ptr + 1)); - if ( cx == 0 ) { - break; - } - int16_t length = (((int)(pgm_read_byte_near(currentFont + ptr + 2) & 0xff) << 8) + (int)(pgm_read_byte_near(currentFont + ptr + 3) & 0xff)); - - if ( cx == c ) { - if ( length < 8 ) { -// Serial.print( "Invalid " ); -// Serial.print( c ); -// Serial.println( " glyph definition. Font corrupted?" ); - break; - } - - glyphWidth = 0xff & pgm_read_byte_near(currentFont + ptr + 4); - found = true; - -#ifndef NO_TEXT_WRAP - if ( wrapText && caretX + glyphWidth > width - textWrapMarginRight ) { - breakPos = t; - repeat = true; - break; - } -#endif - - drawGlyph(fontType, clean, caretX, caretY, glyphHeight, currentFont + ptr, length); - break; - } - ptr += length; - } - -#ifndef NO_TEXT_WRAP - if ( repeat ) { - t--; - continue; - } -#endif - - if ( kerning != NULL && kerning[kernPtr] > -100 ) { - kern = kerning[kernPtr]; - if (kerning[kernPtr+1] > -100) { - kernPtr++; - } - } - - if ( found ) { - caretX += glyphWidth; - if ( kern > -100 ) { - caretX += kern; - } - } - } - -#ifndef NO_TEXT_WRAP - if ( relOrigin ) { - setOriginRelative(); - } else { - setOriginAbsolute(); - } -#endif - - setColor(fg); -} - -int16_t PixelsBase::getTextLineHeight() { - if ( currentFont == NULL ) { - return 0; - } - -// int16_t fontType = pgm_read_byte_near(currentFont + 2); -// if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { -// return 0; -// } - - return pgm_read_byte_near(currentFont + 3); -} - -int16_t PixelsBase::getTextBaseline() { - if ( currentFont == NULL ) { - return 0; - } - -// int16_t fontType = pgm_read_byte_near(currentFont + 2); -// if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { -// return 0; -// } - - return pgm_read_byte_near(currentFont + 4); -} - -int16_t PixelsBase::getCharWidth(char c) { - if ( currentFont == NULL ) { - return 0; - } - - int16_t ptr = HEADER_LENGTH; - while ( 1 ) { - char cx = (char)(((int)pgm_read_byte_near(currentFont + ptr + 0) << 8) + pgm_read_byte_near(currentFont + ptr + 1)); - if ( cx == 0 ) { - break; - } - int16_t length = (((int)(pgm_read_byte_near(currentFont + ptr + 2) & 0xff) << 8) + (int)(pgm_read_byte_near(currentFont + ptr + 3) & 0xff)); - - if ( cx == c ) { - if ( length < 8 ) { -// Serial.print( "Invalid " ); -// Serial.print( c ); -// Serial.println( " glyph definition. Font corrupted?" ); - break; - } -// Serial.print( c ); - - return 0xff & pgm_read_byte_near(currentFont + ptr + 4); - } - - ptr += length; - } - - return 0; -} - -int16_t PixelsBase::getTextWidth(String text, int8_t kerning[]) { - if ( currentFont == NULL ) { - return 0; - } - - int16_t kernPtr = 0; - int16_t kern = -100; // no kerning - int16_t x1 = 0; - - for (uint16_t t = 0; t < text.length(); t++) { - char c = text.charAt(t); - - int16_t width = 0; - boolean found = false; - int16_t ptr = HEADER_LENGTH; - while ( 1 ) { - char cx = (char)(((int)pgm_read_byte_near(currentFont + ptr + 0) << 8) + pgm_read_byte_near(currentFont + ptr + 1)); - if ( cx == 0 ) { - break; - } - int16_t length = (((int)(pgm_read_byte_near(currentFont + ptr + 2) & 0xff) << 8) + (int)(pgm_read_byte_near(currentFont + ptr + 3) & 0xff)); - - if ( cx == c ) { - if ( length < 8 ) { -// Serial.print( "Invalid " ); -// Serial.print( c ); -// Serial.println( " glyph definition. Font corrupted?" ); - break; - } -// Serial.print( c ); - found = true; - width = 0xff & pgm_read_byte_near(currentFont + ptr + 4); - } - - ptr += length; - } - - if ( kerning != NULL && kerning[kernPtr] > -100 ) { - kern = kerning[kernPtr]; - if (kerning[kernPtr+1] > -100) { - kernPtr++; - } - } - - if ( found ) { - x1 += width; - if ( kern > -100 ) { - x1+= kern; - } - } - } - - return x1; -} - -void PixelsBase::drawGlyph(int16_t fontType, boolean clean, int16_t xx, int16_t yy, - int16_t glyphHeight, prog_uchar* data, int16_t length) { - - int16_t glyphWidth = 0xff & pgm_read_byte_near(data + 4); - int16_t mLeft = 0x7f & pgm_read_byte_near(data + 5); - int16_t mTop = 0xff & pgm_read_byte_near(data + 6); - int16_t mRight = 0x7f & pgm_read_byte_near(data + 7); - - int16_t effWidth = glyphWidth - mLeft - mRight; - - boolean vraster = (0x80 & pgm_read_byte_near(data + 5)) > 0; - boolean compressed = (pgm_read_byte_near(data + 7) & 0x80) > 0; - - RGB* fg = foreground; - RGB* bg = background; - - int16_t ctr = 0; -#ifndef NO_FILL_TEXT_BACKGROUND - int16_t prev = -1; - int16_t last = -1; -#endif - - int16_t eff = vraster ? - glyphHeight - mTop - mRight : - glyphWidth - mLeft - mRight; - - int16_t offsetLeft = mLeft + xx; - int16_t offsetTop = mTop + yy; - - int16_t hEdge = xx + glyphWidth; - int16_t vEdge = yy + glyphHeight; - - length -= 8; - - if ( !(fontType == BITMASK_FONT && !compressed) ) { - - int16_t edge = vraster ? offsetTop + eff - 1 : offsetLeft + eff - 1; - - for ( int16_t i = 0; i < length; i++ ) { - int16_t p1 = ctr / eff; - int16_t p2 = ctr % eff; - - int16_t b = 0xff & pgm_read_byte_near(data + 8 + i); - int16_t len = 0x7f & b; - boolean color = fontType == BITMASK_FONT ? (0x80 & b) > 0 : true; - - if ( color || glyphPrintMode == FILL_TEXT_BACKGROUND ) { - -#ifndef NO_FILL_TEXT_BACKGROUND - if ( glyphPrintMode == FILL_TEXT_BACKGROUND && prev != p1 ) { - setColor(bg); - if ( vraster ) { - if ( prev < 0 ) { - fillRectangle(xx, yy, mLeft + 1, glyphHeight + 1); - } else { - vLine(offsetLeft + p1, yy, vEdge); - } - } else { - if ( prev < 0 ) { - fillRectangle(xx, yy, glyphWidth + 1, mTop + 1); - } else { - hLine(xx, offsetTop + p1, hEdge); - } - } - prev = p1; - } -#endif - int16_t x = vraster ? offsetLeft + p1 : offsetLeft + p2; - int16_t y = vraster ? offsetTop + p2 : offsetTop + p1; - - if ( color && !clean ) { - setColor(fg); - } else { - setColor(bg); - } - - if ( fontType == BITMASK_FONT || (0xc0 & b) > 0 ) { - if ( fontType == ANTIALIASED_FONT ) { - len = 0x3f & b; - ctr += len; - } - - if ( fontType == BITMASK_FONT || (0x80 & b) > 0 ) { - - while ( p2 + len > eff ) { - if ( color ) { - if ( vraster ) { - vLine(x, y, edge); - } else { - hLine(x, y, edge); - } - } - if (fontType == BITMASK_FONT) { - ctr += eff - p2; - } - - len -= eff - p2; - p2 = 0; - p1++; - x = vraster ? offsetLeft + p1 : offsetLeft; - y = vraster ? offsetTop : offsetTop + p1; - -#ifndef NO_FILL_TEXT_BACKGROUND - if ( glyphPrintMode == FILL_TEXT_BACKGROUND ) { - setColor(bg); - if ( vraster ) { - vLine(x, yy, vEdge); - } else { - hLine(xx, y, hEdge); - } - if ( !clean ) { - setColor(fg); - } - prev = p1; - } -#endif - } - - if ( color ) { - if ( vraster ) { - vLine(x, y, y + len - 1); - } else { - hLine(x, y, x + len - 1); - } - } else { - setColor(fg); - } - -#ifndef NO_FILL_TEXT_BACKGROUND - } else if ( fontType == ANTIALIASED_FONT && glyphPrintMode == FILL_TEXT_BACKGROUND ) { - setColor(bg); - while ( p2 + len > eff ) { - len -= eff - p2; - p2 = 0; - p1++; - x = vraster ? offsetLeft + p1 : offsetLeft; - y = vraster ? offsetTop : offsetTop + p1; - - if ( vraster ) { - vLine(x, yy, vEdge); - } else { - hLine(xx, y, hEdge); - } - } - prev = p1; - if ( !clean ) { - setColor(fg); - } -#endif - } - } else if (fontType == ANTIALIASED_FONT) { - if ( clean ) { - setColor(bg); - } else { - uint8_t opacity = (0xff & (b << 2)); - RGB* cl = computeColor(fg, opacity); - setColor(cl); - } - drawPixel(x, y); - ctr++; - } -#ifndef NO_FILL_TEXT_BACKGROUND - last = p1; -#endif - } - if ( fontType == BITMASK_FONT ) { - ctr += len; - } - } - -#ifndef NO_FILL_TEXT_BACKGROUND - if ( glyphPrintMode == FILL_TEXT_BACKGROUND ) { - setColor(bg); - if ( vraster ) { - fillRectangle(offsetLeft + last + 1, yy, glyphWidth - mLeft - last - 1, glyphHeight + 1); - } else { - fillRectangle(xx, offsetTop + last + 1, glyphWidth, glyphHeight - mTop - last); - } - } -#endif - - } else { - - if ( clean ) { - setColor(bg); - } - - for ( int16_t i = 0; i < length; i++ ) { - int16_t b = 0xff & pgm_read_byte_near(data + 8 + i); - int16_t x = i * 8 % effWidth; - int16_t y = i * 8 / effWidth; - -#ifndef NO_FILL_TEXT_BACKGROUND - if ( glyphPrintMode == FILL_TEXT_BACKGROUND && prev != y ) { - setColor(bg); - if ( prev < 0 ) { - fillRectangle(xx, yy, glyphWidth + 1, mTop + 1); - } else { - hLine(xx, offsetTop + y, hEdge); - } - if ( !clean ) { - setColor(fg); - } - prev = y; - } -#endif - - for ( uint8_t j = 0; j < 8; j++ ) { - if ( x + j == effWidth ) { - x = -j; - y++; -#ifndef NO_FILL_TEXT_BACKGROUND - if ( glyphPrintMode == FILL_TEXT_BACKGROUND && prev != y ) { - setColor(bg); - hLine(xx, offsetTop + y, hEdge); - if ( !clean ) { - setColor(fg); - } - prev = y; - } -#endif - } - int mask = 1 << (7 - j); - if ( (b & mask) == 0 ) { - drawPixel(offsetLeft + x + j, offsetTop + y); - } - } -#ifndef NO_FILL_TEXT_BACKGROUND - last = y; -#endif - } - -#ifndef NO_FILL_TEXT_BACKGROUND - if ( glyphPrintMode == FILL_TEXT_BACKGROUND ) { - setColor(bg); - fillRectangle(xx, offsetTop + last + 1, glyphWidth + 1, glyphHeight - mTop - last); - } -#endif - } - - setColor(fg); -} - -void PixelsBase::scrollText( int16_t x, int16_t y, String text, uint8_t scrollStep, uint8_t repeat, uint16_t maxScroll ) { - - int extraRowDelay = 0; // increase to slow down - - if ( getOrientation() % 2 == 0 ) { - setOrientation(LANDSCAPE); - } - - if ( repeat != 0 ) { - repeat++; - } - - int maxX = getWidth() - 1; - int tw = getTextWidth(text); - - int skip = -1; - int loopLen = tw + x; - int space = maxX - x; - - if (getScroll() > 0) { - skip = (x - getScroll()) % getWidth(); - loopLen += maxX - getScroll(); - space = getScroll() - x; - } - - int easingLen = 5; - if ( loopLen / 2 < easingLen) { - easingLen = loopLen / 2; - } - loopLen += easingLen; - - int dlx = 8; - int factor = 3 + extraRowDelay; - int remains = 0; - - long maxLatency = 0; - boolean firstLoop = true; - - if ( maxScroll > 0 ) { - loopLen = maxScroll; - repeat = 2; - } - - do { - for ( int i = 0; i < loopLen; i+=scrollStep ) { - - long startMillis = millis(); - - int p = -1; - int e = -1; - int xx = 0; - int cw = 0; - - int l = 0; - int f = 0; - for (int t = 0; t < (int)text.length(); t++) { - char c = text.charAt(t); - f = l; - cw = getCharWidth(c); - if ( cw < 0 ) { - return; - } - l += cw; - if ( l > remains && p < 0 ) { - xx = f; - p = t; - } - if ( l > space ) { - e = t + 1; - break; - } - } - if ( p < 0 ) { - p = text.length(); - } - if ( e < 0 ) { - e = text.length(); - } - - String s = text.substring(p, e); - int q = (x + xx) % getWidth(); - - if ( q != 0 || x != getWidth() || getScroll() != 0 ) { - if (i > skip) { - print(q, y, s); - } - } - if ( q > maxX - cw && q > getScroll() && getScroll() != 0 ) { - print(q - getWidth(), y, s); - } - - remains = space; - space += scrollStep; - scroll(scrollStep, SCROLL_CLEAN); - - long endMillis = millis(); -#ifndef PIXELMEISTER - if ( s.length() < 3 ) { - long latency = endMillis - startMillis; - - if (maxLatency > latency) { - delay((int)(maxLatency - latency)); - } else { - if( firstLoop ) { - maxLatency = latency; - } - } - } -#endif - - if ( i < easingLen ) { - delay(dlx+(easingLen-i)*(easingLen-i)*factor/2); - } else { - if ( loopLen > 150 ) { - delay(factor); - } else { - delay(dlx+factor); - } - } - startMillis = endMillis; - } - - firstLoop = false; - - remains = 0; - space = 0; - easingLen = 0; - skip = -1; - - x = getWidth(); - loopLen = x + tw; - - if ( maxScroll <= 0 ) { - scroll(-getScroll(), 0); - } - - if ( repeat != 0 ) { - repeat--; - } - } while ( repeat == 0 || repeat > 1 ); -} - - -/* Low level */ - -void PixelsBase::putColor(int16_t x, int16_t y, boolean steep, double alpha) { - - if ( steep ) { - int16_t tmp = x; - x = y; - y = tmp; - } - - if ( x < 0 || x >= width || y < 0 || y >= height ) { - return; - } - - RGB* result; - if ( alpha != 1 ) { - RGB* bg = getPixel(x, y); - result = computeColor(bg, alpha); - RGB* sav = getColor(); - setColor(result); - drawPixel(x, y); - setColor(sav); - } else { - drawPixel(x, y); - } -} - - -RGB* PixelsBase::computeColor(RGB* bg, double alpha) { - if ( alpha < 0 ) { - alpha = 0; - return bg; - } - if ( alpha > 1 ) { - alpha = 1; - } - computedBgColor->setColor( (int32_t)(bg->red * (1 - alpha) + foreground->red * alpha), - (int32_t)(bg->green * (1 - alpha) + foreground->green * alpha), - (int32_t)(bg->blue * (1 - alpha) + foreground->blue * alpha)); - - return computedBgColor; -} - -RGB* PixelsBase::computeColor(RGB* fg, uint8_t opacity) { - int32_t sr = (int32_t)fg->red * (255 - opacity) + background->red * opacity; - int32_t sg = (int32_t)fg->green * (255 - opacity) + background->green * opacity; - int32_t sb = (int32_t)fg->blue * (255 - opacity) + background->blue * opacity; - sr /= 255; - sg /= 255; - sb /= 255; - if ( sr > 255 ) { - sr = 255; - } - if ( sg > 255 ) { - sg = 255; - } - if ( sb > 255 ) { - sb = 255; - } - computedFgColor->setColor(sr, sg, sb); - - return computedFgColor; -} - -void PixelsBase::scroll(int16_t dy, int8_t flags) { - scroll(dy, 0, deviceWidth, flags); -} - -void PixelsBase::scroll(int16_t dy, int16_t x1, int16_t x2, int8_t flags) { - - if(!canScroll()) { - return; - } - - int16_t mdy = dy > 0 ? dy : -dy; - - if (mdy > 1 && (flags & SCROLL_SMOOTH) > 0) { - - int16_t easingLen = 8; - if ( mdy / 2 < easingLen) { - easingLen = mdy / 2; - } - - int16_t dlx = (flags & SCROLL_CLEAN) > 0 ? 0 : 7; - int16_t factor = 1; - - int16_t step = dy < 0 ? -1 : 1; - for ( int16_t i = 0; i < easingLen; i++ ) { - delay(dlx+(easingLen-i)*(easingLen-i)*factor/2+extraScrollDelay); - scroll(step, x1, x2, flags & SCROLL_CLEAN); - } - for ( int16_t i = 0; i < mdy - easingLen*2; i++ ) { - scroll(step, x1, x2, flags & SCROLL_CLEAN); - if ( mdy > 150 ) { - delay(factor); - } else { - delay(dlx+factor+extraScrollDelay); - } - } - for ( int16_t i = 1; i <= easingLen; i++ ) { - scroll(step, x1, x2, flags & SCROLL_CLEAN); - delay(dlx+i*i*factor/2+extraScrollDelay); - } - - } else { - - RGB* sav = getColor(); - setColor(getBackground()); - boolean savorigin = relativeOrigin; - relativeOrigin = false; - - beginGfxOperation(); - - if ( (flags & SCROLL_CLEAN) > 0 && dy > 0 ) { - if( (orientation % 2) == 0 ) { // PORTRAIT(_FLIP) - fillRectangle(0, 0, deviceWidth, mdy); - } else { - fillRectangle(0, 0, mdy, deviceWidth); - } - } - - currentScroll += dy; - while ( currentScroll < 0 ) { - currentScroll += deviceHeight; - } - currentScroll %= deviceHeight; - - scrollCmd(); - - if ( (flags & SCROLL_CLEAN) > 0 && dy < 0 ) { - if( (orientation % 2) == 0 ) { // PORTRAIT(_FLIP) - fillRectangle(0, 0, deviceWidth, mdy); - } else { - fillRectangle(0, 0, mdy, deviceWidth); - } - } - - relativeOrigin = savorigin; - setColor(sav); - - endGfxOperation(true); - } -} - - -void PixelsBase::drawPixel(int16_t x, int16_t y) { - - if ( x < 0 || y < 0 || x >= width || y >= height ) { - return; - } - - int xx = x; - int yy = y; - - int s = getScroll(); - - if ( relativeOrigin ) { - switch( orientation ) { - case PORTRAIT: - if ( s > 0 && y >= s ) { - return; - } - break; - case LANDSCAPE: - if ( s > 0 && x >= s ) { - return; - } - xx = deviceWidth - y - 1; - yy = x; - break; - case PORTRAIT_FLIP: - if ( s > 0 && y >= s ) { - return; - } - xx = deviceWidth - x - 1; - yy = deviceHeight - y - 1; - break; - case LANDSCAPE_FLIP: - if ( s > 0 && x >= s ) { - return; - } - xx = y; - yy = deviceHeight - x - 1; - break; - } - } else { - switch( orientation ) { - case PORTRAIT: - yy += s; - break; - case LANDSCAPE: - xx = deviceWidth - y - 1; - yy = x + s; - break; - case PORTRAIT_FLIP: - xx = deviceWidth - x - 1; - yy = 2 * deviceHeight - y - 1 - s; - break; - case LANDSCAPE_FLIP: - xx = y; - yy = 2 * deviceHeight - x - 1 - s; - break; - } - yy %= deviceHeight; - } - - beginGfxOperation(); - setRegion(xx, yy, xx, yy); - setCurrentPixel(foreground); - endGfxOperation(); -} - -void PixelsBase::fill(int color, int16_t x1, int16_t y1, int16_t x2, int16_t y2) { - - Bounds bb(x1, y1, x2, y2); - if( !transformBounds(bb) ) { - return; - } - - beginGfxOperation(); - - if ( relativeOrigin ) { - quickFill(color, bb.x1, bb.y1, bb.x2, bb.y2); - } else { - int s = currentScroll; - if ( orientation > 1 ) { - s = (deviceHeight - s - 1) % deviceHeight; - bb.y1 += s; - bb.y2 += s; - } else { - bb.y1 += s; - bb.y2 += s; - } - bb.y1 %= deviceHeight; - bb.y2 %= deviceHeight; - - if ( bb.y1 > bb.y2 ) { - quickFill(color, bb.x1, bb.y1, bb.x2, deviceHeight-1); - quickFill(color, bb.x1, 0, bb.x2, bb.y2); - } else { - quickFill(color, bb.x1, bb.y1, bb.x2, bb.y2); - } - } - - endGfxOperation(); -} - -void PixelsBase::hLine(int16_t x1, int16_t y, int16_t x2) { - fill(foreground->convertTo565(), x1, y, x2, y); -} - -void PixelsBase::vLine(int16_t x, int16_t y1, int16_t y2) { - fill(foreground->convertTo565(), x, y1, x, y2); -} - -void PixelsBase::resetRegion() { - setRegion(0, 0, deviceWidth, deviceHeight); -} - -void PixelsBase::setCurrentPixel(int16_t color) { - deviceWriteData(highByte(color), lowByte(color)); -} - -void PixelsBase::setCurrentPixel(RGB* color) { - int16_t c = color->convertTo565(); - deviceWriteData(highByte(c), lowByte(c)); -} - -boolean PixelsBase::transformBounds(Bounds& bb) { - - int16_t buf; - switch( orientation ) { - case PORTRAIT: - break; - case LANDSCAPE: - buf = bb.x1; - bb.x1 = deviceWidth - bb.y1 - 1; - bb.y1 = buf; - buf = bb.x2; - bb.x2 = deviceWidth - bb.y2 - 1; - bb.y2 = buf; - break; - case PORTRAIT_FLIP: - bb.y1 = deviceHeight - bb.y1 - 1; - bb.y2 = deviceHeight - bb.y2 - 1; - bb.x1 = deviceWidth - bb.x1 - 1; - bb.x2 = deviceWidth - bb.x2 - 1; - break; - case LANDSCAPE_FLIP: - buf = bb.y1; - bb.y1 = deviceHeight - bb.x1 - 1; - bb.x1 = buf; - buf = bb.y2; - bb.y2 = deviceHeight - bb.x2 - 1; - bb.x2 = buf; - break; - } - - if (bb.y2 < bb.y1) { - swap(bb.y1, bb.y2); - } - - if (bb.x2 < bb.x1) { - swap(bb.x1, bb.x2); - } - - return true; -} - -boolean PixelsBase::checkBounds(Bounds& bb) { - if (bb.x2 < bb.x1) { - swap(bb.x1, bb.x2); - } - if (bb.y2 < bb.y1) { - swap(bb.y1, bb.y2); - } - - if ( bb.x1 < 0 ) { - if ( bb.x2 < 0 ) { - return false; - } - bb.x1 = 0; - } - if ( bb.x2 >= deviceWidth ) { - if ( bb.x1 >= deviceWidth ) { - return false; - } - bb.x2 = deviceWidth - 1; - } - - int16_t s = (relativeOrigin && orientation > 1) ? (deviceHeight - currentScroll) % deviceHeight : 0; - if ( bb.y1 < s ) { - if ( bb.y2 < s ) { - return false; - } - bb.y1 = s; - } - s = (relativeOrigin && orientation < 2 && currentScroll > 0) ? currentScroll : deviceHeight; - if ( bb.y2 >= s ) { - if ( bb.y1 >= s ) { - return false; - } - bb.y2 = s - 1; - } - - return true; -} - - +/* + * Pixels. Graphics library for TFT displays. + * + * Copyright (C) 2012-2015 Igor Repinetski + * + * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. + * + * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ + * + * This library includes some code portions and algoritmic ideas derived from works of + * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) + * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) + */ + +#include "Pixels.h" +#include + +/* JK ADDITION */ +#define SPICLOCK 30000000 +#define WIDTH ILI9341_TFTWIDTH +#define HEIGHT ILI9341_TFTHEIGHT +/* JK ADDITION END */ + +RGB::RGB(uint8_t r, uint8_t g, uint8_t b) { + setColor(r, g, b); +} + +RGB::RGB() { +} + +void RGB::setColor(int32_t r, int32_t g, int32_t b) { + red = r; + green = g; + blue = b; + col = (((uint16_t)red / 8) << 11) | ((green / 4) << 5) | (blue / 8); +} + + +RGB RGB::convert565toRGB(uint16_t color) { + uint8_t r = ((0xf800 & color)>>11) * 255 / 31; + uint8_t g = ((0x7e0 & color)>>5) * 255 / 63; + uint8_t b = (0x1f & color) * 255 / 31; + return RGB(r, g, b); +} + +uint16_t RGB::convertRGBto565(RGB color) { + return ((color.red / 8) << 11) | ((color.green / 4) << 5) | (color.blue / 8); +} + +uint16_t RGB::convertTo565() { + return col; // ((red / 8) << 11) | ((green / 4) << 5) | (blue / 8); +} + +regtype *registerCS; // chip select +regsize bitmaskCS; + +PixelsBase::PixelsBase(uint16_t width, uint16_t height) { + deviceWidth = width < height ? width : height; + deviceHeight = width > height ? width : height; + this->width = width; + this->height = height; + setOrientation( width > height ? LANDSCAPE : PORTRAIT ); + + relativeOrigin = true; + + currentScroll = 0; + scrollSupported = true; + scrollEnabled = true; + extraScrollDelay = 0; + + lineWidth = 1; + fillDirection = 0; + + computedBgColor = new RGB(0, 0, 0); + computedFgColor = new RGB(0, 0, 0); + bgBuffer = new RGB(0, 0, 0); + fgBuffer = new RGB(0, 0, 0); + + gfxOpNestingDepth = 0; + + setBackground(0,0,0); + setColor(0xFF,0xFF,0xFF); +} + +void PixelsBase::setOrientation( uint8_t direction ){ + + if ( (orientation < 2 && direction > 1) || (orientation > 1 && direction < 2) ) { + currentScroll = 2 * deviceHeight - currentScroll; + currentScroll %= deviceHeight; + } + orientation = direction; + + landscape = false; + + switch ( orientation ) { + case LANDSCAPE_FLIP: + case LANDSCAPE: + width = deviceHeight; + height = deviceWidth; + landscape = true; + break; + case PORTRAIT_FLIP: + width = deviceWidth; + height = deviceHeight; + break; + default: + width = deviceWidth; + height = deviceHeight; + orientation = PORTRAIT; + break; + } +} + +/* Graphic primitives */ + +void PixelsBase::clear() { + boolean s = relativeOrigin; + relativeOrigin = false; + RGB* sav = getColor(); + setColor(background); + fillRectangle(0, 0, width, height); + setColor(sav); + relativeOrigin = s; +} + +RGB* PixelsBase::getPixel(int16_t x, int16_t y) { + return getBackground(); +} + +void PixelsBase::drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { + + beginGfxOperation(); + + if (y1 == y2 && lineWidth == 1) { + hLine(x1, y1, x2); + } else if (x1 == x2 && lineWidth == 1) { + vLine(x1, y1, y2); + } else { + if ( lineWidth == 1 ) { + if ( antialiasing ) { + drawLineAntialiased(x1, y1, x2, y2); + } else { + int16_t dx; + int16_t dy; + int16_t sx; + int16_t sy; + + if ( x2 > x1 ) { + dx = x2 - x1; + sx = 1; + } else { + dx = x1 - x2; + sx = -1; + } + + if ( y2 > y1 ) { + dy = y2 - y1; + sy = 1; + } else { + dy = y1 - y2; + sy = -1; + } + + int16_t x = x1; + int16_t y = y1; + int16_t err = dx - dy; + int16_t e2; + while (true) { + drawPixel(x, y); + if (x == x2 && y == y2) { + break; + } + e2 = err << 1; + if (e2 > -dy) { + err = err - dy; + x = x + sx; + } + if (e2 < dx) { + err = err + dx; + y = y + sy; + } + } + } + } else { + drawFatLineAntialiased(x1, y1, x2, y2); + } + } + + endGfxOperation(); +} + +void PixelsBase::drawRectangle(int16_t x, int16_t y, int16_t width, int16_t height) { + beginGfxOperation(); + hLine(x, y, x+width-2); + vLine(x+width-1, y, y+height-2); + hLine(x+1, y+height-1, x+width-1); + vLine(x, y+1, y+height-1); + endGfxOperation(); +} + +void PixelsBase::fillRectangle(int16_t x, int16_t y, int16_t width, int16_t height) { + beginGfxOperation(); + fill(foreground->convertTo565(), x, y, x+width-1, y+height-1); + endGfxOperation(); +} + +void PixelsBase::drawRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t radius) { + + if ( radius < 1 ) { + drawRectangle(x, y, width, height); + return; + } + + if ( radius > height >> 2 ) { + radius = height >> 2; + } + if ( radius > width >> 1 ) { + radius = width >> 1; + } + + beginGfxOperation(); + + if ( antialiasing ) { + drawRoundRectangleAntialiased(x, y, width, height, radius, radius, 0); + } else { + height--; + width--; + + hLine(x + radius, y + height, x + width - radius); + hLine(x + radius, y, x + width - radius ); + vLine(x + width, y + radius, y + height - radius); + vLine(x, y + radius, y + height - radius); + + int16_t shiftX = width - (radius << 1); + int16_t shiftY = height - (radius << 1); + int16_t f = 1 - radius; + int16_t ddF_x = 1; + int16_t ddF_y = - (radius << 1); + int16_t x1 = 0; + int16_t y1 = radius; + + int16_t xx = x + radius; + int16_t yy = y + radius; + + while (x1 < y1) { + if (f >= 0) { + y1--; + ddF_y += 2; + f += ddF_y; + } + x1++; + ddF_x += 2; + f += ddF_x; + + drawPixel(xx + x1 + shiftX, yy + y1 + shiftY); + drawPixel(xx - x1, yy + y1 + shiftY); + drawPixel(xx + x1 + shiftX, yy - y1); + drawPixel(xx - x1, yy - y1); + drawPixel(xx + y1 + shiftX, yy + x1 + shiftY); + drawPixel(xx - y1, yy + x1 + shiftY); + drawPixel(xx + y1 + shiftX, yy - x1); + drawPixel(xx - y1, yy - x1); + } + } + + endGfxOperation(); +} + +void PixelsBase::fillRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t radius) { + + if ( radius < 1 ) { + fillRectangle(x, y, width, height); + return; + } + + if ( radius > height >> 1 ) { + radius = height >> 1; + } + if ( radius > width >> 1 ) { + radius = width >> 1; + } + + beginGfxOperation(); + + if ( antialiasing ) { + drawRoundRectangleAntialiased(x, y, width-1, height-1, radius, radius, true); + } + + fillRectangle(x + radius, y + height - radius, width - (radius << 1), radius); + fillRectangle(x, y + radius, width, height - (radius << 1)); + fillRectangle(x + radius, y, width - (radius << 1), radius); + + height--; + width--; + + int16_t shiftX = width - (radius << 1); + int16_t shiftY = height - (radius << 1); + int16_t f = 1 - radius; + int16_t ddF_x = 1; + int16_t ddF_y = -(radius << 1); + int16_t x1 = 0; + int16_t y1 = radius; + + int16_t xx = x + radius; + int16_t yy = y + radius; + + while (x1 < y1) { + if (f >= 0) { + y1--; + ddF_y += 2; + f += ddF_y; + } + x1++; + ddF_x += 2; + f += ddF_x; + + hLine(xx + shiftX, yy - y1, xx + shiftX + x1); + hLine(xx - x1, yy - y1, xx); + hLine(xx + shiftX, yy - x1, xx + shiftX + y1); + hLine(xx - y1, yy - x1, xx); + + hLine(xx + shiftX, yy + y1 + shiftY, xx + x1 + shiftX); + hLine(xx + shiftX, yy + x1 + shiftY, xx + shiftX + y1); + hLine(xx - x1, yy + y1 + shiftY, xx); + hLine(xx - y1, yy + x1 + shiftY, xx); + } + + endGfxOperation(); +} + +void PixelsBase::drawCircle(int16_t x, int16_t y, int16_t r) { + + drawOval(x-r, y-r, r<<1, r<<1); + +// if ( antialiasing ) { +// drawCircleAntialiaced(x, y, r, false); +// } else { +// int16_t f = 1 - r; +// int16_t ddF_x = 1; +// int16_t ddF_y = -2 * r; +// int16_t x1 = 0; +// int16_t y1 = r; + +// drawPixel(x, y + r); +// drawPixel(x, y - r); +// drawPixel(x + r, y); +// drawPixel(x - r, y); + +// while (x1 < y1) { +// if (f >= 0) { +// y1--; +// ddF_y += 2; +// f += ddF_y; +// } +// x1++; +// ddF_x += 2; +// f += ddF_x; +// drawPixel(x + x1, y + y1); +// drawPixel(x - x1, y + y1); +// drawPixel(x + x1, y - y1); +// drawPixel(x - x1, y - y1); +// drawPixel(x + y1, y + x1); +// drawPixel(x - y1, y + x1); +// drawPixel(x + y1, y - x1); +// drawPixel(x - y1, y - x1); +// } +// } +} + +void PixelsBase::fillCircle(int16_t x, int16_t y, int16_t r) { +// int16_t yy; +// int16_t xx; + + fillOval(x-r, y-r, r<<1, r<<1); + +// if ( antialiasing ) { +// drawCircleAntialiaced(x, y, r, true); +// } + +// for (yy = -r; yy <= r; yy++) { +// for (xx = -r; xx <= r; xx++) { +// if ((xx * xx) + (yy * yy) <= (r * r)) { +// drawPixel(x+xx, y+yy); +// } +// } +// } +} + +void PixelsBase::drawOval(int16_t x, int16_t y, int16_t width, int16_t height) { + + if ((width <= 0) || (height <= 0)) { + return; + } + + beginGfxOperation(); + + if ( antialiasing ) { + drawRoundRectangleAntialiased(x, y, width, height, width/2, height/2, 0); + } else { + height--; + width--; + + int16_t ix, iy; + int16_t h, i, j, k; + int16_t oh, oi, oj, ok; + int16_t xmh, xph, ypk, ymk; + int16_t xmi, xpi, ymj, ypj; + int16_t xmj, xpj, ymi, ypi; + int16_t xmk, xpk, ymh, yph; + + int16_t rx = width / 2; + int16_t ry = height / 2; + + int16_t xx = x + rx; + int16_t yy = y + ry; + + if (width == 1) { + vLine(xx, yy, yy + height - 1); + endGfxOperation(); + return; + } + if (height == 1) { + hLine(xx, yy, xx + width - 1); + endGfxOperation(); + return; + } + + oh = oi = oj = ok = 0xFFFF; + + if (width > height) { + ix = 0; + iy = rx << 6; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if (((ok != k) && (oj != k)) || ((oj != j) && (ok != j)) || (k != j)) { + xph = xx + h; + xmh = xx - h; + if (k > 0) { + ypk = yy + k; + ymk = yy - k; + drawPixel(xmh, ypk); + drawPixel(xph, ypk); + drawPixel(xmh, ymk); + drawPixel(xph, ymk); + } else { + drawPixel(xmh, yy); + drawPixel(xph, yy); + } + ok = k; + xpi = xx + i; + xmi = xx - i; + if (j > 0) { + ypj = yy + j; + ymj = yy - j; + drawPixel(xmi, ypj); + drawPixel(xpi, ypj); + drawPixel(xmi, ymj); + drawPixel(xpi, ymj); + } else { + drawPixel(xmi, yy); + drawPixel(xpi, yy); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry << 6; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if (((oi != i) && (oh != i)) || ((oh != h) && (oi != h) && (i != h))) { + xmj = xx - j; + xpj = xx + j; + if (i > 0) { + ypi = yy + i; + ymi = yy - i; + drawPixel(xmj, ypi); + drawPixel(xpj, ypi); + drawPixel(xmj, ymi); + drawPixel(xpj, ymi); + } else { + drawPixel(xmj, yy); + drawPixel(xpj, yy); + } + oi = i; + xmk = xx - k; + xpk = xx + k; + if (h > 0) { + yph = yy + h; + ymh = yy - h; + drawPixel(xmk, yph); + drawPixel(xpk, yph); + drawPixel(xmk, ymh); + drawPixel(xpk, ymh); + } else { + drawPixel(xmk, yy); + drawPixel(xpk, yy); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + } + + endGfxOperation(); +} + +void PixelsBase::fillOval(int16_t xx, int16_t yy, int16_t width, int16_t height) { + + height--; + width--; + + int16_t rx = width / 2; + int16_t ry = height / 2; + + int16_t x = xx + rx; + int16_t y = yy + ry; + + int16_t ix, iy; + int16_t h, i, j, k; + int16_t oh, oi, oj, ok; + int16_t xmh, xph; + int16_t xmi, xpi; + int16_t xmj, xpj; + int16_t xmk, xpk; + + if ((rx < 0) || (ry < 0)) { + return; + } + + if (width < 2) { + vLine(xx, yy, yy + height); + return; + } + + if (height < 2) { + hLine(xx, yy, xx + width); + return; + } + + beginGfxOperation(); + + if ( antialiasing ) { + drawRoundRectangleAntialiased(x-rx, y-ry, rx<<1, ry<<1, rx, ry, true); + } + + oh = oi = oj = ok = 0xFFFF; + + if (rx > ry) { + ix = 0; + iy = rx << 6; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if ((ok != k) && (oj != k)) { + xph = x + h; + xmh = x - h; + if (k > 0) { + hLine(xmh, y + k, xph); + hLine(xmh, y - k, xph); + } else { + hLine(xmh, y, xph); + } + ok = k; + } + if ((oj != j) && (ok != j) && (k != j)) { + xmi = x - i; + xpi = x + i; + if (j > 0) { + hLine(xmi, y + j, xpi); + hLine(xmi, y - j, xpi); + } else { + hLine(xmi, y, xpi); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry << 6; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if ((oi != i) && (oh != i)) { + xmj = x - j; + xpj = x + j; + if (i > 0) { + hLine(xmj, y + i, xpj); + hLine(xmj, y - i, xpj); + } else { + hLine(xmj, y, xpj); + } + oi = i; + } + if ((oh != h) && (oi != h) && (i != h)) { + xmk = x - k; + xpk = x + k; + if (h > 0) { + hLine(xmk, y + h, xpk); + hLine(xmk, y - h, xpk); + } else { + hLine(xmk, y, xpk); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + + endGfxOperation(); +} + +void PixelsBase::drawIcon(int16_t xx, int16_t yy, prog_uchar* data) { + + int16_t fontType = BITMASK_FONT; + if ( pgm_read_byte_near(data + 1) == 'a' ) { + fontType = ANTIALIASED_FONT; + } + + int16_t length = ((0xFF & (int32_t)pgm_read_byte_near(data + 2)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 3)); + + int16_t height = pgm_read_byte_near(data + 4); + + beginGfxOperation(); + drawGlyph(fontType, false, xx, yy, height, data + 1, length-1); + endGfxOperation(); +} + +void PixelsBase::cleanIcon(int16_t xx, int16_t yy, prog_uchar* data) { + + int16_t fontType = BITMASK_FONT; + if ( pgm_read_byte_near(data + 1) == 'a' ) { + fontType = ANTIALIASED_FONT; + } + + int16_t length = ((0xFF & (int32_t)pgm_read_byte_near(data + 2)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 3)); + + int16_t height = pgm_read_byte_near(data + 4); + + beginGfxOperation(); + drawGlyph(fontType, true, xx, yy, height, data + 1, length-1); + endGfxOperation(); +} + +int8_t PixelsBase::drawBitmap(int16_t x, int16_t y, int16_t width, int16_t height, prog_uint16_t* data) { + + Bounds bb(x, y, x+width-1, y+height-1); + if( !transformBounds(bb) ) { + return 1; + } + + if( !checkBounds(bb) ) { + return 1; + } + + beginGfxOperation(); + setRegion(bb.x1, bb.y1, bb.x2, bb.y2); + + int sc = currentScroll; + if ( sc == 0 ) { + sc = deviceHeight; + } + + switch( orientation ) { + case PORTRAIT: + { + for ( int16_t j = bb.y1; j <= bb.y2; j++ ) { + for ( int16_t i = bb.x1; i <= bb.x2; i++ ) { + int16_t px = pgm_read_word_near(data + (j - y) * width + i - x); + setCurrentPixel(px); + } + } + } + break; + case LANDSCAPE: + { + int h1 = height - max(0, (y + height) - deviceWidth) - 1; + int w1 = width - max(0, (x + width) - sc) - 1; + int w = bb.x2 - bb.x1 + 1; + int h = bb.y2 - bb.y1 + 1; + for ( int16_t j = h - 1; j >= 0; j-- ) { + for ( int16_t i = 0; i < w; i++ ) { + int16_t px = pgm_read_word_near(data + (h1 - i) * width + (w1 - j)); + setCurrentPixel(px); + } + } + } + break; + case PORTRAIT_FLIP: + { + int h = bb.y2 - bb.y1 + 1; + int w = bb.x2 - bb.x1 + 1; + int cutH = y < 0 ? 0 : height - h; + int cutW = x < 0 ? 0 : width - w; + for ( int16_t j = 0; j < h; j++ ) { + for ( int16_t i = 0; i < w; i++ ) { + int16_t px = pgm_read_word_near(data + (height - j - 1 - cutH) * width + (width - i - 1 - cutW)); + setCurrentPixel(px); + } + } + } + break; + case LANDSCAPE_FLIP: + { + int h1 = height - max(0, (y + height) - deviceWidth) - 1; + int w1 = width - max(0, (x + width) - sc) - 1; + int w = bb.x2 - bb.x1 + 1; + int h = bb.y2 - bb.y1 + 1; + for ( int16_t j = 0; j < h; j++ ) { + for ( int16_t i = w - 1; i >= 0; i-- ) { + int16_t px = pgm_read_word_near(data + (h1 - i) * width + (w1 - j)); + setCurrentPixel(px); + } + } + } + break; + } + + endGfxOperation(); + return 0; +} + +int8_t PixelsBase::drawCompressedBitmap(int16_t x, int16_t y, prog_uchar* data) { + + if ( data == NULL ) { + return -1; + } + + if ( pgm_read_byte_near(data + 0) != 'Z' ) { + // Unknown compression method + return -2; + } + + int32_t compressedLen = ((0xFF & (int32_t)pgm_read_byte_near(data + 1)) << 16) + ((0xFF & (int32_t)pgm_read_byte_near(data + 2)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 3)); + if ( compressedLen < 0 ) { + // Unknown compression method or compressed data inconsistence + return -3; + } + + int32_t resultLen = ((0xFF & (int32_t)pgm_read_byte_near(data + 4)) << 16) + ((0xFF & (int32_t)pgm_read_byte_near(data + 5)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 6)); + if ( resultLen < 0 ) { + // Unknown compression method or compression format error + return resultLen; + } + + uint8_t windowLen = 0xFF & (int16_t)pgm_read_byte_near(data + 7); + if ( windowLen < 0 || windowLen > 254 ) { + // corrupted content + return -5; + } + + int16_t width = ((0xFF & (int32_t)pgm_read_byte_near(data + 8)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 9)); + if ( width < 0 ) { + // Unknown compression method or compression format error (width parameter is invalid) + return -6; + } + + int16_t height = ((0xFF & (int32_t)pgm_read_byte_near(data + 10)) << 8) + (0xFF & (int32_t)pgm_read_byte_near(data + 11)); + if ( height < 0 ) { + // Unknown compression method or compression format error (height parameter is invalid) + return -7; + } + + uint8_t window[windowLen]; + int16_t wptr = 0; + + int32_t ctr = 0; + + uint8_t buf; + bool bufEmpty = true; + + int* raster = NULL; + int rasterPtr = 0; + int rasterLine = y; + + raster = new int[width]; + + beginGfxOperation(); + + BitStream bs( data, compressedLen, 96 ); + while ( true ) { + + uint8_t bit = bs.readBit(); + if ( bit == 0 ) { // literal + uint8_t bits = bs.readBits(8); + if ( bufEmpty ) { + buf = bits; + bufEmpty = false; + } else { + uint16_t px = buf; + px <<= 8; + px |= bits; + raster[rasterPtr++] = px; + if ( rasterPtr == width ) { + Bounds bb(x, rasterLine, x + width - 1, rasterLine); + if( transformBounds(bb) && checkBounds(bb) ) { + setRegion(bb.x1, bb.y1, bb.x2, bb.y2); + + int ww = width; + int corr = 0; + if (bb.x1 == bb.x2) { + ww = bb.y2 - bb.y1 + 1; + } else { + ww = bb.x2 - bb.x1 + 1; + } + + if (x < 0) { + corr = -x; + ww += corr; + } + + if ( orientation < 2 ) { + for ( int i = corr; i < ww; i++ ) { + setCurrentPixel(raster[i]); + } + } else { + for ( int i = min(width, ww - 1); i >= corr; i-- ) { + setCurrentPixel(raster[i]); + } + } + } + + rasterLine++; + rasterPtr = 0; + } + bufEmpty = true; + } + ctr++; + window[wptr++] = bits; + if ( wptr >= windowLen ) { + wptr -= windowLen; + } + } else { + uint8_t offset = (uint8_t)bs.readNumber() - 1; + uint8_t matchCount = (uint8_t)bs.readNumber() - 1; + + while( matchCount-- > 0 ) { + int16_t p1 = wptr - offset; + while ( p1 < 0 ) { + p1 += windowLen; + } + while ( p1 >= windowLen ) { + p1 -= windowLen; + } + int16_t p2 = wptr; + while ( p2 >= windowLen ) { + p2 -= windowLen; + } + wptr++; + ctr++; + + if ( bufEmpty ) { + buf = window[p1]; + bufEmpty = false; + } else { + uint16_t px = buf; + px <<= 8; + px |= window[p1]; + + raster[rasterPtr++] = px; + if ( rasterPtr == width ) { + Bounds bb(x, rasterLine, x + width - 1, rasterLine); + if( transformBounds(bb) && checkBounds(bb) ) { + setRegion(bb.x1, bb.y1, bb.x2, bb.y2); + + int corr = 0; + int ww; + if (bb.x1 == bb.x2) { + ww = bb.y2 - bb.y1 + 1; + } else { + ww = bb.x2 - bb.x1 + 1; + } + + if (x < 0) { + corr = -x; + ww += corr; + } + + + if ( orientation < 2 ) { + for ( int i = corr; i < ww; i++ ) { + setCurrentPixel(raster[i]); + } + } else { + for ( int i = min(width, ww - 1); i >= corr; i-- ) { + setCurrentPixel(raster[i]); + } + } + } + + rasterLine++; + rasterPtr = 0; + } + bufEmpty = true; + } + window[p2] = window[p1]; + } + + while ( wptr >= windowLen ) { + wptr -= windowLen; + } + } + if ( ctr > resultLen ) { + break; + } + } + + delete raster; + + endGfxOperation(); + + return 0; +} + + +int8_t PixelsBase::loadBitmap(int16_t x, int16_t y, int16_t sx, int16_t sy, String path) { +// int16_t* data = loadFileBytes( path ); + return 0; // drawBitmap(x, y, sx, sy, data); +} + +/* ------- Antialiasing ------- */ +/* To be overriden with Pixels_Antialiasing.h */ + +void PixelsBase::drawLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2) {} + +void PixelsBase::drawFatLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2) {} + +void PixelsBase::drawRoundRectangleAntialiased(int16_t x, int16_t y, int16_t width, int16_t height, int16_t rx, int16_t ry, boolean bordermode) {} + +void PixelsBase::drawCircleAntialiaced( int16_t x, int16_t y, int16_t radius, boolean bordermode ) {} + +/* TEXT */ + + +int PixelsBase::setFont(prog_uchar font[]) { + int16_t p1 = pgm_read_byte_near(font + 0); + int16_t p2 = pgm_read_byte_near(font + 1); + if ( p1 != 'Z' || p2 != 'F' ) { +// Serial.print("Invalid font prefix "); +// Serial.print( p1 ); +// Serial.print( " " ); +// Serial.println( p2 ); + currentFont = NULL; + return -1; + } + int16_t fontType = pgm_read_byte_near(font + 2); + if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { +// Serial.println("Unsupported font type"); + currentFont = NULL; + return -1; + } + currentFont = font; + return 0; +} + +void PixelsBase::print(int16_t xx, int16_t yy, String text, int8_t kerning[]) { + beginGfxOperation(); + printString(xx, yy, text, 0, kerning); + endGfxOperation(); +} + + +#ifndef NO_TEXT_WRAP +int16_t PixelsBase::computeBreakPos(String text, int16_t t) { + int16_t breakPos = -1; + String s = t == 0 ? text : text.substring(t, text.length()); + int16_t w = getTextWidth(s); + if ( w + caretX > width - textWrapMarginRight || text.indexOf('\n') >= 0 ) { + char prev = 0; + w = 0; + for ( uint16_t j = t; j < text.length(); j++ ) { + char cc = text.charAt(j); + w += getCharWidth(cc); + if ( (cc == ' ' && prev != ' ') || (cc == '\n' && breakPos >= 0) ) { + if ( caretX + w > width - textWrapMarginRight ) { + break; + } else { + breakPos = j; + if ( cc == '\n' ) { + break; + } + } + } else if ( cc == '\n' ) { + breakPos = j; + break; + } + prev = cc; + } + } + return breakPos; +} +#endif + +void PixelsBase::cleanText(int16_t xx, int16_t yy, String text, int8_t kerning[]) { + beginGfxOperation(); + printString(xx, yy, text, 1, kerning); + endGfxOperation(); +} + +void PixelsBase::printString(int16_t xx, int16_t yy, String text, boolean clean, int8_t kerning[]) { + + if ( currentFont == NULL ) { + return; + } + + int16_t fontType = pgm_read_byte_near(currentFont + 2); + if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { + return; + } + + RGB* fg = foreground; + + int16_t kernPtr = 0; + int16_t kern = -100; // no kerning + + int16_t glyphHeight = pgm_read_byte_near(currentFont + 3); + + caretX = xx; + caretY = yy; + + int16_t glyphWidth = 0; + int breakPos = -1; + +#ifndef NO_TEXT_WRAP + boolean relOrigin = isOriginRelative(); + if ( wrapText ) { + breakPos = computeBreakPos(text, 0); + } +#endif + + for (uint16_t t = 0; t < text.length(); t++) { + char c = text.charAt(t); + +#ifndef NO_TEXT_WRAP + if ( t == breakPos ) { + if ( c == ' ' || c == '\n' ) { + breakPos++; + continue; + } + caretX = textWrapMarginLeft; + caretY = caretY + glyphHeight + textWrapLineGap; + if ( textWrapScroll && (orientation == PORTRAIT_FLIP || orientation == PORTRAIT) && + caretY + glyphHeight + textWrapMarginBottom > height ) { + + RGB* sav = NULL; + if ( textWrapScrollFill != NULL ) { + sav = getBackground(); + setBackground(textWrapScrollFill); + } + scroll(-(height - caretY - glyphHeight - textWrapMarginBottom), SCROLL_SMOOTH | SCROLL_CLEAN); + if ( sav != NULL ) { + setBackground(sav); + } + setOriginAbsolute(); + + caretY = height - glyphHeight - textWrapMarginBottom; + } + breakPos = computeBreakPos(text, t); + } + + boolean repeat = false; +#endif + + boolean found = false; + int16_t ptr = HEADER_LENGTH; + while ( 1 ) { + char cx = (char)(((int)pgm_read_byte_near(currentFont + ptr + 0) << 8) + pgm_read_byte_near(currentFont + ptr + 1)); + if ( cx == 0 ) { + break; + } + int16_t length = (((int)(pgm_read_byte_near(currentFont + ptr + 2) & 0xff) << 8) + (int)(pgm_read_byte_near(currentFont + ptr + 3) & 0xff)); + + if ( cx == c ) { + if ( length < 8 ) { +// Serial.print( "Invalid " ); +// Serial.print( c ); +// Serial.println( " glyph definition. Font corrupted?" ); + break; + } + + glyphWidth = 0xff & pgm_read_byte_near(currentFont + ptr + 4); + found = true; + +#ifndef NO_TEXT_WRAP + if ( wrapText && caretX + glyphWidth > width - textWrapMarginRight ) { + breakPos = t; + repeat = true; + break; + } +#endif + + drawGlyph(fontType, clean, caretX, caretY, glyphHeight, currentFont + ptr, length); + break; + } + ptr += length; + } + +#ifndef NO_TEXT_WRAP + if ( repeat ) { + t--; + continue; + } +#endif + + if ( kerning != NULL && kerning[kernPtr] > -100 ) { + kern = kerning[kernPtr]; + if (kerning[kernPtr+1] > -100) { + kernPtr++; + } + } + + if ( found ) { + caretX += glyphWidth; + if ( kern > -100 ) { + caretX += kern; + } + } + } + +#ifndef NO_TEXT_WRAP + if ( relOrigin ) { + setOriginRelative(); + } else { + setOriginAbsolute(); + } +#endif + + setColor(fg); +} + +int16_t PixelsBase::getTextLineHeight() { + if ( currentFont == NULL ) { + return 0; + } + +// int16_t fontType = pgm_read_byte_near(currentFont + 2); +// if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { +// return 0; +// } + + return pgm_read_byte_near(currentFont + 3); +} + +int16_t PixelsBase::getTextBaseline() { + if ( currentFont == NULL ) { + return 0; + } + +// int16_t fontType = pgm_read_byte_near(currentFont + 2); +// if ( fontType != ANTIALIASED_FONT && fontType != BITMASK_FONT ) { +// return 0; +// } + + return pgm_read_byte_near(currentFont + 4); +} + +int16_t PixelsBase::getCharWidth(char c) { + if ( currentFont == NULL ) { + return 0; + } + + int16_t ptr = HEADER_LENGTH; + while ( 1 ) { + char cx = (char)(((int)pgm_read_byte_near(currentFont + ptr + 0) << 8) + pgm_read_byte_near(currentFont + ptr + 1)); + if ( cx == 0 ) { + break; + } + int16_t length = (((int)(pgm_read_byte_near(currentFont + ptr + 2) & 0xff) << 8) + (int)(pgm_read_byte_near(currentFont + ptr + 3) & 0xff)); + + if ( cx == c ) { + if ( length < 8 ) { +// Serial.print( "Invalid " ); +// Serial.print( c ); +// Serial.println( " glyph definition. Font corrupted?" ); + break; + } +// Serial.print( c ); + + return 0xff & pgm_read_byte_near(currentFont + ptr + 4); + } + + ptr += length; + } + + return 0; +} + +int16_t PixelsBase::getTextWidth(String text, int8_t kerning[]) { + if ( currentFont == NULL ) { + return 0; + } + + int16_t kernPtr = 0; + int16_t kern = -100; // no kerning + int16_t x1 = 0; + + for (uint16_t t = 0; t < text.length(); t++) { + char c = text.charAt(t); + + int16_t width = 0; + boolean found = false; + int16_t ptr = HEADER_LENGTH; + while ( 1 ) { + char cx = (char)(((int)pgm_read_byte_near(currentFont + ptr + 0) << 8) + pgm_read_byte_near(currentFont + ptr + 1)); + if ( cx == 0 ) { + break; + } + int16_t length = (((int)(pgm_read_byte_near(currentFont + ptr + 2) & 0xff) << 8) + (int)(pgm_read_byte_near(currentFont + ptr + 3) & 0xff)); + + if ( cx == c ) { + if ( length < 8 ) { +// Serial.print( "Invalid " ); +// Serial.print( c ); +// Serial.println( " glyph definition. Font corrupted?" ); + break; + } +// Serial.print( c ); + found = true; + width = 0xff & pgm_read_byte_near(currentFont + ptr + 4); + } + + ptr += length; + } + + if ( kerning != NULL && kerning[kernPtr] > -100 ) { + kern = kerning[kernPtr]; + if (kerning[kernPtr+1] > -100) { + kernPtr++; + } + } + + if ( found ) { + x1 += width; + if ( kern > -100 ) { + x1+= kern; + } + } + } + + return x1; +} + +void PixelsBase::drawGlyph(int16_t fontType, boolean clean, int16_t xx, int16_t yy, + int16_t glyphHeight, prog_uchar* data, int16_t length) { + + int16_t glyphWidth = 0xff & pgm_read_byte_near(data + 4); + int16_t mLeft = 0x7f & pgm_read_byte_near(data + 5); + int16_t mTop = 0xff & pgm_read_byte_near(data + 6); + int16_t mRight = 0x7f & pgm_read_byte_near(data + 7); + + int16_t effWidth = glyphWidth - mLeft - mRight; + + boolean vraster = (0x80 & pgm_read_byte_near(data + 5)) > 0; + boolean compressed = (pgm_read_byte_near(data + 7) & 0x80) > 0; + + RGB* fg = foreground; + RGB* bg = background; + + int16_t ctr = 0; +#ifndef NO_FILL_TEXT_BACKGROUND + int16_t prev = -1; + int16_t last = -1; +#endif + + int16_t eff = vraster ? + glyphHeight - mTop - mRight : + glyphWidth - mLeft - mRight; + + int16_t offsetLeft = mLeft + xx; + int16_t offsetTop = mTop + yy; + + int16_t hEdge = xx + glyphWidth; + int16_t vEdge = yy + glyphHeight; + + length -= 8; + + if ( !(fontType == BITMASK_FONT && !compressed) ) { + + int16_t edge = vraster ? offsetTop + eff - 1 : offsetLeft + eff - 1; + + for ( int16_t i = 0; i < length; i++ ) { + int16_t p1 = ctr / eff; + int16_t p2 = ctr % eff; + + int16_t b = 0xff & pgm_read_byte_near(data + 8 + i); + int16_t len = 0x7f & b; + boolean color = fontType == BITMASK_FONT ? (0x80 & b) > 0 : true; + + if ( color || glyphPrintMode == FILL_TEXT_BACKGROUND ) { + +#ifndef NO_FILL_TEXT_BACKGROUND + if ( glyphPrintMode == FILL_TEXT_BACKGROUND && prev != p1 ) { + setColor(bg); + if ( vraster ) { + if ( prev < 0 ) { + fillRectangle(xx, yy, mLeft + 1, glyphHeight + 1); + } else { + vLine(offsetLeft + p1, yy, vEdge); + } + } else { + if ( prev < 0 ) { + fillRectangle(xx, yy, glyphWidth + 1, mTop + 1); + } else { + hLine(xx, offsetTop + p1, hEdge); + } + } + prev = p1; + } +#endif + int16_t x = vraster ? offsetLeft + p1 : offsetLeft + p2; + int16_t y = vraster ? offsetTop + p2 : offsetTop + p1; + + if ( color && !clean ) { + setColor(fg); + } else { + setColor(bg); + } + + if ( fontType == BITMASK_FONT || (0xc0 & b) > 0 ) { + if ( fontType == ANTIALIASED_FONT ) { + len = 0x3f & b; + ctr += len; + } + + if ( fontType == BITMASK_FONT || (0x80 & b) > 0 ) { + + while ( p2 + len > eff ) { + if ( color ) { + if ( vraster ) { + vLine(x, y, edge); + } else { + hLine(x, y, edge); + } + } + if (fontType == BITMASK_FONT) { + ctr += eff - p2; + } + + len -= eff - p2; + p2 = 0; + p1++; + x = vraster ? offsetLeft + p1 : offsetLeft; + y = vraster ? offsetTop : offsetTop + p1; + +#ifndef NO_FILL_TEXT_BACKGROUND + if ( glyphPrintMode == FILL_TEXT_BACKGROUND ) { + setColor(bg); + if ( vraster ) { + vLine(x, yy, vEdge); + } else { + hLine(xx, y, hEdge); + } + if ( !clean ) { + setColor(fg); + } + prev = p1; + } +#endif + } + + if ( color ) { + if ( vraster ) { + vLine(x, y, y + len - 1); + } else { + hLine(x, y, x + len - 1); + } + } else { + setColor(fg); + } + +#ifndef NO_FILL_TEXT_BACKGROUND + } else if ( fontType == ANTIALIASED_FONT && glyphPrintMode == FILL_TEXT_BACKGROUND ) { + setColor(bg); + while ( p2 + len > eff ) { + len -= eff - p2; + p2 = 0; + p1++; + x = vraster ? offsetLeft + p1 : offsetLeft; + y = vraster ? offsetTop : offsetTop + p1; + + if ( vraster ) { + vLine(x, yy, vEdge); + } else { + hLine(xx, y, hEdge); + } + } + prev = p1; + if ( !clean ) { + setColor(fg); + } +#endif + } + } else if (fontType == ANTIALIASED_FONT) { + if ( clean ) { + setColor(bg); + } else { + uint8_t opacity = (0xff & (b << 2)); + RGB* cl = computeColor(fg, opacity); + setColor(cl); + } + drawPixel(x, y); + ctr++; + } +#ifndef NO_FILL_TEXT_BACKGROUND + last = p1; +#endif + } + if ( fontType == BITMASK_FONT ) { + ctr += len; + } + } + +#ifndef NO_FILL_TEXT_BACKGROUND + if ( glyphPrintMode == FILL_TEXT_BACKGROUND ) { + setColor(bg); + if ( vraster ) { + fillRectangle(offsetLeft + last + 1, yy, glyphWidth - mLeft - last - 1, glyphHeight + 1); + } else { + fillRectangle(xx, offsetTop + last + 1, glyphWidth, glyphHeight - mTop - last); + } + } +#endif + + } else { + + if ( clean ) { + setColor(bg); + } + + for ( int16_t i = 0; i < length; i++ ) { + int16_t b = 0xff & pgm_read_byte_near(data + 8 + i); + int16_t x = i * 8 % effWidth; + int16_t y = i * 8 / effWidth; + +#ifndef NO_FILL_TEXT_BACKGROUND + if ( glyphPrintMode == FILL_TEXT_BACKGROUND && prev != y ) { + setColor(bg); + if ( prev < 0 ) { + fillRectangle(xx, yy, glyphWidth + 1, mTop + 1); + } else { + hLine(xx, offsetTop + y, hEdge); + } + if ( !clean ) { + setColor(fg); + } + prev = y; + } +#endif + + for ( uint8_t j = 0; j < 8; j++ ) { + if ( x + j == effWidth ) { + x = -j; + y++; +#ifndef NO_FILL_TEXT_BACKGROUND + if ( glyphPrintMode == FILL_TEXT_BACKGROUND && prev != y ) { + setColor(bg); + hLine(xx, offsetTop + y, hEdge); + if ( !clean ) { + setColor(fg); + } + prev = y; + } +#endif + } + int mask = 1 << (7 - j); + if ( (b & mask) == 0 ) { + drawPixel(offsetLeft + x + j, offsetTop + y); + } + } +#ifndef NO_FILL_TEXT_BACKGROUND + last = y; +#endif + } + +#ifndef NO_FILL_TEXT_BACKGROUND + if ( glyphPrintMode == FILL_TEXT_BACKGROUND ) { + setColor(bg); + fillRectangle(xx, offsetTop + last + 1, glyphWidth + 1, glyphHeight - mTop - last); + } +#endif + } + + setColor(fg); +} + +void PixelsBase::scrollText( int16_t x, int16_t y, String text, uint8_t scrollStep, uint8_t repeat, uint16_t maxScroll ) { + + int extraRowDelay = 0; // increase to slow down + + if ( getOrientation() % 2 == 0 ) { + setOrientation(LANDSCAPE); + } + + if ( repeat != 0 ) { + repeat++; + } + + int maxX = getWidth() - 1; + int tw = getTextWidth(text); + + int skip = -1; + int loopLen = tw + x; + int space = maxX - x; + + if (getScroll() > 0) { + skip = (x - getScroll()) % getWidth(); + loopLen += maxX - getScroll(); + space = getScroll() - x; + } + + int easingLen = 5; + if ( loopLen / 2 < easingLen) { + easingLen = loopLen / 2; + } + loopLen += easingLen; + + int dlx = 8; + int factor = 3 + extraRowDelay; + int remains = 0; + + long maxLatency = 0; + boolean firstLoop = true; + + if ( maxScroll > 0 ) { + loopLen = maxScroll; + repeat = 2; + } + + do { + for ( int i = 0; i < loopLen; i+=scrollStep ) { + + long startMillis = millis(); + + int p = -1; + int e = -1; + int xx = 0; + int cw = 0; + + int l = 0; + int f = 0; + for (int t = 0; t < (int)text.length(); t++) { + char c = text.charAt(t); + f = l; + cw = getCharWidth(c); + if ( cw < 0 ) { + return; + } + l += cw; + if ( l > remains && p < 0 ) { + xx = f; + p = t; + } + if ( l > space ) { + e = t + 1; + break; + } + } + if ( p < 0 ) { + p = text.length(); + } + if ( e < 0 ) { + e = text.length(); + } + + String s = text.substring(p, e); + int q = (x + xx) % getWidth(); + + if ( q != 0 || x != getWidth() || getScroll() != 0 ) { + if (i > skip) { + print(q, y, s); + } + } + if ( q > maxX - cw && q > getScroll() && getScroll() != 0 ) { + print(q - getWidth(), y, s); + } + + remains = space; + space += scrollStep; + scroll(scrollStep, SCROLL_CLEAN); + + long endMillis = millis(); +#ifndef PIXELMEISTER + if ( s.length() < 3 ) { + long latency = endMillis - startMillis; + + if (maxLatency > latency) { + delay((int)(maxLatency - latency)); + } else { + if( firstLoop ) { + maxLatency = latency; + } + } + } +#endif + + if ( i < easingLen ) { + delay(dlx+(easingLen-i)*(easingLen-i)*factor/2); + } else { + if ( loopLen > 150 ) { + delay(factor); + } else { + delay(dlx+factor); + } + } + startMillis = endMillis; + } + + firstLoop = false; + + remains = 0; + space = 0; + easingLen = 0; + skip = -1; + + x = getWidth(); + loopLen = x + tw; + + if ( maxScroll <= 0 ) { + scroll(-getScroll(), 0); + } + + if ( repeat != 0 ) { + repeat--; + } + } while ( repeat == 0 || repeat > 1 ); +} + + +/* Low level */ + +void PixelsBase::putColor(int16_t x, int16_t y, boolean steep, double alpha) { + + if ( steep ) { + int16_t tmp = x; + x = y; + y = tmp; + } + + if ( x < 0 || x >= width || y < 0 || y >= height ) { + return; + } + + RGB* result; + if ( alpha != 1 ) { + RGB* bg = getPixel(x, y); + result = computeColor(bg, alpha); + RGB* sav = getColor(); + setColor(result); + drawPixel(x, y); + setColor(sav); + } else { + drawPixel(x, y); + } +} + + +RGB* PixelsBase::computeColor(RGB* bg, double alpha) { + if ( alpha < 0 ) { + alpha = 0; + return bg; + } + if ( alpha > 1 ) { + alpha = 1; + } + computedBgColor->setColor( (int32_t)(bg->red * (1 - alpha) + foreground->red * alpha), + (int32_t)(bg->green * (1 - alpha) + foreground->green * alpha), + (int32_t)(bg->blue * (1 - alpha) + foreground->blue * alpha)); + + return computedBgColor; +} + +RGB* PixelsBase::computeColor(RGB* fg, uint8_t opacity) { + int32_t sr = (int32_t)fg->red * (255 - opacity) + background->red * opacity; + int32_t sg = (int32_t)fg->green * (255 - opacity) + background->green * opacity; + int32_t sb = (int32_t)fg->blue * (255 - opacity) + background->blue * opacity; + sr /= 255; + sg /= 255; + sb /= 255; + if ( sr > 255 ) { + sr = 255; + } + if ( sg > 255 ) { + sg = 255; + } + if ( sb > 255 ) { + sb = 255; + } + computedFgColor->setColor(sr, sg, sb); + + return computedFgColor; +} + +void PixelsBase::scroll(int16_t dy, int8_t flags) { + scroll(dy, 0, deviceWidth, flags); +} + +void PixelsBase::scroll(int16_t dy, int16_t x1, int16_t x2, int8_t flags) { + + if(!canScroll()) { + return; + } + + int16_t mdy = dy > 0 ? dy : -dy; + + if (mdy > 1 && (flags & SCROLL_SMOOTH) > 0) { + + int16_t easingLen = 8; + if ( mdy / 2 < easingLen) { + easingLen = mdy / 2; + } + + int16_t dlx = (flags & SCROLL_CLEAN) > 0 ? 0 : 7; + int16_t factor = 1; + + int16_t step = dy < 0 ? -1 : 1; + for ( int16_t i = 0; i < easingLen; i++ ) { + delay(dlx+(easingLen-i)*(easingLen-i)*factor/2+extraScrollDelay); + scroll(step, x1, x2, flags & SCROLL_CLEAN); + } + for ( int16_t i = 0; i < mdy - easingLen*2; i++ ) { + scroll(step, x1, x2, flags & SCROLL_CLEAN); + if ( mdy > 150 ) { + delay(factor); + } else { + delay(dlx+factor+extraScrollDelay); + } + } + for ( int16_t i = 1; i <= easingLen; i++ ) { + scroll(step, x1, x2, flags & SCROLL_CLEAN); + delay(dlx+i*i*factor/2+extraScrollDelay); + } + + } else { + + RGB* sav = getColor(); + setColor(getBackground()); + boolean savorigin = relativeOrigin; + relativeOrigin = false; + + beginGfxOperation(); + + if ( (flags & SCROLL_CLEAN) > 0 && dy > 0 ) { + if( (orientation % 2) == 0 ) { // PORTRAIT(_FLIP) + fillRectangle(0, 0, deviceWidth, mdy); + } else { + fillRectangle(0, 0, mdy, deviceWidth); + } + } + + currentScroll += dy; + while ( currentScroll < 0 ) { + currentScroll += deviceHeight; + } + currentScroll %= deviceHeight; + + scrollCmd(); + + if ( (flags & SCROLL_CLEAN) > 0 && dy < 0 ) { + if( (orientation % 2) == 0 ) { // PORTRAIT(_FLIP) + fillRectangle(0, 0, deviceWidth, mdy); + } else { + fillRectangle(0, 0, mdy, deviceWidth); + } + } + + relativeOrigin = savorigin; + setColor(sav); + + endGfxOperation(true); + } +} + + +void PixelsBase::drawPixel(int16_t x, int16_t y) { + + if ( x < 0 || y < 0 || x >= width || y >= height ) { + return; + } + + int xx = x; + int yy = y; + + int s = getScroll(); + + if ( relativeOrigin ) { + switch( orientation ) { + case PORTRAIT: + if ( s > 0 && y >= s ) { + return; + } + break; + case LANDSCAPE: + if ( s > 0 && x >= s ) { + return; + } + xx = deviceWidth - y - 1; + yy = x; + break; + case PORTRAIT_FLIP: + if ( s > 0 && y >= s ) { + return; + } + xx = deviceWidth - x - 1; + yy = deviceHeight - y - 1; + break; + case LANDSCAPE_FLIP: + if ( s > 0 && x >= s ) { + return; + } + xx = y; + yy = deviceHeight - x - 1; + break; + } + } else { + switch( orientation ) { + case PORTRAIT: + yy += s; + break; + case LANDSCAPE: + xx = deviceWidth - y - 1; + yy = x + s; + break; + case PORTRAIT_FLIP: + xx = deviceWidth - x - 1; + yy = 2 * deviceHeight - y - 1 - s; + break; + case LANDSCAPE_FLIP: + xx = y; + yy = 2 * deviceHeight - x - 1 - s; + break; + } + yy %= deviceHeight; + } + + beginGfxOperation(); + setRegion(xx, yy, xx, yy); + setCurrentPixel(foreground); + endGfxOperation(); +} + +void PixelsBase::fill(int color, int16_t x1, int16_t y1, int16_t x2, int16_t y2) { + + Bounds bb(x1, y1, x2, y2); + if( !transformBounds(bb) ) { + return; + } + + beginGfxOperation(); + + if ( relativeOrigin ) { + quickFill(color, bb.x1, bb.y1, bb.x2, bb.y2); + } else { + int s = currentScroll; + if ( orientation > 1 ) { + s = (deviceHeight - s - 1) % deviceHeight; + bb.y1 += s; + bb.y2 += s; + } else { + bb.y1 += s; + bb.y2 += s; + } + bb.y1 %= deviceHeight; + bb.y2 %= deviceHeight; + + if ( bb.y1 > bb.y2 ) { + quickFill(color, bb.x1, bb.y1, bb.x2, deviceHeight-1); + quickFill(color, bb.x1, 0, bb.x2, bb.y2); + } else { + quickFill(color, bb.x1, bb.y1, bb.x2, bb.y2); + } + } + + endGfxOperation(); +} + +void PixelsBase::hLine(int16_t x1, int16_t y, int16_t x2) { + fill(foreground->convertTo565(), x1, y, x2, y); +} + +void PixelsBase::vLine(int16_t x, int16_t y1, int16_t y2) { + fill(foreground->convertTo565(), x, y1, x, y2); +} + +void PixelsBase::resetRegion() { + setRegion(0, 0, deviceWidth, deviceHeight); +} + +void PixelsBase::setCurrentPixel(int16_t color) { + deviceWriteData(highByte(color), lowByte(color)); +} + +void PixelsBase::setCurrentPixel(RGB* color) { + int16_t c = color->convertTo565(); + deviceWriteData(highByte(c), lowByte(c)); +} + +boolean PixelsBase::transformBounds(Bounds& bb) { + + int16_t buf; + switch( orientation ) { + case PORTRAIT: + break; + case LANDSCAPE: + buf = bb.x1; + bb.x1 = deviceWidth - bb.y1 - 1; + bb.y1 = buf; + buf = bb.x2; + bb.x2 = deviceWidth - bb.y2 - 1; + bb.y2 = buf; + break; + case PORTRAIT_FLIP: + bb.y1 = deviceHeight - bb.y1 - 1; + bb.y2 = deviceHeight - bb.y2 - 1; + bb.x1 = deviceWidth - bb.x1 - 1; + bb.x2 = deviceWidth - bb.x2 - 1; + break; + case LANDSCAPE_FLIP: + buf = bb.y1; + bb.y1 = deviceHeight - bb.x1 - 1; + bb.x1 = buf; + buf = bb.y2; + bb.y2 = deviceHeight - bb.x2 - 1; + bb.x2 = buf; + break; + } + + if (bb.y2 < bb.y1) { + swap(bb.y1, bb.y2); + } + + if (bb.x2 < bb.x1) { + swap(bb.x1, bb.x2); + } + + return true; +} + +boolean PixelsBase::checkBounds(Bounds& bb) { + if (bb.x2 < bb.x1) { + swap(bb.x1, bb.x2); + } + if (bb.y2 < bb.y1) { + swap(bb.y1, bb.y2); + } + + if ( bb.x1 < 0 ) { + if ( bb.x2 < 0 ) { + return false; + } + bb.x1 = 0; + } + if ( bb.x2 >= deviceWidth ) { + if ( bb.x1 >= deviceWidth ) { + return false; + } + bb.x2 = deviceWidth - 1; + } + + int16_t s = (relativeOrigin && orientation > 1) ? (deviceHeight - currentScroll) % deviceHeight : 0; + if ( bb.y1 < s ) { + if ( bb.y2 < s ) { + return false; + } + bb.y1 = s; + } + s = (relativeOrigin && orientation < 2 && currentScroll > 0) ? currentScroll : deviceHeight; + if ( bb.y2 >= s ) { + if ( bb.y1 >= s ) { + return false; + } + bb.y2 = s - 1; + } + + return true; +} + + diff --git a/Pixels.h b/Pixels.h index e994d76..3aa8a01 100755 --- a/Pixels.h +++ b/Pixels.h @@ -1,930 +1,953 @@ -/* - * Pixels. Graphics library for TFT displays. - * - * Copyright (C) 2012-2015 Igor Repinetski - * - * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. - * - * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister - * - * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ - * - * Commercial use of the library is possible for licensees of "Pixelmeister (for industry)" product. - * - * This library includes some code portions and algoritmic ideas derived from works of - * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) - * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) - * - CMBSolutions git()cmbsolutions.nl - */ - -/* - * Currently supported platforms: - * - * 1. Reference platform: Arduino Mega, TFT_PQ 2.4 (ILI9325 controller), ITDB02 MEGA Shield v1.1 - * - * More platforms coming soon - */ - -#ifndef PIXELS_BASE_H -#define PIXELS_BASE_H - -// #define DISABLE_ANTIALIASING 1 -// #define NO_FILL_TEXT_BACKGROUND 1 -// #define NO_TEXT_WRAP 1 - - -#define SPI_CLOCK_DIV4 0x00 -#define SPI_CLOCK_DIV16 0x01 -#define SPI_CLOCK_DIV64 0x02 -#define SPI_CLOCK_DIV128 0x03 -#define SPI_CLOCK_DIV2 0x04 -#define SPI_CLOCK_DIV8 0x05 -#define SPI_CLOCK_DIV32 0x06 -// #define SPI_CLOCK_DIV64 0x07 - -#define SPI_MODE0 0x00 -#define SPI_MODE1 0x04 -#define SPI_MODE2 0x08 -#define SPI_MODE3 0x0C - -#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR -#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR -#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR - - -#if defined(__SAM3X8E__) -#include -#endif - -#if defined(__AVR__) || defined(TEENSYDUINO) - #include - - #define regtype volatile uint8_t - #define regsize uint8_t - - #define cbi(reg, bitmask) *reg &= ~bitmask - #define sbi(reg, bitmask) *reg |= bitmask - #define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask); - #define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask); - #define prog_uchar const unsigned char - #define prog_uint16_t const uint16_t - -#elif defined(__SAM3X8E__) - #include - #define PROGMEM - - #define prog_uchar const unsigned char - #define prog_uint16_t const uint16_t - - #define pgm_read_byte(x) (*((char *)x)) - #define pgm_read_word(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) - #define pgm_read_byte_near(x) (*((char *)x)) - #define pgm_read_byte_far(x) (*((char *)x)) -// #define pgm_read_word(x) (*((short *)(x & 0xfffffffe))) -// #define pgm_read_word_near(x) (*((short *)(x & 0xfffffffe)) -// #define pgm_read_word_far(x) (*((short *)(x & 0xfffffffe))) - #define pgm_read_word_near(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) - #define pgm_read_word_far(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x)))) - #define PSTR(x) x - - #define cbi(reg, bitmask) *reg &= ~bitmask - #define sbi(reg, bitmask) *reg |= bitmask - #define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask); - #define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask); - - #define cport(port, data) port &= data - #define sport(port, data) port |= data - - #define regtype volatile uint32_t - #define regsize uint32_t - -#else - #define PROGMEM - -#ifndef prog_uchar - #define prog_uchar byte - #define prog_uint16_t const uint16_t -#endif - - #define regtype volatile uint32_t - #define regsize uint16_t -#endif - -#define swap(a, b) {int16_t buf = a; a = b; b = buf;} - -#define BITMASK_FONT 1 -#define ANTIALIASED_FONT 2 -#define HEADER_LENGTH 5 - -#define SCROLL_SMOOTH 1 -#define SCROLL_CLEAN 2 - -#define FILL_TOPDOWN 0 -#define FILL_LEFTRIGHT 0 -#define FILL_DOWNTOP 1 -#define FILL_RIGHTLEFT 2 - -#define ORIGIN_RELATIVE true // origin relative to a current scroll position -#define ORIGIN_ABSOLUTE false // origin matches physical device pixel coordinates - -#define PORTRAIT 0 -#define LANDSCAPE 1 -#define PORTRAIT_FLIP 2 -#define LANDSCAPE_FLIP 3 - -#define TRANSPARENT_TEXT_BACKGROUND 0 -#define FILL_TEXT_BACKGROUND 1 - - -#define ipart(X) ((int16_t)(X)) -#define iround(X) ((uint16_t)(((double)(X))+0.5)) -#define fpart(X) (((double)(X))-(double)ipart(X)) -#define rfpart(X) (1.0-fpart(X)) - -extern regtype *registerCS; // chip select -extern regsize bitmaskCS; - -#define chipSelect() cbi(registerCS, bitmaskCS) -#define chipDeselect() sbi(registerCS, bitmaskCS) - -class RGB { -private: - uint16_t col; -public: - uint8_t red; - uint8_t green; - uint8_t blue; - - RGB(uint8_t r, uint8_t g, uint8_t b); - RGB(); - - void setColor(int32_t r, int32_t g, int32_t b); - - RGB convert565toRGB(uint16_t color); - uint16_t convertRGBto565(RGB color); - uint16_t convertTo565(); -}; - -class Bounds { -public: - int16_t x1; - int16_t y1; - int16_t x2; - int16_t y2; - - Bounds( int16_t xx1, int16_t yy1, int16_t xx2, int16_t yy2 ) { - x1 = xx1; - y1 = yy1; - x2 = xx2; - y2 = yy2; - } -}; - -class PixelsBase { -protected: - /* device physical dimension in portrait orientation */ - int16_t deviceWidth; - int16_t deviceHeight; - - /* device logical dimension in current orientation */ - int16_t width; - int16_t height; - - boolean landscape; - - uint8_t orientation; - - boolean relativeOrigin; - - /* currently selected font */ - prog_uchar* currentFont; - - RGB* foreground; - RGB* background; - - double lineWidth; - - uint8_t fillDirection; - boolean antialiasing; - - boolean scrollSupported; - boolean scrollEnabled; - - int16_t currentScroll; - int16_t flipScroll; - boolean scrollCleanMode; - uint16_t extraScrollDelay; - - int16_t caretX; - int16_t caretY; - - int8_t glyphPrintMode; - -#ifndef NO_TEXT_WRAP - boolean wrapText; - int16_t textWrapMarginLeft; - int16_t textWrapMarginRight; - int16_t textWrapLineGap; - - boolean textWrapScroll; - int16_t textWrapMarginBottom; - RGB* textWrapScrollFill; -#endif - - int gfxOpNestingDepth; - - boolean transformBounds(Bounds& bb); - boolean checkBounds(Bounds& bb); - void printString(int16_t xx, int16_t yy, String text, boolean clean, int8_t kerning[] = NULL); - void drawGlyph(int16_t fontType, boolean clean, int16_t xx, int16_t yy, - int16_t height, prog_uchar* data, int16_t length); - -#ifndef NO_TEXT_WRAP - int16_t computeBreakPos(String text, int16_t t); -#endif - - virtual int32_t setRegion(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { return -1; } - - void setCurrentPixel(RGB* color); - void setCurrentPixel(int16_t color); - void fill(int b, int16_t x1, int16_t y1, int16_t x2, int16_t y2); - virtual void quickFill(int b, int16_t x1, int16_t y1, int16_t x2, int16_t y2) {} - void putColor(int16_t x, int16_t y, boolean steep, double weight); - RGB* computeColor(RGB* bg, double weight); - RGB* computeColor(RGB* fg, uint8_t opacity); - - void resetRegion(); - - void hLine(int16_t x1, int16_t y1, int16_t x2); - void vLine(int16_t x1, int16_t y1, int16_t y2); - - virtual void deviceWriteData(uint8_t hi, uint8_t lo) {} - - virtual void scrollCmd() {} - - virtual void drawCircleAntialiaced(int16_t x, int16_t y, int16_t radius, boolean bordermode); - virtual void drawFatLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2); - virtual void drawLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2); - virtual void drawRoundRectangleAntialiased(int16_t x, int16_t y, int16_t width, int16_t height, int16_t rx, int16_t ry, boolean bordermode); - - int16_t* loadFileBytes(String); - - RGB* computedBgColor; - RGB* computedFgColor; - RGB* bgBuffer; - RGB* fgBuffer; - - virtual void beginGfxOperation() { - chipSelect(); - gfxOpNestingDepth++; - } - - virtual void endGfxOperation(boolean force) { - gfxOpNestingDepth--; - if ( gfxOpNestingDepth <= 0 ) { - chipDeselect(); - gfxOpNestingDepth = 0; - } - } - - void endGfxOperation() { - endGfxOperation(false); - } - -public: - - /** - * Constructs a new Pixels object for the reference platform TFT_PQ 2.4 (ILI9325 controller) + ITDB02 MEGA Shield v1.1. - * @param width target device width (in pixels) - * @param height target device height (in pixels) - */ - PixelsBase(uint16_t width, uint16_t height); - /** - * Initializes hardware with defaults. - */ - virtual void init() {} - /** - * Sets the current coordinate space orientation. - * Default value depends on initially given device width and height (PORTRAIT if height > width, otherwise LANDSCAPE). - * @param orientation accepts PORTRAIT, LANDSCAPE, PORTRAIT_FLIP or LANDSCAPE_FLIP - */ - void setOrientation(uint8_t orientation); - /** - * Returns the current coordinate space orientation. - * @return the current orientation - * @see setOrientation(uint8_t) - */ - inline uint8_t getOrientation() { - return orientation; - } - /** - * Enables or disables antialiasing by a drawing of graphical primitives. The metod does not impact antialiased fonts. - * Antialiased output in general requires more resources/time to output comparing to "grainy" output mode. - * Takes no effect if Pixels_Antialiasing.h is not included - * @param enable a boolean value that determines whether the antialiasing should be enabled or not - */ - virtual void enableAntialiasing(boolean enable) { - antialiasing = false; - } - - /** - * Gets the current antialiasing mode. - * @return a boolean value that shows whether the antialiasing is be enabled or not - */ - inline boolean isAntialiased() { - return antialiasing; - } - /** - * Enables or disables scroll feature. - * @param enable a boolean value that determines whether the scroll feature should be enabled or not - * @see scroll(int16_t,int8_t) - */ - inline void enableScroll(boolean enable) { - scrollEnabled = enable; - } - /** - * @returns true if the target device can scroll and the scroll feature is enabled. - * @see scroll(int16_t,int8_t) - * @see enableScroll(boolean) - */ - inline boolean canScroll() { - return scrollEnabled & scrollSupported; - } - /** - * Sets the current line width. For time being the line width is respected by - * drawLine(int16_t,int16_t,int16_t,int16_t) and a line width control is still "under construction". - * @param width new line width. - */ - inline void setLineWidth(double width) { - lineWidth = width; - } - /** - * Returns the current line width. - * @return the current line width. - */ - inline double getLineWidth() { - return lineWidth; - } - /** - * Returns device width. - * @return device width. - */ - inline int16_t getWidth() { - return width; - } - /** - * Returns device height. - * @return device height. - */ - inline int16_t getHeight() { - return height; - } - /** - * Bounds the coordinate space to the device controller video RAM. The physical output depends on the actual scroll position. - */ - inline void setOriginRelative() { - relativeOrigin = true; - } - /** - * Bounds the coordinate space to physical device pixels, ignoring actual scroll position. - * @see scroll(int16_t,int8_t) - */ - inline void setOriginAbsolute() { - relativeOrigin = false; - } - /** - * @return true if the current positioning is relative. - * @see setOriginRelative() - * @see setOriginAbsolute() - */ - inline boolean isOriginRelative() { - return relativeOrigin; - } - /** - * Outout fine tuning method for slow devices - * @param direction accepts FILL_TOPDOWN, FILL_LEFTRIGHT, FILL_DOWNTOP or FILL_RIGHTLEFT - */ - virtual void setFillDirection(uint8_t direction) {} - /** - * Fills the screen with the current background color - */ - void clear(); - /** - * Sets the current background color to the specified color. - * All subsequent relevant graphics operations use this specified color. - * @param r the red component - * @param g the green component - * @param b the blue component - */ - inline void setBackground(uint8_t r, uint8_t g, uint8_t b) { - bgBuffer->setColor(r, g, b); - setBackground(bgBuffer); - } - /** - * Sets the current color to the specified color. - * All subsequent graphics operations use this specified color. - * @param r the red component - * @param g the green component - * @param b the blue component - */ - inline void setColor(uint8_t r, uint8_t g, uint8_t b) { - fgBuffer->setColor(r, g, b); - setColor(fgBuffer); - } - /** - * Sets the current background color to the specified color. - * All subsequent relevant graphics operations use this specified color. - * @param color color object reference - */ - inline void setBackground(RGB* color) { - background = color; - } - /** - * Sets the current color to the specified color. - * All subsequent graphics operations use this specified color. - * @param color color object - */ - inline void setColor(RGB* color) { - foreground = color; - } - /** - * Gets the graphics context's current background color. - * @return the graphics context's current background color. - */ - inline RGB* getBackground() { - return background; - } - /** - * Gets the graphics context's current color. - * @return the graphics context's current color. - */ - inline RGB* getColor() { - return foreground; - } - /** - * Gets a pixel color at the point - * (x, y) in the current coordinate system. - * If video RAM read is not supported by the hardware, returns the - * graphics context's current background color - * @param x x coordinate. - * @param y y coordinate. - * @return pixel color or the graphics context's current background color. - */ - RGB* getPixel(int16_t x, int16_t y); - /** - * Draws a pixel, using the current color, at the point - * (x, y) - * in the current coordinate system. - * @param x x coordinate. - * @param y y coordinate. - */ - void drawPixel(int16_t x, int16_t y); - /** - * Draws a line, using the current color, between the points - * (x1, y1) and (x2, y2) - * in current coordinate system. - * @param x1 the first point's x coordinate. - * @param y1 the first point's y coordinate. - * @param x2 the second point's x coordinate. - * @param y2 the second point's y coordinate. - * @see setOriginRelative() - * @see setOriginAbsolute() - */ - void drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2); - /** - * Draws the outline of a circle, defined by center coordinates and a radius, - * with the current color. - * @param x the x coordinate of the circle center. - * @param y the y coordinate of the circle center. - * @param radius circle radius. - * @see fillCircle(int16_t,int16_t,int16_t) - */ - void drawCircle(int16_t x, int16_t y, int16_t radius); - /** - * Draws the outline of an oval. - * The result is a circle or ellipse that fits within the - * rectangle specified by the x, y, - * width, and height arguments. - *

- * The oval covers an area that is - * width + 1 pixels wide - * and height + 1 pixels tall. - * @param x the x coordinate of the upper left - * corner of the oval to be drawn. - * @param y the y coordinate of the upper left - * corner of the oval to be drawn. - * @param width the width of the oval to be drawn. - * @param height the height of the oval to be drawn. - * @see fillOval(int16_t,int16_t,int16_t,int16_t) - */ - void drawOval(int16_t x, int16_t y, int16_t width, int16_t height); - /** - * Draws the outline of the specified rectangle. - * The left and right edges of the rectangle are at - * x and x + width. - * The top and bottom edges are at - * y and y + height. - * The rectangle is drawn using the current color. - * @param x the x coordinate - * of the rectangle to be drawn. - * @param y the y coordinate - * of the rectangle to be drawn. - * @param width the width of the rectangle to be drawn. - * @param height the height of the rectangle to be drawn. - * @see fillRectangle(int16_t,int16_t,int16_t,int16_t) - */ - void drawRectangle(int16_t x, int16_t y, int16_t width, int16_t height); - /** - * Draws an outlined round-cornered rectangle using the current color. - * The left and right edges of the rectangle - * are at x and x + width, - * respectively. The top and bottom edges of the rectangle are at - * y and y + height. - * @param x the x coordinate of the rectangle to be drawn. - * @param y the y coordinate of the rectangle to be drawn. - * @param width the width of the rectangle to be drawn. - * @param height the height of the rectangle to be drawn. - * @param r horizontal and vertical diameters of the arc - * at the four corners. - * @see fillRoundRectangle(int16_t,int16_t,int16_t,int16_t,int16_t) - */ - void drawRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t r); - /** - * Fills a circle, defined by center coordinates and a radius, with the current color. - * @param x the x coordinate of the circle center. - * @param y the y coordinate of the circle center. - * @param radius circle radius. - * @see drawCircle(int16_t,int16_t,int16_t) - */ - void fillCircle(int16_t x, int16_t y, int16_t radius); - /** - * Fills an oval bounded by the specified rectangle with the - * current color. - * @param x the x coordinate of the upper left corner - * of the oval to be filled. - * @param y the y coordinate of the upper left corner - * of the oval to be filled. - * @param width the width of the oval to be filled. - * @param height the height of the oval to be filled. - * @see drawOval(int16_t,int16_t,int16_t,int16_t) - */ - void fillOval(int16_t x, int16_t y, int16_t width, int16_t height); - /** - * Fills the specified rectangle. - * The left and right edges of the rectangle are at - * x and x + width - 1. - * The top and bottom edges are at - * y and y + height - 1. - * The resulting rectangle covers an area - * width pixels wide by - * height pixels tall. - * The rectangle is filled using the current color. - * @param x the x coordinate - * of the rectangle to be filled. - * @param y the y coordinate - * of the rectangle to be filled. - * @param width the width of the rectangle to be filled. - * @param height the height of the rectangle to be filled. - * @see drawRectangle(int16_t,int16_t,int16_t,int16_t) - */ - void fillRectangle(int16_t x, int16_t y, int16_t width, int16_t height); - /** - * Fills the specified rounded corner rectangle with the current color. - * The left and right edges of the rectangle - * are at x and x + width - 1, - * respectively. The top and bottom edges of the rectangle are at - * y and y + height - 1. - * @param x the x coordinate of the rectangle to be filled. - * @param y the y coordinate of the rectangle to be filled. - * @param width the width of the rectangle to be filled. - * @param height the height of the rectangle to be filled. - * @param r horizontal and vertical diameters of the arc at the four corners. - * @see drawRoundRectangle(int16_t,int16_t,int16_t,int16_t,int16_t) - */ - void fillRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t r); - /** - * Draws specified bitmap image. - * The image is drawn with its top-left corner at - * (xy) in the current coordinate - * space. - * @param data the specified bitmap image to be drawn. This method does - * nothing if img is null. - * @param x the x coordinate. - * @param y the y coordinate. - * @param width the width of the image. - * @param height the height of the image. - * @see loadBitmap(int16_t,int16_t,int16_t,int16_t,String) - */ - int8_t drawBitmap(int16_t x, int16_t y, int16_t width, int16_t height, prog_uint16_t* data); - /** - * Draws specified bitmap image. - * The image is drawn with its top-left corner at - * (xy) in the current coordinate - * space. - * @param data compressed (with Pixelmeister) bitmap image bytes. This method does - * nothing if img is null. - * @param x the x coordinate. - * @param y the y coordinate. - * @see drawBitmap(int16_t,int16_t,int16_t,int16_t,int[]) - */ - int8_t drawCompressedBitmap(int16_t x, int16_t y, prog_uchar* data); - /** - * Draws an icon, prepared with Pixelmeister. - * The icon is drawn with its top-left corner at - * (xy) in the current coordinate - * space. - * @param data icon image bytes. This method does - * nothing if data is null. - * @param x the x coordinate. - * @param y the y coordinate. - */ - void drawIcon(int16_t xx, int16_t yy, prog_uchar data[]); - /** - * Paint icon pixels with background color. The icon should be in Pixelmeister format. - * The icon is drawn with its top-left corner at - * (xy) in the current coordinate - * space. - * @param data icon image bytes. This method does - * nothing if data is null. - * @param x the x coordinate. - * @param y the y coordinate. - */ - void cleanIcon(int16_t xx, int16_t yy, prog_uchar data[]); - /** - * @return height of an icon, created with Pixelmeister - * @param data icon image bytes. - */ - int16_t getIconHeight(prog_uchar* data) { - return pgm_read_byte_near(data + 4); - } - /** - * @return width of an icon, created with Pixelmeister - * @param data icon image bytes. - */ - int16_t getIconWidth(prog_uchar* data) { - return pgm_read_byte_near(data + 5); - } - /** - * Loads from an external FAT-drive and draws specified bitmap image. - * The image is drawn with its top-left corner at - * (xy) in the current coordinate - * space. - * @param path to the image. - * @param x the x coordinate. - * @param y the y coordinate. - * @param width the width of the image. - * @param height the height of the image. - * @see drawBitmap(int16_t,int16_t,int16_t,int16_t,int[]) - */ - int8_t loadBitmap(int16_t x, int16_t y, int16_t width, int16_t height, String path); - /** - * Under construction - */ - void scroll(int16_t dy, int16_t x1, int16_t x2, int8_t flags); - /** - * Scrolls the display content to a given number of pixels (if the device controller - * supports scrolling). Scroll axis is vertical by portrait orientation and horizontal - * by landscape orientation (hardware scpecifics). - * SCROLL_CLEAN flag forces to paint wrapped regions with background color. SCROLL_SMOOTH - * eases begin and end of a scroll movement (by big dy values). - * @param dy negative or positive scroll distance - * @param flags can be 0, SCROLL_SMOOTH or/and SCROLL_CLEAN - */ - void scroll(int16_t dy, int8_t flags); - - /** - * Returns the current scroll position - */ - int getScroll() { -// return orientation < 2 ? currentScroll : deviceHeight - currentScroll; - return currentScroll; - } - - void setScrollStepDelay(uint16_t ms) { - extraScrollDelay = ms; - } - - /** - * returns current print mode - * @see setPrintMode(int8_t) - */ - int8_t getPrintMode() { - return glyphPrintMode; - } - - /** - * sets text output mode - * @param printMode FILL_TEXT_BACKGROUND=1 forces to fill glyph background with current background color; - * TRANSPARENT_TEXT_BACKGROUND=0 forces to print glyphs with no background (default) - */ - void setPrintMode(int8_t printMode) { -#ifndef NO_FILL_TEXT_BACKGROUND - glyphPrintMode = printMode; -#endif - } - - /** - * Enables text line auto wrap - * @param marginLeft start X coordinate of wrapped tex line - * @param marginRight screen space right side, to avoid to print to - * @param lineGap vertical gap between text lines - */ - void enableTextWrap(int16_t marginLeft, int16_t marginRight, int16_t lineGap) { -#ifndef NO_TEXT_WRAP - wrapText = true; - textWrapMarginLeft = marginLeft; - textWrapMarginRight = marginRight; - textWrapLineGap = lineGap; -#endif - } - - /** - * Disables text line auto wrap - * @see enableTextWrap(marginLeft,marginRight,int16_t) - */ - void disableTextWrap() { -#ifndef NO_TEXT_WRAP - wrapText = false; -#endif - } - - /** - * Enables auto-scroll if a wrapped line does not fit remaining vertical space. - * Takes no effect if text wrap is not enabled or by landscape page orientation. - * @param marginBottom bottom screen space, to avoid to print to - * @param scrollFill background color to pre-fill blank area - * @see enableTextWrap(marginLeft,marginRight,int16_t) - */ - void enableTextWrapScroll(int16_t marginBottom, RGB* scrollFill = NULL) { -#ifndef NO_TEXT_WRAP - textWrapMarginBottom = marginBottom; - textWrapScrollFill = scrollFill; - textWrapScroll = true; -#endif - } - - /** - * Disables auto-scroll if a wrapped line - * @see enableTextWrapScroll(marginBottom,scrollFill) - */ - void disableTextWrapScroll() { -#ifndef NO_TEXT_WRAP - textWrapScroll = false; -#endif - } - - int16_t getCaretX() { - return caretX; - } - - int16_t getCaretY() { - return caretY; - } - - /** - * Sets this graphics context's font to the specified font. - * All subsequent text operations using the context use this font. - * @param font a font, converted from TTF with - * Pixelmeister. - */ - int setFont(prog_uchar font[]); - /** - * Draws the text given by the specified string, using current font and color. - * The baseline of the leftmost character is at position (xy) - * in the current coordinate system. - * @param text the string to be drawn. - * @param xx the x coordinate. - * @param yy the y coordinate. - * @param kerning optional array of integer kerning values in a range from - * -99 to 99 to be applied to the string glyphs correspondingly. "-100" value ends - * the kerning data. If the string has more characters than the array length, a value - * precedes "-100" is used for the rest of the string glyphs. - * @see setFont(prog_uchar font[]) - * @see setOriginRelative() - * @see setOriginAbsolute() - * @see cleanText(int16_t,int16_t,String,int8_t[]) - */ - void print(int16_t xx, int16_t yy, String text, int8_t kerning[] = NULL); - /** - * The method oposes print(int16_t,int16_t,String,int8_t[]) Erases the text given by - * the specified string by filling glyph shapes with the current background color. - * The baseline of the leftmost character is at position (xy) - * in the current coordinate system. - * @param text the string to be erased. - * @param xx the x coordinate. - * @param yy the y coordinate. - * @param kerning aptional array of integer kerning values in a range from - * -99 to 99 to be applied to the string glyphs correspondingly. "-100" value ends - * the kerning data. If the string has more characters than the array length, a value - * precedes "-100" is usedas a kerning hint for the rest of the string glyphs. - * @see setFont(prog_uchar font[]) - * @see setOriginRelative() - * @see setOriginAbsolute() - * @see print(int16_t,int16_t,String,int8_t[]) - */ - void cleanText(int16_t xx, int16_t yy, String text, int8_t kerning[] = NULL); - /** - * Gets the current font text line height - * @return text line height in pixels - */ - int16_t getTextLineHeight(); - /** - * Gets the current font baseline offsett - * @return text baseline offset - */ - int16_t getTextBaseline(); - /** - * Computes needed horizontal space to print the given text with print(int16_t,int16_t,String,int8_t) - * @param text the text string. - * @param kerning optional array of integer kerning values in a range from - * -99 to 99 to be applied to the string glyphs correspondingly. "-100" value ends - * the kerning data. If the string has more characters than the array length, a value - * precedes "-100" is used as a kerning hint for the rest of the string glyphs. - * @see print(int16_t,int16_t,String,int8_t[]) - * @return text baseline offset - */ - int16_t getTextWidth(String text, int8_t kerning[] = NULL); - - /** - * Returns width for a given character - */ - int16_t getCharWidth(char c); - - - void scrollText( int16_t x, int16_t y, String text, uint8_t scrollStep, uint8_t repeat, uint16_t maxScroll ); -}; - -class BitStream { -private: - prog_uchar* data; - size_t size; - int32_t bitpos; - -public: - BitStream (prog_uchar* src_buffer, size_t byte_size, int8_t offset = 0) { - bitpos = offset; - data = src_buffer; - size = byte_size + (offset>>3); - } - - bool endOfData() { - return (size_t)((bitpos + 1) >> 3) >= size; - } - - uint8_t testCurrentByte() { - uint8_t res = (uint8_t)pgm_read_byte_near(data + (bitpos>>3)); - return res; - } - - uint8_t readBit() { - uint8_t res = (uint8_t)(pgm_read_byte_near(data + (bitpos>>3)) & (uint8_t)( (uint16_t)0x80 >> (bitpos & 7) )); - bitpos++; - return res; - } - - uint8_t readBits(uint8_t len) { - - uint16_t end_offset = (bitpos + len - 1)>>3; - if ( end_offset >= size ) { - return 0; - } - - uint8_t i; - uint16_t byte_offset = bitpos >> 3; - if (byte_offset == end_offset) { - uint16_t x = pgm_read_byte_near(data + byte_offset); - i = (uint8_t)(x >> (8 - ((bitpos & 7) + len))) & ((1 << len) - 1); - } else { - uint16_t x = ((uint16_t)pgm_read_byte_near(data + byte_offset) << 8) + pgm_read_byte_near(data + end_offset); - i = (uint8_t)(x >> (16 - ((bitpos & 7) + len))) & ((1 << len) - 1); - } - bitpos += len; - return i; - } - - uint16_t readNumber() { - int16_t count; - - int8_t ctr = 1; - int16_t base = 2; - do { - if ( readBit() == 0 ) { - uint8_t bits = readBits(ctr); - count = base + bits; - break; - } else { - ctr++; - base *= 2; - if ( ctr == 7 ) { - uint8_t bits = readBits(ctr); - count = base + bits; - break; - } - } - } while ( true ); - - return (uint16_t)count; - } -}; - -#endif +/* + * Pixels. Graphics library for TFT displays. + * + * Copyright (C) 2012-2015 Igor Repinetski + * + * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. + * + * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ + * + * Commercial use of the library is possible for licensees of "Pixelmeister (for industry)" product. + * + * This library includes some code portions and algoritmic ideas derived from works of + * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) + * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) + * - CMBSolutions git()cmbsolutions.nl + */ + +/* + * Currently supported platforms: + * + * 1. Reference platform: Arduino Mega, TFT_PQ 2.4 (ILI9325 controller), ITDB02 MEGA Shield v1.1 + * + * More platforms coming soon + */ + +#ifndef PIXELS_BASE_H +#define PIXELS_BASE_H + +// #define DISABLE_ANTIALIASING 1 +// #define NO_FILL_TEXT_BACKGROUND 1 +// #define NO_TEXT_WRAP 1 + + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +// #define SPI_CLOCK_DIV64 0x07 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + + +#if defined(__SAM3X8E__) +#include +#endif + +#if defined(__AVR__) || defined(TEENSYDUINO) + #include + + #define regtype volatile uint8_t + #define regsize uint8_t + + #define cbi(reg, bitmask) *reg &= ~bitmask + #define sbi(reg, bitmask) *reg |= bitmask + #define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask); + #define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask); + #define prog_uchar const unsigned char + #define prog_uint16_t const uint16_t + +#elif defined(__SAM3X8E__) + #include + #define PROGMEM + + #define prog_uchar const unsigned char + #define prog_uint16_t const uint16_t + + #define pgm_read_byte(x) (*((char *)x)) + #define pgm_read_word(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) + #define pgm_read_byte_near(x) (*((char *)x)) + #define pgm_read_byte_far(x) (*((char *)x)) +// #define pgm_read_word(x) (*((short *)(x & 0xfffffffe))) +// #define pgm_read_word_near(x) (*((short *)(x & 0xfffffffe)) +// #define pgm_read_word_far(x) (*((short *)(x & 0xfffffffe))) + #define pgm_read_word_near(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) + #define pgm_read_word_far(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x)))) + #define PSTR(x) x + + #define cbi(reg, bitmask) *reg &= ~bitmask + #define sbi(reg, bitmask) *reg |= bitmask + #define pulse_high(reg, bitmask) sbi(reg, bitmask); cbi(reg, bitmask); + #define pulse_low(reg, bitmask) cbi(reg, bitmask); sbi(reg, bitmask); + + #define cport(port, data) port &= data + #define sport(port, data) port |= data + + #define regtype volatile uint32_t + #define regsize uint32_t + +#else + #define PROGMEM + +#ifndef prog_uchar + #define prog_uchar byte + #define prog_uint16_t const uint16_t +#endif + + #define regtype volatile uint32_t + #define regsize uint16_t +#endif + +#define swap(a, b) {int16_t buf = a; a = b; b = buf;} + +#define BITMASK_FONT 1 +#define ANTIALIASED_FONT 2 +#define HEADER_LENGTH 5 + +#define SCROLL_SMOOTH 1 +#define SCROLL_CLEAN 2 + +#define FILL_TOPDOWN 0 +#define FILL_LEFTRIGHT 0 +#define FILL_DOWNTOP 1 +#define FILL_RIGHTLEFT 2 + +#define ORIGIN_RELATIVE true // origin relative to a current scroll position +#define ORIGIN_ABSOLUTE false // origin matches physical device pixel coordinates + +#define PORTRAIT 0 +#define LANDSCAPE 1 +#define PORTRAIT_FLIP 2 +#define LANDSCAPE_FLIP 3 + +#define TRANSPARENT_TEXT_BACKGROUND 0 +#define FILL_TEXT_BACKGROUND 1 + + +#define ipart(X) ((int16_t)(X)) +#define iround(X) ((uint16_t)(((double)(X))+0.5)) +#define fpart(X) (((double)(X))-(double)ipart(X)) +#define rfpart(X) (1.0-fpart(X)) + +extern regtype *registerCS; // chip select +extern regsize bitmaskCS; + +#define chipSelect() cbi(registerCS, bitmaskCS) +#define chipDeselect() sbi(registerCS, bitmaskCS) + +// Color definitions +#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */ +#define ILI9341_NAVY 0x000F /* 0, 0, 128 */ +#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ILI9341_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ILI9341_MAROON 0x7800 /* 128, 0, 0 */ +#define ILI9341_PURPLE 0x780F /* 128, 0, 128 */ +#define ILI9341_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ILI9341_BLUE 0x001F /* 0, 0, 255 */ +#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */ +#define ILI9341_CYAN 0x07FF /* 0, 255, 255 */ +#define ILI9341_RED 0xF800 /* 255, 0, 0 */ +#define ILI9341_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */ +#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ILI9341_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ILI9341_PINK 0xF81F +#define ILI9341_CARDBLUE 0x0169 /* 0, 45, 75 */ +#define ILI9341_DARKBLUE 0x0000 /* 0, 45, 75 */ + +class RGB { +private: + uint16_t col; +public: + uint8_t red; + uint8_t green; + uint8_t blue; + + RGB(uint8_t r, uint8_t g, uint8_t b); + RGB(); + + void setColor(int32_t r, int32_t g, int32_t b); + + RGB convert565toRGB(uint16_t color); + uint16_t convertRGBto565(RGB color); + uint16_t convertTo565(); +}; + +class Bounds { +public: + int16_t x1; + int16_t y1; + int16_t x2; + int16_t y2; + + Bounds( int16_t xx1, int16_t yy1, int16_t xx2, int16_t yy2 ) { + x1 = xx1; + y1 = yy1; + x2 = xx2; + y2 = yy2; + } +}; + +class PixelsBase { +protected: + /* device physical dimension in portrait orientation */ + int16_t deviceWidth; + int16_t deviceHeight; + + /* device logical dimension in current orientation */ + int16_t width; + int16_t height; + + boolean landscape; + + uint8_t orientation; + + boolean relativeOrigin; + + /* currently selected font */ + prog_uchar* currentFont; + + RGB* foreground; + RGB* background; + + double lineWidth; + + uint8_t fillDirection; + boolean antialiasing; + + boolean scrollSupported; + boolean scrollEnabled; + + int16_t currentScroll; + int16_t flipScroll; + boolean scrollCleanMode; + uint16_t extraScrollDelay; + + int16_t caretX; + int16_t caretY; + + int8_t glyphPrintMode; + +#ifndef NO_TEXT_WRAP + boolean wrapText; + int16_t textWrapMarginLeft; + int16_t textWrapMarginRight; + int16_t textWrapLineGap; + + boolean textWrapScroll; + int16_t textWrapMarginBottom; + RGB* textWrapScrollFill; +#endif + + int gfxOpNestingDepth; + + boolean transformBounds(Bounds& bb); + boolean checkBounds(Bounds& bb); + void printString(int16_t xx, int16_t yy, String text, boolean clean, int8_t kerning[] = NULL); + void drawGlyph(int16_t fontType, boolean clean, int16_t xx, int16_t yy, + int16_t height, prog_uchar* data, int16_t length); + +#ifndef NO_TEXT_WRAP + int16_t computeBreakPos(String text, int16_t t); +#endif + + virtual int32_t setRegion(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { return -1; } + + void setCurrentPixel(RGB* color); + void setCurrentPixel(int16_t color); + void fill(int b, int16_t x1, int16_t y1, int16_t x2, int16_t y2); + virtual void quickFill(int b, int16_t x1, int16_t y1, int16_t x2, int16_t y2) {} + void putColor(int16_t x, int16_t y, boolean steep, double weight); + RGB* computeColor(RGB* bg, double weight); + RGB* computeColor(RGB* fg, uint8_t opacity); + + void resetRegion(); + + void hLine(int16_t x1, int16_t y1, int16_t x2); + void vLine(int16_t x1, int16_t y1, int16_t y2); + + virtual void deviceWriteData(uint8_t hi, uint8_t lo) {} + + virtual void scrollCmd() {} + + virtual void drawCircleAntialiaced(int16_t x, int16_t y, int16_t radius, boolean bordermode); + virtual void drawFatLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2); + virtual void drawLineAntialiased(int16_t x1, int16_t y1, int16_t x2, int16_t y2); + virtual void drawRoundRectangleAntialiased(int16_t x, int16_t y, int16_t width, int16_t height, int16_t rx, int16_t ry, boolean bordermode); + + int16_t* loadFileBytes(String); + + RGB* computedBgColor; + RGB* computedFgColor; + RGB* bgBuffer; + RGB* fgBuffer; + + virtual void beginGfxOperation() { + chipSelect(); + gfxOpNestingDepth++; + } + + virtual void endGfxOperation(boolean force) { + gfxOpNestingDepth--; + if ( gfxOpNestingDepth <= 0 ) { + chipDeselect(); + gfxOpNestingDepth = 0; + } + } + + void endGfxOperation() { + endGfxOperation(false); + } + +public: + + /** + * Constructs a new Pixels object for the reference platform TFT_PQ 2.4 (ILI9325 controller) + ITDB02 MEGA Shield v1.1. + * @param width target device width (in pixels) + * @param height target device height (in pixels) + */ + PixelsBase(uint16_t width, uint16_t height); + /** + * Initializes hardware with defaults. + */ + virtual void init() {} + /** + * Sets the current coordinate space orientation. + * Default value depends on initially given device width and height (PORTRAIT if height > width, otherwise LANDSCAPE). + * @param orientation accepts PORTRAIT, LANDSCAPE, PORTRAIT_FLIP or LANDSCAPE_FLIP + */ + void setOrientation(uint8_t orientation); + /** + * Returns the current coordinate space orientation. + * @return the current orientation + * @see setOrientation(uint8_t) + */ + inline uint8_t getOrientation() { + return orientation; + } + /** + * Enables or disables antialiasing by a drawing of graphical primitives. The metod does not impact antialiased fonts. + * Antialiased output in general requires more resources/time to output comparing to "grainy" output mode. + * Takes no effect if Pixels_Antialiasing.h is not included + * @param enable a boolean value that determines whether the antialiasing should be enabled or not + */ + virtual void enableAntialiasing(boolean enable) { + antialiasing = false; + } + + /** + * Gets the current antialiasing mode. + * @return a boolean value that shows whether the antialiasing is be enabled or not + */ + inline boolean isAntialiased() { + return antialiasing; + } + /** + * Enables or disables scroll feature. + * @param enable a boolean value that determines whether the scroll feature should be enabled or not + * @see scroll(int16_t,int8_t) + */ + inline void enableScroll(boolean enable) { + scrollEnabled = enable; + } + /** + * @returns true if the target device can scroll and the scroll feature is enabled. + * @see scroll(int16_t,int8_t) + * @see enableScroll(boolean) + */ + inline boolean canScroll() { + return scrollEnabled & scrollSupported; + } + /** + * Sets the current line width. For time being the line width is respected by + * drawLine(int16_t,int16_t,int16_t,int16_t) and a line width control is still "under construction". + * @param width new line width. + */ + inline void setLineWidth(double width) { + lineWidth = width; + } + /** + * Returns the current line width. + * @return the current line width. + */ + inline double getLineWidth() { + return lineWidth; + } + /** + * Returns device width. + * @return device width. + */ + inline int16_t getWidth() { + return width; + } + /** + * Returns device height. + * @return device height. + */ + inline int16_t getHeight() { + return height; + } + /** + * Bounds the coordinate space to the device controller video RAM. The physical output depends on the actual scroll position. + */ + inline void setOriginRelative() { + relativeOrigin = true; + } + /** + * Bounds the coordinate space to physical device pixels, ignoring actual scroll position. + * @see scroll(int16_t,int8_t) + */ + inline void setOriginAbsolute() { + relativeOrigin = false; + } + /** + * @return true if the current positioning is relative. + * @see setOriginRelative() + * @see setOriginAbsolute() + */ + inline boolean isOriginRelative() { + return relativeOrigin; + } + /** + * Outout fine tuning method for slow devices + * @param direction accepts FILL_TOPDOWN, FILL_LEFTRIGHT, FILL_DOWNTOP or FILL_RIGHTLEFT + */ + virtual void setFillDirection(uint8_t direction) {} + /** + * Fills the screen with the current background color + */ + void clear(); + /** + * Sets the current background color to the specified color. + * All subsequent relevant graphics operations use this specified color. + * @param r the red component + * @param g the green component + * @param b the blue component + */ + inline void setBackground(uint8_t r, uint8_t g, uint8_t b) { + bgBuffer->setColor(r, g, b); + setBackground(bgBuffer); + } + /** + * Sets the current color to the specified color. + * All subsequent graphics operations use this specified color. + * @param r the red component + * @param g the green component + * @param b the blue component + */ + inline void setColor(uint8_t r, uint8_t g, uint8_t b) { + fgBuffer->setColor(r, g, b); + setColor(fgBuffer); + } + /** + * Sets the current background color to the specified color. + * All subsequent relevant graphics operations use this specified color. + * @param color color object reference + */ + inline void setBackground(RGB* color) { + background = color; + } + /** + * Sets the current color to the specified color. + * All subsequent graphics operations use this specified color. + * @param color color object + */ + inline void setColor(RGB* color) { + foreground = color; + } + /** + * Gets the graphics context's current background color. + * @return the graphics context's current background color. + */ + inline RGB* getBackground() { + return background; + } + /** + * Gets the graphics context's current color. + * @return the graphics context's current color. + */ + inline RGB* getColor() { + return foreground; + } + /** + * Gets a pixel color at the point + * (x, y) in the current coordinate system. + * If video RAM read is not supported by the hardware, returns the + * graphics context's current background color + * @param x x coordinate. + * @param y y coordinate. + * @return pixel color or the graphics context's current background color. + */ + RGB* getPixel(int16_t x, int16_t y); + /** + * Draws a pixel, using the current color, at the point + * (x, y) + * in the current coordinate system. + * @param x x coordinate. + * @param y y coordinate. + */ + void drawPixel(int16_t x, int16_t y); + /** + * Draws a line, using the current color, between the points + * (x1, y1) and (x2, y2) + * in current coordinate system. + * @param x1 the first point's x coordinate. + * @param y1 the first point's y coordinate. + * @param x2 the second point's x coordinate. + * @param y2 the second point's y coordinate. + * @see setOriginRelative() + * @see setOriginAbsolute() + */ + void drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2); + /** + * Draws the outline of a circle, defined by center coordinates and a radius, + * with the current color. + * @param x the x coordinate of the circle center. + * @param y the y coordinate of the circle center. + * @param radius circle radius. + * @see fillCircle(int16_t,int16_t,int16_t) + */ + void drawCircle(int16_t x, int16_t y, int16_t radius); + /** + * Draws the outline of an oval. + * The result is a circle or ellipse that fits within the + * rectangle specified by the x, y, + * width, and height arguments. + *

+ * The oval covers an area that is + * width + 1 pixels wide + * and height + 1 pixels tall. + * @param x the x coordinate of the upper left + * corner of the oval to be drawn. + * @param y the y coordinate of the upper left + * corner of the oval to be drawn. + * @param width the width of the oval to be drawn. + * @param height the height of the oval to be drawn. + * @see fillOval(int16_t,int16_t,int16_t,int16_t) + */ + void drawOval(int16_t x, int16_t y, int16_t width, int16_t height); + /** + * Draws the outline of the specified rectangle. + * The left and right edges of the rectangle are at + * x and x + width. + * The top and bottom edges are at + * y and y + height. + * The rectangle is drawn using the current color. + * @param x the x coordinate + * of the rectangle to be drawn. + * @param y the y coordinate + * of the rectangle to be drawn. + * @param width the width of the rectangle to be drawn. + * @param height the height of the rectangle to be drawn. + * @see fillRectangle(int16_t,int16_t,int16_t,int16_t) + */ + void drawRectangle(int16_t x, int16_t y, int16_t width, int16_t height); + /** + * Draws an outlined round-cornered rectangle using the current color. + * The left and right edges of the rectangle + * are at x and x + width, + * respectively. The top and bottom edges of the rectangle are at + * y and y + height. + * @param x the x coordinate of the rectangle to be drawn. + * @param y the y coordinate of the rectangle to be drawn. + * @param width the width of the rectangle to be drawn. + * @param height the height of the rectangle to be drawn. + * @param r horizontal and vertical diameters of the arc + * at the four corners. + * @see fillRoundRectangle(int16_t,int16_t,int16_t,int16_t,int16_t) + */ + void drawRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t r); + /** + * Fills a circle, defined by center coordinates and a radius, with the current color. + * @param x the x coordinate of the circle center. + * @param y the y coordinate of the circle center. + * @param radius circle radius. + * @see drawCircle(int16_t,int16_t,int16_t) + */ + void fillCircle(int16_t x, int16_t y, int16_t radius); + /** + * Fills an oval bounded by the specified rectangle with the + * current color. + * @param x the x coordinate of the upper left corner + * of the oval to be filled. + * @param y the y coordinate of the upper left corner + * of the oval to be filled. + * @param width the width of the oval to be filled. + * @param height the height of the oval to be filled. + * @see drawOval(int16_t,int16_t,int16_t,int16_t) + */ + void fillOval(int16_t x, int16_t y, int16_t width, int16_t height); + /** + * Fills the specified rectangle. + * The left and right edges of the rectangle are at + * x and x + width - 1. + * The top and bottom edges are at + * y and y + height - 1. + * The resulting rectangle covers an area + * width pixels wide by + * height pixels tall. + * The rectangle is filled using the current color. + * @param x the x coordinate + * of the rectangle to be filled. + * @param y the y coordinate + * of the rectangle to be filled. + * @param width the width of the rectangle to be filled. + * @param height the height of the rectangle to be filled. + * @see drawRectangle(int16_t,int16_t,int16_t,int16_t) + */ + void fillRectangle(int16_t x, int16_t y, int16_t width, int16_t height); + /** + * Fills the specified rounded corner rectangle with the current color. + * The left and right edges of the rectangle + * are at x and x + width - 1, + * respectively. The top and bottom edges of the rectangle are at + * y and y + height - 1. + * @param x the x coordinate of the rectangle to be filled. + * @param y the y coordinate of the rectangle to be filled. + * @param width the width of the rectangle to be filled. + * @param height the height of the rectangle to be filled. + * @param r horizontal and vertical diameters of the arc at the four corners. + * @see drawRoundRectangle(int16_t,int16_t,int16_t,int16_t,int16_t) + */ + void fillRoundRectangle(int16_t x, int16_t y, int16_t width, int16_t height, int16_t r); + /** + * Draws specified bitmap image. + * The image is drawn with its top-left corner at + * (xy) in the current coordinate + * space. + * @param data the specified bitmap image to be drawn. This method does + * nothing if img is null. + * @param x the x coordinate. + * @param y the y coordinate. + * @param width the width of the image. + * @param height the height of the image. + * @see loadBitmap(int16_t,int16_t,int16_t,int16_t,String) + */ + int8_t drawBitmap(int16_t x, int16_t y, int16_t width, int16_t height, prog_uint16_t* data); + /** + * Draws specified bitmap image. + * The image is drawn with its top-left corner at + * (xy) in the current coordinate + * space. + * @param data compressed (with Pixelmeister) bitmap image bytes. This method does + * nothing if img is null. + * @param x the x coordinate. + * @param y the y coordinate. + * @see drawBitmap(int16_t,int16_t,int16_t,int16_t,int[]) + */ + int8_t drawCompressedBitmap(int16_t x, int16_t y, prog_uchar* data); + /** + * Draws an icon, prepared with Pixelmeister. + * The icon is drawn with its top-left corner at + * (xy) in the current coordinate + * space. + * @param data icon image bytes. This method does + * nothing if data is null. + * @param x the x coordinate. + * @param y the y coordinate. + */ + void drawIcon(int16_t xx, int16_t yy, prog_uchar data[]); + /** + * Paint icon pixels with background color. The icon should be in Pixelmeister format. + * The icon is drawn with its top-left corner at + * (xy) in the current coordinate + * space. + * @param data icon image bytes. This method does + * nothing if data is null. + * @param x the x coordinate. + * @param y the y coordinate. + */ + void cleanIcon(int16_t xx, int16_t yy, prog_uchar data[]); + /** + * @return height of an icon, created with Pixelmeister + * @param data icon image bytes. + */ + int16_t getIconHeight(prog_uchar* data) { + return pgm_read_byte_near(data + 4); + } + /** + * @return width of an icon, created with Pixelmeister + * @param data icon image bytes. + */ + int16_t getIconWidth(prog_uchar* data) { + return pgm_read_byte_near(data + 5); + } + /** + * Loads from an external FAT-drive and draws specified bitmap image. + * The image is drawn with its top-left corner at + * (xy) in the current coordinate + * space. + * @param path to the image. + * @param x the x coordinate. + * @param y the y coordinate. + * @param width the width of the image. + * @param height the height of the image. + * @see drawBitmap(int16_t,int16_t,int16_t,int16_t,int[]) + */ + int8_t loadBitmap(int16_t x, int16_t y, int16_t width, int16_t height, String path); + /** + * Under construction + */ + void scroll(int16_t dy, int16_t x1, int16_t x2, int8_t flags); + /** + * Scrolls the display content to a given number of pixels (if the device controller + * supports scrolling). Scroll axis is vertical by portrait orientation and horizontal + * by landscape orientation (hardware scpecifics). + * SCROLL_CLEAN flag forces to paint wrapped regions with background color. SCROLL_SMOOTH + * eases begin and end of a scroll movement (by big dy values). + * @param dy negative or positive scroll distance + * @param flags can be 0, SCROLL_SMOOTH or/and SCROLL_CLEAN + */ + void scroll(int16_t dy, int8_t flags); + + /** + * Returns the current scroll position + */ + int getScroll() { +// return orientation < 2 ? currentScroll : deviceHeight - currentScroll; + return currentScroll; + } + + void setScrollStepDelay(uint16_t ms) { + extraScrollDelay = ms; + } + + /** + * returns current print mode + * @see setPrintMode(int8_t) + */ + int8_t getPrintMode() { + return glyphPrintMode; + } + + /** + * sets text output mode + * @param printMode FILL_TEXT_BACKGROUND=1 forces to fill glyph background with current background color; + * TRANSPARENT_TEXT_BACKGROUND=0 forces to print glyphs with no background (default) + */ + void setPrintMode(int8_t printMode) { +#ifndef NO_FILL_TEXT_BACKGROUND + glyphPrintMode = printMode; +#endif + } + + /** + * Enables text line auto wrap + * @param marginLeft start X coordinate of wrapped tex line + * @param marginRight screen space right side, to avoid to print to + * @param lineGap vertical gap between text lines + */ + void enableTextWrap(int16_t marginLeft, int16_t marginRight, int16_t lineGap) { +#ifndef NO_TEXT_WRAP + wrapText = true; + textWrapMarginLeft = marginLeft; + textWrapMarginRight = marginRight; + textWrapLineGap = lineGap; +#endif + } + + /** + * Disables text line auto wrap + * @see enableTextWrap(marginLeft,marginRight,int16_t) + */ + void disableTextWrap() { +#ifndef NO_TEXT_WRAP + wrapText = false; +#endif + } + + /** + * Enables auto-scroll if a wrapped line does not fit remaining vertical space. + * Takes no effect if text wrap is not enabled or by landscape page orientation. + * @param marginBottom bottom screen space, to avoid to print to + * @param scrollFill background color to pre-fill blank area + * @see enableTextWrap(marginLeft,marginRight,int16_t) + */ + void enableTextWrapScroll(int16_t marginBottom, RGB* scrollFill = NULL) { +#ifndef NO_TEXT_WRAP + textWrapMarginBottom = marginBottom; + textWrapScrollFill = scrollFill; + textWrapScroll = true; +#endif + } + + /** + * Disables auto-scroll if a wrapped line + * @see enableTextWrapScroll(marginBottom,scrollFill) + */ + void disableTextWrapScroll() { +#ifndef NO_TEXT_WRAP + textWrapScroll = false; +#endif + } + + int16_t getCaretX() { + return caretX; + } + + int16_t getCaretY() { + return caretY; + } + + /** + * Sets this graphics context's font to the specified font. + * All subsequent text operations using the context use this font. + * @param font a font, converted from TTF with + * Pixelmeister. + */ + int setFont(prog_uchar font[]); + /** + * Draws the text given by the specified string, using current font and color. + * The baseline of the leftmost character is at position (xy) + * in the current coordinate system. + * @param text the string to be drawn. + * @param xx the x coordinate. + * @param yy the y coordinate. + * @param kerning optional array of integer kerning values in a range from + * -99 to 99 to be applied to the string glyphs correspondingly. "-100" value ends + * the kerning data. If the string has more characters than the array length, a value + * precedes "-100" is used for the rest of the string glyphs. + * @see setFont(prog_uchar font[]) + * @see setOriginRelative() + * @see setOriginAbsolute() + * @see cleanText(int16_t,int16_t,String,int8_t[]) + */ + void print(int16_t xx, int16_t yy, String text, int8_t kerning[] = NULL); + /** + * The method oposes print(int16_t,int16_t,String,int8_t[]) Erases the text given by + * the specified string by filling glyph shapes with the current background color. + * The baseline of the leftmost character is at position (xy) + * in the current coordinate system. + * @param text the string to be erased. + * @param xx the x coordinate. + * @param yy the y coordinate. + * @param kerning aptional array of integer kerning values in a range from + * -99 to 99 to be applied to the string glyphs correspondingly. "-100" value ends + * the kerning data. If the string has more characters than the array length, a value + * precedes "-100" is usedas a kerning hint for the rest of the string glyphs. + * @see setFont(prog_uchar font[]) + * @see setOriginRelative() + * @see setOriginAbsolute() + * @see print(int16_t,int16_t,String,int8_t[]) + */ + void cleanText(int16_t xx, int16_t yy, String text, int8_t kerning[] = NULL); + /** + * Gets the current font text line height + * @return text line height in pixels + */ + int16_t getTextLineHeight(); + /** + * Gets the current font baseline offsett + * @return text baseline offset + */ + int16_t getTextBaseline(); + /** + * Computes needed horizontal space to print the given text with print(int16_t,int16_t,String,int8_t) + * @param text the text string. + * @param kerning optional array of integer kerning values in a range from + * -99 to 99 to be applied to the string glyphs correspondingly. "-100" value ends + * the kerning data. If the string has more characters than the array length, a value + * precedes "-100" is used as a kerning hint for the rest of the string glyphs. + * @see print(int16_t,int16_t,String,int8_t[]) + * @return text baseline offset + */ + int16_t getTextWidth(String text, int8_t kerning[] = NULL); + + /** + * Returns width for a given character + */ + int16_t getCharWidth(char c); + + + void scrollText( int16_t x, int16_t y, String text, uint8_t scrollStep, uint8_t repeat, uint16_t maxScroll ); +}; + +class BitStream { +private: + prog_uchar* data; + size_t size; + int32_t bitpos; + +public: + BitStream (prog_uchar* src_buffer, size_t byte_size, int8_t offset = 0) { + bitpos = offset; + data = src_buffer; + size = byte_size + (offset>>3); + } + + bool endOfData() { + return (size_t)((bitpos + 1) >> 3) >= size; + } + + uint8_t testCurrentByte() { + uint8_t res = (uint8_t)pgm_read_byte_near(data + (bitpos>>3)); + return res; + } + + uint8_t readBit() { + uint8_t res = (uint8_t)(pgm_read_byte_near(data + (bitpos>>3)) & (uint8_t)( (uint16_t)0x80 >> (bitpos & 7) )); + bitpos++; + return res; + } + + uint8_t readBits(uint8_t len) { + + uint16_t end_offset = (bitpos + len - 1)>>3; + if ( end_offset >= size ) { + return 0; + } + + uint8_t i; + uint16_t byte_offset = bitpos >> 3; + if (byte_offset == end_offset) { + uint16_t x = pgm_read_byte_near(data + byte_offset); + i = (uint8_t)(x >> (8 - ((bitpos & 7) + len))) & ((1 << len) - 1); + } else { + uint16_t x = ((uint16_t)pgm_read_byte_near(data + byte_offset) << 8) + pgm_read_byte_near(data + end_offset); + i = (uint8_t)(x >> (16 - ((bitpos & 7) + len))) & ((1 << len) - 1); + } + bitpos += len; + return i; + } + + uint16_t readNumber() { + int16_t count; + + int8_t ctr = 1; + int16_t base = 2; + do { + if ( readBit() == 0 ) { + uint8_t bits = readBits(ctr); + count = base + bits; + break; + } else { + ctr++; + base *= 2; + if ( ctr == 7 ) { + uint8_t bits = readBits(ctr); + count = base + bits; + break; + } + } + } while ( true ); + + return (uint16_t)count; + } +}; + +#endif diff --git a/Pixels_ILI9341.h b/Pixels_ILI9341.h old mode 100644 new mode 100755 index a584328..203af4f --- a/Pixels_ILI9341.h +++ b/Pixels_ILI9341.h @@ -1,259 +1,263 @@ -/* - * Pixels. Graphics library for TFT displays. - * - * Copyright (C) 2012-2013 Igor Repinetski - * - * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. - * - * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister - * - * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ - * - * This library includes some code portions and algoritmic ideas derived from works of - * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) - * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) - */ - -/* - * Pixels port to ILI9341 controller, SPI mode (ElecFreaks TFT2.2SP Shield) - * SPI is in bit banging mode, as the shield does not connect the hardware SPI (SCL=13, SDA=11) - * to the display controller - */ - -#include "Pixels.h" - -#ifndef PIXELS_ILI9341_H -#define PIXELS_ILI9341_H -#define PIXELS_MAIN - -#if defined(PIXELS_ANTIALIASING_H) -#define PixelsBase PixelsAntialiased -#endif - -class Pixels : public PixelsBase -#if defined(PIXELS_SPISW_H) - , public SPIsw -#elif defined(PIXELS_SPIHW_H) - , public SPIhw -#elif defined(PIXELS_PPI8_H) - , public PPI8 -#elif defined(PIXELS_PPI16_H) - , public PPI16 -#endif -{ -protected: - void deviceWriteData(uint8_t high, uint8_t low) { - writeData(high, low); - } - - int32_t setRegion(int16_t x1, int16_t y1, int16_t x2, int16_t y2); - void quickFill(int b, int16_t x1, int16_t y1, int16_t x2, int16_t y2); - void setFillDirection(uint8_t direction); - - void scrollCmd(); - -public: - Pixels() : PixelsBase(240, 320) { // ElecFreaks TFT2.2SP shield as default - scrollSupported = true; - setSpiPins(4, 3, 7, 5, 6); // dummy code in PPI case - setPpiPins(38, 39, 40, 41, 0); // dummy code in SPI case - } - - Pixels(uint16_t width, uint16_t height) : PixelsBase( width, height) { - scrollSupported = true; - setSpiPins(4, 3, 7, 5, 6); // dummy code in PPI case - setPpiPins(38, 39, 40, 41, 0); // dummy code in SPI case - } - - void init(); -}; - -#if defined(PIXELS_ANTIALIASING_H) -#undef PixelsBase -#endif - -void Pixels::init() { - - initInterface(); - - chipSelect(); - - writeCmd(0xCB); - writeData(0x39); - writeData(0x2C); - writeData(0x00); - writeData(0x34); - writeData(0x02); - - writeCmd(0xCF); - writeData(0x00); - writeData(0XC1); - writeData(0X30); - - writeCmd(0xE8); - writeData(0x85); - writeData(0x00); - writeData(0x78); - - writeCmd(0xEA); - writeData(0x00); - writeData(0x00); - - writeCmd(0xED); - writeData(0x64); - writeData(0x03); - writeData(0X12); - writeData(0X81); - - writeCmd(0xF7); - writeData(0x20); - - writeCmd(0xC0); //Power control - writeData(0x23); //VRH[5:0] - - writeCmd(0xC1); //Power control - writeData(0x10); //SAP[2:0];BT[3:0] - - writeCmd(0xC5); //VCM control - writeData(0x3e); //Contrast - writeData(0x28); - - writeCmd(0xC7); //VCM control2 - writeData(0x86); //-- - - writeCmd(0x36); // Memory Access Control - writeData(0x48); //C8 //48 68竖屏//28 E8 横屏 - - writeCmd(0x3A); - writeData(0x55); - - writeCmd(0xB1); - writeData(0x00); - writeData(0x18); - - writeCmd(0xB6); // Display Function Control - writeData(0x08); - writeData(0x82); - writeData(0x27); -/* - writeCmd(0xF2); // 3Gamma Function Disable - writeData(0x00); - - writeCmd(0x26); //Gamma curve selected - writeData(0x01); - - writeCmd(0xE0); //Set Gamma - writeData(0x0F); - writeData(0x31); - writeData(0x2B); - writeData(0x0C); - writeData(0x0E); - writeData(0x08); - writeData(0x4E); - writeData(0xF1); - writeData(0x37); - writeData(0x07); - writeData(0x10); - writeData(0x03); - writeData(0x0E); - writeData(0x09); - writeData(0x00); - - writeCmd(0XE1); //Set Gamma - writeData(0x00); - writeData(0x0E); - writeData(0x14); - writeData(0x03); - writeData(0x11); - writeData(0x07); - writeData(0x31); - writeData(0xC1); - writeData(0x48); - writeData(0x08); - writeData(0x0F); - writeData(0x0C); - writeData(0x31); - writeData(0x36); - writeData(0x0F); -*/ - writeCmd(0x11); //Exit Sleep - delay(120); - - writeCmd(0x29); //Display on - writeCmd(0x2c); - - chipDeselect(); -} - -void Pixels::scrollCmd() { - int16_t s = (orientation > 1 ? deviceHeight - currentScroll : currentScroll) % deviceHeight; - writeCmd(0x37); - writeData(highByte(s)); - writeData(lowByte(s)); -} - -void Pixels::setFillDirection(uint8_t direction) { - fillDirection = direction; -} - -void Pixels::quickFill (int color, int16_t x1, int16_t y1, int16_t x2, int16_t y2) { - - int32_t counter = setRegion(x1, y1, x2, y2); - if( counter == 0 ) { - return; - } - - registerSelect(); - - uint8_t lo = lowByte(color); - uint8_t hi = highByte(color); - - for (int16_t i = 0; i < counter / 20; i++) { - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - writeData(hi);writeData(lo); - } - for (int32_t i = 0; i < counter % 20; i++) { - writeData(hi);writeData(lo); - } -} - -int32_t Pixels::setRegion(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { - - Bounds bb(x1, y1, x2, y2); - if( !checkBounds(bb) ) { - return 0; - } - - writeCmd(0x2a); - writeData(bb.x1>>8); - writeData(bb.x1); - writeData(bb.x2>>8); - writeData(bb.x2); - writeCmd(0x2b); - writeData(y1>>8); - writeData(bb.y1); - writeData(bb.y2>>8); - writeData(bb.y2); - writeCmd(0x2c); - - return (int32_t)(bb.x2 - bb.x1 + 1) * (bb.y2 - bb.y1 + 1); -} -#endif +/* + * Pixels. Graphics library for TFT displays. + * + * Copyright (C) 2012-2013 Igor Repinetski + * + * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. + * + * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ + * + * This library includes some code portions and algoritmic ideas derived from works of + * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) + * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) + */ + +/* + * Pixels port to ILI9341 controller, SPI mode (ElecFreaks TFT2.2SP Shield) + * SPI is in bit banging mode, as the shield does not connect the hardware SPI (SCL=13, SDA=11) + * to the display controller + */ + +#include "Pixels.h" + +#ifndef PIXELS_ILI9341_H +#define PIXELS_ILI9341_H +#define PIXELS_MAIN + +#if defined(PIXELS_ANTIALIASING_H) +#define PixelsBase PixelsAntialiased +#endif + +class Pixels : public PixelsBase +#if defined(PIXELS_SPISW_H) + , public SPIsw +#elif defined(PIXELS_SPIHW_H) + , public SPIhw +#elif defined(PIXELS_PPI8_H) + , public PPI8 +#elif defined(PIXELS_PPI16_H) + , public PPI16 +#endif +{ +protected: + void deviceWriteData(uint8_t high, uint8_t low) { + writeData(high, low); + } + + int32_t setRegion(int16_t x1, int16_t y1, int16_t x2, int16_t y2); + void quickFill(int b, int16_t x1, int16_t y1, int16_t x2, int16_t y2); + void setFillDirection(uint8_t direction); + + void scrollCmd(); + +public: + Pixels() : PixelsBase(240, 320) { // ElecFreaks TFT2.2SP shield as default + scrollSupported = true; + setSpiPins(4, 3, 7, 5, 6); // dummy code in PPI case + setPpiPins(38, 39, 40, 41, 0); // dummy code in SPI case + } + + Pixels(uint16_t width, uint16_t height) : PixelsBase( width, height) { + scrollSupported = true; + setSpiPins(4, 3, 7, 5, 6); // dummy code in PPI case + setPpiPins(38, 39, 40, 41, 0); // dummy code in SPI case + } + + void init(); +}; + +#if defined(PIXELS_ANTIALIASING_H) +#undef PixelsBase +#endif + +void Pixels::init() { + + initInterface(); + + chipSelect(); + + writeCmd(0xCB); + writeData(0x39); + writeData(0x2C); + writeData(0x00); + writeData(0x34); + writeData(0x02); + + writeCmd(0xCF); + writeData(0x00); + writeData(0XC1); + writeData(0X30); + + writeCmd(0xE8); + writeData(0x85); + writeData(0x00); + writeData(0x78); + + writeCmd(0xEA); + writeData(0x00); + writeData(0x00); + + writeCmd(0xED); + writeData(0x64); + writeData(0x03); + writeData(0X12); + writeData(0X81); + + writeCmd(0xF7); + writeData(0x20); + + writeCmd(0xC0); //Power control + writeData(0x23); //VRH[5:0] + + writeCmd(0xC1); //Power control + writeData(0x10); //SAP[2:0];BT[3:0] + + writeCmd(0xC5); //VCM control + writeData(0x3e); //Contrast + writeData(0x28); + + writeCmd(0xC7); //VCM control2 + writeData(0x86); //-- + + writeCmd(0x36); // Memory Access Control + writeData(0x48); //C8 //48 68竖屏//28 E8 横屏 + + writeCmd(0x3A); + writeData(0x55); + + writeCmd(0xB1); + writeData(0x00); + writeData(0x18); + + writeCmd(0xB6); // Display Function Control + writeData(0x08); + writeData(0x82); + writeData(0x27); +/* + writeCmd(0xF2); // 3Gamma Function Disable + writeData(0x00); + + writeCmd(0x26); //Gamma curve selected + writeData(0x01); + + writeCmd(0xE0); //Set Gamma + writeData(0x0F); + writeData(0x31); + writeData(0x2B); + writeData(0x0C); + writeData(0x0E); + writeData(0x08); + writeData(0x4E); + writeData(0xF1); + writeData(0x37); + writeData(0x07); + writeData(0x10); + writeData(0x03); + writeData(0x0E); + writeData(0x09); + writeData(0x00); + + writeCmd(0XE1); //Set Gamma + writeData(0x00); + writeData(0x0E); + writeData(0x14); + writeData(0x03); + writeData(0x11); + writeData(0x07); + writeData(0x31); + writeData(0xC1); + writeData(0x48); + writeData(0x08); + writeData(0x0F); + writeData(0x0C); + writeData(0x31); + writeData(0x36); + writeData(0x0F); +*/ + writeCmd(0x11); //Exit Sleep + delay(120); + + writeCmd(0x29); //Display on + writeCmd(0x2c); + + chipDeselect(); +} + +void Pixels::scrollCmd() { + chipSelect(); + int16_t s = (orientation > 1 ? deviceHeight - currentScroll : currentScroll) % deviceHeight; + writeCmd(0x37); + writeData(highByte(s)); + writeData(lowByte(s)); + chipDeselect(); +} + +void Pixels::setFillDirection(uint8_t direction) { + fillDirection = direction; +} + +void Pixels::quickFill (int color, int16_t x1, int16_t y1, int16_t x2, int16_t y2) { + chipSelect(); + + int32_t counter = setRegion(x1, y1, x2, y2); + if( counter == 0 ) { + return; + } + + registerSelect(); + + uint8_t lo = lowByte(color); + uint8_t hi = highByte(color); + + for (int16_t i = 0; i < counter / 20; i++) { + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + writeData(hi);writeData(lo); + } + for (int32_t i = 0; i < counter % 20; i++) { + writeData(hi);writeData(lo); + } + chipDeselect(); +} + +int32_t Pixels::setRegion(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { + + Bounds bb(x1, y1, x2, y2); + if( !checkBounds(bb) ) { + return 0; + } + + writeCmd(0x2a); + writeData(bb.x1>>8); + writeData(bb.x1); + writeData(bb.x2>>8); + writeData(bb.x2); + writeCmd(0x2b); + writeData(y1>>8); + writeData(bb.y1); + writeData(bb.y2>>8); + writeData(bb.y2); + writeCmd(0x2c); + + return (int32_t)(bb.x2 - bb.x1 + 1) * (bb.y2 - bb.y1 + 1); +} +#endif diff --git a/Pixels_SPIhw.h b/Pixels_SPIhw.h old mode 100644 new mode 100755 index cd447a9..7377519 --- a/Pixels_SPIhw.h +++ b/Pixels_SPIhw.h @@ -1,284 +1,569 @@ -/* - * Pixels. Graphics library for TFT displays. - * - * Copyright (C) 2012-2013 Igor Repinetski - * - * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. - * - * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister - * - * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ - * - * This library includes some code portions and algoritmic ideas derived from works of - * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) - * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) - */ - -/* - * Hardware SPI layer (SCL=13, SDA=11 on Arduino) - */ - -#include "Pixels.h" - -#ifdef PIXELS_MAIN -#error Pixels_SPIhw.h must be included before Pixels_.h -#endif - -#ifndef PIXELS_SPIHW_H -#define PIXELS_SPIHW_H - -#define SPI(X) SPDR=X;while(!(SPSR&_BV(SPIF))) - -//#undef chipDeselect -//#define chipDeselect() - -class SPIhw { -private: - uint8_t pinSCL; - uint8_t pinSDA; - uint8_t pinWR; - uint8_t pinCS; - uint8_t pinRST; - - regtype *registerSCL; - regtype *registerSDA; - regtype *registerWR; - regsize bitmaskSCL; - regsize bitmaskSDA; - regsize bitmaskWR; - - void beginSPI(); - void endSPI(); - - bool eightBit; - - uint32_t ctar0; - uint32_t ctar1; - - void updatectars(); - int spiModeRequest; - -protected: - void reset() { - digitalWrite(pinRST,LOW); - delay(100); - digitalWrite(pinRST,HIGH); - delay(100); - } - - void writeCmd(uint8_t b); - __attribute__((noinline)) void writeData(uint8_t data); // noinline saves 4-5kb sketch code in the case. An impact to performance is to be learned. - - void writeData(uint8_t hi, uint8_t lo) { - writeData(hi); - writeData(lo); - } - - void writeDataTwice(uint8_t b) { - writeData(b); - writeData(b); - } - - void writeCmdData(uint8_t cmd, uint16_t data) { - writeCmd(cmd); - writeData(highByte(data)); - writeData(lowByte(data)); - } - -public: - void setSPIBitOrder(uint8_t bitOrder); - void setSPIDataMode(uint8_t mode); - void setSPIClockDivider(uint8_t rate); -// void setSPIEightBit(bool bits) { -// eightBit = bits; -// } - - /** - * Overrides SPI pins - * @param scl - * @param sda - * @param cs chip select - * @param rst reset - * @param wr write pin; if not omitted (and not equals to 255) - switches to eight bit mode transfer - */ - inline void setSpiPins(uint8_t scl, uint8_t sda, uint8_t cs, uint8_t rst, uint8_t wr = 255) { - pinSCL = scl; - pinSDA = sda; - pinCS = cs; - pinRST = rst; - pinWR = wr; - eightBit = wr != 255; - } - - /** - * Overrides PPI pins - * @param cs chip select - */ - inline void setPpiPins(uint8_t rs, uint8_t wr, uint8_t cs, uint8_t rst, uint8_t rd) { - } - - inline void registerSelect() { - } - - void initInterface(); -}; - -void SPIhw::initInterface() { - registerSCL = portOutputRegister(digitalPinToPort(pinSCL)); - bitmaskSCL = digitalPinToBitMask(pinSCL); - registerSDA = portOutputRegister(digitalPinToPort(pinSDA)); - bitmaskSDA = digitalPinToBitMask(pinSDA); - registerWR = portOutputRegister(digitalPinToPort(pinWR)); - bitmaskWR = digitalPinToBitMask(pinWR); - registerCS = portOutputRegister(digitalPinToPort(pinCS)); - bitmaskCS = digitalPinToBitMask(pinCS); - - pinMode(pinSCL,OUTPUT); - pinMode(pinSDA,OUTPUT); - pinMode(pinWR,OUTPUT); - pinMode(pinRST,OUTPUT); - pinMode(pinCS,OUTPUT); - digitalWrite(pinCS, HIGH); - - reset(); - - beginSPI(); - - if ( spiModeRequest > 0 ) { - setSPIDataMode(spiModeRequest-1); - } - - // setSPIBitOrder(MSBFIRST); - // setSPIDataMode(SPI_MODE0); - // setSPIClockDivider(SPI_CLOCK_DIV64); - -} - -void SPIhw::writeCmd(uint8_t cmd) { -#if defined(TEENSYDUINO) - chipSelect(); -#endif - - if ( eightBit ) { - *registerWR &= ~bitmaskWR; - } else { - SPCR &= ~_BV(SPE); // Disable SPI to get control of the SCK pin. - cbi(registerSDA, bitmaskSDA); - cbi(registerSCL, bitmaskSCL); // Pull SPI SCK high - // delay(1); // Insert extra time to the hight pulse, typically needed - sbi(registerSCL, bitmaskSCL); // Pull SPI SCK low - SPCR |= _BV(SPE); // Enable SPI again - } - -#if defined(TEENSYDUINO) - SPI0_SR = SPI_SR_TCF; - SPI0_PUSHR = cmd; - while (!(SPI0_SR & SPI_SR_TCF)) ; // wait - chipDeselect(); -#else - SPDR = cmd; - while (!(SPSR & _BV(SPIF))); -#endif -} - -void SPIhw::writeData(uint8_t data) { -#if defined(TEENSYDUINO) - chipSelect(); -#endif - - if ( eightBit ) { - *registerWR |= bitmaskWR; - } else { - SPCR &= ~_BV(SPE); // Disable SPI to get control of the SCK pin. - sbi(registerSDA, bitmaskSDA); - cbi(registerSCL, bitmaskSCL); // Pull SPI SCK high - // delay(1); // Insert extra time to the hight pulse, typically needed - sbi(registerSCL, bitmaskSCL); // Pull SPI SCK low - SPCR |= _BV(SPE); // Enable SPI again - } - -#if defined(TEENSYDUINO) - SPI0_SR = SPI_SR_TCF; - SPI0_PUSHR = data; - while (!(SPI0_SR & SPI_SR_TCF)) ; // wait - chipDeselect(); -#else - SPDR = data; - while (!(SPSR & _BV(SPIF))); -#endif -} - -void SPIhw::beginSPI() { - - digitalWrite(pinCS, HIGH); - pinMode(pinCS, OUTPUT); - - cbi(registerSCL, bitmaskSCL); - cbi(registerSDA, bitmaskSDA); - digitalWrite(pinCS, HIGH); - -#if defined(TEENSYDUINO) - // Warning: if the SS pin ever becomes a LOW INPUT then SPI - // automatically switches to Slave, so the data direction of - // the SS pin MUST be kept as OUTPUT. - - SIM_SCGC6 |= SIM_SCGC6_SPI0; - SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); - SPI0_CTAR0 = (SPI_CTAR_FMSZ(7) | - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0)) & ~SPI_CTAR_LSBFE; - // SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); - SPI0_CTAR1 = (SPI_CTAR_FMSZ(15) | - SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0)) & ~SPI_CTAR_LSBFE; - // SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); - SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); -// SPCR.enable_pins(); // pins managed by SPCRemulation in avr_emulation.h -#else - SPCR |= _BV(MSTR); - SPCR |= _BV(SPE); - - DDRB = DDRB | B00000001; // PB0 as OUTPUT - PORTB = PORTB | B00000001; // PB0 as HIGH -#endif -} - -void SPIhw::endSPI() { -#if defined(TEENSYDUINO) -// SPCR.disable_pins(); - SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); -#else - SPCR &= ~_BV(SPE); -#endif -} - -void SPIhw::setSPIBitOrder(uint8_t bitOrder) { -#if defined(TEENSYDUINO) - // TODO -#else - if(bitOrder == LSBFIRST) { - SPCR |= _BV(DORD); - } else { - SPCR &= ~(_BV(DORD)); - } -#endif -} - -void SPIhw::setSPIDataMode(uint8_t mode) { -#if defined(TEENSYDUINO) - spiModeRequest = mode + 1; - SIM_SCGC6 |= SIM_SCGC6_SPI0; - SPCR = (SPCR & ~SPI_MODE_MASK) | mode; -#else - SPCR = (SPCR & ~SPI_MODE_MASK) | mode; -#endif -} - -void SPIhw::setSPIClockDivider(uint8_t rate) { -#if defined(TEENSYDUINO) - // TODO -#else - SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK); - SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((rate >> 2) & SPI_2XCLOCK_MASK); -#endif -} - -#endif // PIXELS_SPIHW_H +/* + * Pixels. Graphics library for TFT displays. + * + * Copyright (C) 2012-2013 Igor Repinetski + * + * The code is written in C/C++ for Arduino and can be easily ported to any microcontroller by rewritting the low level pin access functions. + * + * Text output methods of the library rely on Pixelmeister's font data format. See: http://pd4ml.com/pixelmeister + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ + * + * This library includes some code portions and algoritmic ideas derived from works of + * - Andreas Schiffler -- aschiffler at ferzkopp dot net (SDL_gfx Project) + * - K. Townsend http://microBuilder.eu (lpc1343codebase Project) + */ + +/* + * Hardware SPI layer (SCL=13, SDA=11 on Arduino) + */ + +#include "Pixels.h" +#include + +#ifdef PIXELS_MAIN +#error Pixels_SPIhw.h must be included before Pixels_.h +#endif + +#ifndef PIXELS_SPIHW_H +#define PIXELS_SPIHW_H + + +#define WIDTH 320 +#define HEIGHT 240 + +// At all other speeds, SPI.beginTransaction() will use the fastest available clock +#define SPICLOCK 30000000 +#define ILI9341_TFTWIDTH 240 +#define ILI9341_TFTHEIGHT 320 + +#define ILI9341_NOP 0x00 +#define ILI9341_SWRESET 0x01 +#define ILI9341_RDDID 0x04 +#define ILI9341_RDDST 0x09 + +#define ILI9341_SLPIN 0x10 +#define ILI9341_SLPOUT 0x11 +#define ILI9341_PTLON 0x12 +#define ILI9341_NORON 0x13 + +#define ILI9341_RDMODE 0x0A +#define ILI9341_RDMADCTL 0x0B +#define ILI9341_RDPIXFMT 0x0C +#define ILI9341_RDIMGFMT 0x0D +#define ILI9341_RDSELFDIAG 0x0F + +#define ILI9341_INVOFF 0x20 +#define ILI9341_INVON 0x21 +#define ILI9341_GAMMASET 0x26 +#define ILI9341_DISPOFF 0x28 +#define ILI9341_DISPON 0x29 + +#define ILI9341_CASET 0x2A +#define ILI9341_PASET 0x2B +#define ILI9341_RAMWR 0x2C +#define ILI9341_RAMRD 0x2E + +#define ILI9341_PTLAR 0x30 +#define ILI9341_MADCTL 0x36 +#define ILI9341_VSCRSADD 0x37 +#define ILI9341_PIXFMT 0x3A + +#define ILI9341_FRMCTR1 0xB1 +#define ILI9341_FRMCTR2 0xB2 +#define ILI9341_FRMCTR3 0xB3 +#define ILI9341_INVCTR 0xB4 +#define ILI9341_DFUNCTR 0xB6 + +#define ILI9341_PWCTR1 0xC0 +#define ILI9341_PWCTR2 0xC1 +#define ILI9341_PWCTR3 0xC2 +#define ILI9341_PWCTR4 0xC3 +#define ILI9341_PWCTR5 0xC4 +#define ILI9341_VMCTR1 0xC5 +#define ILI9341_VMCTR2 0xC7 + +#define ILI9341_RDID1 0xDA +#define ILI9341_RDID2 0xDB +#define ILI9341_RDID3 0xDC +#define ILI9341_RDID4 0xDD + +#define ILI9341_GMCTRP1 0xE0 +#define ILI9341_GMCTRN1 0xE1 + +#define MADCTL_MY 0x80 +#define MADCTL_MX 0x40 +#define MADCTL_MV 0x20 +#define MADCTL_ML 0x10 +#define MADCTL_RGB 0x00 +#define MADCTL_BGR 0x08 +#define MADCTL_MH 0x04 + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +#define SPI_CLOCK_DIV64 0x07 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + +#define SPI(X) SPDR=X;while(!(SPSR&_BV(SPIF))) + +//#undef chipDeselect +//#define chipDeselect() + +class SPIhw { +private: + uint8_t pinSCL; + uint8_t pinSDA; + uint8_t pinWR; + uint8_t pinCS; + uint8_t pinRST; + + /* JK */ + uint8_t cs = 10; + uint8_t dc = 9; + uint8_t rst = 2; + uint8_t mosi = 11; + uint8_t sclk = 13; + uint8_t miso = 12; + + regtype *registerSCL; + regtype *registerSDA; + regtype *registerWR; + regsize bitmaskSCL; + regsize bitmaskSDA; + regsize bitmaskWR; + + void beginSPI(); + void endSPI(); + + bool eightBit; + + uint32_t ctar0; + uint32_t ctar1; + + void updatectars(); + int spiModeRequest; + +protected: + uint8_t _rst; + uint8_t _cs, _dc; + uint8_t pcs_data, pcs_command; + uint8_t _miso, _mosi, _sclk; + + void reset() { + pinMode(_rst, OUTPUT); + digitalWrite(_rst, HIGH); + delay(5); + digitalWrite(_rst, LOW); + delay(20); + digitalWrite(_rst, HIGH); + delay(150); + + /* + digitalWrite(pinRST,LOW); + delay(100); + digitalWrite(pinRST,HIGH); + delay(100); + */ + } + + /* JK ADDITION */ + void setAddr(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) + __attribute__((always_inline)) { + writecommand_cont(ILI9341_CASET); // Column addr set + writedata16_cont(x0); // XSTART + writedata16_cont(x1); // XEND + writecommand_cont(ILI9341_PASET); // Row addr set + writedata16_cont(y0); // YSTART + writedata16_cont(y1); // YEND + } + void waitFifoNotFull(void) { + uint32_t sr; + uint32_t tmp __attribute__((unused)); + do { + sr = KINETISK_SPI0.SR; + if (sr & 0xF0) tmp = KINETISK_SPI0.POPR; // drain RX FIFO + } while ((sr & (15 << 12)) > (3 << 12)); + } + void waitFifoEmpty(void) { + uint32_t sr; + uint32_t tmp __attribute__((unused)); + do { + sr = KINETISK_SPI0.SR; + if (sr & 0xF0) tmp = KINETISK_SPI0.POPR; // drain RX FIFO + } while ((sr & 0xF0F0) > 0); // wait both RX & TX empty + } + void waitTransmitComplete(void) __attribute__((always_inline)) { + uint32_t tmp __attribute__((unused)); + while (!(KINETISK_SPI0.SR & SPI_SR_TCF)) ; // wait until final output done + tmp = KINETISK_SPI0.POPR; // drain the final RX FIFO word + } + void waitTransmitComplete(uint32_t mcr) __attribute__((always_inline)) { + uint32_t tmp __attribute__((unused)); + while (1) { + uint32_t sr = KINETISK_SPI0.SR; + if (sr & SPI_SR_EOQF) break; // wait for last transmit + if (sr & 0xF0) tmp = KINETISK_SPI0.POPR; + } + KINETISK_SPI0.SR = SPI_SR_EOQF; + SPI0_MCR = mcr; + while (KINETISK_SPI0.SR & 0xF0) { + tmp = KINETISK_SPI0.POPR; + } + } + void writecommand_cont(uint8_t c) __attribute__((always_inline)) { + KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT; + waitFifoNotFull(); + } + void writedata8_cont(uint8_t c) __attribute__((always_inline)) { + KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT; + waitFifoNotFull(); + } + void writedata16_cont(uint16_t d) __attribute__((always_inline)) { + KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT; + waitFifoNotFull(); + } + void writecommand_last(uint8_t c) __attribute__((always_inline)) { + uint32_t mcr = SPI0_MCR; + KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ; + waitTransmitComplete(mcr); + } + void writedata8_last(uint8_t c) __attribute__((always_inline)) { + uint32_t mcr = SPI0_MCR; + KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ; + waitTransmitComplete(mcr); + } + void writedata16_last(uint16_t d) __attribute__((always_inline)) { + uint32_t mcr = SPI0_MCR; + KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ; + waitTransmitComplete(mcr); + } + /* END JK ADDITION */ + + void writeCmd(uint8_t b); + __attribute__((noinline)) void writeData(uint8_t data); // noinline saves 4-5kb sketch code in the case. An impact to performance is to be learned. + + void writeData(uint8_t hi, uint8_t lo) { + writeData(hi); + writeData(lo); + } + + void writeDataTwice(uint8_t b) { + writeData(b); + writeData(b); + } + + void writeCmdData(uint8_t cmd, uint16_t data) { + writeCmd(cmd); + writeData(highByte(data)); + writeData(lowByte(data)); + } + +public: + void setSPIBitOrder(uint8_t bitOrder); + void setSPIDataMode(uint8_t mode); + void setSPIClockDivider(uint8_t rate); + + + /** + * Overrides SPI pins + * @param scl + * @param sda + * @param cs chip select + * @param rst reset + * @param wr write pin; if not omitted (and not equals to 255) - switches to eight bit mode transfer + */ + inline void setSpiPins(uint8_t scl, uint8_t sda, uint8_t cs, uint8_t rst, uint8_t wr = 255) { + pinSCL = scl; + pinSDA = sda; + pinCS = cs; + pinRST = rst; + pinWR = wr; + eightBit = wr != 255; + + /* JK ADDITION */ + _sclk = scl; + _mosi = sda; + _cs = cs; + _rst = rst; + _dc = wr; + _miso = 12; + /* JK ADDITION END */ + } + + /** + * Overrides PPI pins + * @param cs chip select + */ + inline void setPpiPins(uint8_t rs, uint8_t wr, uint8_t cs, uint8_t rst, uint8_t rd) { + } + + inline void registerSelect() { + } + + void initInterface(); +}; + +/* JK ADDITION */ +static const uint8_t init_commands[] = { + 4, 0xEF, 0x03, 0x80, 0x02, + 4, 0xCF, 0x00, 0XC1, 0X30, + 5, 0xED, 0x64, 0x03, 0X12, 0X81, + 4, 0xE8, 0x85, 0x00, 0x78, + 6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, + 2, 0xF7, 0x20, + 3, 0xEA, 0x00, 0x00, + 2, ILI9341_PWCTR1, 0x23, // Power control + 2, ILI9341_PWCTR2, 0x10, // Power control + 3, ILI9341_VMCTR1, 0x3e, 0x28, // VCM control + 2, ILI9341_VMCTR2, 0x86, // VCM control2 + 2, ILI9341_MADCTL, 0x48, // Memory Access Control + 2, ILI9341_PIXFMT, 0x55, + 3, ILI9341_FRMCTR1, 0x00, 0x18, + 4, ILI9341_DFUNCTR, 0x08, 0x82, 0x27, // Display Function Control + 2, 0xF2, 0x00, // Gamma Function Disable + 2, ILI9341_GAMMASET, 0x01, // Gamma curve selected + 16, ILI9341_GMCTRP1, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma + 16, ILI9341_GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma + 0 +}; +/* JK ADDITION END */ + +void SPIhw::initInterface() { + registerSCL = portOutputRegister(digitalPinToPort(pinSCL)); + bitmaskSCL = digitalPinToBitMask(pinSCL); + registerSDA = portOutputRegister(digitalPinToPort(pinSDA)); + bitmaskSDA = digitalPinToBitMask(pinSDA); + registerWR = portOutputRegister(digitalPinToPort(pinWR)); + bitmaskWR = digitalPinToBitMask(pinWR); + registerCS = portOutputRegister(digitalPinToPort(pinCS)); + bitmaskCS = digitalPinToBitMask(pinCS); + + pinMode(pinSCL,OUTPUT); + pinMode(pinSDA,OUTPUT); + pinMode(pinWR,OUTPUT); + pinMode(pinRST,OUTPUT); + pinMode(pinCS,OUTPUT); + digitalWrite(pinCS, HIGH); + + if ((_mosi == 11 || _mosi == 7) && (_miso == 12 || _miso == 8) && (_sclk == 13 || _sclk == 14)) { + SPI.setMOSI(_mosi); + SPI.setMISO(_miso); + SPI.setSCK(_sclk); + } + else { + return; + } + + SPI.begin(); + + if (SPI.pinIsChipSelect(_cs, _dc)) { + pcs_data = SPI.setCS(_cs); + pcs_command = pcs_data | SPI.setCS(_dc); + } + else { + pcs_data = 0; + pcs_command = 0; + return; + } + + reset(); + + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0)); + const uint8_t *addr = init_commands; + + while (1) { + uint8_t count = *addr++; + if (count-- == 0) break; + writecommand_cont(*addr++); + while (count-- > 0) { + writedata8_cont(*addr++); + } + } + + writecommand_last(ILI9341_SLPOUT); // Exit Sleep + SPI.endTransaction(); + + delay(120); + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0)); + writecommand_last(ILI9341_DISPON); // Display on + SPI.endTransaction(); + + + // beginSPI(); + + // if ( spiModeRequest > 0 ) { + // setSPIDataMode(spiModeRequest-1); + // } + + // setSPIBitOrder(MSBFIRST); + // setSPIDataMode(SPI_MODE0); + // setSPIClockDivider(SPI_CLOCK_DIV64); + +} + +void SPIhw::writeCmd(uint8_t cmd) { + /* JK ADDITION */ + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0)); + writecommand_last(cmd); + SPI.endTransaction(); + /* JK ADDITION END */ + + /* + #if defined(TEENSYDUINO) + chipSelect(); + #endif + + if ( eightBit ) { + *registerWR &= ~bitmaskWR; + } else { + SPCR &= ~_BV(SPE); // Disable SPI to get control of the SCK pin. + cbi(registerSDA, bitmaskSDA); + cbi(registerSCL, bitmaskSCL); // Pull SPI SCK high + // delay(1); // Insert extra time to the hight pulse, typically needed + sbi(registerSCL, bitmaskSCL); // Pull SPI SCK low + SPCR |= _BV(SPE); // Enable SPI again + } + + #if defined(TEENSYDUINO) + SPI0_SR = SPI_SR_TCF; + SPI0_PUSHR = cmd; + while (!(SPI0_SR & SPI_SR_TCF)) ; // wait + chipDeselect(); + #else + SPDR = cmd; + while (!(SPSR & _BV(SPIF))); + #endif + */ +} + +void SPIhw::writeData(uint8_t data) { + /* JK ADDITION */ + SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0)); + writedata8_last(data); + SPI.endTransaction(); + /* JK ADDITION END */ + + /* + #if defined(TEENSYDUINO) + chipSelect(); + #endif + + if ( eightBit ) { + *registerWR |= bitmaskWR; + } else { + SPCR &= ~_BV(SPE); // Disable SPI to get control of the SCK pin. + sbi(registerSDA, bitmaskSDA); + cbi(registerSCL, bitmaskSCL); // Pull SPI SCK high + // delay(1); // Insert extra time to the hight pulse, typically needed + sbi(registerSCL, bitmaskSCL); // Pull SPI SCK low + SPCR |= _BV(SPE); // Enable SPI again + } + + #if defined(TEENSYDUINO) + SPI0_SR = SPI_SR_TCF; + SPI0_PUSHR = data; + while (!(SPI0_SR & SPI_SR_TCF)) ; // wait + chipDeselect(); + #else + SPDR = data; + while (!(SPSR & _BV(SPIF))); + #endif + */ +} + +void SPIhw::beginSPI() { + + digitalWrite(pinCS, HIGH); + pinMode(pinCS, OUTPUT); + + cbi(registerSCL, bitmaskSCL); + cbi(registerSDA, bitmaskSDA); + digitalWrite(pinCS, HIGH); + +#if defined(TEENSYDUINO) + // Warning: if the SS pin ever becomes a LOW INPUT then SPI + // automatically switches to Slave, so the data direction of + // the SS pin MUST be kept as OUTPUT. + + /* JK ADDITION */ + uint32_t ctar = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0) | SPI_CTAR_DBR; + SIM_SCGC6 |= SIM_SCGC6_SPI0; + SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); + SPI0_CTAR0 = (SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0)) & ~SPI_CTAR_LSBFE; + SPI0_CTAR1 = (SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0)) & ~SPI_CTAR_LSBFE; + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); + /* JK ADDITION END */ + + /* + SIM_SCGC6 |= SIM_SCGC6_SPI0; + SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); + SPI0_CTAR0 = (SPI_CTAR_FMSZ(7) | + SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0)) & ~SPI_CTAR_LSBFE; + SPI0_CTAR1 = (SPI_CTAR_FMSZ(15) | + SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0)) & ~SPI_CTAR_LSBFE; + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); + */ + +#else + SPCR |= _BV(MSTR); + SPCR |= _BV(SPE); + + DDRB = DDRB | B00000001; // PB0 as OUTPUT + PORTB = PORTB | B00000001; // PB0 as HIGH +#endif +} + +void SPIhw::endSPI() { +#if defined(TEENSYDUINO) + /* JK ADDITION */ + SPI.end(); + /* JK ADDITION END */ + //SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); +#else + SPCR &= ~_BV(SPE); +#endif +} + +void SPIhw::setSPIBitOrder(uint8_t bitOrder) { + if (bitOrder == LSBFIRST) { + SPCR |= _BV(DORD); + } else { + SPCR &= ~(_BV(DORD)); + } +} + +void SPIhw::setSPIDataMode(uint8_t mode) { +#if defined(TEENSYDUINO) + spiModeRequest = mode + 1; + SIM_SCGC6 |= SIM_SCGC6_SPI0; + SPCR = (SPCR & ~SPI_MODE_MASK) | mode; +#else + SPCR = (SPCR & ~SPI_MODE_MASK) | mode; +#endif +} + +void SPIhw::setSPIClockDivider(uint8_t rate) { +#if defined(TEENSYDUINO) + // TODO +#else + SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK); + SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((rate >> 2) & SPI_2XCLOCK_MASK); +#endif +} + +#endif // PIXELS_SPIHW_H