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