diff --git a/Extensions/Sprite.cpp b/Extensions/Sprite.cpp index f3d7e3e..2c8a88b 100644 --- a/Extensions/Sprite.cpp +++ b/Extensions/Sprite.cpp @@ -13,7 +13,7 @@ /*************************************************************************************** ** Function name: TFT_eSprite ** Description: Class constructor -*************************************************************************************x*/ +***************************************************************************************/ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) { _tft = tft; // Pointer to tft class so we can call member functions @@ -33,8 +33,8 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) _xptr = 0; // pushColor coordinate _yptr = 0; - _xpivot = 0; - _ypivot = 0; + _xPivot = 0; + _yPivot = 0; _colorMap = nullptr; @@ -47,20 +47,18 @@ TFT_eSprite::TFT_eSprite(TFT_eSPI *tft) /*************************************************************************************** ** Function name: createSprite ** Description: Create a sprite (bitmap) of defined width and height -*************************************************************************************x*/ +***************************************************************************************/ // cast returned value to (uint8_t*) for 8 bit or (uint16_t*) for 16 bit colours void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) { if ( _created ) return _img8_1; - if ( w < 1 || h < 1 ) return NULL; + if ( w < 1 || h < 1 ) return nullptr; _iwidth = _dwidth = _bitwidth = w; _iheight = _dheight = h; - _colorMap = nullptr; - this->cursor_x = 0; this->cursor_y = 0; @@ -71,8 +69,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _sh = h; _scolor = TFT_BLACK; - _xpivot = w/2; - _ypivot = h/2; + _xPivot = w/2; + _yPivot = h/2; _img8 = (uint8_t*) callocSprite(w, h, frames); _img8_1 = _img8; @@ -94,6 +92,8 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) _img8_2 = _img8 + (w * h + 1); } + if ( (_bpp == 4) && (_colorMap == nullptr)) createPalette(default_4bit_palette); + // This is to make it clear what pointer size is expected to be used // but casting in the user sketch is needed due to the use of void* if ( (_bpp == 1) && (frames > 1) ) @@ -105,17 +105,38 @@ void* TFT_eSprite::createSprite(int16_t w, int16_t h, uint8_t frames) if (_img8) { _created = true; - return _img8; + return _img8_1; } - return NULL; + return nullptr; +} + + +/*************************************************************************************** +** Function name: getPointer +** Description: Returns pointer to start of sprite memory area +***************************************************************************************/ +void* TFT_eSprite::getPointer(void) +{ + if (!_created) return nullptr; + return _img8_1; +} + + +/*************************************************************************************** +** Function name: created +** Description: Returns true if sprite has been created +***************************************************************************************/ +bool TFT_eSprite::created(void) +{ + return _created; } /*************************************************************************************** ** Function name: ~TFT_eSprite ** Description: Class destructor -*************************************************************************************x*/ +***************************************************************************************/ TFT_eSprite::~TFT_eSprite(void) { deleteSprite(); @@ -129,14 +150,14 @@ TFT_eSprite::~TFT_eSprite(void) /*************************************************************************************** ** Function name: callocSprite ** Description: Allocate a memory area for the Sprite and return pointer -*************************************************************************************x*/ +***************************************************************************************/ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) { // Add one extra "off screen" pixel to point out-of-bounds setWindow() coordinates // this means push/writeColor functions do not need additional bounds checks and // hence will run faster in normal circumstances. - uint8_t* ptr8 = NULL; + uint8_t* ptr8 = nullptr; if (frames > 2) frames = 2; // Currently restricted to 2 frame buffers if (frames < 1) frames = 1; @@ -144,10 +165,17 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) if (_bpp == 16) { #if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT) - if ( psramFound() && this->_psram_enable && !_tft->DMA_Enabled) ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t)); + if ( psramFound() && this->_psram_enable && !_tft->DMA_Enabled) + { + ptr8 = ( uint8_t*) ps_calloc(frames * w * h + frames, sizeof(uint16_t)); + //Serial.println("PSRAM"); + } else #endif - ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint16_t)); + { + ptr8 = ( uint8_t*) calloc(frames * w * h + frames, sizeof(uint16_t)); + //Serial.println("Normal RAM"); + } } else if (_bpp == 8) @@ -193,9 +221,9 @@ void* TFT_eSprite::callocSprite(int16_t w, int16_t h, uint8_t frames) /*************************************************************************************** ** Function name: createPalette (from RAM array) ** Description: Set a palette for a 4-bit per pixel sprite -*************************************************************************************x*/ +***************************************************************************************/ -void TFT_eSprite::createPalette(uint16_t colorMap[], int colors) +void TFT_eSprite::createPalette(uint16_t colorMap[], uint8_t colors) { if (_colorMap != nullptr) { @@ -204,14 +232,18 @@ void TFT_eSprite::createPalette(uint16_t colorMap[], int colors) if (colorMap == nullptr) { - return; // do nothing other than clear the existing map + // Create a color map using the default FLASH map + createPalette(default_4bit_palette); + return; } - // allocate color map + // Allocate and clear memory for 16 color map _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t)); - if (colors > 16) - colors = 16; - for (auto i = 0; i < colors; i++) + + if (colors > 16) colors = 16; + + // Copy map colors + for (uint8_t i = 0; i < colors; i++) { _colorMap[i] = colorMap[i]; } @@ -220,25 +252,23 @@ void TFT_eSprite::createPalette(uint16_t colorMap[], int colors) /*************************************************************************************** ** Function name: createPalette (from FLASH array) ** Description: Set a palette for a 4-bit per pixel sprite -*************************************************************************************x*/ +***************************************************************************************/ -void TFT_eSprite::createPalette(const uint16_t colorMap[], int colors) +void TFT_eSprite::createPalette(const uint16_t colorMap[], uint8_t colors) { - if (_colorMap != nullptr) - { - free(_colorMap); - } - if (colorMap == nullptr) { - return; // do nothing other than clear the existing map + // Create a color map using the default FLASH map + colorMap = default_4bit_palette; } - // allocate color map + // Allocate and clear memory for 16 color map _colorMap = (uint16_t *)calloc(16, sizeof(uint16_t)); - if (colors > 16) - colors = 16; - for (auto i = 0; i < colors; i++) + + if (colors > 16) colors = 16; + + // Copy map colors + for (uint8_t i = 0; i < colors; i++) { _colorMap[i] = pgm_read_word(colorMap++); } @@ -247,11 +277,11 @@ void TFT_eSprite::createPalette(const uint16_t colorMap[], int colors) /*************************************************************************************** ** Function name: frameBuffer ** Description: For 1 bpp Sprites, select the frame used for graphics -*************************************************************************************x*/ +***************************************************************************************/ // Frames are numbered 1 and 2 void* TFT_eSprite::frameBuffer(int8_t f) { - if (!_created) return NULL; + if (!_created) return nullptr; if ( f == 2 ) _img8 = _img8_2; else _img8 = _img8_1; @@ -268,34 +298,37 @@ void* TFT_eSprite::frameBuffer(int8_t f) /*************************************************************************************** ** Function name: setColorDepth ** Description: Set bits per pixel for colour (1, 8 or 16) -*************************************************************************************x*/ +***************************************************************************************/ void* TFT_eSprite::setColorDepth(int8_t b) { - // Can't change an existing sprite's colour depth so delete it - if (_created) free(_img8_1); + // Do not re-create the sprite if the colour depth does not change + if (_bpp == b) return _img8_1; - // Now define the new colour depth + // Validate the new colour depth if ( b > 8 ) _bpp = 16; // Bytes per pixel else if ( b > 4 ) _bpp = 8; else if ( b > 1 ) _bpp = 4; else _bpp = 1; + // Can't change an existing sprite's colour depth so delete it + if (_created) free(_img8_1); + // If it existed, re-create the sprite with the new colour depth if (_created) { _created = false; - return createSprite(_iwidth, _iheight); + return createSprite(_dwidth, _dheight); } - return NULL; + return nullptr; } /*************************************************************************************** ** Function name: getColorDepth ** Description: Get bits per pixel for colour (1, 8 or 16) -*************************************************************************************x*/ +***************************************************************************************/ int8_t TFT_eSprite::getColorDepth(void) { @@ -306,7 +339,7 @@ int8_t TFT_eSprite::getColorDepth(void) /*************************************************************************************** ** Function name: setBitmapColor -** Description: Set the foreground foreground and background colour +** Description: Set the 1bpp foreground foreground and background colour ***************************************************************************************/ void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b) { @@ -317,23 +350,22 @@ void TFT_eSprite::setBitmapColor(uint16_t c, uint16_t b) /*************************************************************************************** ** Function name: setPaletteColor -** Description: Set the palette color at the given index +** Description: Set the 4bpp palette color at the given index ***************************************************************************************/ void TFT_eSprite::setPaletteColor(uint8_t index, uint16_t color) { - if (_colorMap == nullptr || index > 15) - return; // out of bounds + if (_colorMap == nullptr || index > 15) return; // out of bounds + _colorMap[index] = color; } /*************************************************************************************** ** Function name: getPaletteColor -** Description: Return the palette color at index, or 0 (black) on error. +** Description: Return the palette color at 4bpp index, or 0 on error. ***************************************************************************************/ uint16_t TFT_eSprite::getPaletteColor(uint8_t index) { - if (_colorMap == nullptr || index > 15) - return 0; + if (_colorMap == nullptr || index > 15) return 0; // out of bounds return _colorMap[index]; } @@ -341,30 +373,32 @@ uint16_t TFT_eSprite::getPaletteColor(uint8_t index) /*************************************************************************************** ** Function name: deleteSprite ** Description: Delete the sprite to free up memory (RAM) -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::deleteSprite(void) { - if (!_created ) return; - if (_colorMap != nullptr) { free(_colorMap); + _colorMap = nullptr; } - free(_img8_1); - - _created = false; + if (_created) + { + free(_img8_1); + _img8 = nullptr; + _created = false; + } } /*************************************************************************************** ** Function name: setPivot ** Description: Set the pivot point in this Sprite -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::setPivot(int16_t x, int16_t y) { - _xpivot = x; - _ypivot = y; + _xPivot = x; + _yPivot = y; } @@ -374,7 +408,7 @@ void TFT_eSprite::setPivot(int16_t x, int16_t y) ***************************************************************************************/ int16_t TFT_eSprite::getPivotX(void) { - return _xpivot; + return _xPivot; } @@ -384,18 +418,18 @@ int16_t TFT_eSprite::getPivotX(void) ***************************************************************************************/ int16_t TFT_eSprite::getPivotY(void) { - return _ypivot; + return _yPivot; } /*************************************************************************************** ** Function name: pushRotated - Fast fixed point integer maths version ** Description: Push rotated Sprite to TFT screen -*************************************************************************************x*/ +***************************************************************************************/ #define FP_SCALE 10 bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) { - if ( !_created || _bpp == 4) return false; + if ( !_created) return false; // Bounding box parameters int16_t min_x; @@ -410,28 +444,29 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) int32_t xt = min_x - _tft->_xpivot; int32_t yt = min_y - _tft->_ypivot; - uint32_t xe = _iwidth << FP_SCALE; - uint32_t ye = _iheight << FP_SCALE; - uint32_t tpcolor = transp; // convert to unsigned - + uint32_t xe = _dwidth << FP_SCALE; + uint32_t ye = _dheight << FP_SCALE; + uint16_t tpcolor = transp; // convert to unsigned + if (_bpp == 4) tpcolor = _colorMap[transp & 0x0F]; + tpcolor = tpcolor>>8 | tpcolor<<8; // Working with swapped color bytes _tft->startWrite(); // Avoid transaction overhead for every tft pixel // Scan destination bounding box and fetch transformed pixels from source Sprite for (int32_t y = min_y; y <= max_y; y++, yt++) { int32_t x = min_x; - uint32_t xs = (_cosra * xt - (_sinra * yt - (_xpivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); - uint32_t ys = (_sinra * xt + (_cosra * yt + (_ypivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; } if (x == max_x) continue; uint32_t pixel_count = 0; do { - uint32_t rp; + uint16_t rp; int32_t xp = xs >> FP_SCALE; int32_t yp = ys >> FP_SCALE; - if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; rp = rp>>8 | rp<<8; } - else rp = readPixel(xp, yp); + if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; } + else { rp = readPixel(xp, yp); rp = rp>>8 | rp<<8; } if (tpcolor == rp) { if (pixel_count) { // TFT window is already clipped, so this is faster than pushImage() @@ -441,7 +476,7 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) } } else { - sline_buffer[pixel_count++] = rp>>8 | rp<<8; + sline_buffer[pixel_count++] = rp; } } while (++x < max_x && (xs += _cosra) < xe && (ys += _sinra) < ye); if (pixel_count) { @@ -460,10 +495,11 @@ bool TFT_eSprite::pushRotated(int16_t angle, int32_t transp) /*************************************************************************************** ** Function name: pushRotated - Fast fixed point integer maths version ** Description: Push a rotated copy of the Sprite to another Sprite -*************************************************************************************x*/ +***************************************************************************************/ +// Not compatible with 4bpp bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp) { - if ( !_created || _bpp == 4) return false; // Check this Sprite is created + if ( !_created || _bpp == 4) return false; // Check this Sprite is created if ( !spr->_created || spr->_bpp == 4) return false; // Ckeck destination Sprite is created // Bounding box parameters @@ -477,11 +513,11 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp) uint16_t sline_buffer[max_x - min_x + 1]; - int32_t xt = min_x - spr->_xpivot; - int32_t yt = min_y - spr->_ypivot; - uint32_t xe = _iwidth << FP_SCALE; - uint32_t ye = _iheight << FP_SCALE; - uint32_t tpcolor = transp; // convert to unsigned + int32_t xt = min_x - spr->_xPivot; + int32_t yt = min_y - spr->_yPivot; + uint32_t xe = _dwidth << FP_SCALE; + uint32_t ye = _dheight << FP_SCALE; + uint16_t tpcolor = transp>>8 | transp<<8; // convert to unsigned swapped bytes bool oldSwapBytes = spr->getSwapBytes(); spr->setSwapBytes(false); @@ -489,19 +525,19 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp) // Scan destination bounding box and fetch transformed pixels from source Sprite for (int32_t y = min_y; y <= max_y; y++, yt++) { int32_t x = min_x; - uint32_t xs = (_cosra * xt - (_sinra * yt - (_xpivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); - uint32_t ys = (_sinra * xt + (_cosra * yt + (_ypivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + uint32_t xs = (_cosra * xt - (_sinra * yt - (_xPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); + uint32_t ys = (_sinra * xt + (_cosra * yt + (_yPivot << FP_SCALE)) + (1 << (FP_SCALE - 1))); while ((xs >= xe || ys >= ye) && x < max_x) { x++; xs += _cosra; ys += _sinra; } if (x == max_x) continue; uint32_t pixel_count = 0; do { - uint32_t rp; + uint16_t rp; int32_t xp = xs >> FP_SCALE; int32_t yp = ys >> FP_SCALE; - if (_bpp == 16) {rp = _img[xp + yp * _iwidth]; rp = rp>>8 | rp<<8; } - else rp = readPixel(xp, yp); + if (_bpp == 16) rp = _img[xp + yp * _iwidth]; + else { rp = readPixel(xp, yp); rp = rp>>8 | rp<<8; } if (tpcolor == rp) { if (pixel_count) { spr->pushImage(x - pixel_count, y, pixel_count, 1, sline_buffer); @@ -522,12 +558,12 @@ bool TFT_eSprite::pushRotated(TFT_eSprite *spr, int16_t angle, int32_t transp) /*************************************************************************************** ** Function name: getRotatedBounds ** Description: Get TFT bounding box of a rotated Sprite wrt pivot -*************************************************************************************x*/ +***************************************************************************************/ bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y) { // Get the bounding box of this rotated source Sprite relative to Sprite pivot - getRotatedBounds(angle, width(), height(), _xpivot, _ypivot, min_x, min_y, max_x, max_y); + getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y); // Move bounding box so source Sprite pivot coincides with TFT pivot *min_x += _tft->_xpivot; @@ -554,18 +590,18 @@ bool TFT_eSprite::getRotatedBounds(int16_t angle, int16_t *min_x, int16_t *min_y /*************************************************************************************** ** Function name: getRotatedBounds ** Description: Get destination Sprite bounding box of a rotated Sprite wrt pivot -*************************************************************************************x*/ +***************************************************************************************/ bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y) { // Get the bounding box of this rotated source Sprite relative to Sprite pivot - getRotatedBounds(angle, width(), height(), _xpivot, _ypivot, min_x, min_y, max_x, max_y); + getRotatedBounds(angle, width(), height(), _xPivot, _yPivot, min_x, min_y, max_x, max_y); // Move bounding box so source Sprite pivot coincides with destination Sprite pivot - *min_x += spr->_xpivot; - *max_x += spr->_xpivot; - *min_y += spr->_ypivot; - *max_y += spr->_ypivot; + *min_x += spr->_xPivot; + *max_x += spr->_xPivot; + *min_y += spr->_yPivot; + *max_y += spr->_yPivot; // Test only to show bounding box // spr->fillSprite(TFT_BLACK); @@ -590,7 +626,7 @@ bool TFT_eSprite::getRotatedBounds(TFT_eSprite *spr, int16_t angle, int16_t *min /*************************************************************************************** ** Function name: rotatedBounds ** Description: Get bounding box of a rotated Sprite wrt pivot -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t xp, int16_t yp, int16_t *min_x, int16_t *min_y, int16_t *max_x, int16_t *max_y) { @@ -644,7 +680,7 @@ void TFT_eSprite::getRotatedBounds(int16_t angle, int16_t w, int16_t h, int16_t /*************************************************************************************** ** Function name: pushSprite ** Description: Push the sprite to the TFT at x, y -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { if (!_created) return; @@ -653,17 +689,13 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) { bool oldSwapBytes = _tft->getSwapBytes(); _tft->setSwapBytes(false); - _tft->pushImage(x, y, _iwidth, _iheight, _img ); + _tft->pushImage(x, y, _dwidth, _dheight, _img ); _tft->setSwapBytes(oldSwapBytes); } else if (_bpp == 4) { - if (_colorMap == nullptr) { - return; - } _tft->pushImage(x, y, _dwidth, _dheight, _img4, false, _colorMap); } - else _tft->pushImage(x, y, _dwidth, _dheight, _img8, (bool)(_bpp == 8)); } @@ -671,7 +703,7 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y) /*************************************************************************************** ** Function name: pushSprite ** Description: Push the sprite to the TFT at x, y with transparent colour -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) { if (!_created) return; @@ -680,7 +712,7 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) { bool oldSwapBytes = _tft->getSwapBytes(); _tft->setSwapBytes(false); - _tft->pushImage(x, y, _iwidth, _iheight, _img, transp ); + _tft->pushImage(x, y, _dwidth, _dheight, _img, transp ); _tft->setSwapBytes(oldSwapBytes); } else if (_bpp == 8) @@ -696,10 +728,171 @@ void TFT_eSprite::pushSprite(int32_t x, int32_t y, uint16_t transp) } +/*************************************************************************************** +** Function name: pushToSprite +** Description: Push the sprite to another sprite at x, y +***************************************************************************************/ +// Note: The following sprite to sprite colour depths are currently supported: +// Source Destination +// 16bpp -> 16bpp +// 16bpp -> 8bpp +// 4bpp -> 4bpp (note: color translation depends on the 2 sprites pallet colors) +// 1bpp -> 1bpp (note: color translation depends on the 2 sprites bitmap colors) + +bool TFT_eSprite::pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y) +{ + if (!_created) return false; + if (!dspr->created()) return false; + + // Check destination sprite compatibility + int8_t ds_bpp = dspr->getColorDepth(); + if (_bpp == 16 && ds_bpp != 16 && ds_bpp != 8) return false; + if (_bpp == 8) return false; + if (_bpp == 4 && ds_bpp != 4) return false; + if (_bpp == 1 && ds_bpp != 1) return false; + + bool oldSwapBytes = dspr->getSwapBytes(); + dspr->setSwapBytes(false); + dspr->pushImage(x, y, _dwidth, _dheight, _img ); + dspr->setSwapBytes(oldSwapBytes); + + return true; +} + + +/*************************************************************************************** +** Function name: pushToSprite +** Description: Push the sprite to another sprite at x, y with transparent colour +***************************************************************************************/ +/* >>>>>> Using a transparent color is not supported at the moment <<<<<< +void TFT_eSprite::pushToSprite(TFT_eSprite *spr, int32_t x, int32_t y, uint16_t transp) +{ + if (!_created) return; + + if (_bpp == 16) + { + bool oldSwapBytes = spr->getSwapBytes(); + spr->setSwapBytes(false); + spr->pushImage(x, y, _dwidth, _dheight, _img, transp ); + spr->setSwapBytes(oldSwapBytes); + } + else if (_bpp == 8) + { + transp = (uint8_t)((transp & 0xE000)>>8 | (transp & 0x0700)>>6 | (transp & 0x0018)>>3); + spr->pushImage(x, y, _dwidth, _dheight, _img8, (uint8_t)transp, (bool)true); + } + else if (_bpp == 4) + { + spr->pushImage(x, y, _dwidth, _dheight, _img4, (uint8_t)(transp & 0x0F), false, _colorMap); + } + else spr->pushImage(x, y, _dwidth, _dheight, _img8, 0, (bool)false); +} +*/ + +/*************************************************************************************** +** Function name: pushSprite +** Description: Push a cropped sprite to the TFT at tx, ty +***************************************************************************************/ +bool TFT_eSprite::pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh) +{ + if (!_created) return false; + + // Perform window boundary checks and crop if needed + setWindow(sx, sy, sx + sw - 1, sy + sh - 1); + + /* These global variables are now populated for the sprite + _xs = x start coordinate + _ys = y start coordinate + _xe = x end coordinate (inclusive) + _ye = y end coordinate (inclusive) + */ + + // Calculate new sprite window bounding box width and height + sw = _xe - _xs + 1; + sh = _ye - _ys + 1; + + if (_ys >= _iheight) return false; + + if (_bpp == 16) + { + bool oldSwapBytes = _tft->getSwapBytes(); + _tft->setSwapBytes(false); + + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img + _iwidth * _ys ); + else // Render line by line + while (sh--) + _tft->pushImage(tx, ty++, sw, 1, _img + _xs + _iwidth * _ys++ ); + + _tft->setSwapBytes(oldSwapBytes); + } + else if (_bpp == 8) + { + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img8 + _iwidth * _ys, true ); + else // Render line by line + while (sh--) + _tft->pushImage(tx, ty++, sw, 1, _img8 + _xs + _iwidth * _ys++, true ); + } + else if (_bpp == 4) + { + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img4 + (_iwidth>>1) * _ys, false, _colorMap ); + else // Render line by line + { + uint32_t ds = _xs&1; // Odd x start pixel + + uint32_t de = 0; // Odd x end pixel + if ((sw > ds) && (_xe&1)) de = 1; + + uint32_t dm = 0; // Midsection pixel count + if (sw > (ds+de)) dm = sw - ds - de; + sw--; + + uint32_t yp = (_xs + ds + _iwidth * _ys)>>1; + _tft->startWrite(); + while (sh--) + { + if (ds) _tft->drawPixel(tx, ty, readPixel(_xs, _ys) ); + if (dm) _tft->pushImage(tx + ds, ty, dm, 1, _img4 + yp, false, _colorMap ); + if (de) _tft->drawPixel(tx + sw, ty, readPixel(_xe, _ys) ); + _ys++; + ty++; + yp += (_iwidth>>1); + } + _tft->endWrite(); + } + } + else // 1bpp + { + // Check if a faster block copy to screen is possible + if ( sx == 0 && sw == _dwidth) + _tft->pushImage(tx, ty, sw, sh, _img8 + (_iwidth>>3) * _ys, false ); + else // Render line by line + { + _tft->startWrite(); + _tft->setWindow(tx, ty, tx+sw-1, ty+sh-1); + while (sh--) + { + for (uint32_t dx = _xs; dx < _xs + sw; dx++) _tft->pushColor(readPixel(dx, _ys)); + ty++; + _ys++; + } + _tft->endWrite(); + } + } + + return true; +} + + /*************************************************************************************** ** Function name: readPixelValue ** Description: Read the color map index of a pixel at defined coordinates -*************************************************************************************x*/ +***************************************************************************************/ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) { if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFF; @@ -718,6 +911,7 @@ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) if (_bpp == 4) { + if (x >= _dwidth) return 0xFF; if ((x & 0x01) == 0) return _img4[((x+y*_iwidth)>>1)] >> 4; // even index = bits 7 .. 4 else @@ -726,6 +920,7 @@ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) if (_bpp == 1) { + // Note: _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) if (_rotation == 1) { uint16_t tx = x; @@ -753,7 +948,7 @@ uint16_t TFT_eSprite::readPixelValue(int32_t x, int32_t y) /*************************************************************************************** ** Function name: readPixel ** Description: Read 565 colour of a pixel at defined coordinates -*************************************************************************************x*/ +***************************************************************************************/ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) { if ((x < 0) || (x >= _iwidth) || (y < 0) || (y >= _iheight) || !_created) return 0xFFFF; @@ -763,7 +958,7 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) uint16_t color = _img[x + y * _iwidth]; return (color >> 8) | (color << 8); } - + if (_bpp == 8) { uint16_t color = _img8[x + y * _iwidth]; @@ -779,6 +974,7 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) if (_bpp == 4) { + if (x >= _dwidth) return 0xFFFF; uint16_t color; if ((x & 0x01) == 0) color = _colorMap[_img4[((x+y*_iwidth)>>1)] >> 4]; // even index = bits 7 .. 4 @@ -787,6 +983,8 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) return color; } + // Note: Must be 1bpp + // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) if (_rotation == 1) { uint16_t tx = x; @@ -814,18 +1012,19 @@ uint16_t TFT_eSprite::readPixel(int32_t x, int32_t y) /*************************************************************************************** ** Function name: pushImage -** Description: push 565 colour image into a defined area of a sprite -*************************************************************************************x*/ +** Description: push image into a defined area of a sprite +***************************************************************************************/ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) { - if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0) || !_created) return; + if (data == nullptr || !_created) return; + if ((x >= _iwidth) || (y >= _iheight) || (w == 0) || (h == 0)) return; if ((x + w < 0) || (y + h < 0)) return; - int32_t xo = 0; - int32_t yo = 0; + int32_t xo = 0; + int32_t yo = 0; - int32_t xs = x; - int32_t ys = y; + int32_t xs = x; + int32_t ys = y; int32_t ws = w; int32_t hs = h; @@ -838,17 +1037,34 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ if (_bpp == 16) // Plot a 16 bpp image into a 16 bpp Sprite { - for (int32_t yp = yo; yp < yo + hs; yp++) + // Pointer within original image + uint8_t *ptro = (uint8_t *)data + ((xo + yo * w) << 1); + // Pointer within sprite image + uint8_t *ptrs = (uint8_t *)_img + ((xs + ys * _iwidth) << 1); + + if(_iswapBytes) { - x = xs; - for (int32_t xp = xo; xp < xo + ws; xp++) + while (hs--) { - uint16_t color = data[xp + yp * w]; - if(_iswapBytes) color = color<<8 | color>>8; - _img[x + ys * _iwidth] = color; - x++; + // Fast copy with a 1 byte shift + memcpy(ptrs+1, ptro, (ws<<1) - 1); + // Now correct just the even numbered bytes + for (int32_t xp = 0; xp < (ws<<1); xp+=2) + { + ptrs[xp] = ptro[xp+1];; + } + ptro += w<<1; + ptrs += _iwidth<<1; + } + } + else + { + while (hs--) + { + memcpy(ptrs, ptro, ws<<1); + ptro += w << 1; + ptrs += _iwidth << 1; } - ys++; } } else if (_bpp == 8) // Plot a 16 bpp image into a 8 bpp Sprite @@ -859,8 +1075,9 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ for (int32_t xp = xo; xp < xo + ws; xp++) { uint16_t color = data[xp + yp * w]; - if(_iswapBytes) color = color<<8 | color>>8; - _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); + // When data source is a sprite, the bytes are already swapped + if(!_iswapBytes) _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE0) | (color & 0x07)<<2 | (color & 0x1800)>>11); + else _img8[x + ys * _iwidth] = (uint8_t)((color & 0xE000)>>8 | (color & 0x0700)>>6 | (color & 0x0018)>>3); x++; } ys++; @@ -868,12 +1085,47 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ } else if (_bpp == 4) { - // not supported. The image is unlikely to have the correct colors for the color map. - // we could implement a way to push a 4-bit image using the color map? - #ifdef TFT_eSPI_DEBUG - Serial.println("pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) not implemented"); - #endif - return; + // The image is assumed to be 4 bit, where each byte corresponds to two pixels. + // much faster when aligned to a byte boundary, because the alternative is slower, requiring + // tedious bit operations. + + const uint8_t *dataBuf = (uint8_t *)data; + int sWidth = (_iwidth >> 1); + + if ((xs & 0x01) == 0 && (xo & 0x01) == 0 && (ws & 0x01) == 0) + { + if ((ws & 0x01) == 0) // use memcpy for better perf. + { + xs = (xs >> 1) + ys * sWidth; + ws = (ws >> 1); + xo = (xo >> 1) + yo * (w>>1); + while (hs--) + { + memcpy(_img4 + xs, dataBuf + xo, ws); + xo += (w >> 1); + xs += sWidth; + } + } + } + else // not optimized + { + // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) + for (int32_t yp = yo; yp < yo + hs; yp++) + { + x = xs; + for (int32_t xp = xo; xp < xo + ws; xp++) + { + uint32_t color; + if ((xp & 0x01) == 0) + color = (dataBuf[((xp+yp*w)>>1)] & 0xF0) >> 4; // even index = bits 7 .. 4 + else + color = dataBuf[((xp-1+yp*w)>>1)] & 0x0F; // odd index = bits 3 .. 0. + drawPixel(x, ys, color); + x++; + } + ys++; + } + } } else // 1bpp @@ -916,7 +1168,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_ /*************************************************************************************** ** Function name: pushImage ** Description: push 565 colour FLASH (PROGMEM) image into a defined area -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data) { #ifdef ESP32 @@ -983,6 +1235,7 @@ void TFT_eSprite::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const u else // 1bpp { + // _dwidth and _dheight bounds not checked (rounded up -iwidth and _iheight used) // Move coordinate rotation to support fn if (_rotation == 1) { @@ -1042,26 +1295,26 @@ bool TFT_eSprite::getSwapBytes(void) /*************************************************************************************** ** Function name: setWindow -** Description: Set the bounds of a window for pushColor and writeColor -*************************************************************************************x*/ +** Description: Set the bounds of a window in the sprite +***************************************************************************************/ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { if (x0 > x1) swap_coord(x0, x1); if (y0 > y1) swap_coord(y0, y1); - if ((x0 >= _iwidth) || (x1 < 0) || (y0 >= _iheight) || (y1 < 0)) + if ((x0 >= _dwidth) || (x1 < 0) || (y0 >= _dheight) || (y1 < 0)) { // Point to that extra "off screen" pixel _xs = 0; - _ys = _iheight; + _ys = _dheight; _xe = 0; - _ye = _iheight; + _ye = _dheight; } else { if (x0 < 0) x0 = 0; - if (x1 >= _iwidth) x1 = _iwidth - 1; + if (x1 >= _dwidth) x1 = _dwidth - 1; if (y0 < 0) y0 = 0; - if (y1 >= _iheight) y1 = _iheight - 1; + if (y1 >= _dheight) y1 = _dheight - 1; _xs = x0; _ys = y0; @@ -1077,7 +1330,7 @@ void TFT_eSprite::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) /*************************************************************************************** ** Function name: pushColor ** Description: Send a new pixel to the set window -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::pushColor(uint32_t color) { if (!_created ) return; @@ -1099,7 +1352,7 @@ void TFT_eSprite::pushColor(uint32_t color) _img4[(_xptr + _yptr * _iwidth)>>1] = (_img4[(_xptr + _yptr * _iwidth)>>1] & 0xF0) | c; // new color is the low bits } } - + else drawPixel(_xptr, _yptr, color); // Increment x @@ -1119,7 +1372,7 @@ void TFT_eSprite::pushColor(uint32_t color) /*************************************************************************************** ** Function name: pushColor ** Description: Send a "len" new pixels to the set window -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) { if (!_created ) return; @@ -1141,7 +1394,7 @@ void TFT_eSprite::pushColor(uint32_t color, uint16_t len) /*************************************************************************************** ** Function name: writeColor ** Description: Write a pixel with pre-formatted colour to the set window -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::writeColor(uint16_t color) { if (!_created ) return; @@ -1179,7 +1432,7 @@ void TFT_eSprite::writeColor(uint16_t color) /*************************************************************************************** ** Function name: setScrollRect ** Description: Set scroll area within the sprite and the gap fill colour -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) { if ((x >= _iwidth) || (y >= _iheight) || !_created ) return; @@ -1190,7 +1443,7 @@ void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint if ((x + w) > _iwidth ) w = _iwidth - x; if ((y + h) > _iheight) h = _iheight - y; - if ( w < 1 || h < 1) return; + if ( w < 1 || h < 1) return; _sx = x; _sy = y; @@ -1204,7 +1457,7 @@ void TFT_eSprite::setScrollRect(int32_t x, int32_t y, int32_t w, int32_t h, uint /*************************************************************************************** ** Function name: scroll ** Description: Scroll dx,dy pixels, positive right,down, negative left,up -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::scroll(int16_t dx, int16_t dy) { if (abs(dx) >= _sw || abs(dy) >= _sh) @@ -1216,7 +1469,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) // Fetch the scroll area width and height set by setScrollRect() uint32_t w = _sw - abs(dx); // line width to copy uint32_t h = _sh - abs(dy); // lines to copy - int32_t iw = _iwidth; // width of sprite + int32_t iw = _iwidth; // rounded up width of sprite // Fetch the x,y origin set by setScrollRect() uint32_t tx = _sx; // to x @@ -1302,7 +1555,7 @@ void TFT_eSprite::scroll(int16_t dx, int16_t dy) /*************************************************************************************** ** Function name: fillSprite ** Description: Fill the whole sprite with defined colour -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::fillSprite(uint32_t color) { if (!_created ) return; @@ -1333,7 +1586,7 @@ void TFT_eSprite::fillSprite(uint32_t color) /*************************************************************************************** ** Function name: setCursor ** Description: Set the sprite text cursor x,y position -*************************************************************************************x*/ +***************************************************************************************/ // Not needed - using TFT_eSPI class function and this->cursor_x/y //void TFT_eSprite::setCursor(int16_t x, int16_t y) //{ @@ -1345,13 +1598,13 @@ void TFT_eSprite::fillSprite(uint32_t color) /*************************************************************************************** ** Function name: width ** Description: Return the width of sprite -*************************************************************************************x*/ +***************************************************************************************/ // Return the size of the display int16_t TFT_eSprite::width(void) { if (!_created ) return 0; - if (_bpp > 1) return _iwidth; + if (_bpp > 1) return _dwidth; if (_rotation == 1 || _rotation == 3) return _dheight; @@ -1362,12 +1615,12 @@ int16_t TFT_eSprite::width(void) /*************************************************************************************** ** Function name: height ** Description: Return the height of sprite -*************************************************************************************x*/ +***************************************************************************************/ int16_t TFT_eSprite::height(void) { if (!_created ) return 0; - if (_bpp > 1) return _iheight; + if (_bpp > 4) return _dheight; if (_rotation == 1 || _rotation == 3) return _dwidth; @@ -1378,7 +1631,7 @@ int16_t TFT_eSprite::height(void) /*************************************************************************************** ** Function name: setRotation ** Description: Rotate coordinate frame for 1bpp sprite -*************************************************************************************x*/ +***************************************************************************************/ // Does nothing for 8 and 16 bpp sprites. TODO allow rotation of these sprites void TFT_eSprite::setRotation(uint8_t rotation) { @@ -1395,7 +1648,7 @@ void TFT_eSprite::setRotation(uint8_t rotation) /*************************************************************************************** ** Function name: getRotation ** Description: Get rotation for 1bpp sprite -*************************************************************************************x*/ +***************************************************************************************/ uint8_t TFT_eSprite::getRotation(void) { @@ -1406,7 +1659,7 @@ uint8_t TFT_eSprite::getRotation(void) /*************************************************************************************** ** Function name: drawPixel ** Description: push a single pixel at an arbitrary position -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) { // Range checking @@ -1462,7 +1715,7 @@ void TFT_eSprite::drawPixel(int32_t x, int32_t y, uint32_t color) /*************************************************************************************** ** Function name: drawLine ** Description: draw a line between 2 arbitrary points -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { if (!_created ) return; @@ -1518,7 +1771,7 @@ void TFT_eSprite::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint3 /*************************************************************************************** ** Function name: drawFastVLine ** Description: draw a vertical line -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) { @@ -1573,7 +1826,7 @@ void TFT_eSprite::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) /*************************************************************************************** ** Function name: drawFastHLine ** Description: draw a horizontal line -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) { @@ -1629,13 +1882,13 @@ void TFT_eSprite::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) /*************************************************************************************** ** Function name: fillRect ** Description: draw a filled rectangle -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { if (!_created ) return; if ((x >= _iwidth) || (y >= _iheight)) return; - + if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } @@ -1698,9 +1951,9 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t { yp = (yp + 1) >> 1; while (h--) { - drawPixel(x, y+h-1, color & 0x0F); + drawPixel(x, y+h, color & 0x0F); if (w > 1) - memset(_img4 + (yp + ((x-1)>>1)), c2, (w-1)>>1); + memset(_img4 + yp, c2, (w-1)>>1); // same as above but you have a hangover on the left instead yp += (_iwidth >> 1); } @@ -1709,10 +1962,10 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t { yp = (yp + 1) >> 1; while (h--) { - drawPixel(x, y+h-1, color & 0x0F); - drawPixel(x+w-1, y+h-1, color & 0x0F); + drawPixel(x, y+h, color & 0x0F); + if (w > 1) drawPixel(x+w-1, y+h, color & 0x0F); if (w > 2) - memset(_img4 + (yp + ((x-1)>>1)), c2, (w-2)>>1); + memset(_img4 + yp, c2, (w-2)>>1); // maximal hacking, single pixels on left and right. yp += (_iwidth >> 1); } @@ -1734,7 +1987,7 @@ void TFT_eSprite::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t /*************************************************************************************** ** Function name: write ** Description: draw characters piped through serial stream -*************************************************************************************x*/ +***************************************************************************************/ size_t TFT_eSprite::write(uint8_t utf8) { uint16_t uniCode = decodeUTF8(utf8); @@ -1816,7 +2069,7 @@ size_t TFT_eSprite::write(uint8_t utf8) height = height * textsize; - if (utf8 == '\n') + if (utf8 == '\n') { this->cursor_y += height; this->cursor_x = 0; @@ -1871,7 +2124,7 @@ size_t TFT_eSprite::write(uint8_t utf8) /*************************************************************************************** ** Function name: drawChar ** Description: draw a single character in the Adafruit GLCD or freefont -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size) { if (!_created ) return; @@ -2013,7 +2266,7 @@ void TFT_eSprite::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uin /*************************************************************************************** ** Function name: drawChar ** Description: draw a unicode onto the screen -*************************************************************************************x*/ +***************************************************************************************/ // Any UTF-8 decoding must be done before calling drawChar() int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y) { @@ -2210,7 +2463,7 @@ int16_t TFT_eSprite::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t fo /*************************************************************************************** ** Function name: drawGlyph ** Description: Write a character to the sprite cursor position -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::drawGlyph(uint16_t code) { if (code < 0x21) @@ -2241,15 +2494,15 @@ void TFT_eSprite::drawGlyph(uint16_t code) uint16_t gNum = 0; bool found = this->getUnicodeIndex(code, &gNum); - + uint16_t fg = this->textcolor; uint16_t bg = this->textbgcolor; if (found) { - + bool newSprite = !_created; - + if (newSprite) { createSprite(this->gWidth[gNum], this->gFont.yAdvance); @@ -2345,7 +2598,7 @@ void TFT_eSprite::drawGlyph(uint16_t code) /*************************************************************************************** ** Function name: printToSprite ** Description: Write a string to the sprite cursor position -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::printToSprite(String string) { if(!this->fontLoaded) return; @@ -2360,14 +2613,14 @@ void TFT_eSprite::printToSprite(String string) /*************************************************************************************** ** Function name: printToSprite ** Description: Write a string to the sprite cursor position -*************************************************************************************x*/ +***************************************************************************************/ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) { if(!this->fontLoaded) return; uint16_t n = 0; bool newSprite = !_created; - + if (newSprite) { int16_t sWidth = 1; @@ -2411,7 +2664,7 @@ void TFT_eSprite::printToSprite(char *cbuffer, uint16_t len) //String string) /*************************************************************************************** ** Function name: printToSprite ** Description: Print character in a Sprite, create sprite if needed -*************************************************************************************x*/ +***************************************************************************************/ int16_t TFT_eSprite::printToSprite(int16_t x, int16_t y, uint16_t index) { bool newSprite = !_created; diff --git a/Extensions/Sprite.h b/Extensions/Sprite.h index 74ba6b2..3b8d556 100644 --- a/Extensions/Sprite.h +++ b/Extensions/Sprite.h @@ -9,17 +9,23 @@ class TFT_eSprite : public TFT_eSPI { public: - TFT_eSprite(TFT_eSPI *tft); + explicit TFT_eSprite(TFT_eSPI *tft); + ~TFT_eSprite(void); // Create a sprite of width x height pixels, return a pointer to the RAM area // Sketch can cast returned value to (uint16_t*) for 16 bit depth if needed // RAM required is: // - 1 bit per pixel for 1 bit colour depth + // - 1 nibble per pixel for 4 bit colour // - 1 byte per pixel for 8 bit colour // - 2 bytes per pixel for 16 bit color depth - ~TFT_eSprite(void); + void* createSprite(int16_t width, int16_t height, uint8_t frames = 1); - void* createSprite(int16_t width, int16_t height, uint8_t frames = 1); + // Returns a pointer to the sprite or nullptr if not created, user must cast to pointer type + void* getPointer(void); + + // Returns true if sprite has been created + bool created(void); // Delete the sprite to free up the RAM void deleteSprite(void); @@ -34,8 +40,8 @@ class TFT_eSprite : public TFT_eSPI { int8_t getColorDepth(void); // Set the palette for a 4 bit depth sprite. Only the first 16 colours in the map are used. - void createPalette(uint16_t *palette, int colors = 16); // Palette in RAM - void createPalette(const uint16_t *palette, int colors = 16); // Palette in FLASH + void createPalette(uint16_t *palette = nullptr, uint8_t colors = 16); // Palette in RAM + void createPalette(const uint16_t *palette = nullptr, uint8_t colors = 16); // Palette in FLASH // Set a single palette index to the given color void setPaletteColor(uint8_t index, uint16_t color); @@ -114,7 +120,7 @@ class TFT_eSprite : public TFT_eSPI { // 16bpp = colour, 8bpp = byte, 4bpp = colour index, 1bpp = 1 or 0 uint16_t readPixelValue(int32_t x, int32_t y); - // Write an image (colour bitmap) to the sprite. Not implemented for _bpp == 4. + // Write an image (colour bitmap) to the sprite. void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, uint16_t *data); void pushImage(int32_t x0, int32_t y0, int32_t w, int32_t h, const uint16_t *data); @@ -127,6 +133,14 @@ class TFT_eSprite : public TFT_eSPI { void pushSprite(int32_t x, int32_t y); void pushSprite(int32_t x, int32_t y, uint16_t transparent); + // Push a windowed area of the sprite to the TFT at tx, ty + bool pushSprite(int32_t tx, int32_t ty, int32_t sx, int32_t sy, int32_t sw, int32_t sh); + + // Push the sprite to another sprite at x,y. This fn calls pushImage() in the destination sprite (dspr) class. + // >>>>>> Using a transparent color is not supported at the moment <<<<<< + bool pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y); + bool pushToSprite(TFT_eSprite *dspr, int32_t x, int32_t y, uint16_t transparent); + int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), drawChar(uint16_t uniCode, int32_t x, int32_t y); @@ -154,15 +168,15 @@ class TFT_eSprite : public TFT_eSPI { uint8_t _bpp; // bits per pixel (1, 8 or 16) uint16_t *_img; // pointer to 16 bit sprite - uint8_t *_img8; // pointer to 8 bit sprite - uint8_t *_img4; // pointer to 4 bit sprite (uses color map) - uint8_t *_img8_1; // pointer to frame 1 - uint8_t *_img8_2; // pointer to frame 2 + uint8_t *_img8; // pointer to 8 bit sprite frame 1 or frame 2 + uint8_t *_img4; // pointer to 4 bit sprite (uses color map) + uint8_t *_img8_1; // pointer to frame 1 + uint8_t *_img8_2; // pointer to frame 2 uint16_t *_colorMap; // color map: 16 entries, used with 4 bit color map. - int16_t _xpivot; // x pivot point coordinate - int16_t _ypivot; // y pivot point coordinate + int16_t _xPivot; // x pivot point coordinate + int16_t _yPivot; // y pivot point coordinate int32_t _sinra; int32_t _cosra; diff --git a/Processors/TFT_eSPI_ESP32.c b/Processors/TFT_eSPI_ESP32.c index d61dde7..b866d28 100644 --- a/Processors/TFT_eSPI_ESP32.c +++ b/Processors/TFT_eSPI_ESP32.c @@ -457,7 +457,11 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ if ( (color >> 8) == (color & 0x00FF) ) { if (!len) return; tft_Write_16(color); + #if defined (SSD1963_DRIVER) + while (--len) {WR_L; WR_H; WR_L; WR_H; WR_L; WR_H;} + #else while (--len) {WR_L; WR_H; WR_L; WR_H;} + #endif } else while (len--) {tft_Write_16(color);} } @@ -489,28 +493,35 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ //////////////////////////////////////////////////////////////////////////////////////// -#if defined ESP32_DMA && !defined (TFT_PARALLEL_8_BIT) // DMA FUNCTIONS +#if defined (ESP32_DMA) && !defined (TFT_PARALLEL_8_BIT) // DMA FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////// /*************************************************************************************** ** Function name: dmaBusy -** Description: Check if DMA is busy (currently blocking!) +** Description: Check if DMA is busy ***************************************************************************************/ bool TFT_eSPI::dmaBusy(void) { if (!DMA_Enabled || !spiBusyCheck) return false; - //spi_transaction_t rtrans; - //bool trans_result=spi_device_polling_transmit(dmaHAL, &rtrans); - //return trans_result; - // This works but blocks - dmaWait(); - return false; + + spi_transaction_t *rtrans; + esp_err_t ret; + uint8_t checks = spiBusyCheck; + for (int i = 0; i < checks; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0); + if (ret == ESP_OK) spiBusyCheck--; + } + + //Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck); + if (spiBusyCheck ==0) return false; + return true; } /*************************************************************************************** ** Function name: dmaWait -** Description: Check if DMA is busy (blocking!) +** Description: Wait until DMA is over (blocking!) ***************************************************************************************/ void TFT_eSPI::dmaWait(void) { @@ -535,6 +546,11 @@ void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) { if ((len == 0) || (!DMA_Enabled)) return; dmaWait(); + + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (image[i] = image[i] << 8 | image[i] >> 8); + } + esp_err_t ret; static spi_transaction_t trans; @@ -548,7 +564,7 @@ void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); assert(ret == ESP_OK); - spiBusyCheck = 1; + spiBusyCheck++; } @@ -574,12 +590,10 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t if (dw < 1 || dh < 1) return; - if (buffer == nullptr) buffer = image; - uint32_t len = dw*dh; - dmaWait(); - + if (buffer == nullptr) { buffer = image; dmaWait(); } + // If image is clipped, copy pixels into a contiguous block if ( (dw != w) || (dh != h) ) { if(_swapBytes) { @@ -606,43 +620,24 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t } } + if (spiBusyCheck) dmaWait(); // Incase we did not wait earlier + + setAddrWindow(x, y, dw, dh); + esp_err_t ret; - static spi_transaction_t trans[6]; - for (int i = 0; i < 6; i++) - { - memset(&trans[i], 0, sizeof(spi_transaction_t)); - if ((i & 1) == 0) - { - trans[i].length = 8; - trans[i].user = (void *)0; - } - else - { - trans[i].length = 8 * 4; - trans[i].user = (void *)1; - } - trans[i].flags = SPI_TRANS_USE_TXDATA; - } - trans[0].tx_data[0] = 0x2A; //Column Address Set - trans[1].tx_data[0] = x >> 8; //Start Col High - trans[1].tx_data[1] = x & 0xFF; //Start Col Low - trans[1].tx_data[2] = (x + dw - 1) >> 8; //End Col High - trans[1].tx_data[3] = (x + dw - 1) & 0xFF; //End Col Low - trans[2].tx_data[0] = 0x2B; //Page address set - trans[3].tx_data[0] = y >> 8; //Start page high - trans[3].tx_data[1] = y & 0xFF; //start page low - trans[3].tx_data[2] = (y + dh - 1) >> 8; //end page high - trans[3].tx_data[3] = (y + dh - 1) & 0xFF; //end page low - trans[4].tx_data[0] = 0x2C; //memory write - trans[5].tx_buffer = buffer; //finally send the line data - trans[5].length = dw * 2 * 8 * dh; //Data length, in bits - trans[5].flags = 0; //undo SPI_TRANS_USE_TXDATA flag - for (int i = 0; i < 6; i++) - { - ret = spi_device_queue_trans(dmaHAL, &trans[i], portMAX_DELAY); - assert(ret == ESP_OK); - } - spiBusyCheck = 6; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = buffer; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; } //////////////////////////////////////////////////////////////////////////////////////// @@ -658,8 +653,8 @@ extern "C" void dc_callback(); void IRAM_ATTR dc_callback(spi_transaction_t *spi_tx) { - if ((bool)spi_tx->user) DC_D; - else DC_C; + if ((bool)spi_tx->user) {DC_D;} + else {DC_C;} } /*************************************************************************************** @@ -714,7 +709,7 @@ bool TFT_eSPI::initDMA(void) void TFT_eSPI::deInitDMA(void) { if (!DMA_Enabled) return; - + spi_bus_remove_device(dmaHAL); spi_bus_free(spi_host); DMA_Enabled = false; } diff --git a/Processors/TFT_eSPI_ESP32.h b/Processors/TFT_eSPI_ESP32.h index 0da6a18..d1cca5f 100644 --- a/Processors/TFT_eSPI_ESP32.h +++ b/Processors/TFT_eSPI_ESP32.h @@ -39,9 +39,26 @@ // Define a generic flag for 8 bit parallel #if defined (ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility - #define TFT_PARALLEL_8_BIT // Generic parallel flag + #if !defined (TFT_PARALLEL_8_BIT) + #define TFT_PARALLEL_8_BIT // Generic parallel flag + #endif #endif +// Ensure ESP32 specific flag is defined for 8 bit parallel +#if defined (TFT_PARALLEL_8_BIT) + #if !defined (ESP32_PARALLEL) + #define ESP32_PARALLEL + #endif +#endif + +// Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions +#if !defined(TFT_PARALLEL_8_BIT) && !defined(ILI9488_DRIVER) && !defined (RPI_DISPLAY_TYPE) + #define ESP32_DMA + // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions + #define DMA_BUSY_CHECK dmaWait() +#else + #define DMA_BUSY_CHECK +#endif // If smooth font is used then it is likely SPIFFS will be needed #ifdef SMOOTH_FONT @@ -60,10 +77,16 @@ #define DC_D // No macro allocated so it generates no code #else #if defined (TFT_PARALLEL_8_BIT) - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + // TFT_DC, by design, must be in range 0-31 for single register parallel write + #if (TFT_DC >= 0) && (TFT_DC < 32) + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts = (1 << TFT_DC) + #else + #define DC_C + #define DC_D + #endif #else - #if TFT_DC >= 32 + #if (TFT_DC >= 32) #ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change #define DC_C GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); \ GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) @@ -73,16 +96,20 @@ #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) #endif - #elif TFT_DC >= 0 - #ifdef RPI_DISPLAY_TYPE // RPi ILI9486 display needs a slower DC change - #define DC_C GPIO.out_w1tc = (1 << TFT_DC); \ - GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ - GPIO.out_w1ts = (1 << TFT_DC) - #elif defined (RPI_DISPLAY_TYPE) // Other RPi displays need a slower C->D change - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ - GPIO.out_w1ts = (1 << TFT_DC) + #elif (TFT_DC >= 0) + #if defined (RPI_DISPLAY_TYPE) + #if defined (ILI9486_DRIVER) + // RPi ILI9486 display needs a slower DC change + #define DC_C GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #else + // Other RPi displays need a slower C->D change + #define DC_C GPIO.out_w1tc = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) + #endif #else #define DC_C GPIO.out_w1tc = (1 << TFT_DC)//;GPIO.out_w1tc = (1 << TFT_DC) #define DC_D GPIO.out_w1ts = (1 << TFT_DC)//;GPIO.out_w1ts = (1 << TFT_DC) @@ -98,8 +125,9 @@ // Define the CS (TFT chip select) pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TFT_CS - #define CS_L // No macro allocated so it generates no code - #define CS_H // No macro allocated so it generates no code + #define TFT_CS -1 // Keep DMA code happy + #define CS_L // No macro allocated so it generates no code + #define CS_H // No macro allocated so it generates no code #else #if defined (TFT_PARALLEL_8_BIT) #if TFT_CS >= 32 @@ -113,8 +141,8 @@ #define CS_H #endif #else - #if TFT_CS >= 32 - #ifdef RPI_DISPLAY_TYPE // RPi ILI9486 display needs a slower CS change + #if (TFT_CS >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change #define CS_L GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); \ GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ @@ -123,12 +151,12 @@ #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) #endif - #elif TFT_CS >= 0 - #ifdef RPI_DISPLAY_TYPE // RPi ILI9486 display needs a slower CS change + #elif (TFT_CS >= 0) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change #define CS_L GPIO.out_w1ts = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) #define CS_H GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1ts = (1 << TFT_CS) #else - #define CS_L GPIO.out_w1tc = (1 << TFT_CS);GPIO.out_w1tc = (1 << TFT_CS) + #define CS_L GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) #define CS_H GPIO.out_w1ts = (1 << TFT_CS)//;GPIO.out_w1ts = (1 << TFT_CS) #endif #else @@ -141,9 +169,18 @@ //////////////////////////////////////////////////////////////////////////////////////// // Define the WR (TFT Write) pin drive code //////////////////////////////////////////////////////////////////////////////////////// -#ifdef TFT_WR - #define WR_L GPIO.out_w1tc = (1 << TFT_WR) - #define WR_H GPIO.out_w1ts = (1 << TFT_WR) +#if defined (TFT_WR) + #if (TFT_WR >= 0) + // TFT_WR, by design, must be in range 0-31 for single register parallel write + #define WR_L GPIO.out_w1tc = (1 << TFT_WR) + #define WR_H GPIO.out_w1ts = (1 << TFT_WR) + #else + #define WR_L + #define WR_H + #endif +#else + #define WR_L + #define WR_H #endif //////////////////////////////////////////////////////////////////////////////////////// @@ -158,12 +195,64 @@ #endif //////////////////////////////////////////////////////////////////////////////////////// -// Make sure TFT_MISO is defined if not used to avoid an error message +// Make sure SPI default pins are assigned if not specified by user or set to -1 //////////////////////////////////////////////////////////////////////////////////////// #if !defined (TFT_PARALLEL_8_BIT) - #ifndef TFT_MISO - #define TFT_MISO -1 + + #ifdef USE_HSPI_PORT + + #ifndef TFT_MISO + #define TFT_MISO 12 + #endif + #if (TFT_MISO == -1) + #undef TFT_MISO + #define TFT_MISO 12 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 13 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 13 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 14 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 14 + #endif + + #else // VSPI port + + #ifndef TFT_MISO + #define TFT_MISO 19 + #endif + #if (TFT_MISO == -1) + #undef TFT_MISO + #define TFT_MISO 19 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 23 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 23 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 18 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 18 + #endif + #endif + #endif //////////////////////////////////////////////////////////////////////////////////////// @@ -204,13 +293,26 @@ // Write 8 bits to TFT #define tft_Write_8(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)(C)); WR_H - // Write 16 bits to TFT - #define tft_Write_16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); WR_H + #if defined (SSD1963_DRIVER) - // 16 bit write with swapped bytes - #define tft_Write_16S(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H + // Write 18 bit color to TFT + #define tft_Write_16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0xF800)>> 8)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x07E0)>> 3)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x001F)<< 3)); WR_H + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) uint16_t Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + + // Write 16 bits to TFT + #define tft_Write_16(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H + + // 16 bit write with swapped bytes + #define tft_Write_16S(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H + #endif // Write 32 bits to TFT #define tft_Write_32(C) GPIO.out_w1tc = clr_mask; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 24)); WR_H; \ @@ -232,10 +334,18 @@ // Read pin #ifdef TFT_RD - #define RD_L GPIO.out_w1tc = (1 << TFT_RD) - //#define RD_L digitalWrite(TFT_WR, LOW) - #define RD_H GPIO.out_w1ts = (1 << TFT_RD) - //#define RD_H digitalWrite(TFT_WR, HIGH) + #if (TFT_RD >= 32) + #define RD_L GPIO.out1_w1tc.val = = (1 << (TFT_RD - 32)) + #define RD_H GPIO.out1_w1ts.val = = (1 << (TFT_RD - 32)) + #elif (TFT_RD >= 0) + #define RD_L GPIO.out_w1tc = (1 << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H GPIO.out_w1ts = (1 << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) + #else + #define RD_L + #define RD_H + #endif #endif //////////////////////////////////////////////////////////////////////////////////////// @@ -302,16 +412,6 @@ //////////////////////////////////////////////////////////////////////////////////////// #else - #define ESP32_DMA // DMA is available for SPI - - // Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions - #ifdef ESP32_DMA - // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions - #define DMA_BUSY_CHECK { if (DMA_Enabled) dmaWait(); } - #else - #define DMA_BUSY_CHECK - #endif - // ESP32 low level SPI writes for 8, 16 and 32 bit values // to avoid the function call overhead #define TFT_WRITE_BITS(D, B) \ diff --git a/Processors/TFT_eSPI_ESP8266.c b/Processors/TFT_eSPI_ESP8266.c index 7a7287c..31e87e7 100644 --- a/Processors/TFT_eSPI_ESP8266.c +++ b/Processors/TFT_eSPI_ESP8266.c @@ -304,7 +304,7 @@ return; SPI1U1 = (511 << SPILMOSI); while(len>31) { -#if defined SPI_FREQUENCY && (SPI_FREQUENCY == 80000000) +#if (defined (SPI_FREQUENCY) && (SPI_FREQUENCY == 80000000)) if(SPI1CMD & SPIBUSY) // added to sync with flag change #endif while(SPI1CMD & SPIBUSY) {} diff --git a/Processors/TFT_eSPI_ESP8266.h b/Processors/TFT_eSPI_ESP8266.h index f3065f1..3b475cf 100644 --- a/Processors/TFT_eSPI_ESP8266.h +++ b/Processors/TFT_eSPI_ESP8266.h @@ -19,7 +19,7 @@ #define DMA_BUSY_CHECK // DMA not available, leave blank // Initialise processor specific SPI functions, used by init() -#if !defined (SUPPORT_TRANSACTIONS) && defined (ESP8266) +#if (!defined (SUPPORT_TRANSACTIONS) && defined (ESP8266)) #define INIT_TFT_DATA_BUS \ spi.setBitOrder(MSBFIRST); \ spi.setDataMode(TFT_SPI_MODE); \ diff --git a/Processors/TFT_eSPI_Generic.h b/Processors/TFT_eSPI_Generic.h index ecf69de..1b44d02 100644 --- a/Processors/TFT_eSPI_Generic.h +++ b/Processors/TFT_eSPI_Generic.h @@ -58,6 +58,13 @@ #define CS_H digitalWrite(TFT_CS, HIGH) #endif +//////////////////////////////////////////////////////////////////////////////////////// +// Make sure TFT_RD is defined if not used to avoid an error message +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_RD + #define TFT_RD -1 +#endif + //////////////////////////////////////////////////////////////////////////////////////// // Define the WR (TFT Write) pin drive code //////////////////////////////////////////////////////////////////////////////////////// diff --git a/Processors/TFT_eSPI_STM32.c b/Processors/TFT_eSPI_STM32.c index c3acaa7..dc42e67 100644 --- a/Processors/TFT_eSPI_STM32.c +++ b/Processors/TFT_eSPI_STM32.c @@ -10,8 +10,11 @@ // No globals #else // Use STM32 default SPI port - SPIClass& spi = SPI; - + #if !defined (TFT_MOSI) || !defined (TFT_MISO) || !defined (TFT_SCLK) + SPIClass& spi = SPI; + #else + SPIClass spi(TFT_MOSI, TFT_MISO, TFT_SCLK); + #endif // SPI HAL peripheral handle SPI_HandleTypeDef spiHal; #endif @@ -81,24 +84,42 @@ void TFT_eSPI::end_SDA_Read(void) ** Description: Write a block of pixels of the same colour ***************************************************************************************/ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ - // Loop unrolling improves speed dramtically graphics test 0.634s => 0.374s - while (len>31) { - // 32D macro writes 16 bits twice - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - len-=32; - } - while (len>7) { - tft_Write_32D(color); tft_Write_32D(color); - tft_Write_32D(color); tft_Write_32D(color); - len-=8; - } + // Loop unrolling improves speed dramtically graphics test 0.634s => 0.374s + while (len>31) { + #if !defined (SSD1963_DRIVER) + // 32D macro writes 16 bits twice + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + #else + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + #endif + len-=32; + } + + while (len>7) { + #if !defined (SSD1963_DRIVER) + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + #else + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + #endif + len-=8; + } + while (len--) {tft_Write_16(color);} } @@ -128,7 +149,7 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ ***************************************************************************************/ void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) { -#ifdef STM_PORTA_DATA_BUS +#if defined (STM_PORTA_DATA_BUS) #if defined (STM32F1xx) if (mode == OUTPUT) GPIOA->CRL = 0x33333333; else GPIOA->CRL = 0x88888888; @@ -136,7 +157,7 @@ void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) if (mode == OUTPUT) GPIOA->MODER = (GPIOA->MODER & 0xFFFF0000) | 0x00005555; else GPIOA->MODER &= 0xFFFF0000; #endif -#elif STM_PORTB_DATA_BUS +#elif defined (STM_PORTB_DATA_BUS) #if defined (STM32F1xx) if (mode == OUTPUT) GPIOB->CRL = 0x33333333; else GPIOB->CRL = 0x88888888; @@ -191,12 +212,12 @@ uint8_t TFT_eSPI::readByte(void) uint8_t b = 0; RD_L; -#ifdef STM_PORTA_DATA_BUS +#if defined (STM_PORTA_DATA_BUS) b = GPIOA->IDR; b = GPIOA->IDR; b = GPIOA->IDR; b = (GPIOA->IDR) & 0xFF; -#elif STM_PORTB_DATA_BUS +#elif defined (STM_PORTB_DATA_BUS) b = GPIOB->IDR; b = GPIOB->IDR; b = GPIOB->IDR; @@ -484,15 +505,20 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t // The DMA functions here work with SPI only (not parallel) #if defined (STM32F2xx) || defined (STM32F4xx) || defined (STM32F7xx) /*************************************************************************************** -** Function name: DMA2_StreamX_IRQHandler -** Description: Override the default HAL stream 3 interrupt handler +** Function name: DMAX_StreamX_IRQHandler +** Description: Override the default HAL stream X interrupt handler ***************************************************************************************/ -extern "C" void DMA2_Stream3_IRQHandler(); -void DMA2_Stream3_IRQHandler(void) -{ - // Call the default end of buffer handler - HAL_DMA_IRQHandler(&dmaHal); -} + #if (TFT_SPI_PORT == 1) + extern "C" void DMA2_Stream3_IRQHandler(); + void DMA2_Stream3_IRQHandler(void) + #elif (TFT_SPI_PORT == 2) + extern "C" void DMA1_Stream4_IRQHandler(); + void DMA1_Stream4_IRQHandler(void) + #endif + { + // Call the default end of buffer handler + HAL_DMA_IRQHandler(&dmaHal); + } /*************************************************************************************** ** Function name: initDMA @@ -503,9 +529,14 @@ void DMA2_Stream3_IRQHandler(void) // https://electronics.stackexchange.com/questions/379813/configuring-the-dma-request-multiplexer-on-a-stm32h7-mcu bool TFT_eSPI::initDMA(void) { - __HAL_RCC_DMA2_CLK_ENABLE(); // Enable DMA2 clock + #if (TFT_SPI_PORT == 1) + __HAL_RCC_DMA2_CLK_ENABLE(); // Enable DMA2 clock + dmaHal.Init.Channel = DMA_CHANNEL_3; // DMA channel 3 is for SPI1 TX + #elif (TFT_SPI_PORT == 2) + __HAL_RCC_DMA1_CLK_ENABLE(); // Enable DMA2 clock + dmaHal.Init.Channel = DMA_CHANNEL_0; // DMA channel 0 is for SPI2 TX + #endif - dmaHal.Init.Channel = DMA_CHANNEL_3; // DMA channel 3 is for SPI1 TX dmaHal.Init.Mode = DMA_NORMAL; //DMA_CIRCULAR; // // Normal = send buffer once dmaHal.Init.Direction = DMA_MEMORY_TO_PERIPH; // Copy memory to the peripheral dmaHal.Init.PeriphInc = DMA_PINC_DISABLE; // Don't increment peripheral address @@ -517,10 +548,13 @@ bool TFT_eSPI::initDMA(void) // Insert error message here? return DMA_Enabled = false; }; + #if (TFT_SPI_PORT == 1) + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); // Enable DMA end interrupt handler + #elif (TFT_SPI_PORT == 2) + HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); // Enable DMA end interrupt handler + #endif - HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); // Enable DMA end interrupt handler - - __HAL_LINKDMA(&spiHal, hdmatx, dmaHal); // Attach DMA engine to SPI peripheral + __HAL_LINKDMA(&spiHal, hdmatx, dmaHal); // Attach DMA engine to SPI peripheral return DMA_Enabled = true; } @@ -530,13 +564,18 @@ bool TFT_eSPI::initDMA(void) ** Function name: DMA1_ChannelX_IRQHandler ** Description: Override the default HAL stream 3 interrupt handler ***************************************************************************************/ -extern "C" void DMA1_Channel3_IRQHandler(); + #if (TFT_SPI_PORT == 1) + extern "C" void DMA1_Channel3_IRQHandler(); + void DMA1_Channel3_IRQHandler(void) + #elif (TFT_SPI_PORT == 2) + extern "C" void DMA1_Channel5_IRQHandler(); + void DMA1_Channel5_IRQHandler(void) + #endif + { + // Call the default end of buffer handler + HAL_DMA_IRQHandler(&dmaHal); + } -void DMA1_Channel3_IRQHandler(void) -{ - // Call the default end of buffer handler - HAL_DMA_IRQHandler(&dmaHal); -} //*/ /*************************************************************************************** ** Function name: initDMA @@ -544,7 +583,7 @@ void DMA1_Channel3_IRQHandler(void) ***************************************************************************************/ bool TFT_eSPI::initDMA(void) { - __HAL_RCC_DMA1_CLK_ENABLE(); // Enable DMA2 clock + __HAL_RCC_DMA1_CLK_ENABLE(); // Enable DMA1 clock dmaHal.Init.Mode = DMA_NORMAL; //DMA_CIRCULAR; // // Normal = send buffer once dmaHal.Init.Direction = DMA_MEMORY_TO_PERIPH; // Copy memory to the peripheral @@ -561,9 +600,13 @@ bool TFT_eSPI::initDMA(void) return DMA_Enabled = false; }; - HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 1, 0); - HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn); // Enable DMA end interrupt handler - + #if (TFT_SPI_PORT == 1) + HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn); // Enable DMA end interrupt handler + #elif (TFT_SPI_PORT == 2) + HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn); // Enable DMA end interrupt handler + #endif return DMA_Enabled = true; } diff --git a/Processors/TFT_eSPI_STM32.h b/Processors/TFT_eSPI_STM32.h index 4da52f7..943fe2b 100644 --- a/Processors/TFT_eSPI_STM32.h +++ b/Processors/TFT_eSPI_STM32.h @@ -146,23 +146,49 @@ //////////////////////////////////////////////////////////////////////////////////////// #else + // Use SPI1 as default if not defined + #ifndef TFT_SPI_PORT + #define TFT_SPI_PORT 1 + #endif + // Global define is _VARIANT_ARDUINO_STM32_, see board package stm32_def.h for specific variants #if defined (STM32F2xx) || defined (STM32F4xx) || defined (STM32F7xx) + #define STM32_DMA // DMA is available with these processors - // Initialise processor specific SPI and DMA instances - used by init() - #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1; \ - dmaHal.Instance = DMA2_Stream3 - // The DMA hard-coding for SPI1 is in TFT_eSPI_STM32.c as follows: - // DMA_CHANNEL_3 - // DMA2_Stream3_IRQn and DMA2_Stream3_IRQHandler() + + #if (TFT_SPI_PORT == 1) + // Initialise processor specific SPI and DMA instances - used by init() + #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1; \ + dmaHal.Instance = DMA2_Stream3 + // The DMA hard-coding for SPI1 is in TFT_eSPI_STM32.c as follows: + // DMA_CHANNEL_3 + // DMA2_Stream3_IRQn and DMA2_Stream3_IRQHandler() + #elif (TFT_SPI_PORT == 2) + // Initialise processor specific SPI and DMA instances - used by init() + #define INIT_TFT_DATA_BUS spiHal.Instance = SPI2; \ + dmaHal.Instance = DMA1_Stream4 + // The DMA hard-coding for SPI2 is in TFT_eSPI_STM32.c as follows: + // DMA_CHANNEL_4 + // DMA1_Stream4_IRQn and DMA1_Stream4_IRQHandler() + #endif + #elif defined (STM32F1xx) // For Blue Pill and STM32F1xx processors with DMA support #define STM32_DMA // DMA is available with these processors - #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1; \ - dmaHal.Instance = DMA1_Channel3 + #if (TFT_SPI_PORT == 1) + #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1; \ + dmaHal.Instance = DMA1_Channel3 + #elif (TFT_SPI_PORT == 2) + #define INIT_TFT_DATA_BUS spiHal.Instance = SPI2; \ + dmaHal.Instance = DMA1_Channel5 + #endif #else // For STM32 processor with no implemented DMA support (yet) - #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1 + #if (TFT_SPI_PORT == 1) + #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1 + #elif (TFT_SPI_PORT == 2) + #define INIT_TFT_DATA_BUS spiHal.Instance = SPI2 + #endif #endif #endif @@ -217,12 +243,21 @@ // Define the RD (TFT Read) pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifdef TFT_RD - // Convert Arduino pin reference Dx or STM pin reference PXn to port and mask - #define RD_PORT digitalPinToPort(TFT_RD) - #define RD_PIN_MASK digitalPinToBitMask(TFT_RD) - // Use bit set reset register - #define RD_L RD_PORT->BSRR = RD_PIN_MASK<<16 - #define RD_H RD_PORT->BSRR = RD_PIN_MASK + #if (TFT_RD >= 0) + // Convert Arduino pin reference Dx or STM pin reference PXn to port and mask + #define RD_PORT digitalPinToPort(TFT_RD) + #define RD_PIN_MASK digitalPinToBitMask(TFT_RD) + // Use bit set reset register + #define RD_L RD_PORT->BSRR = RD_PIN_MASK<<16 + #define RD_H RD_PORT->BSRR = RD_PIN_MASK + #else + #define RD_L + #define RD_H + #endif +#else + #define TFT_RD -1 + #define RD_L + #define RD_H #endif //////////////////////////////////////////////////////////////////////////////////////// @@ -376,6 +411,31 @@ GPIOB->BSRR = D3_BSR_MASK(C) | D4_BSR_MASK(C) | D5_BSR_MASK(C) | D6_BSR_MASK(C); \ WR_STB // Need to slow down strobe + #if defined (SSD1963_DRIVER) + + // Write 18 bit color to TFT (untested) + #define tft_Write_16(C) uint8_t r = (((C) & 0xF800)>> 8); uint8_t g = (((C) & 0x07E0)>> 3); uint8_t b = (((C) & 0x001F)<< 3); \ + GPIOA->BSRR = D0_BSR_MASK(r) | D2_BSR_MASK(r) | D7_BSR_MASK(r); \ + WR_L; \ + GPIOC->BSRR = D1_BSR_MASK(r); \ + GPIOB->BSRR = D3_BSR_MASK(r) | D4_BSR_MASK(r) | D5_BSR_MASK(r) | D6_BSR_MASK(r); \ + WR_STB; \ + GPIOA->BSRR = D0_BSR_MASK(g) | D2_BSR_MASK(g) | D7_BSR_MASK(g); \ + WR_L; \ + GPIOC->BSRR = D1_BSR_MASK(g); \ + GPIOB->BSRR = D3_BSR_MASK(g) | D4_BSR_MASK(g) | D5_BSR_MASK(g) | D6_BSR_MASK(g); \ + WR_STB; \ + GPIOA->BSRR = D0_BSR_MASK(b) | D2_BSR_MASK(b) | D7_BSR_MASK(b); \ + WR_L; \ + GPIOC->BSRR = D1_BSR_MASK(b); \ + GPIOB->BSRR = D3_BSR_MASK(b) | D4_BSR_MASK(b) | D5_BSR_MASK(b) | D6_BSR_MASK(b); \ + WR_STB // Need to slow down strobe + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) uint16_t Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + // Write 16 bits to TFT #define tft_Write_16(C) GPIOA->BSRR = D8_BSR_MASK(C) | D10_BSR_MASK(C) | D15_BSR_MASK(C); \ WR_L; \ @@ -399,6 +459,7 @@ GPIOC->BSRR = D9_BSR_MASK(C); \ GPIOB->BSRR = D11_BSR_MASK(C) | D12_BSR_MASK(C) | D13_BSR_MASK(C) | D14_BSR_MASK(C); \ WR_STB + #endif #define tft_Write_32(C) tft_Write_16((uint16_t)((C)>>16)); tft_Write_16((uint16_t)(C)) @@ -492,6 +553,31 @@ GPIOE->BSRR = D3_BSR_MASK(C) | D5_BSR_MASK(C) | D6_BSR_MASK(C); \ WR_STB + #if defined (SSD1963_DRIVER) + + // Write 18 bit color to TFT (untested) + #define tft_Write_16(C) uint8_t r = (((C) & 0xF800)>> 8); uint8_t g = (((C) & 0x07E0)>> 3); uint8_t b = (((C) & 0x001F)<< 3); \ + GPIOF->BSRR = D0_BSR_MASK(r) | D2_BSR_MASK(r) | D4_BSR_MASK(r) | D7_BSR_MASK(r); \ + WR_L; \ + GPIOD->BSRR = D1_BSR_MASK(r); \ + GPIOE->BSRR = D3_BSR_MASK(r) | D5_BSR_MASK(r) | D6_BSR_MASK(r); \ + WR_STB; \ + GPIOF->BSRR = D0_BSR_MASK(g) | D2_BSR_MASK(g) | D4_BSR_MASK(g) | D7_BSR_MASK(g); \ + WR_L; \ + GPIOD->BSRR = D1_BSR_MASK(g); \ + GPIOE->BSRR = D3_BSR_MASK(g) | D5_BSR_MASK(g) | D6_BSR_MASK(g); \ + WR_STB; \ + GPIOF->BSRR = D0_BSR_MASK(b) | D2_BSR_MASK(b) | D4_BSR_MASK(b) | D7_BSR_MASK(b); \ + WR_L; \ + GPIOD->BSRR = D1_BSR_MASK(b); \ + GPIOE->BSRR = D3_BSR_MASK(b) | D5_BSR_MASK(b) | D6_BSR_MASK(b); \ + WR_STB // Need to slow down strobe + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) uint16_t Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + // Write 16 bits to TFT #define tft_Write_16(C) GPIOF->BSRR = D8_BSR_MASK(C) | D10_BSR_MASK(C) | D12_BSR_MASK(C) | D15_BSR_MASK(C); \ WR_L; \ @@ -516,6 +602,8 @@ GPIOE->BSRR = D11_BSR_MASK(C) | D13_BSR_MASK(C) | D14_BSR_MASK(C); \ WR_STB + #endif + #define tft_Write_32(C) tft_Write_16((uint16_t)((C)>>16)); tft_Write_16((uint16_t)(C)) #define tft_Write_32C(C,D) tft_Write_16((uint16_t)(C)); tft_Write_16((uint16_t)(D)) @@ -637,7 +725,20 @@ // Write 8 bits to TFT #define tft_Write_8(C) GPIOA->BSRR = (0x00FF0000 | (uint8_t)(C)); WR_L; WR_STB - + + #if defined (SSD1963_DRIVER) + + // Write 18 bit color to TFT (untested) + #define tft_Write_16(C) uint8_t r = (((C) & 0xF800)>> 8); uint8_t g = (((C) & 0x07E0)>> 3); uint8_t b = (((C) & 0x001F)<< 3); \ + GPIOA->BSRR = (0x00FF0000 | (uint8_t)(r)); WR_L; WR_STB; \ + GPIOA->BSRR = (0x00FF0000 | (uint8_t)(g)); WR_L; WR_STB; \ + GPIOA->BSRR = (0x00FF0000 | (uint8_t)(b)); WR_L; WR_STB + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) uint16_t Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + // Write 16 bits to TFT #define tft_Write_16(C) GPIOA->BSRR = (0x00FF0000 | (uint8_t)(C>>8)); WR_L; WR_STB; \ GPIOA->BSRR = (0x00FF0000 | (uint8_t)(C>>0)); WR_L; WR_STB @@ -645,6 +746,7 @@ // 16 bit write with swapped bytes #define tft_Write_16S(C) GPIOA->BSRR = (0x00FF0000 | (uint8_t)(C>>0)); WR_L; WR_STB; \ GPIOA->BSRR = (0x00FF0000 | (uint8_t)(C>>8)); WR_L; WR_STB + #endif #define tft_Write_32(C) tft_Write_16((uint16_t)((C)>>16)); tft_Write_16((uint16_t)(C)) @@ -666,7 +768,20 @@ // Write 8 bits to TFT #define tft_Write_8(C) GPIOB->BSRR = (0x00FF0000 | (uint8_t)(C)); WR_L; WR_STB - + + #if defined (SSD1963_DRIVER) + + // Write 18 bit color to TFT (untested) + #define tft_Write_16(C) uint8_t r = (((C) & 0xF800)>> 8); uint8_t g = (((C) & 0x07E0)>> 3); uint8_t b = (((C) & 0x001F)<< 3); \ + GPIOB->BSRR = (0x00FF0000 | (uint8_t)(r)); WR_L; WR_STB; \ + GPIOB->BSRR = (0x00FF0000 | (uint8_t)(g)); WR_L; WR_STB; \ + GPIOB->BSRR = (0x00FF0000 | (uint8_t)(b)); WR_L; WR_STB + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) uint16_t Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + // Write 16 bits to TFT #define tft_Write_16(C) GPIOB->BSRR = (0x00FF0000 | (uint8_t)(C>>8)); WR_L; WR_STB; \ GPIOB->BSRR = (0x00FF0000 | (uint8_t)(C>>0)); WR_L; WR_STB @@ -674,6 +789,7 @@ // 16 bit write with swapped bytes #define tft_Write_16S(C) GPIOB->BSRR = (0x00FF0000 | (uint8_t)(C>>0)); WR_L; WR_STB; \ GPIOB->BSRR = (0x00FF0000 | (uint8_t)(C>>8)); WR_L; WR_STB + #endif #define tft_Write_32(C) tft_Write_16((uint16_t)((C)>>16)); tft_Write_16((uint16_t)(C)) @@ -766,6 +882,46 @@ D7_PIN_PORT->BSRR = D7_BSR_MASK(C); \ WR_STB + #if defined (SSD1963_DRIVER) + + // Write 18 bit color to TFT (untested) + #define tft_Write_16(C) uint8_t r = (((C) & 0xF800)>> 8); uint8_t g = (((C) & 0x07E0)>> 3); uint8_t b = (((C) & 0x001F)<< 3); \ + D0_PIN_PORT->BSRR = D8_BSR_MASK(r); \ + D1_PIN_PORT->BSRR = D9_BSR_MASK(r); \ + D2_PIN_PORT->BSRR = D10_BSR_MASK(r); \ + D3_PIN_PORT->BSRR = D11_BSR_MASK(r); \ + WR_L; \ + D4_PIN_PORT->BSRR = D12_BSR_MASK(r); \ + D5_PIN_PORT->BSRR = D13_BSR_MASK(r); \ + D6_PIN_PORT->BSRR = D14_BSR_MASK(r); \ + D7_PIN_PORT->BSRR = D15_BSR_MASK(r); \ + WR_STB;\ + D0_PIN_PORT->BSRR = D8_BSR_MASK(g); \ + D1_PIN_PORT->BSRR = D9_BSR_MASK(g); \ + D2_PIN_PORT->BSRR = D10_BSR_MASK(g); \ + D3_PIN_PORT->BSRR = D11_BSR_MASK(g); \ + WR_L; \ + D4_PIN_PORT->BSRR = D12_BSR_MASK(g); \ + D5_PIN_PORT->BSRR = D13_BSR_MASK(g); \ + D6_PIN_PORT->BSRR = D14_BSR_MASK(g); \ + D7_PIN_PORT->BSRR = D15_BSR_MASK(g); \ + WR_STB;\ + D0_PIN_PORT->BSRR = D0_BSR_MASK(b); \ + D1_PIN_PORT->BSRR = D1_BSR_MASK(b); \ + D2_PIN_PORT->BSRR = D2_BSR_MASK(b); \ + D3_PIN_PORT->BSRR = D3_BSR_MASK(b); \ + WR_L; \ + D4_PIN_PORT->BSRR = D4_BSR_MASK(b); \ + D5_PIN_PORT->BSRR = D5_BSR_MASK(b); \ + D6_PIN_PORT->BSRR = D6_BSR_MASK(b); \ + D7_PIN_PORT->BSRR = D7_BSR_MASK(b); \ + WR_STB + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) uint16_t Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + // Write 16 bits to TFT #define tft_Write_16(C) D0_PIN_PORT->BSRR = D8_BSR_MASK(C); \ D1_PIN_PORT->BSRR = D9_BSR_MASK(C); \ @@ -809,6 +965,7 @@ D6_PIN_PORT->BSRR = D14_BSR_MASK(C); \ D7_PIN_PORT->BSRR = D15_BSR_MASK(C); \ WR_STB + #endif #define tft_Write_32(C) tft_Write_16((uint16_t)((C)>>16)); tft_Write_16((uint16_t)(C)) diff --git a/README.md b/README.md index 48ffa66..f1f32cd 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,24 @@ -# Tips -If you load a new copy of TFT_eSPI then it will over-write your setups if they are kept within the TFT_eSPI folder. One way around this is to create a new folder in your Arduino library folder called "TFT_eSPI_Setups". You then place your custom setup.h files in there. After an upgrade simply edit the User_Setup_Select.h file to point to your custom setup file e.g.: -``` -#include <../TFT_eSPI_Setups/my_custom_setup.h> -``` -You must make sure only one setup file is called. In the the custom setup file I add the file path as a commented out first line that can be cut and pasted back into the upgraded User_Setup_Select.h file. The ../ at the start of the path means go up one directory level. Clearly you could use different file paths or directory names as long as it does not clash with another library or folder name. - -You can take this one step further and have your own setup select file and then you only need to replace the Setup.h line reference in User_Setup_Select.h to, for example: -``` -#include <../TFT_eSPI_Setups/my_setup_select.h> -``` -To select a new setup you then edit your own my_setup_select.h file (which will not get over-written during an upgrade). +# Sprite class change +The Sprite class has been updated to remove an inconsistency for the setSwapBytes() function. Although all the examples are unchanged, user sketches may be affected. If the colors of the sprite change when loading this new version 2.2.16 then it may be necessary to change the swap bytes setting, e.g. for a sprite instance "spr" use either: spr.setSwapBytes(true) or spr.setSwapBytes(false) to correct the colour. # News -1. The library now supports SPI DMA transfers for both ESP32 and STM32 processors. The DMA Test examples now work on the ESP32 for SPI displays (excluding RPi type and ILI9488). +1. A companion library [U8g2_for_TFT_eSPI](https://github.com/Bodmer/U8g2_for_TFT_eSPI) has been created to allow U8g2 library fonts to be used with TFT_eSPI. -2. A new option has been added for STM32 processors to optimise performance where Port A (or B) pins 0-7 are used for the 8 bit parallel interface data pins 0-7 to the TFT. This gives a dramatic 8 times better rendering performance for the lower clock rate STM32 processors such as the STM32F103 "Blue Pill" or STM411 "Black Pill" since no time consuming data bit manipulation is required. See setup file "User_Setups/Setup35_ILI9341_STM32_Port_Bus.h". +2. The library now supports SPI DMA transfers for both ESP32 and STM32 processors. The DMA Test examples now work on the ESP32 for SPI displays (excluding RPi type and ILI9488). -3. A new "Animated_dial" example has been added to show how dials can be created using a rotated Sprite for the needle. To run this example the TFT must support reading from the screen RAM. The dial rim and scale is a jpeg image, created using a paint program. +3. A new option has been added for STM32 processors to optimise performance where Port A (or B) pins 0-7 are used for the 8 bit parallel interface data pins 0-7 to the TFT. This gives a dramatic 8 times better rendering performance for the lower clock rate STM32 processors such as the STM32F103 "Blue Pill" or STM411 "Black Pill" since no time consuming data bit manipulation is required. See setup file "User_Setups/Setup35_ILI9341_STM32_Port_Bus.h". + +4. A new "Animated_dial" example has been added to show how dials can be created using a rotated Sprite for the needle. To run this example the TFT must support reading from the screen RAM. The dial rim and scale is a jpeg image, created using a paint program. ![Animated_dial](https://i.imgur.com/S736Rg6.png) -4. Anti-aliased (smooth) fonts can now be stored as arrays in FLASH (program) memory. This means that processors such as STM32 that do not have SPIFFS support can use the fonts. The processor must have sufficient FLASH memory to store the fonts used. +5. Anti-aliased (smooth) fonts can now be stored as arrays in FLASH (program) memory. This means that processors such as STM32 that do not have SPIFFS support can use the fonts. The processor must have sufficient FLASH memory to store the fonts used. -5. The Sprite class now supports 4 bits per pixel with a 16 color palette. Three new examples have been added. +6. The Sprite class now supports 4 bits per pixel with a 16 color palette. Three new examples have been added. -6. The library has been upgraded to support STM32 processors when used with SPI or 8 bit parallel displays. DMA capability for SPI displays has been added for STM32F103 (e.g. "Blue Pill") and STM32F2xx/4xx/7xx (e.g. 32/64/144 Nucleo boards). New DMA demo examples have been added (for STM32 only). - -7. The ST7796 display controller has been added. The ST7796 RPi MHS-4.0 inch Display-B type display is supported (this is fast for a SPI display as an ESP32 can clock it at 80MHz (ESP8266 at 40MHz)), see setups 27 and 28. - -8. A callback function has been added, this allows antialiased fonts to be rendered over colour gradients or images. Two new examples have been added to illustrate this new capability: - - "Smooth_font_reading_TFT" - - "Smooth_font_gradient" - - ![AA_gradien](https://i.imgur.com/YMBcPHp.png) +7. The library has been upgraded to support STM32 processors when used with SPI or 8 bit parallel displays. DMA capability for SPI displays has been added for STM32F103 (e.g. "Blue Pill") and STM32F2xx/4xx/7xx (e.g. 32/64/144 Nucleo boards). New DMA demo examples have been added (for STM32 only). +8. The ST7796 display controller has been added. The ST7796 RPi MHS-4.0 inch Display-B type display is supported (this is fast for a SPI display as an ESP32 can clock it at 80MHz (ESP8266 at 40MHz)), see setups 27 and 28. # TFT_eSPI @@ -126,6 +109,19 @@ IO32 wired to IO36 If the display board is fitted with a resistance based touch screen then this can be used by performing the modifications described here and the fork of the Adafruit library: https://github.com/s60sc/Adafruit_TouchScreen +# Tips +If you load a new copy of TFT_eSPI then it will over-write your setups if they are kept within the TFT_eSPI folder. One way around this is to create a new folder in your Arduino library folder called "TFT_eSPI_Setups". You then place your custom setup.h files in there. After an upgrade simply edit the User_Setup_Select.h file to point to your custom setup file e.g.: +``` +#include <../TFT_eSPI_Setups/my_custom_setup.h> +``` +You must make sure only one setup file is called. In the the custom setup file I add the file path as a commented out first line that can be cut and pasted back into the upgraded User_Setup_Select.h file. The ../ at the start of the path means go up one directory level. Clearly you could use different file paths or directory names as long as it does not clash with another library or folder name. + +You can take this one step further and have your own setup select file and then you only need to replace the Setup.h line reference in User_Setup_Select.h to, for example: +``` +#include <../TFT_eSPI_Setups/my_setup_select.h> +``` +To select a new setup you then edit your own my_setup_select.h file (which will not get over-written during an upgrade). + # ePaper displays The library was intended to support only TFT displays but using a Sprite as a 1 bit per pixel screen buffer permits support for the Waveshare 2 and 3 colour SPI ePaper displays. This addition to the library is experimental and only one example is provided. Further examples will be added. diff --git a/TFT_Drivers/SSD1963_Defines.h b/TFT_Drivers/SSD1963_Defines.h new file mode 100644 index 0000000..1c3e913 --- /dev/null +++ b/TFT_Drivers/SSD1963_Defines.h @@ -0,0 +1,56 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#if defined (SSD1963_480_DRIVER) + #define TFT_WIDTH 272 + #define TFT_HEIGHT 480 +#elif defined (SSD1963_800_DRIVER) + #define TFT_WIDTH 480 + #define TFT_HEIGHT 800 +#elif defined (SSD1963_800ALT_DRIVER) + #define TFT_WIDTH 480 + #define TFT_HEIGHT 800 +#elif defined (SSD1963_800BD_DRIVER) + #define TFT_WIDTH 480 + #define TFT_HEIGHT 800 +#endif + +//Set driver type common to all initialisation options +#ifndef SSD1963_DRIVER + #define SSD1963_DRIVER +#endif + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 // Not used unless commandlist invoked + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#ifdef TFT_RGB_ORDER + #if (TFT_RGB_ORDER == 1) + #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB + #else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR + #endif +#else + #define TFT_MAD_COLOR_ORDER TFT_MAD_BGR +#endif + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 diff --git a/TFT_Drivers/SSD1963_Init.h b/TFT_Drivers/SSD1963_Init.h new file mode 100644 index 0000000..f785961 --- /dev/null +++ b/TFT_Drivers/SSD1963_Init.h @@ -0,0 +1,398 @@ +#if defined (SSD1963_480_DRIVER) + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x23); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x54); + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x01); + writedata(0x1F); + writedata(0xFF); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x20); + writedata(0x00); + writedata(0x01); //Set HDP 479 + writedata(0xDF); + writedata(0x01); //Set VDP 271 + writedata(0x0F); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x02); //Set HT 531 + writedata(0x13); + writedata(0x00); //Set HPS 8 + writedata(0x08); + writedata(0x2B); //Set HPW 43 + writedata(0x00); //Set LPS 2 + writedata(0x02); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x01); //Set VT 288 + writedata(0x20); + writedata(0x00); //Set VPS 4 + writedata(0x04); + writedata(0x0c); //Set VPW 12 + writedata(0x00); //Set FPS 2 + writedata(0x02); + + writecommand(0xBA); + writedata(0x0F); //GPIO[3:0] out 1 + + writecommand(0xB8); + writedata(0x07); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); + + writecommand(0xF0); //pixel data interface + writedata(0x00); //8 bit bus + + delay(1); + + writecommand(0xB8); + writedata(0x0f); //GPIO is controlled by host GPIO[3:0]=output GPIO[0]=1 LCD ON GPIO[0]=1 LCD OFF + writedata(0x01); //GPIO0 normal + + writecommand(0xBA); + writedata(0x01); //GPIO[0] out 1 --- LCD display on/off control PIN + + writecommand(0x2A); + writedata(0); + writedata(0); + writedata((271 & 0xFF00)>>8); + writedata(271 & 0xFF); + + writecommand(0x2B); + writedata(0); + writedata(0); + writedata((479 & 0xFF00)>>8); + writedata(479 & 0xFF); + + writecommand(0x2C); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0xf0); + writedata(0x01); + writedata(0xf0); + writedata(0x00); + writedata(0x00); + + writecommand(0xd0); + writedata(0x0d); + + writecommand(0x2C); + +#elif defined (SSD1963_800_DRIVER) + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x1E); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x54); + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x03); + writedata(0xFF); + writedata(0xFF); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x20); + writedata(0x00); + writedata(0x03); //Set HDP 799 + writedata(0x1F); + writedata(0x01); //Set VDP 479 + writedata(0xDF); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x03); //Set HT 928 + writedata(0xA0); + writedata(0x00); //Set HPS 46 + writedata(0x2E); + writedata(0x30); //Set HPW 48 + writedata(0x00); //Set LPS 15 + writedata(0x0F); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x02); //Set VT 525 + writedata(0x0D); + writedata(0x00); //Set VPS 16 + writedata(0x10); + writedata(0x10); //Set VPW 16 + writedata(0x00); //Set FPS 8 + writedata(0x08); + + writecommand(0xBA); + writedata(0x0F); //GPIO[3:0] out 1 + + writecommand(0xB8); + writedata(0x07); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); + + writecommand(0xF0); //pixel data interface + writedata(0x00); //8 bit bus + + delay(1); + + writecommand(0xB8); + writedata(0x0f); //GPIO is controlled by host GPIO[3:0]=output GPIO[0]=1 LCD ON GPIO[0]=1 LCD OFF + writedata(0x01); //GPIO0 normal + + writecommand(0xBA); + writedata(0x01); //GPIO[0] out 1 --- LCD display on/off control PIN + + writecommand(0x2A); + writedata(0); + writedata(0); + writedata((479 & 0xFF00)>>8); + writedata(479 & 0xFF); + + writecommand(0x2B); + writedata(0); + writedata(0); + writedata((799 & 0xFF00)>>8); + writedata(799 & 0xFF); + + writecommand(0x2C); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0xf0); + writedata(0x01); + writedata(0xf0); + writedata(0x00); + writedata(0x00); + + writecommand(0xd0); + writedata(0x0d); + + writecommand(0x2C); + +#elif defined (SSD1963_800ALT_DRIVER) + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x23); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x04); + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x04); + writedata(0x93); + writedata(0xE0); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x00); // 0x24 + writedata(0x00); + writedata(0x03); //Set HDP 799 + writedata(0x1F); + writedata(0x01); //Set VDP 479 + writedata(0xDF); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x03); //Set HT 928 + writedata(0xA0); + writedata(0x00); //Set HPS 46 + writedata(0x2E); + writedata(0x30); //Set HPW 48 + writedata(0x00); //Set LPS 15 + writedata(0x0F); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x02); //Set VT 525 + writedata(0x0D); + writedata(0x00); //Set VPS 16 + writedata(0x10); + writedata(0x10); //Set VPW 16 + writedata(0x00); //Set FPS 8 + writedata(0x08); + + writecommand(0xBA); + writedata(0x05); //GPIO[3:0] out 1 + + writecommand(0xB8); + writedata(0x07); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); // -- Set rotation + + writecommand(0xF0); //pixel data interface + writedata(0x00); //8 bit bus + + delay(10); + + writecommand(0x2A); + writedata(0); + writedata(0); + writedata((479 & 0xFF00)>>8); + writedata(479 & 0xFF); + + writecommand(0x2B); + writedata(0); + writedata(0); + writedata((799 & 0xFF00)>>8); + writedata(799 & 0xFF); + + writecommand(0x2C); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0xF0); + writedata(0x01); + writedata(0xF0); + writedata(0x00); + writedata(0x00); + + writecommand(0xD0); + writedata(0x0D); + + writecommand(0x2C); + +#elif defined (SSD1963_800BD_DRIVER) // Copied from Buy Display code + + writecommand(0xE2); //PLL multiplier, set PLL clock to 120M + writedata(0x23); //N=0x36 for 6.5M, 0x23 for 10M crystal + writedata(0x02); + writedata(0x54); + + writecommand(0xE0); // PLL enable + writedata(0x01); + + delay(10); + + writecommand(0xE0); + writedata(0x03); + + delay(10); + + writecommand(0x01); // software reset + + delay(100); + + writecommand(0xE6); //PLL setting for PCLK, depends on resolution + writedata(0x03); + writedata(0x33); + writedata(0x33); + + writecommand(0xB0); //LCD SPECIFICATION + writedata(0x20); + writedata(0x00); + writedata(799 >> 8); //Set HDP 799 + writedata(799 & 0xFF); + writedata(479 >> 8); //Set VDP 479 + writedata(479 & 0xFF); + writedata(0x00); + + writecommand(0xB4); //HSYNC + writedata(0x04); //Set HT + writedata(0x1F); + writedata(0x00); //Set HPS + writedata(0xD2); + writedata(0x00); //Set HPW + writedata(0x00); //Set LPS + writedata(0x00); + writedata(0x00); + + writecommand(0xB6); //VSYNC + writedata(0x02); //Set VT + writedata(0x0C); + writedata(0x00); //Set VPS + writedata(0x22); + writedata(0x00); //Set VPW + writedata(0x00); //Set FPS + writedata(0x00); + + writecommand(0xB8); + writedata(0x0F); //GPIO3=input, GPIO[2:0]=output + writedata(0x01); //GPIO0 normal + + writecommand(0xBA); + writedata(0x01); //GPIO[0] out 1 --- LCD display on/off control PIN + + writecommand(0x36); //rotation + writedata(0x21 | TFT_MAD_COLOR_ORDER); //set to rotate + + //writecommand(0x003A); //Set the current pixel format for RGB image data + //writedata(0x0050); //16-bit/pixel + + writecommand(0xF0); //pixel data interface + writedata(0x00); //000 = 8 bit bus, 011 = 16 bit, 110 = 9 bit + + writecommand(0xBC); + writedata(0x40); //contrast value + writedata(0x80); //brightness value + writedata(0x40); //saturation value + writedata(0x01); //Post Processor Enable + + + delay(10); + + writecommand(0x29); //display on + + writecommand(0xBE); //set PWM for B/L + writedata(0x06); + writedata(0x80); + writedata(0x01); + writedata(0xF0); + writedata(0x00); + writedata(0x00); + + writecommand(0xD0); + writedata(0x0D); + +#endif \ No newline at end of file diff --git a/TFT_Drivers/SSD1963_Rotation.h b/TFT_Drivers/SSD1963_Rotation.h new file mode 100644 index 0000000..6e61171 --- /dev/null +++ b/TFT_Drivers/SSD1963_Rotation.h @@ -0,0 +1,29 @@ + +// This is the command sequence that rotates the SSD1963 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + writedata(0x21 | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 1: + writedata(0x00 | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + case 2: + writedata(0x22 | TFT_MAD_COLOR_ORDER); + _width = _init_width; + _height = _init_height; + break; + case 3: + writedata(0x03 | TFT_MAD_COLOR_ORDER); + _width = _init_height; + _height = _init_width; + break; + + } diff --git a/TFT_Drivers/ST7789_2_Rotation.h b/TFT_Drivers/ST7789_2_Rotation.h index d25cc0c..44a363e 100644 --- a/TFT_Drivers/ST7789_2_Rotation.h +++ b/TFT_Drivers/ST7789_2_Rotation.h @@ -5,8 +5,16 @@ switch (rotation) { case 0: // Portrait #ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 0; + if (_init_width == 135) + { + colstart = 52; + rowstart = 40; + } + else + { + colstart = 0; + rowstart = 0; + } #endif writedata(TFT_MAD_COLOR_ORDER); @@ -16,8 +24,16 @@ case 1: // Landscape (Portrait + 90) #ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 0; + if (_init_width == 135) + { + colstart = 40; + rowstart = 53; + } + else + { + colstart = 0; + rowstart = 0; + } #endif writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_COLOR_ORDER); @@ -27,8 +43,16 @@ case 2: // Inverter portrait #ifdef CGRAM_OFFSET - colstart = 0; - rowstart = 80; + if (_init_width == 135) + { + colstart = 53; + rowstart = 40; + } + else + { + colstart = 0; + rowstart = 80; + } #endif writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); @@ -37,8 +61,16 @@ break; case 3: // Inverted landscape #ifdef CGRAM_OFFSET - colstart = 80; - rowstart = 0; + if (_init_width == 135) + { + colstart = 40; + rowstart = 52; + } + else + { + colstart = 80; + rowstart = 0; + } #endif writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_COLOR_ORDER); diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f59c08c..59d6276 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -105,6 +105,11 @@ inline void TFT_eSPI::end_tft_read(void){ if(!inTransaction) {CS_H;} #endif SET_BUS_WRITE_MODE; + +// The ST7796 appears to need a 4ms delay after a CGRAM read, otherwise subsequent writes will fail! +#ifdef ST7796_DRIVER + delay(4); +#endif } /*************************************************************************************** @@ -335,8 +340,6 @@ void TFT_eSPI::init(uint8_t tc) } // end of: if just _booted // Toggle RST low to reset - begin_tft_write(); - #ifdef TFT_RST if (TFT_RST >= 0) { digitalWrite(TFT_RST, HIGH); @@ -350,8 +353,6 @@ void TFT_eSPI::init(uint8_t tc) writecommand(TFT_SWRST); // Software reset #endif - end_tft_write(); - delay(150); // Wait for reset to complete begin_tft_write(); @@ -399,6 +400,9 @@ void TFT_eSPI::init(uint8_t tc) #elif defined (ST7789_2_DRIVER) #include "TFT_Drivers/ST7789_2_Init.h" +#elif defined (SSD1963_DRIVER) + #include "TFT_Drivers/SSD1963_Init.h" + #endif #ifdef TFT_INVERSION_ON @@ -475,6 +479,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (ST7789_2_DRIVER) #include "TFT_Drivers/ST7789_2_Rotation.h" +#elif defined (SSD1963_DRIVER) + #include "TFT_Drivers/SSD1963_Rotation.h" + #endif delayMicroseconds(10); @@ -703,6 +710,8 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) bool wasInTransaction = inTransaction; if (inTransaction) { inTransaction= false; end_tft_write();} + uint16_t color = 0; + begin_tft_read(); readAddrWindow(x0, y0, 1, 1); // Sets CS low @@ -716,11 +725,18 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) //#if !defined (ILI9488_DRIVER) - // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte - // as the TFT stores colours as 18 bits - uint8_t r = tft_Read_8(); - uint8_t g = tft_Read_8(); - uint8_t b = tft_Read_8(); + #if defined (ST7796_DRIVER) + // Read the 2 bytes + color = ((tft_Read_8()) << 8) | (tft_Read_8()); + #else + // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte + // as the TFT stores colours as 18 bits + uint8_t r = tft_Read_8(); + uint8_t g = tft_Read_8(); + uint8_t b = tft_Read_8(); + color = color565(r, g, b); + #endif + /* #else @@ -729,6 +745,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) uint8_t r = (tft_Read_8()&0x7E)<<1; uint8_t g = (tft_Read_8()&0x7E)<<1; uint8_t b = (tft_Read_8()&0x7E)<<1; + color = color565(r, g, b); #endif */ @@ -743,7 +760,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) // Reinstate the transaction if one was in progress if(wasInTransaction) { begin_tft_write(); inTransaction = true; } - return color565(r, g, b); + return color; #endif } @@ -810,6 +827,8 @@ void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *da #else // SPI interface + uint16_t color = 0; + begin_tft_read(); readAddrWindow(x, y, w, h); @@ -825,29 +844,35 @@ void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *da uint32_t len = w * h; while (len--) { - #if !defined (ILI9488_DRIVER) - - // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte - // as the TFT stores colours as 18 bits - uint8_t r = tft_Read_8(); - uint8_t g = tft_Read_8(); - uint8_t b = tft_Read_8(); + #if !defined (ILI9488_DRIVER) + #if defined (ST7796_DRIVER) + // Read the 2 bytes + color = ((tft_Read_8()) << 8) | (tft_Read_8()); #else + // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte + // as the TFT stores colours as 18 bits + uint8_t r = tft_Read_8(); + uint8_t g = tft_Read_8(); + uint8_t b = tft_Read_8(); + color = color565(r, g, b); + #endif + + #else // The 6 colour bits are in MS 6 bits of each byte but we do not include the extra clock pulse // so we use a trick and mask the middle 6 bits of the byte, then only shift 1 place left uint8_t r = (tft_Read_8()&0x7E)<<1; uint8_t g = (tft_Read_8()&0x7E)<<1; uint8_t b = (tft_Read_8()&0x7E)<<1; - - #endif + color = color565(r, g, b); + #endif // Swapped colour byte order for compatibility with pushRect() - *data++ = (r & 0xF8) | (g & 0xE0) >> 5 | (b & 0xF8) << 5 | (g & 0x1C) << 11; + *data++ = color << 8 | color >> 8; } - CS_H; + //CS_H; #ifdef TFT_SDA_READ end_SDA_Read(); @@ -860,14 +885,14 @@ void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *da /*************************************************************************************** -** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") +** Function name: push rectangle ** Description: push 565 pixel colours into a defined area ***************************************************************************************/ void TFT_eSPI::pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) { - // Function deprecated, remains for backwards compatibility - // New pushImage() is better as it will crop partly off-screen image blocks + bool swap = _swapBytes; _swapBytes = false; pushImage(x, y, w, h, data); + _swapBytes = swap; } @@ -946,6 +971,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *d uint16_t lineBuf[dw]; // Use buffer to minimise setWindow call count + // The little endian transp color must be byte swapped if the image is big endian if (!_swapBytes) transp = transp >> 8 | transp << 8; while (dh--) @@ -1062,6 +1088,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 uint16_t lineBuf[dw]; + // The little endian transp color must be byte swapped if the image is big endian if (!_swapBytes) transp = transp >> 8 | transp << 8; while (dh--) { @@ -1247,7 +1274,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da } pushPixels(lineBuf, dw); - dy++; } _swapBytes = swap; // Restore old value @@ -2614,8 +2640,12 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) { //begin_tft_write(); // Must be called before setWindow - addr_col = 0xFFFF; +#if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { swap_coord(x0, y0); swap_coord(x1, y1); } +#endif + addr_row = 0xFFFF; + addr_col = 0xFFFF; #ifdef CGRAM_OFFSET x0+=colstart; @@ -2624,16 +2654,11 @@ void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) y1+=rowstart; #endif - // Column addr set DC_C; tft_Write_8(TFT_CASET); DC_D; tft_Write_32C(x0, x1); - - // Row addr set DC_C; tft_Write_8(TFT_PASET); DC_D; tft_Write_32C(y0, y1); - DC_C; tft_Write_8(TFT_RAMWR); - DC_D; //end_tft_write(); // Must be called after setWindow @@ -2661,6 +2686,10 @@ void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) ye += rowstart; #endif +#if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { swap_coord(xs, ys); swap_coord(xe, ye); } +#endif + // Column addr set DC_C; tft_Write_8(TFT_CASET); DC_D; tft_Write_32C(xs, xe); @@ -2692,21 +2721,33 @@ void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) y+=rowstart; #endif +#if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { swap_coord(x, y); } +#endif + begin_tft_write(); +#ifdef MULTI_TFT_SUPPORT + // No optimisation + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32D(x); + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32D(y); +#else // No need to send x if it has not changed (speeds things up) - if (addr_col != x) { + if (addr_col != (x<<16 | x)) { DC_C; tft_Write_8(TFT_CASET); DC_D; tft_Write_32D(x); - addr_col = x; + addr_col = (x<<16 | x); } // No need to send y if it has not changed (speeds things up) - if (addr_row != y) { + if (addr_row != (y<<16 | y)) { DC_C; tft_Write_8(TFT_PASET); DC_D; tft_Write_32D(y); - addr_row = y; + addr_row = (y<<16 | y); } +#endif DC_C; tft_Write_8(TFT_RAMWR); DC_D; tft_Write_16(color); @@ -3529,8 +3570,7 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) inTransaction = true; w *= height; // Now w is total number of pixels in the character - if ((textsize != 1) || (textcolor == textbgcolor)) { - if (textcolor != textbgcolor) fillRect(x, pY, width * textsize, textsize * height, textbgcolor); + if (textcolor == textbgcolor) { int32_t px = 0, py = pY; // To hold character block start and end column and row values int32_t pc = 0; // Pixel count uint8_t np = textsize * textsize; // Number of pixels in a drawn pixel @@ -3576,21 +3616,60 @@ int16_t TFT_eSPI::drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font) } } } - else { // Text colour != background && textsize = 1 - // so use faster drawing of characters and background using block write - setWindow(x, y, x + width - 1, y + height - 1); + else { + // Text colour != background and textsize = 1 and character is within screen area + // so use faster drawing of characters and background using block write + if ((textsize == 1) && (x >= 0) && (x + width <= _width) && (y >= 0) && (y + height <= _height)) + { + setWindow(x, y, x + width - 1, y + height - 1); - // Maximum font size is equivalent to 180x180 pixels in area - while (w > 0) { - line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here - if (line & 0x80) { - line &= 0x7F; - line++; w -= line; - pushBlock(textcolor,line); + // Maximum font size is equivalent to 180x180 pixels in area + while (w > 0) { + line = pgm_read_byte((uint8_t *)flash_address++); // 8 bytes smaller when incrementing here + if (line & 0x80) { + line &= 0x7F; + line++; w -= line; + pushBlock(textcolor,line); + } + else { + line++; w -= line; + pushBlock(textbgcolor,line); + } } - else { - line++; w -= line; - pushBlock(textbgcolor,line); + } + else + { + int32_t px = 0, py = 0; // To hold character pixel coords + int32_t tx = 0, ty = 0; // To hold character TFT pixel coords + int32_t pc = 0; // Pixel count + int32_t pl = 0; // Pixel line length + uint16_t pcol = 0; // Pixel color + + while (pc < w) { + line = pgm_read_byte((uint8_t *)flash_address); + flash_address++; + if (line & 0x80) { pcol = textcolor; line &= 0x7F; } + else pcol = textbgcolor; + line++; + px = pc % width; + tx = x + textsize * px; + py = pc / width; + ty = y + textsize * py; + + pl = 0; + pc += line; + while (line--) { + pl++; + if ((px+pl) >= width) { + fillRect(tx, ty, pl * textsize, textsize, pcol); + pl = 0; + px = 0; + tx = x; + py ++; + ty += textsize; + } + } + if (pl) fillRect(tx, ty, pl * textsize, textsize, pcol); } } } @@ -3726,11 +3805,11 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 padding += 2; break; } - // Check coordinates are OK, adjust if not +/* // Check coordinates are OK, adjust if not if (poX < 0) poX = 0; if (poX+cwidth > width()) poX = width() - cwidth; if (poY < 0) poY = 0; - if (poY+cheight-baseline> height()) poY = height() - cheight; + if (poY+cheight-baseline> height()) poY = height() - cheight; //*/ } @@ -3808,9 +3887,8 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 break; case 2: fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); - padXc = (padX-cwidth)>>1; - if (padXc>poX) padXc = poX; - fillRect(poX - padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); + padXc = poX - ((padX-cwidth)>>1); + fillRect(padXc,poY,(padX-cwidth)>>1,cheight, textbgcolor); break; case 3: if (padXc>padX) padXc = padX; @@ -3839,7 +3917,6 @@ int16_t TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8 case 2: drawRect(padXc,poY,(padX-sumX)>>1, cheight, TFT_WHITE); padXc = (padX-sumX)>>1; - if (padXc>poX) padXc = poX; drawRect(poX - padXc,poY,(padX-sumX)>>1,cheight, TFT_WHITE); break; case 3: @@ -4204,6 +4281,14 @@ void TFT_eSPI::getSetup(setup_t &tft_settings) tft_settings.pin_tft_d7 = -1; #endif +#if defined (TFT_BL) + tft_settings.pin_tft_led = TFT_BL; +#endif + +#if defined (TFT_BACKLIGHT_ON) + tft_settings.pin_tft_led_on = TFT_BACKLIGHT_ON; +#endif + #if defined (TOUCH_CS) tft_settings.pin_tch_cs = TOUCH_CS; tft_settings.tch_spi_freq = SPI_TOUCH_FREQUENCY/100000; diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 77b05bf..07ceabe 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -16,7 +16,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "2.2.4" +#define TFT_ESPI_VERSION "2.2.23" /*************************************************************************************** ** Section 1: Load required header files @@ -335,6 +335,9 @@ int8_t pin_tft_d5; int8_t pin_tft_d6; int8_t pin_tft_d7; +int8_t pin_tft_led; +int8_t pin_tft_led_on; + int8_t pin_tch_cs; // Touch chip select pin int16_t tft_spi_freq;// TFT write SPI frequency @@ -450,7 +453,7 @@ class TFT_eSPI : public Print { // The next functions can be used as a pair to copy screen blocks (or horizontal/vertical lines) to another location // Read a block of pixels to a data buffer, buffer is 16 bit and the size must be at least w * h void readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); - // Write a block of pixels to the screen - this is a deprecated alternative to pushImage() + // Write a block of pixels to the screen which have been read by readRect() void pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); // These are used to render images or sprites stored in RAM arrays (used by Sprite class for 16bpp Sprites) @@ -575,7 +578,7 @@ class TFT_eSPI : public Print { uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0); - // DMA support functions - these are currently just for SPI writes whe using the STM32 processors + // DMA support functions - these are currently just for SPI writes when using the ESP32 or STM32 processors // Bear in mind DMA will only be of benefit in particular circumstances and can be tricky // to manage by noobs. The functions have however been designed to be noob friendly and // avoid a few DMA behaviour "gotchas". @@ -590,7 +593,7 @@ class TFT_eSPI : public Print { // processor leaves a function or its content being changed while the DMA engine is reading it. // // The compiler MAY change the implied scope of a buffer which has been set aside by creating - // and an array. For example a buffer defined before a "for-next" loop may get de-allocated when + // an array. For example a buffer defined before a "for-next" loop may get de-allocated when // the loop ends. To avoid this use, for example, malloc() and free() to take control of when // the buffer space is available and ensure it is not released until DMA is complete. // @@ -605,7 +608,7 @@ class TFT_eSPI : public Print { // Push an image to the TFT using DMA, buffer is optional and grabs (double buffers) a copy of the image // Use the buffer if the image data will get over-written or destroyed while DMA is in progress - // If swapping colour bytes is defined, and the double buffer option is NOT used then the bytes + // If swapping colour bytes is defined, and the double buffer option is NOT used, then the bytes // in the original data image will be swapped by the function before DMA is initiated. // The function will wait for the last DMA to complete if it is called while a previous DMA is still // in progress, this simplifies the sketch and helps avoid "gotchas". @@ -615,9 +618,8 @@ class TFT_eSPI : public Print { void pushPixelsDMA(uint16_t* image, uint32_t len); // Check if the DMA is complete - use while(tft.dmaBusy); for a blocking wait - // Note: for ESP32 the dmaBusy() function is blocking at the moment - to be updated - bool dmaBusy(void); - void dmaWait(void); + bool dmaBusy(void); // returns true if DMA is still in progress + void dmaWait(void); // wait until DMA is complete bool DMA_Enabled = false; // Flag for DMA enabled state uint8_t spiBusyCheck = 0; // Number of ESP32 transfer buffers to check diff --git a/Tools/Images/README.md b/Tools/Images/README.md new file mode 100644 index 0000000..02405a9 --- /dev/null +++ b/Tools/Images/README.md @@ -0,0 +1,26 @@ +## bmp2array4bit + +bmp2array4bit.py reads a bmp file, and creates C (or C++) code that contains two arrays for adding images to four-bit sprites. See [Sprite_image_4bit](../../examples/Sprite/Sprite_image_4bit) for an example. + +It is loosely based on Spark Fun's bmp2array script, https://github.com/sparkfun/BMPtoArray/blob/master/bmp2array.py. The bmp file format is documented in https://en.wikipedia.org/wiki/BMP_file_format. + +You'll need python 3.6 (the original uses Python 2.7) + +`usage: python bmp2array4bit.py [-v] star.bmp [-o myfile.c]` + +Create the bmp file in Gimp (www.gimp.org) from any image as follows: + +* Remove the alpha channel (if it has one) + Layer -> Transparency -> Remove Alpha Channel +* Set the mode to indexed. + Image -> Mode -> Indexed... +* Select Generate optimum palette with 16 colors (max) +* Export the file with a .bmp extension. Do **NOT** select options: + * Run-Length Encoded + * Compatibility Options: "Do not write color space information" + * There are no Advanced Options available with these settings + +(There are other tools that will produce bmp files, and these should work provided you don't use run-length encoding or other advanced features). + +The first array produced is the palette for the image. +The second is the image itself. diff --git a/Tools/Images/bmp2array4bit.py b/Tools/Images/bmp2array4bit.py new file mode 100644 index 0000000..3b6d850 --- /dev/null +++ b/Tools/Images/bmp2array4bit.py @@ -0,0 +1,251 @@ +''' + + This script takes in a bitmap and outputs a text file that is a + byte array used in Arduino files. + + It is loosely based on Spark Fun's bmp2array script. + + You'll need python 3.6 (the original use Python 2.7) + + usage: python fourbitbmp2array.py [-v] star.bmp [-o myfile.c] + + Create the bmp file in Gimp by : + + . Remove the alpha channel (if it has one) Layer -> Transparency -> Remove Alpha Channel + . Set the mode to indexed. Image -> Mode -> Indexed... + . Select Generate optimum palette with 16 colors (max) + . Export the file with a .bmp extension. Options are: + . Run-Length Encoded: not selected + . Compatibility Options: "Do not write color space information" not selected + . There are no Advanced Options available with these settings + + + + +''' + +import sys +import struct +import math +import argparse +import os + +debug = None + +def debugOut(s): + if debug: + print(s) + +# look at arguments +parser = argparse.ArgumentParser(description="Convert bmp file to C array") +parser.add_argument("-v", "--verbose", help="debug output", action="store_true") +parser.add_argument("input", help="input file name") +parser.add_argument("-o", "--output", help="output file name") +args = parser.parse_args() + +if not os.path.exists(args.input): + parser.print_help() + print("The input file {} does not exist".format(args.input)) + sys.exit(1) + +if args.output == None: + output = os.path.basename(args.input).replace(".bmp", ".c") +else: + output = args.output + +debug = args.verbose + +try: + #Open our input file which is defined by the first commandline argument + #then dump it into a list of bytes + infile = open(args.input,"rb") #b is for binary + contents = bytearray(infile.read()) + infile.close() +except: + print("could not read input file {}".format(args.input)) + sys.exit(1) + +# first two bytes should be "BM" +upto = 2 +#Get the size of this image +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +fileSize = struct.unpack("I", bytearray(data)) + +upto += 4 +# four bytes are reserved + +upto += 4 + +debugOut("Size of file: {}".format(fileSize[0])) + +#Get the header offset amount +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +offset = struct.unpack("I", bytearray(data)) + +debugOut("Offset: {}".format(offset[0])) +upto += 4 + +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +headersize = struct.unpack("I", bytearray(data)) +headerLength = headersize[0] +startOfDefinitions = headerLength + upto +debugOut("header size: {}, up to {}, startOfDefinitions {}".format(headersize[0], upto, startOfDefinitions)) +upto += 4 + +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("width: {}".format(t[0])) +width = t[0] + +upto += 4 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("height: {}".format(t[0])) +height = t[0] + +# 26 +upto += 4 + +data = struct.pack("BB", contents[upto], contents[upto+1]) +t = struct.unpack("H", bytearray(data)) +debugOut("planes: {}".format(t[0])) + +upto = upto + 2 +data = struct.pack("BB", contents[upto], contents[upto+1]) +t = struct.unpack("H", bytearray(data)) +debugOut("bits per pixel: {}".format(t[0])) +bitsPerPixel = t[0] + +upto = upto + 2 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("biCompression: {}".format(t[0])) + +upto = upto + 4 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("biSizeImage: {}".format(t[0])) + +upto = upto + 4 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("biXPelsPerMeter: {}".format(t[0])) + +upto = upto + 4 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("biYPelsPerMeter: {}".format(t[0])) + +upto = upto + 4 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("biClrUsed: {}".format(t[0])) +colorsUsed = t + +upto = upto + 4 +data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) +t = struct.unpack("I", bytearray(data)) +debugOut("biClrImportant: {}".format(t[0])) + +upto += 4 + +debugOut("Upto: {} Number of colors used: {} definitions start at: {}".format(upto, colorsUsed[0], startOfDefinitions)) + +#Create color definition array and init the array of color values +colorIndex = [] #(colorsUsed[0]) +for i in range(colorsUsed[0]): + colorIndex.append(0) + +#Assign the colors to the array. upto = 54 +# startOfDefinitions = upto +for i in range(colorsUsed[0]): + upto = startOfDefinitions + (i * 4) + blue = contents[upto] + green = contents[upto + 1] + red = contents[upto + 2] + # ignore the alpha channel. + + # data = struct.pack("BBBB", contents[upto], contents[upto+1], contents[upto+2], contents[upto+3]) + # t = struct.unpack("I", bytearray(data)) + # colorIndex[i] = t[0] + + colorIndex[i] = (((red & 0xf8)<<8) + ((green & 0xfc)<<3)+(blue>>3)) + debugOut("color at index {0} is {1:04x}, (r,g,b,a) = ({2:02x}, {3:02x}, {4:02x}, {5:02x})".format(i, colorIndex[i], red, green, blue, contents[upto+3])) + +#debugOut(the color definitions +# for i in range(colorsUsed[0]): +# print hex(colorIndex[i]) + +# perfect, except upside down. + +#Make a string to hold the output of our script +arraySize = (len(contents) - offset[0]) +outputString = "/* This was generated using a script based on the SparkFun BMPtoArray python script" + '\n' +outputString += " See https://github.com/sparkfun/BMPtoArray for more info */" + '\n\n' +outputString += "static const uint16_t palette[" + str(colorsUsed[0]) + "] = {"; +for i in range(colorsUsed[0]): + # print hexlify(colorIndex[i]) + if i % 4 == 0: + outputString += "\n\t" + outputString += "0x{:04x}, ".format(colorIndex[i]) + +outputString = outputString[:-2] +outputString += "\n};\n\n" +outputString += "// width is " + str(width) + ", height is " + str(height) + "\n" +outputString += "static const uint8_t myGraphic[" + str(arraySize) + "] PROGMEM = {" + '\n' + +if bitsPerPixel != 4: + print("Expected 4 bits per pixel; found {}".format(bitsPerPixel)) + sys.exit(1) + +#Start converting spots to values +#Start at the offset and go to the end of the file +dropLastNumber = True #(width % 4) == 2 or (width % 4) == 1 +paddedWidth = int(math.ceil(bitsPerPixel * width / 32.0) * 4) +debugOut("array range is {} {} len(contents) is {} paddedWidth is {} width is {}".format(offset[0], fileSize[0], len(contents), paddedWidth, width)) + +r = 0 +width = int(width / 2) +#for i in range(offset[0], fileSize[0]): # close but image is upside down. Each row is correct but need to swap columns. +#for i in range(fileSize[0], offset[0], -1): + +for col in range(height-1, -1, -1): + i = 0 + for row in range(width): + colorCode1 = contents[row + col*paddedWidth + offset[0]] + + if r > 0 and r % width == 0: + i = 0 + outputString += '\n\n' + elif (i + 1) % 12 == 0 : + outputString += '\n' + i = 0 + + #debugOut("cell ({0}, {1})".format(row, col) + + r = r + 1 + i = i + 1 + outputString += "0x{:02x}, ".format(colorCode1) + + + +#Once we've reached the end of our input string, pull the last two +#characters off (the last comma and space) since we don't need +#them. Top it off with a closing bracket and a semicolon. +outputString = outputString[:-2] +outputString += "};" + +try: + #Write the output string to our output file + outfile = open(output, "w") + outfile.write(outputString) + outfile.close() +except: + print("could not write output to file {}".format(output)) + sys.exit(1) + +debugOut("{} complete".format(output)) +debugOut("Copy and paste this array into a image.h or other header file") + +if not debug: + print("Completed; the output is in {}".format(output)) diff --git a/Tools/Images/star.bmp b/Tools/Images/star.bmp new file mode 100644 index 0000000..21166da Binary files /dev/null and b/Tools/Images/star.bmp differ diff --git a/User_Setup.h b/User_Setup.h index 754215f..0fb8bf4 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -50,6 +50,9 @@ //#define R61581_DRIVER //#define RM68140_DRIVER //#define ST7796_DRIVER +//#define SSD1963_480_DRIVER // Untested +//#define SSD1963_800_DRIVER // Untested +//#define SSD1963_800ALT_DRIVER // Untested // Some displays support SPI reads via the MISO pin, other displays have a single // bi-directional SDA pin and the library will try to read this via the MOSI line. @@ -236,6 +239,31 @@ //#define TFT_D6 27 //#define TFT_D7 14 +// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ###### + +// The TFT can be connected to SPI port 1 or 2 +//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz +//#define TFT_MOSI PA7 +//#define TFT_MISO PA6 +//#define TFT_SCLK PA5 + +//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz +//#define TFT_MOSI PB15 +//#define TFT_MISO PB14 +//#define TFT_SCLK PB13 + +// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select +//#define TFT_CS D5 // Chip select control pin to TFT CS +//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select) +//#define TFT_RST D7 // Reset pin to TFT RST (or RESET) +// OR alternatively, we can use STM32 port reference names PXnn +//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5 +//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6 +//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7 + +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset + // Use an Arduino pin for initial testing as connecting to processor reset + // may not work (pulse too short at power up?) // ################################################################################## // @@ -278,8 +306,9 @@ // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +#define SPI_FREQUENCY 27000000 // #define SPI_FREQUENCY 40000000 +// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) // #define SPI_FREQUENCY 80000000 // Optional reduced SPI frequency for reading TFT diff --git a/User_Setup_Select.h b/User_Setup_Select.h index abbaa7f..a8dcdb6 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -59,7 +59,7 @@ //#include // Setup for Nucleo board //#include // Setup for Nucleo board and parallel display //#include // Setup for Nucleo board and parallel display -//#include // Setup for "Blue Pill" +//#include // Setup for "Blue/Black Pill" //#include // Setup for Nucleo board @@ -69,8 +69,10 @@ //#include // Setup file configured for ESP32 and RPi TFT with touch //#include // Setup file configured for my ST7735S 80x160 -//#include // Setup file for ESP32 and TTGO T-CameraPlus ST7789 SPI bus TFT 240x240 -//#include // Setup file for ESP32 and TTGO T-Watch ST7789 SPI bus TFT 240x240 +//#include // Setup file for ESP32 and TTGO T-CameraPlus ST7789 SPI bus TFT 240x240 +//#include // Setup file for ESP32 and TTGO T-Watch ST7789 SPI bus TFT 240x240 + +//#include // Setup file for ESP32 and SSD1963 TFT display //#include // Setup file for ESP8266 and ST7789 135 x 240 TFT @@ -154,8 +156,20 @@ #elif defined (RM68140_DRIVER) #include "TFT_Drivers/RM68140_Defines.h" #define TFT_DRIVER 0x6814 +#elif defined (SSD1963_480_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (SSD1963_800_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (SSD1963_800ALT_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 +#elif defined (SSD1963_800BD_DRIVER) + #include "TFT_Drivers/SSD1963_Defines.h" + #define TFT_DRIVER 0x1963 // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE - // XYZZY_init.h and XYZZY_rotation.h must also be added in TFT_eSPI.c + // XYZZY_init.h and XYZZY_rotation.h must also be added in TFT_eSPI.cpp #elif defined (XYZZY_DRIVER) #include "TFT_Drivers/XYZZY_Defines.h" #define TFT_DRIVER 0x0000 diff --git a/User_Setups/Setup14_ILI9341_Parallel.h b/User_Setups/Setup14_ILI9341_Parallel.h index d19a827..2fc5cee 100644 --- a/User_Setups/Setup14_ILI9341_Parallel.h +++ b/User_Setups/Setup14_ILI9341_Parallel.h @@ -1,6 +1,6 @@ // See SetupX_Template.h for all options available -#define ESP32_PARALLEL +#define TFT_PARALLEL_8_BIT #define ILI9341_DRIVER diff --git a/User_Setups/Setup29_ILI9341_STM32.h b/User_Setups/Setup29_ILI9341_STM32.h index 0a8dc82..de612a3 100644 --- a/User_Setups/Setup29_ILI9341_STM32.h +++ b/User_Setups/Setup29_ILI9341_STM32.h @@ -22,7 +22,36 @@ // MOSI and SCK do not need to be defined, connect: // - Arduino SCK to TFT SCK // - Arduino MOSI to TFT SDI(may be marked SDA or MOSI) -// Standard Arduino SPI pins are (SCK=D13, MOSI=D11) this is port pins PA5 and PA7 on Nucleo-F767ZI +// Typical Arduino SPI port 1 pins are (SCK=D13, MISO=D12, MOSI=D11) this is port pins PA5, PA6 and PA7 on Nucleo-F767ZI +// SPI port 2 pins are (SCK=D18, MISO=A7, MOSI=D17) this is port pins PB13, PC2 and PB15 on Nucleo-F767ZI + +/* +#define TFT_SPI_PORT 1 // SPI 1 maximum clock rate is 55MHz +#define TFT_MOSI PA7 +#define TFT_MISO PA6 +#define TFT_SCLK PA5 +//*/ + +/* +#define TFT_SPI_PORT 2 // SPI 2 maximum clock rate is 27MHz +#define TFT_MOSI D17 +#define TFT_MISO A7 +#define TFT_SCLK D18 +//*/ + +/* +#define TFT_SPI_PORT 2 // SPI 2 maximum clock rate is 27MHz +#define TFT_MOSI PB15 +#define TFT_MISO PC2 +#define TFT_SCLK PB13 +//*/ + +/* +#define TFT_SPI_PORT 2 // SPI 2 maximum clock rate is 27MHz +#define TFT_MOSI PB15 +#define TFT_MISO PB14 +#define TFT_SCLK PB13 +//*/ // Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select #define TFT_CS D5 // Chip select control pin to TFT CS @@ -52,7 +81,7 @@ // STM32 support for smooth fonts via program memory (FLASH) arrays #define SMOOTH_FONT - + // Nucleo-F767ZI has a ~216MHZ CPU clock, this is divided by 4, 8, 16 etc #define SPI_FREQUENCY 27000000 // 27MHz SPI clock diff --git a/User_Setups/Setup50_SSD1963_Parallel.h b/User_Setups/Setup50_SSD1963_Parallel.h new file mode 100644 index 0000000..59be2d0 --- /dev/null +++ b/User_Setups/Setup50_SSD1963_Parallel.h @@ -0,0 +1,92 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! + +// ################################################################################## +// +// Section 0. Call up the right driver file and any options for it +// +// ################################################################################## + +// Use ESP32 Parallel interface +#define TFT_PARALLEL_8_BIT + +// Only define one driver, the other ones must be commented out +//#define SSD1963_480_DRIVER // 272 x 480 display +//#define SSD1963_800_DRIVER // 480 x 800 display +//#define SSD1963_800ALT_DRIVER // Alternative 480 x 800 display +#define SSD1963_800BD_DRIVER // 480 x 800 displau sourced from https://www.buydisplay.com/7-tft-screen-touch-lcd-display-module-w-ssd1963-controller-board-mcu + +// ################################################################################## +// +// Section 1. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// ESP32 pins used +#define TFT_CS 33 // Chip select control pin +#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 +#define TFT_RST 32 // Reset pin + +#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 +#define TFT_RD 2 + +#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus +#define TFT_D1 13 // so a single register write sets/clears all bits +#define TFT_D2 26 +#define TFT_D3 25 +#define TFT_D4 17 +#define TFT_D5 16 +#define TFT_D6 27 +#define TFT_D7 14 + +// ################################################################################## +// +// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only) +// +// ################################################################################## + + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT + +// ################################################################################## +// +// Section 4. Not used +// +// ################################################################################## + + +// ################################################################################## +// +// Section 5. Other options +// +// ################################################################################## + + diff --git a/User_Setups/SetupX_Template.h b/User_Setups/SetupX_Template.h index 8c4e5e2..32eea56 100644 --- a/User_Setups/SetupX_Template.h +++ b/User_Setups/SetupX_Template.h @@ -8,6 +8,7 @@ // run without the need to make any more changes for a particular hardware setup! // Note that some sketches are designed for a particular TFT pixel width/height + // ################################################################################## // // Section 1. Call up the right driver file and any options for it @@ -22,7 +23,13 @@ //#define NUCLEO_64_TFT //#define NUCLEO_144_TFT -// Tell the library to use 8 bit parallel mode(otherwise SPI is assumed) +// STM32 8 bit parallel only: +// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7 +// then this will improve rendering performance by a factor of ~8x +//#define STM_PORTA_DATA_BUS +//#define STM_PORTA_DATA_BUS + +// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) //#define TFT_PARALLEL_8_BIT // Display type - only define if RPi display @@ -33,6 +40,7 @@ //#define ST7735_DRIVER // Define additional parameters below for this display //#define ILI9163_DRIVER // Define additional parameters below for this display //#define S6D02A1_DRIVER +//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI //#define HX8357D_DRIVER //#define ILI9481_DRIVER //#define ILI9486_DRIVER @@ -91,13 +99,6 @@ // #define TFT_INVERSION_ON // #define TFT_INVERSION_OFF -// If a backlight control signal is available then define the TFT_BL pin in Section 2 -// below. The backlight will be turned ON when tft.begin() is called, but the library -// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be -// driven with a PWM signal or turned OFF/ON then this must be handled by the user -// sketch. e.g. with digitalWrite(TFT_BL, LOW); - -// #define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options // ################################################################################## // @@ -105,6 +106,17 @@ // // ################################################################################## +// If a backlight control signal is available then define the TFT_BL pin in Section 2 +// below. The backlight will be turned ON when tft.begin() is called, but the library +// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be +// driven with a PWM signal or turned OFF/ON then this must be handled by the user +// sketch. e.g. with digitalWrite(TFT_BL, LOW); + +// #define TFT_BL 32 // LED back-light control pin +// #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) + + + // We must use hardware SPI, a minimum of 3 GPIO pins is needed. // Typical setup for ESP8266 NodeMCU ESP-12 is : // @@ -181,8 +193,6 @@ //#define TFT_RST 4 // Reset pin (could connect to RST pin) //#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST -//#define TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) - //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only @@ -203,10 +213,11 @@ // Wemos D32 boards need to be modified, see diagram in Tools folder. // Only ILI9481 and ILI9341 based displays have been tested! -// Parallel bus is only supported on ESP32 -// Uncomment line below to use ESP32 Parallel interface instead of SPI +// Parallel bus is only supported for the STM32 and ESP32 +// Example below is for ESP32 Parallel interface with UNO displays -//#define ESP32_PARALLEL +// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) +//#define TFT_PARALLEL_8_BIT // The ESP32 and TFT the pins used for testing are: //#define TFT_CS 33 // Chip select control pin (library pulls permanently low @@ -225,6 +236,31 @@ //#define TFT_D6 27 //#define TFT_D7 14 +// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ###### + +// The TFT can be connected to SPI port 1 or 2 +//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz +//#define TFT_MOSI PA7 +//#define TFT_MISO PA6 +//#define TFT_SCLK PA5 + +//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz +//#define TFT_MOSI PB15 +//#define TFT_MISO PB14 +//#define TFT_SCLK PB13 + +// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select +//#define TFT_CS D5 // Chip select control pin to TFT CS +//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select) +//#define TFT_RST D7 // Reset pin to TFT RST (or RESET) +// OR alternatively, we can use STM32 port reference names PXnn +//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5 +//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6 +//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7 + +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset + // Use an Arduino pin for initial testing as connecting to processor reset + // may not work (pulse too short at power up?) // ################################################################################## // @@ -262,14 +298,14 @@ // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) // With an ILI9163 display 27 MHz works OK. -// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 // #define SPI_FREQUENCY 20000000 -#define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 -// #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 +// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) // #define SPI_FREQUENCY 80000000 // Optional reduced SPI frequency for reading TFT diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino deleted file mode 100644 index 313a2f6..0000000 --- a/examples/160 x 128/TFT_SPIFFS_Jpeg/JPEG_functions.ino +++ /dev/null @@ -1,191 +0,0 @@ -/*==================================================================================== - This sketch contains support functions to render the Jpeg images. - - Created by Bodmer 15th Jan 2017 - ==================================================================================*/ - -// Return the minimum of two values a and b -#define minimum(a,b) (((a) < (b)) ? (a) : (b)) - -//==================================================================================== -// Opens the image file and prime the Jpeg decoder -//==================================================================================== -void drawJpeg(const char *filename, int xpos, int ypos) { - - Serial.println("==========================="); - Serial.print("Drawing file: "); Serial.println(filename); - Serial.println("==========================="); - - // Open the named file (the Jpeg decoder library will close it after rendering image) - fs::File jpegFile = SPIFFS.open( filename, "r"); // File handle reference for SPIFFS - // File jpegFile = SD.open( filename, FILE_READ); // or, file handle reference for SD library - - if ( !jpegFile ) { - Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!"); - return; - } - - // Use one of the three following methods to initialise the decoder: - //boolean decoded = JpegDec.decodeFsFile(jpegFile); // Pass a SPIFFS file handle to the decoder, - //boolean decoded = JpegDec.decodeSdFile(jpegFile); // or pass the SD file handle to the decoder, - boolean decoded = JpegDec.decodeFsFile(filename); // or pass the filename (leading / distinguishes SPIFFS files) - // Note: the filename can be a String or character array type - if (decoded) { - // print information about the image to the serial port - jpegInfo(); - - // render the image onto the screen at given coordinates - jpegRender(xpos, ypos); - } - else { - Serial.println("Jpeg file format not supported!"); - } -} - -//==================================================================================== -// Decode and render the Jpeg image onto the TFT screen -//==================================================================================== -void jpegRender(int xpos, int ypos) { - - // retrieve infomration about the image - uint16_t *pImg; - uint16_t mcu_w = JpegDec.MCUWidth; - uint16_t mcu_h = JpegDec.MCUHeight; - uint32_t max_x = JpegDec.width; - uint32_t max_y = JpegDec.height; - - // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs) - // Typically these MCUs are 16x16 pixel blocks - // Determine the width and height of the right and bottom edge image blocks - uint32_t min_w = minimum(mcu_w, max_x % mcu_w); - uint32_t min_h = minimum(mcu_h, max_y % mcu_h); - - // save the current image block size - uint32_t win_w = mcu_w; - uint32_t win_h = mcu_h; - - // record the current time so we can measure how long it takes to draw an image - uint32_t drawTime = millis(); - - // save the coordinate of the right and bottom edges to assist image cropping - // to the screen size - max_x += xpos; - max_y += ypos; - - // read each MCU block until there are no more - while ( JpegDec.readSwappedBytes()) { // Swap byte order so the SPI buffer can be used - - // save a pointer to the image block - pImg = JpegDec.pImage; - - // calculate where the image block should be drawn on the screen - int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU - int mcu_y = JpegDec.MCUy * mcu_h + ypos; - - // check if the image block size needs to be changed for the right edge - if (mcu_x + mcu_w <= max_x) win_w = mcu_w; - else win_w = min_w; - - // check if the image block size needs to be changed for the bottom edge - if (mcu_y + mcu_h <= max_y) win_h = mcu_h; - else win_h = min_h; - - // copy pixels into a contiguous block - if (win_w != mcu_w) - { - uint16_t *cImg; - int p = 0; - cImg = pImg + win_w; - for (int h = 1; h < win_h; h++) - { - p += mcu_w; - for (int w = 0; w < win_w; w++) - { - *cImg = *(pImg + w + p); - cImg++; - } - } - } - - // draw image MCU block only if it will fit on the screen - if ( ( mcu_x + win_w) <= tft.width() && ( mcu_y + win_h) <= tft.height()) - { - tft.pushRect(mcu_x, mcu_y, win_w, win_h, pImg); - } - - else if ( ( mcu_y + win_h) >= tft.height()) JpegDec.abort(); - - } - - // calculate how long it took to draw the image - drawTime = millis() - drawTime; // Calculate the time it took - - // print the results to the serial port - Serial.print ("Total render time was : "); Serial.print(drawTime); Serial.println(" ms"); - Serial.println("====================================="); - -} - -//==================================================================================== -// Print information decoded from the Jpeg image -//==================================================================================== -void jpegInfo() { - - Serial.println("==============="); - Serial.println("JPEG image info"); - Serial.println("==============="); - Serial.print ("Width :"); Serial.println(JpegDec.width); - Serial.print ("Height :"); Serial.println(JpegDec.height); - Serial.print ("Components :"); Serial.println(JpegDec.comps); - Serial.print ("MCU / row :"); Serial.println(JpegDec.MCUSPerRow); - Serial.print ("MCU / col :"); Serial.println(JpegDec.MCUSPerCol); - Serial.print ("Scan type :"); Serial.println(JpegDec.scanType); - Serial.print ("MCU width :"); Serial.println(JpegDec.MCUWidth); - Serial.print ("MCU height :"); Serial.println(JpegDec.MCUHeight); - Serial.println("==============="); - Serial.println(""); -} - -//==================================================================================== -// Open a Jpeg file and send it to the Serial port in a C array compatible format -//==================================================================================== -void createArray(const char *filename) { - - // Open the named file - fs::File jpgFile = SPIFFS.open( filename, "r"); // File handle reference for SPIFFS - // File jpgFile = SD.open( filename, FILE_READ); // or, file handle reference for SD library - - if ( !jpgFile ) { - Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!"); - return; - } - - uint8_t data; - byte line_len = 0; - Serial.println(""); - Serial.println("// Generated by a JPEGDecoder library example sketch:"); - Serial.println("// https://github.com/Bodmer/JPEGDecoder"); - Serial.println(""); - Serial.println("#include "); - Serial.println("// Remove leading / from array name!"); - Serial.print ("const uint8_t "); - while (*filename != '.') Serial.print(*filename++); - Serial.println("[] PROGMEM = {"); // PROGMEM added for AVR processors, it is ignored by Due - - while ( jpgFile.available()) { - - data = jpgFile.read(); - Serial.print("0x"); if (abs(data) < 16) Serial.print("0"); - Serial.print(data, HEX); Serial.print(",");// Add value and comma - line_len++; - if ( line_len >= 32) { - line_len = 0; - Serial.println(); - } - - } - - Serial.println("};\r\n"); - jpgFile.close(); -} -//==================================================================================== diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino deleted file mode 100644 index a0f8225..0000000 --- a/examples/160 x 128/TFT_SPIFFS_Jpeg/SPIFFS_functions.ino +++ /dev/null @@ -1,36 +0,0 @@ -/*==================================================================================== - This sketch contains support functions for the ESP6266 SPIFFS filing system - - Created by Bodmer 15th Jan 2017 - ==================================================================================*/ - -//==================================================================================== -// Print a SPIFFS directory list (root directory) -//==================================================================================== - -void listFiles(void) { - Serial.println(); - Serial.println("SPIFFS files found:"); - - fs::Dir dir = SPIFFS.openDir("/"); // Root directory - String line = "====================================="; - - Serial.println(line); - Serial.println(" File name Size"); - Serial.println(line); - - while (dir.next()) { - String fileName = dir.fileName(); - Serial.print(fileName); - int spaces = 25 - fileName.length(); // Tabulate nicely - while (spaces--) Serial.print(" "); - fs::File f = dir.openFile("r"); - Serial.print(f.size()); Serial.println(" bytes"); - } - - Serial.println(line); - Serial.println(); - delay(1000); -} -//==================================================================================== - diff --git a/examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino b/examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino deleted file mode 100644 index f500a47..0000000 --- a/examples/160 x 128/TFT_SPIFFS_Jpeg/TFT_SPIFFS_Jpeg.ino +++ /dev/null @@ -1,129 +0,0 @@ -/*==================================================================================== - - This sketch demonstrates loading images which have been stored as files in the - built-in FLASH memory on a NodeMCU 1.0 (ESP8266 based, ESP-12E Module) rendering the - images onto a 160 x 128 pixel TFT screen. - - The images are stored in the SPI FLASH Filing System (SPIFFS), which effectively - functions like a tiny "hard drive". This filing system is built into the ESP8266 - Core that can be loaded from the IDE "Boards manager" menu option. This is at - version 2.3.0 at the time of sketch creation. - - The size of the SPIFFS partition can be set in the IDE as 1Mbyte or 3Mbytes. Either - will work with this sketch. Typically most sketches easily fit within 1 Mbyte so a - 3 Mbyte SPIFS partition can be used, in which case it can contain 100's of Jpeg - full screem images. - - The Jpeg library can be found here: - https://github.com/Bodmer/JPEGDecoder - - Images in the Jpeg format can be created using Paint or IrfanView or other picture - editting software. - - Place the images inside the sketch folder, in a folder called "Data". Then upload - all the files in the folder using the Arduino IDE "ESP8266 Sketch Data Upload" option - in the "Tools" menu: - http://www.esp8266.com/viewtopic.php?f=32&t=10081 - https://github.com/esp8266/arduino-esp8266fs-plugin/releases - - This takes some time, but the SPIFFS content is not altered when a new sketch is - uploaded, so there is no need to upload the same files again! - Note: If open, you must close the "Serial Monitor" window to upload data to SPIFFS! - - The IDE will not copy the "data" folder with the sketch if you save the sketch under - another name. It is necessary to manually make a copy and place it in the sketch - folder. - - This sketch includes example images in the Data folder. - - Saving images, uploading and rendering on the TFT screen couldn't be much easier! - - Created by Bodmer 24th Jan 2017 - Tested in Arduino IDE 1.8.0 esp8266 Core 2.3.0 - ==================================================================================*/ - -//==================================================================================== -// Libraries -//==================================================================================== -// Call up the SPIFFS FLASH filing system this is part of the ESP Core -#define FS_NO_GLOBALS -#include - -// JPEG decoder library -#include - -// SPI library, built into IDE -#include - -// Call up the TFT library -#include // Hardware-specific library for ESP8266 -// The TFT control pins are set in the User_Setup.h file <<<<<<<<<<<<<<<<< NOTE! -// that can be found in the "src" folder of the library - -// Invoke TFT library -TFT_eSPI tft = TFT_eSPI(); - -//==================================================================================== -// Setup -//==================================================================================== -void setup() -{ - Serial.begin(250000); // Used for messages and the C array generator - - delay(10); - Serial.println("NodeMCU decoder test!"); - - tft.begin(); - tft.setRotation(0); // 0 & 2 Portrait. 1 & 3 landscape - tft.fillScreen(TFT_BLACK); - - if (!SPIFFS.begin()) { - Serial.println("SPIFFS initialisation failed!"); - while (1) yield(); // Stay here twiddling thumbs waiting - } - Serial.println("\r\nInitialisation done."); - listFiles(); // Lists the files so you can see what is in the SPIFFS - -} - -//==================================================================================== -// Loop -//==================================================================================== -void loop() -{ - // Note the / before the SPIFFS file name must be present, this means the file is in - // the root directory of the SPIFFS, e.g. "/Tiger.jpg" for a file called "Tiger.jpg" - - tft.setRotation(0); // portrait - tft.fillScreen(random(0xFFFF)); - - drawJpeg("/EagleEye160.jpg", 0, 16); - delay(2000); - - tft.fillScreen(random(0xFFFF)); - drawJpeg("/tiger160.jpg", 4, 0); - delay(2000); - - tft.setRotation(1); // landscape - //tft.fillScreen(random(0xFFFF)); - drawJpeg("/arduino160.jpg", 0, 0); - delay(2000); - - tft.fillScreen(TFT_BLACK); - drawJpeg("/Baboon160.jpg", 0, 4); - delay(2000); - - tft.fillScreen(random(0xFFFF)); - drawJpeg("/Mouse160.jpg", 0, 11); - delay(2000); - - // Create arrays from the jpeg images and send them to the serial port for copy and - // pasting into a sketch (used to make arrays fot the TFT_FLASH_Jpeg sketch) - - //createArray("/EagleEye160.jpg"); - //createArray("/tiger160.jpg"); - //createArray("/Baboon160.jpg"); - //createArray("/Mouse160.jpg"); - //while(1) yield(); // Stay here -} -//==================================================================================== - diff --git a/examples/320 x 240/TFT_Matrix/TFT_Matrix.ino b/examples/320 x 240/TFT_Matrix/TFT_Matrix.ino index 2e2e449..838ce79 100644 --- a/examples/320 x 240/TFT_Matrix/TFT_Matrix.ino +++ b/examples/320 x 240/TFT_Matrix/TFT_Matrix.ino @@ -32,7 +32,7 @@ void setup() { Serial.begin(115200); randomSeed(analogRead(A0)); tft.init(); - tft.setRotation(2); + tft.setRotation(0); tft.fillScreen(ILI9341_BLACK); setupScrollArea(TOP_FIXED_AREA, BOT_FIXED_AREA); } diff --git a/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino b/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino index 07f4831..30f46b3 100644 --- a/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino +++ b/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino @@ -6,7 +6,7 @@ // parallel TFT's (tested with ILI9341 and ILI9481) // The sketch will run on processors without DMA and also parallel -// interface TFT's. Comment out line 25 for no DMA. +// interface TFT's. Comment out line 29 for no DMA. // Library here: // https://github.com/Bodmer/TFT_eSPI diff --git a/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino b/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino index 9cc8b02..b0e09e2 100644 --- a/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino +++ b/examples/Generic/ESP32_SDcard_jpeg/ESP32_SDcard_jpeg.ino @@ -153,8 +153,8 @@ void jpegRender(int xpos, int ypos) { // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs) // Typically these MCUs are 16x16 pixel blocks // Determine the width and height of the right and bottom edge image blocks - uint32_t min_w = min(mcu_w, max_x % mcu_w); - uint32_t min_h = min(mcu_h, max_y % mcu_h); + uint32_t min_w = jpg_min(mcu_w, max_x % mcu_w); + uint32_t min_h = jpg_min(mcu_h, max_y % mcu_h); // save the current image block size uint32_t win_w = mcu_w; diff --git a/examples/Smooth Fonts/FLASH_Array/Unicode_test/Unicode_test.ino b/examples/Smooth Fonts/FLASH_Array/Unicode_test/Unicode_test.ino index 899ab74..a8f6bea 100644 --- a/examples/Smooth Fonts/FLASH_Array/Unicode_test/Unicode_test.ino +++ b/examples/Smooth Fonts/FLASH_Array/Unicode_test/Unicode_test.ino @@ -31,7 +31,6 @@ //==================================================================================== // Libraries //==================================================================================== -// Call up the SPIFFS FLASH filing system this is part of the ESP Core #include // Hardware-specific library diff --git a/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino b/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino index b789b50..5bc3ffe 100644 --- a/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino +++ b/examples/Sprite/Rotated_Sprite_1/Rotated_Sprite_1.ino @@ -10,7 +10,7 @@ // screen very simple. The rotation is clockwise with increasing angle. The angle is in // degrees, an angle of 0 means no Sprite rotation. -// The pushRotated() function works with 1, 8 and 16 bit per pixel (bpp) Sprites. +// The pushRotated() function works with 1, 4, 8 and 16 bit per pixel (bpp) Sprites. // The original Sprite is unchanged so can be plotted again at a different angle. @@ -20,6 +20,10 @@ // For 1 bpp Sprites the foreground and background colours are defined with the // function spr.setBitmapColor(foregroundColor, backgroundColor). +// For 4 bpp Sprites the colour map index is used instead of the 16 bit colour +// e.g. spr.setTextColor(5); // Green text in default colour map +// See "Transparent_Sprite_Demo_4bit" example for default colour map details + // Created by Bodmer 6/1/19 as an example to the TFT_eSPI library: // https://github.com/Bodmer/TFT_eSPI @@ -46,7 +50,7 @@ void setup() { void loop() { - int xw = tft.width()/2; // xw, yh is midle of screen + int xw = tft.width()/2; // xw, yh is middle of screen int yh = tft.height()/2; diff --git a/examples/Sprite/Sprite_draw_4bit/Sprite_draw_4bit.ino b/examples/Sprite/Sprite_draw_4bit/Sprite_draw_4bit.ino index 40d9fc1..ca3ebe4 100644 --- a/examples/Sprite/Sprite_draw_4bit/Sprite_draw_4bit.ino +++ b/examples/Sprite/Sprite_draw_4bit/Sprite_draw_4bit.ino @@ -88,7 +88,7 @@ void loop(void) // Pass the palette to the Sprite class spr.createPalette(cmap); - // Push Sprite parially off-screen to test cropping + // Push Sprite partially off-screen to test cropping spr.pushSprite(-40, -40); spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2, 10); spr.pushSprite(tft.width() - WIDTH + 40, tft.height() - HEIGHT + 40); diff --git a/examples/Sprite/Sprite_image_4bit/Sprite_image_4bit.ino b/examples/Sprite/Sprite_image_4bit/Sprite_image_4bit.ino new file mode 100644 index 0000000..bdf5390 --- /dev/null +++ b/examples/Sprite/Sprite_image_4bit/Sprite_image_4bit.ino @@ -0,0 +1,152 @@ +/* + + Sketch to show how a Sprite can use a four-bit image with + a palette to change the appearance of an image while rendering + it only once. + + Example for library: + https://github.com/Bodmer/TFT_eSPI + + A Sprite is notionally an invisible graphics screen that is + kept in the processors RAM. Graphics can be drawn into the + Sprite just as it can be drawn directly to the screen. Once + the Sprite is completed it can be plotted onto the screen in + any position. If there is sufficient RAM then the Sprite can + be the same size as the screen and used as a frame buffer. + + A 16 bit Sprite occupies (2 * width * height) bytes in RAM. + + On a ESP8266 Sprite sizes up to 126 x 160 can be accomodated, + this size requires 40kBytes of RAM for a 16 bit color depth. + + When 8 bit color depth sprites are created they occupy + (width * height) bytes in RAM, so larger sprites can be + created, or the RAM required is halved. + +*/ + + +// Set delay after plotting the sprite +#define DELAY 30 + +// Width and height of sprite +#define WIDTH 164 +#define HEIGHT 164 + +#include "sample_images.h" + +TFT_eSPI tft = TFT_eSPI(); // Declare object "tft" + +TFT_eSprite spr = TFT_eSprite(&tft); // Declare Sprite object "spr" with pointer to "tft" object + +byte red = 31; // Red is the top 5 bits of a 16 bit colour value +byte green = 0;// Green is the middle 6 bits +byte blue = 0; // Blue is the bottom 5 bits +byte state = 0; + +int rloop = 0; +int incr = 1; + +uint16_t cmap[16]; + +void setup() +{ + Serial.begin(9600); + Serial.println(); + + delay(50); + + // Initialise the TFT registers + tft.init(); + + spr.setColorDepth(4); + + // Create a sprite of defined size + spr.createSprite(WIDTH, HEIGHT); + + // Clear the TFT screen to black + tft.fillScreen(TFT_BLACK); + + // push the image - only need to do this once. + spr.pushImage(2, 2, 160, 160, (uint16_t *)stars); + + for (int i = 0; i < 16; i++) + cmap[i] = rainbow(); +} + +void loop(void) +{ + // create a palette with the defined colors and push it. + spr.createPalette(cmap, 16); + spr.pushSprite(tft.width() / 2 - WIDTH / 2, tft.height() / 2 - HEIGHT / 2); + + // update the colors + for (int i = 0; i < 15; i++) { + cmap[i] = cmap[i + 1]; + } + if (incr == 2) { + (void)rainbow(); // skip alternate steps to go faster + } + cmap[15] = rainbow(); + rloop += incr; + if (rloop > 0xc0) { + incr = incr == 2 ? 1 : 2; + rloop = 0; + + } + delay(DELAY); + +} + +// ######################################################################### +// Return a 16 bit rainbow colour +// ######################################################################### +unsigned int rainbow() +{ + switch (state) { + case 0: + green ++; + if (green == 64) { + green = 63; + state = 1; + } + break; + case 1: + red--; + if (red == 255) { + red = 0; + state = 2; + } + break; + case 2: + blue ++; + if (blue == 32) { + blue = 31; + state = 3; + } + break; + case 3: + green --; + if (green == 255) { + green = 0; + state = 4; + } + break; + case 4: + red ++; + if (red == 32) { + red = 31; + state = 5; + } + break; + case 5: + blue --; + if (blue == 255) { + blue = 0; + state = 0; + } + break; + } + return red << 11 | green << 5 | blue; +} + diff --git a/examples/Sprite/Sprite_image_4bit/sample_images.h b/examples/Sprite/Sprite_image_4bit/sample_images.h new file mode 100644 index 0000000..3c56f1d --- /dev/null +++ b/examples/Sprite/Sprite_image_4bit/sample_images.h @@ -0,0 +1,3 @@ +#include // Include the graphics library (this includes the sprite functions) + +extern const uint8_t stars[12800] PROGMEM ; \ No newline at end of file diff --git a/examples/Sprite/Sprite_image_4bit/starImage.cpp b/examples/Sprite/Sprite_image_4bit/starImage.cpp new file mode 100644 index 0000000..531cdeb --- /dev/null +++ b/examples/Sprite/Sprite_image_4bit/starImage.cpp @@ -0,0 +1,1444 @@ +#include "sample_images.h" + + +// width is 160, height is 160 +const uint8_t stars[12800] PROGMEM = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x11, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x11, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x31, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x32, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x33, 0x33, 0x11, 0x10, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x33, 0x21, 0x10, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x34, 0x33, 0x31, 0x11, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x43, 0x33, 0x11, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x44, 0x33, 0x21, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x11, 0x23, 0x33, 0x44, 0x44, 0x33, 0x31, 0x11, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x44, 0x43, 0x32, 0x11, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x46, 0x54, 0x43, 0x33, 0x21, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x56, 0x64, 0x44, 0x33, 0x31, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x23, 0x34, 0x44, 0x66, 0x66, 0x44, 0x43, 0x32, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x12, 0x33, 0x34, 0x45, 0x66, 0x66, 0x54, 0x43, 0x33, +0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x13, 0x33, 0x44, 0x46, 0x66, 0x66, 0x64, 0x44, 0x33, +0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x34, 0x44, 0x66, 0x67, 0x76, 0x65, 0x44, 0x33, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x33, 0x34, 0x45, 0x66, 0x78, 0x86, 0x66, 0x44, 0x43, +0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x88, 0x87, 0x66, 0x64, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x33, 0x44, 0x56, 0x67, 0x88, 0x88, 0x76, 0x65, 0x44, +0x33, 0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x33, 0x34, 0x44, 0x66, 0x68, 0x88, 0x88, 0x86, 0x66, 0x44, +0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x12, 0x33, 0x44, 0x46, 0x66, 0x78, 0x89, 0x98, 0x87, 0x66, 0x54, +0x43, 0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, +0x13, 0x33, 0x44, 0x56, 0x67, 0x88, 0x89, 0x98, 0x88, 0x66, 0x65, +0x44, 0x33, 0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, +0x23, 0x34, 0x44, 0x66, 0x68, 0x88, 0x99, 0x99, 0x88, 0x86, 0x66, +0x44, 0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, +0x33, 0x34, 0x45, 0x66, 0x78, 0x89, 0x99, 0x99, 0x88, 0x87, 0x66, +0x54, 0x43, 0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13, +0x33, 0x44, 0x46, 0x66, 0x88, 0x89, 0x9a, 0x99, 0x98, 0x88, 0x66, +0x64, 0x44, 0x33, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x23, +0x34, 0x44, 0x66, 0x67, 0x88, 0x99, 0x9b, 0xa9, 0x99, 0x88, 0x76, +0x65, 0x44, 0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, +0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x33, +0x34, 0x45, 0x66, 0x78, 0x88, 0x99, 0xab, 0xba, 0x99, 0x88, 0x86, +0x66, 0x54, 0x43, 0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, +0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x33, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x33, +0x44, 0x46, 0x66, 0x88, 0x89, 0x99, 0xbb, 0xbb, 0x99, 0x98, 0x88, +0x66, 0x64, 0x44, 0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x22, +0x23, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x33, 0x33, 0x33, 0x33, 0x22, 0x22, 0x11, 0x11, 0x11, +0x11, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x11, 0x23, 0x33, +0x44, 0x56, 0x67, 0x88, 0x99, 0x9a, 0xbb, 0xbb, 0xa9, 0x99, 0x88, +0x76, 0x65, 0x44, 0x33, 0x31, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11, +0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x22, 0x22, 0x33, 0x33, 0x33, +0x33, 0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x13, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x22, +0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x33, 0x34, +0x44, 0x66, 0x68, 0x88, 0x99, 0xab, 0xbb, 0xbb, 0xb9, 0x99, 0x88, +0x86, 0x66, 0x44, 0x43, 0x33, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, +0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, +0x33, 0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x44, 0x43, 0x33, 0x33, 0x33, 0x33, 0x33, +0x33, 0x33, 0x33, 0x22, 0x22, 0x11, 0x11, 0x11, 0x12, 0x33, 0x44, +0x46, 0x66, 0x78, 0x89, 0x99, 0xbb, 0xbc, 0xbb, 0xba, 0x99, 0x98, +0x87, 0x66, 0x54, 0x44, 0x33, 0x21, 0x11, 0x11, 0x11, 0x22, 0x22, +0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x43, 0x33, +0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x32, 0x22, 0x23, 0x33, 0x44, +0x56, 0x67, 0x88, 0x89, 0x9a, 0xbb, 0xbc, 0xcb, 0xbb, 0xa9, 0x98, +0x88, 0x66, 0x65, 0x44, 0x33, 0x32, 0x22, 0x33, 0x33, 0x33, 0x33, +0x33, 0x33, 0x33, 0x33, 0x34, 0x44, 0x44, 0x44, 0x44, 0x44, 0x43, +0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x33, 0x34, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, +0x44, 0x44, 0x43, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x44, +0x66, 0x68, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, 0xbb, 0xb9, 0x99, +0x88, 0x86, 0x66, 0x44, 0x43, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, +0x34, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x44, 0x43, +0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x34, 0x44, 0x66, 0x66, 0x65, 0x55, 0x54, 0x44, +0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x43, 0x33, 0x33, 0x44, 0x45, +0x66, 0x78, 0x89, 0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xbb, 0xba, 0x99, +0x98, 0x87, 0x66, 0x54, 0x43, 0x33, 0x33, 0x34, 0x44, 0x44, 0x44, +0x44, 0x44, 0x44, 0x44, 0x45, 0x55, 0x56, 0x66, 0x65, 0x44, 0x43, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x33, 0x44, 0x56, 0x66, 0x66, 0x66, 0x66, 0x66, +0x55, 0x55, 0x54, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x56, +0x66, 0x88, 0x89, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xcb, 0xbb, 0x99, +0x98, 0x88, 0x66, 0x64, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, +0x45, 0x55, 0x56, 0x66, 0x66, 0x66, 0x66, 0x66, 0x65, 0x44, 0x33, +0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x13, 0x33, 0x44, 0x46, 0x66, 0x66, 0x66, 0x66, 0x66, +0x66, 0x66, 0x66, 0x66, 0x55, 0x55, 0x44, 0x44, 0x44, 0x44, 0x66, +0x68, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xb9, +0x99, 0x88, 0x76, 0x66, 0x44, 0x44, 0x44, 0x45, 0x55, 0x56, 0x66, +0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x66, 0x64, 0x44, 0x33, +0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x88, 0x88, 0x77, 0x77, +0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x55, 0x55, 0x66, +0x78, 0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xba, +0x99, 0x88, 0x87, 0x66, 0x55, 0x56, 0x66, 0x66, 0x66, 0x66, 0x66, +0x66, 0x66, 0x66, 0x77, 0x77, 0x88, 0x87, 0x66, 0x54, 0x43, 0x33, +0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x11, 0x33, 0x34, 0x45, 0x66, 0x78, 0x88, 0x88, 0x88, +0x88, 0x87, 0x77, 0x77, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, +0x88, 0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, +0x99, 0x98, 0x88, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, +0x77, 0x88, 0x88, 0x88, 0x88, 0x88, 0x86, 0x66, 0x54, 0x43, 0x33, +0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x33, 0x34, 0x44, 0x66, 0x68, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x87, 0x77, 0x76, 0x66, 0x66, 0x67, +0x88, 0x99, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, +0xa9, 0x99, 0x88, 0x76, 0x66, 0x66, 0x67, 0x77, 0x78, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x86, 0x66, 0x44, 0x43, 0x32, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x23, 0x34, 0x44, 0x56, 0x67, 0x88, 0x99, 0x99, +0x99, 0x98, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, 0x78, +0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcd, 0xdc, 0xcc, 0xcc, 0xbb, +0xba, 0x99, 0x88, 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x89, 0x99, 0x99, 0x99, 0x88, 0x76, 0x65, 0x44, 0x33, 0x31, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x56, 0x67, 0x88, 0x89, 0x99, +0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x88, 0x88, 0x88, 0x88, 0x88, +0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, 0xcd, 0xdc, 0xcc, 0xcc, 0xbb, +0xbb, 0x99, 0x98, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x99, 0x99, +0x99, 0x99, 0x99, 0x99, 0x98, 0x88, 0x66, 0x64, 0x44, 0x33, 0x31, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x88, 0x89, 0x99, +0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x88, +0x99, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, +0xbb, 0xa9, 0x98, 0x88, 0x89, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, +0x99, 0x99, 0xaa, 0x99, 0x98, 0x87, 0x66, 0x64, 0x44, 0x33, 0x21, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x12, 0x33, 0x34, 0x45, 0x66, 0x78, 0x89, 0x99, +0xab, 0xbb, 0xba, 0xaa, 0xa9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, +0x99, 0x9b, 0xbb, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, +0xbb, 0xb9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xaa, 0xaa, +0xab, 0xbb, 0xba, 0x99, 0x98, 0x87, 0x66, 0x54, 0x43, 0x33, 0x11, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x66, 0x68, 0x88, 0x99, +0xab, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xaa, 0xa9, 0x99, 0x99, +0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xdd, 0xdc, 0xcc, 0xcc, +0xbb, 0xba, 0x99, 0x99, 0x99, 0x9a, 0xaa, 0xab, 0xbb, 0xbb, 0xbb, +0xbb, 0xbb, 0xb9, 0x99, 0x88, 0x86, 0x66, 0x44, 0x43, 0x32, 0x11, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x11, 0x23, 0x34, 0x44, 0x66, 0x68, 0x88, 0x99, +0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xaa, +0xaa, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, +0xcb, 0xbb, 0xaa, 0xab, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, +0xbb, 0xbb, 0xb9, 0x99, 0x88, 0x76, 0x65, 0x44, 0x43, 0x32, 0x11, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x56, 0x67, 0x88, 0x99, +0x9a, 0xbb, 0xbc, 0xcc, 0xcb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, +0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xdd, 0xde, 0xed, 0xdd, 0xcc, 0xcc, +0xcc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xcc, +0xcb, 0xbb, 0xa9, 0x98, 0x88, 0x76, 0x65, 0x44, 0x33, 0x31, 0x11, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x46, 0x66, 0x88, 0x89, +0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xbb, +0xbb, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xee, 0xee, 0xdd, 0xdc, 0xcc, +0xcc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcb, 0xbb, 0x99, 0x98, 0x88, 0x66, 0x64, 0x44, 0x33, 0x21, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x78, 0x89, +0x99, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xbc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xdd, 0xdc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xbb, 0xba, 0x99, 0x98, 0x87, 0x66, 0x54, 0x43, 0x33, 0x21, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x45, 0x66, 0x78, 0x88, +0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xde, 0xee, 0xee, 0xed, 0xdd, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xbb, 0xba, 0x99, 0x88, 0x86, 0x66, 0x54, 0x43, 0x33, 0x11, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x34, 0x44, 0x66, 0x68, 0x88, +0x99, 0x9b, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, 0xee, 0xed, 0xdd, 0xdc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xbb, 0xb9, 0x99, 0x88, 0x86, 0x66, 0x44, 0x43, 0x32, 0x11, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x33, 0x44, 0x56, 0x67, 0x88, +0x99, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xcc, 0xcc, 0xcb, +0xbb, 0xa9, 0x99, 0x88, 0x76, 0x65, 0x44, 0x33, 0x31, 0x11, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x56, 0x66, 0x88, +0x89, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, +0xdc, 0xcc, 0xcc, 0xdd, 0xde, 0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, +0xcc, 0xcc, 0xcd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, +0xbb, 0xa9, 0x98, 0x88, 0x66, 0x64, 0x44, 0x33, 0x31, 0x10, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x88, +0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, +0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, +0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xcc, 0xcc, 0xcb, +0xbb, 0x99, 0x98, 0x87, 0x66, 0x64, 0x44, 0x33, 0x21, 0x10, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x45, 0x66, 0x78, +0x89, 0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xdd, 0xdd, 0xdd, +0xdd, 0xdd, 0xdd, 0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xdd, +0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xcc, 0xcc, 0xbb, +0xba, 0x99, 0x98, 0x87, 0x66, 0x54, 0x43, 0x33, 0x11, 0x10, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x66, 0x68, +0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, 0xee, +0xee, 0xdd, 0xdd, 0xdd, 0xee, 0xee, 0xef, 0xfe, 0xee, 0xee, 0xdd, +0xdd, 0xde, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcc, 0xbb, +0xb9, 0x99, 0x88, 0x86, 0x66, 0x44, 0x43, 0x32, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x34, 0x44, 0x66, 0x67, +0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, 0xcc, 0xdd, 0xde, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xfe, 0xee, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xcc, 0xcc, 0xcc, 0xbb, +0xb9, 0x99, 0x88, 0x76, 0x65, 0x44, 0x33, 0x32, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x56, 0x67, +0x88, 0x99, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xde, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xff, 0xff, 0xee, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, +0xa9, 0x98, 0x88, 0x76, 0x65, 0x44, 0x33, 0x31, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x46, 0x66, +0x88, 0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xff, 0xff, 0xee, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, +0x99, 0x98, 0x88, 0x66, 0x64, 0x44, 0x33, 0x21, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x33, 0x44, 0x45, 0x66, +0x78, 0x89, 0x99, 0xbb, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xff, 0xff, 0xfe, 0xee, 0xee, +0xee, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xbb, 0xba, +0x99, 0x98, 0x87, 0x66, 0x54, 0x43, 0x33, 0x21, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x45, 0x66, +0x78, 0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xee, 0xee, +0xee, 0xff, 0xee, 0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xee, 0xee, +0xee, 0xff, 0xee, 0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xbb, 0xba, +0x99, 0x88, 0x86, 0x66, 0x44, 0x43, 0x33, 0x11, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x34, 0x44, 0x66, +0x68, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, +0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xfe, 0xee, 0xee, 0xed, 0xdd, 0xcc, 0xcc, 0xcc, 0xbb, 0xb9, +0x99, 0x88, 0x86, 0x66, 0x44, 0x43, 0x32, 0x11, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x23, 0x33, 0x44, 0x56, +0x67, 0x88, 0x99, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xde, 0xee, +0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xfe, 0xee, 0xee, 0xed, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0xa9, +0x99, 0x88, 0x76, 0x65, 0x44, 0x33, 0x32, 0x11, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x56, +0x66, 0x88, 0x89, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, 0xdd, 0xde, 0xee, +0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xfe, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0xa9, +0x98, 0x88, 0x66, 0x65, 0x44, 0x43, 0x33, 0x11, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x44, 0x66, +0x67, 0x88, 0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, 0xcd, 0xdd, 0xee, +0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xee, 0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xcb, 0xbb, 0x99, +0x98, 0x88, 0x76, 0x66, 0x44, 0x44, 0x33, 0x31, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x46, 0x66, +0x78, 0x88, 0x99, 0x9a, 0xbb, 0xbb, 0xcc, 0xcc, 0xcd, 0xdd, 0xee, +0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xee, 0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xbb, 0xbb, 0xa9, +0x99, 0x88, 0x87, 0x66, 0x65, 0x44, 0x43, 0x33, 0x11, 0x10, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x44, 0x66, 0x67, +0x88, 0x89, 0x99, 0xab, 0xbb, 0xbc, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, +0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xee, 0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcc, 0xcb, 0xbb, 0xba, +0x99, 0x98, 0x88, 0x76, 0x66, 0x54, 0x44, 0x33, 0x31, 0x11, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x56, 0x66, 0x78, +0x88, 0x99, 0x9a, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, +0xee, 0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xfe, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xbb, 0xbb, +0xa9, 0x99, 0x88, 0x87, 0x66, 0x65, 0x44, 0x43, 0x33, 0x11, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x45, 0x66, 0x67, 0x88, +0x89, 0x99, 0xab, 0xbb, 0xbc, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, +0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcc, 0xcb, 0xbb, +0xba, 0x99, 0x98, 0x88, 0x76, 0x66, 0x54, 0x44, 0x33, 0x32, 0x11, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x56, 0x66, 0x78, 0x88, +0x99, 0x9a, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, +0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xfe, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xbb, +0xbb, 0xa9, 0x99, 0x88, 0x87, 0x66, 0x65, 0x44, 0x43, 0x33, 0x21, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x13, 0x33, 0x44, 0x45, 0x66, 0x67, 0x88, 0x89, +0x99, 0xab, 0xbb, 0xbc, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, 0xee, +0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, +0xbb, 0xba, 0x99, 0x98, 0x88, 0x76, 0x66, 0x54, 0x44, 0x33, 0x32, +0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x11, 0x33, 0x34, 0x44, 0x56, 0x66, 0x78, 0x88, 0x99, +0x9a, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xee, +0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xfe, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, +0xcb, 0xbb, 0xa9, 0x99, 0x88, 0x87, 0x66, 0x65, 0x44, 0x43, 0x33, +0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x13, 0x33, 0x44, 0x45, 0x66, 0x67, 0x88, 0x89, 0x99, +0xab, 0xbb, 0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, 0xee, 0xee, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcc, +0xcc, 0xbb, 0xba, 0x99, 0x98, 0x88, 0x76, 0x66, 0x54, 0x44, 0x33, +0x32, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x12, 0x33, 0x34, 0x44, 0x56, 0x66, 0x78, 0x88, 0x99, 0x9a, +0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xee, 0xee, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, +0xcc, 0xcb, 0xbb, 0xa9, 0x99, 0x88, 0x87, 0x66, 0x65, 0x44, 0x43, +0x33, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x23, 0x33, 0x44, 0x45, 0x66, 0x67, 0x88, 0x89, 0x99, 0xab, +0xbb, 0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xde, 0xee, 0xee, 0xee, 0xee, +0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xfe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdc, 0xcc, +0xcc, 0xcc, 0xbb, 0xba, 0x99, 0x98, 0x88, 0x76, 0x66, 0x54, 0x44, +0x33, 0x32, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, +0x12, 0x33, 0x34, 0x44, 0x56, 0x66, 0x78, 0x88, 0x99, 0x9a, 0xbb, +0xbc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, +0xee, 0xee, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xcc, +0xcc, 0xcc, 0xcb, 0xbb, 0xa9, 0x99, 0x88, 0x87, 0x66, 0x65, 0x44, +0x43, 0x33, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, +0x23, 0x33, 0x44, 0x45, 0x66, 0x67, 0x88, 0x89, 0x99, 0xab, 0xbb, +0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xdd, 0xde, 0xee, 0xee, 0xee, 0xee, +0xee, 0xee, 0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, +0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdd, 0xdc, +0xcc, 0xcc, 0xcc, 0xbb, 0xba, 0x99, 0x98, 0x88, 0x86, 0x66, 0x54, +0x44, 0x33, 0x32, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, +0x33, 0x34, 0x44, 0x56, 0x66, 0x78, 0x88, 0x99, 0x9a, 0xbb, 0xbc, +0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xee, 0xee, 0xee, +0xee, 0xee, 0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, +0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, +0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xa9, 0x99, 0x88, 0x88, 0x66, 0x65, +0x44, 0x43, 0x33, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x23, +0x33, 0x44, 0x45, 0x66, 0x67, 0x88, 0x89, 0x99, 0xab, 0xbb, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xee, +0xee, 0xee, 0xee, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, +0xee, 0xee, 0xee, 0xee, 0xed, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xbb, 0x99, 0x98, 0x88, 0x86, 0x66, +0x54, 0x44, 0x33, 0x32, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x33, +0x34, 0x44, 0x56, 0x66, 0x78, 0x88, 0x99, 0x9a, 0xbb, 0xbc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, +0xee, 0xee, 0xee, 0xef, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, +0xee, 0xee, 0xee, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xb9, 0x99, 0x88, 0x88, 0x66, +0x65, 0x44, 0x43, 0x33, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x23, 0x33, +0x44, 0x45, 0x66, 0x67, 0x88, 0x89, 0x99, 0xab, 0xbb, 0xbb, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xdd, 0xdd, 0xdd, +0xdd, 0xee, 0xee, 0xef, 0xff, 0xff, 0xee, 0xee, 0xff, 0xff, 0xfe, +0xee, 0xee, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xbb, 0xbb, 0x99, 0x99, 0x88, 0x86, +0x66, 0x54, 0x44, 0x33, 0x32, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x33, 0x34, +0x44, 0x56, 0x66, 0x88, 0x88, 0x99, 0x9a, 0xbb, 0xbb, 0xbb, 0xbb, +0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, +0xdd, 0xee, 0xee, 0xef, 0xff, 0xee, 0xee, 0xee, 0xee, 0xff, 0xfe, +0xee, 0xee, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcb, 0xbb, 0xbb, 0xbb, 0xbb, 0xa9, 0x99, 0x98, 0x88, +0x66, 0x66, 0x44, 0x43, 0x33, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x23, 0x33, 0x44, +0x45, 0x66, 0x68, 0x88, 0x89, 0x99, 0x99, 0x9a, 0xab, 0xbb, 0xbb, +0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, +0xdd, 0xee, 0xee, 0xef, 0xfe, 0xee, 0xee, 0xee, 0xee, 0xef, 0xfe, +0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcc, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xa9, 0x99, 0x99, 0x99, 0x88, +0x86, 0x66, 0x64, 0x44, 0x33, 0x32, 0x11, 0x10, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x33, 0x34, 0x44, +0x56, 0x66, 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0x99, 0xaa, 0xbb, +0xbb, 0xbb, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, +0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, +0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, +0xbb, 0xbb, 0xbb, 0xbb, 0xaa, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, +0x88, 0x66, 0x66, 0x44, 0x43, 0x33, 0x21, 0x11, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x23, 0x33, 0x44, 0x45, +0x66, 0x67, 0x88, 0x88, 0x88, 0x88, 0x89, 0x99, 0x99, 0x99, 0x9a, +0xab, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, +0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, +0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xbb, +0xbb, 0xbb, 0xba, 0xa9, 0x99, 0x99, 0x99, 0x98, 0x88, 0x88, 0x88, +0x87, 0x76, 0x66, 0x64, 0x44, 0x33, 0x32, 0x11, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x33, 0x34, 0x44, 0x56, +0x66, 0x66, 0x66, 0x77, 0x88, 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, +0x99, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xcc, 0xcc, 0xcc, 0xcd, +0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, +0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xbb, 0xbb, +0xbb, 0xaa, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88, 0x77, +0x66, 0x66, 0x66, 0x65, 0x44, 0x43, 0x33, 0x21, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x23, 0x33, 0x44, 0x44, 0x45, +0x56, 0x66, 0x66, 0x66, 0x67, 0x78, 0x88, 0x88, 0x88, 0x89, 0x99, +0x99, 0x99, 0x9a, 0xab, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcd, +0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xde, 0xee, 0xee, 0xee, +0xee, 0xee, 0xdd, 0xdc, 0xcc, 0xcc, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, +0xa9, 0x99, 0x99, 0x99, 0x98, 0x88, 0x88, 0x88, 0x87, 0x76, 0x66, +0x66, 0x66, 0x65, 0x44, 0x44, 0x44, 0x33, 0x33, 0x11, 0x10, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x33, 0x33, 0x44, 0x44, 0x44, +0x44, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x88, 0x88, 0x88, 0x88, +0x99, 0x99, 0x99, 0x99, 0xaa, 0xbb, 0xbb, 0xbb, 0xbc, 0xcc, 0xcd, +0xdd, 0xee, 0xee, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xee, 0xee, 0xee, +0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcb, 0xbb, 0xbb, 0xbb, 0xaa, 0x99, +0x99, 0x99, 0x99, 0x88, 0x88, 0x88, 0x88, 0x77, 0x66, 0x66, 0x66, +0x66, 0x54, 0x44, 0x44, 0x44, 0x43, 0x33, 0x33, 0x31, 0x11, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x01, 0x11, 0x23, 0x33, 0x33, 0x33, 0x34, 0x44, +0x44, 0x44, 0x45, 0x56, 0x66, 0x66, 0x66, 0x67, 0x78, 0x88, 0x88, +0x88, 0x89, 0x99, 0x99, 0x99, 0x9a, 0xab, 0xbb, 0xbc, 0xcc, 0xcc, +0xdd, 0xde, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xdd, 0xdd, 0xee, 0xee, +0xee, 0xed, 0xdd, 0xdc, 0xcc, 0xcb, 0xbb, 0xba, 0xa9, 0x99, 0x99, +0x99, 0x98, 0x88, 0x88, 0x88, 0x87, 0x76, 0x66, 0x66, 0x66, 0x65, +0x44, 0x44, 0x44, 0x44, 0x43, 0x33, 0x33, 0x33, 0x32, 0x11, 0x10, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x12, 0x33, 0x33, 0x33, 0x33, +0x44, 0x44, 0x44, 0x44, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x88, +0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0x9a, 0xbb, 0xbc, 0xcc, 0xcc, +0xdd, 0xde, 0xee, 0xee, 0xed, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xee, +0xee, 0xed, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0xa9, 0x99, 0x99, 0x99, +0x88, 0x88, 0x88, 0x88, 0x77, 0x66, 0x66, 0x66, 0x66, 0x54, 0x44, +0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33, 0x21, 0x11, 0x11, 0x11, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x23, 0x33, 0x33, +0x33, 0x34, 0x44, 0x44, 0x44, 0x45, 0x56, 0x66, 0x66, 0x66, 0x67, +0x78, 0x88, 0x88, 0x88, 0x89, 0x99, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, +0xdd, 0xde, 0xee, 0xed, 0xdd, 0xdd, 0xdc, 0xcd, 0xdd, 0xdd, 0xde, +0xee, 0xed, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0x99, 0x99, 0x98, 0x88, +0x88, 0x88, 0x87, 0x76, 0x66, 0x66, 0x66, 0x65, 0x44, 0x44, 0x44, +0x44, 0x43, 0x33, 0x33, 0x33, 0x32, 0x11, 0x11, 0x11, 0x11, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x11, 0x11, 0x12, 0x33, +0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55, 0x66, 0x66, 0x66, +0x66, 0x77, 0x88, 0x88, 0x88, 0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, +0xdd, 0xde, 0xed, 0xdd, 0xdd, 0xdc, 0xcc, 0xcc, 0xcd, 0xdd, 0xdd, +0xde, 0xed, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0x99, 0x98, 0x88, 0x88, +0x88, 0x77, 0x66, 0x66, 0x66, 0x66, 0x54, 0x44, 0x44, 0x44, 0x44, +0x33, 0x33, 0x33, 0x33, 0x21, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, +0x23, 0x33, 0x33, 0x33, 0x34, 0x44, 0x44, 0x44, 0x45, 0x56, 0x66, +0x66, 0x66, 0x67, 0x78, 0x88, 0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, +0xdd, 0xde, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, +0xdd, 0xed, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0x99, 0x98, 0x88, 0x87, +0x76, 0x66, 0x66, 0x66, 0x65, 0x44, 0x44, 0x44, 0x44, 0x43, 0x33, +0x33, 0x33, 0x32, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x11, +0x11, 0x12, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55, +0x66, 0x66, 0x66, 0x66, 0x78, 0x89, 0x99, 0xbb, 0xbc, 0xcc, 0xcc, +0xdd, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, +0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, 0xbb, 0x99, 0x98, 0x87, 0x66, +0x66, 0x66, 0x66, 0x54, 0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, +0x33, 0x21, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, +0x11, 0x11, 0x11, 0x23, 0x33, 0x33, 0x33, 0x34, 0x44, 0x44, 0x44, +0x44, 0x56, 0x66, 0x66, 0x68, 0x88, 0x99, 0xab, 0xbc, 0xcc, 0xcc, +0xdd, 0xdd, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, +0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, 0xba, 0x99, 0x88, 0x86, 0x66, +0x66, 0x65, 0x44, 0x44, 0x44, 0x44, 0x43, 0x33, 0x33, 0x33, 0x32, +0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x11, 0x11, 0x12, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, +0x44, 0x44, 0x55, 0x66, 0x68, 0x88, 0x99, 0xab, 0xbc, 0xcc, 0xcc, +0xdd, 0xdd, 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, +0xcd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, 0xba, 0x99, 0x88, 0x86, 0x66, +0x54, 0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33, 0x21, 0x11, +0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x23, 0x33, 0x33, 0x33, 0x34, +0x44, 0x44, 0x44, 0x56, 0x68, 0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, +0xdd, 0xdd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbc, 0xcc, 0xcc, 0xcc, +0xcc, 0xdd, 0xdd, 0xcc, 0xcc, 0xcb, 0xba, 0x99, 0x88, 0x86, 0x66, +0x44, 0x44, 0x44, 0x43, 0x33, 0x33, 0x33, 0x32, 0x11, 0x11, 0x11, +0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x11, 0x11, 0x11, 0x12, 0x33, 0x33, 0x33, +0x33, 0x44, 0x44, 0x56, 0x68, 0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, +0xdd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xdd, 0xcc, 0xcc, 0xbb, 0xba, 0x99, 0x88, 0x86, 0x65, +0x44, 0x43, 0x33, 0x33, 0x33, 0x33, 0x21, 0x11, 0x11, 0x11, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x23, 0x33, +0x33, 0x33, 0x44, 0x56, 0x67, 0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xba, 0x99, 0x88, 0x76, 0x65, +0x44, 0x33, 0x33, 0x33, 0x32, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x11, 0x11, 0x12, +0x33, 0x33, 0x44, 0x56, 0x67, 0x88, 0x99, 0xab, 0xbb, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xba, 0x99, 0x88, 0x76, 0x65, +0x44, 0x33, 0x33, 0x21, 0x11, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, +0x12, 0x33, 0x44, 0x56, 0x67, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0xcb, 0xbb, 0xbb, 0xa9, 0x9a, 0xbb, 0xbb, 0xbc, +0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xb9, 0x99, 0x88, 0x76, 0x65, +0x44, 0x33, 0x21, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, +0x12, 0x33, 0x44, 0x46, 0x67, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, +0xcc, 0xcc, 0xcb, 0xbb, 0xbb, 0xba, 0x99, 0x99, 0xab, 0xbb, 0xbb, +0xbc, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xb9, 0x99, 0x88, 0x76, 0x65, +0x44, 0x33, 0x21, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x12, 0x33, 0x44, 0x46, 0x67, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, +0xcc, 0xcc, 0xbb, 0xbb, 0xba, 0x99, 0x99, 0x99, 0x99, 0xab, 0xbb, +0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xbb, 0xb9, 0x99, 0x88, 0x76, 0x64, +0x44, 0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x12, 0x33, 0x44, 0x46, 0x66, 0x88, 0x99, 0x9b, 0xbb, 0xcc, 0xcc, +0xcc, 0xbb, 0xbb, 0xbb, 0xa9, 0x99, 0x99, 0x99, 0x99, 0x9a, 0xbb, +0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xbb, 0xb9, 0x99, 0x88, 0x66, 0x64, +0x44, 0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x33, 0x44, 0x46, 0x66, 0x88, 0x89, 0x9a, 0xbb, 0xcc, 0xcc, +0xcb, 0xbb, 0xbb, 0xa9, 0x99, 0x99, 0x88, 0x88, 0x99, 0x99, 0x9a, +0xbb, 0xbb, 0xbc, 0xcc, 0xcc, 0xbb, 0xb9, 0x99, 0x88, 0x66, 0x64, +0x44, 0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x33, 0x34, 0x46, 0x66, 0x88, 0x89, 0x9a, 0xbb, 0xcc, 0xcb, +0xbb, 0xbb, 0xba, 0x99, 0x99, 0x98, 0x88, 0x88, 0x88, 0x99, 0x99, +0xab, 0xbb, 0xbb, 0xbc, 0xcc, 0xbb, 0xa9, 0x98, 0x88, 0x66, 0x64, +0x43, 0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x33, 0x34, 0x45, 0x66, 0x88, 0x89, 0x9a, 0xbb, 0xcc, 0xbb, +0xbb, 0xba, 0x99, 0x99, 0x98, 0x88, 0x88, 0x88, 0x88, 0x89, 0x99, +0x99, 0xab, 0xbb, 0xbb, 0xcc, 0xbb, 0xa9, 0x98, 0x88, 0x66, 0x64, +0x43, 0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x33, 0x34, 0x45, 0x66, 0x88, 0x89, 0x9a, 0xbb, 0xbb, 0xbb, +0xbb, 0xa9, 0x99, 0x98, 0x88, 0x88, 0x76, 0x67, 0x88, 0x88, 0x89, +0x99, 0x99, 0xbb, 0xbb, 0xbb, 0xbb, 0xa9, 0x98, 0x88, 0x66, 0x54, +0x43, 0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x33, 0x34, 0x45, 0x66, 0x78, 0x89, 0x9a, 0xbb, 0xbb, 0xbb, +0xa9, 0x99, 0x99, 0x88, 0x88, 0x86, 0x66, 0x66, 0x68, 0x88, 0x88, +0x99, 0x99, 0x9a, 0xbb, 0xbb, 0xbb, 0xa9, 0x98, 0x87, 0x66, 0x54, +0x43, 0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x34, 0x45, 0x66, 0x78, 0x89, 0x9a, 0xbb, 0xbb, 0xba, +0x99, 0x99, 0x88, 0x88, 0x87, 0x66, 0x66, 0x66, 0x66, 0x78, 0x88, +0x88, 0x99, 0x99, 0x9a, 0xbb, 0xbb, 0xa9, 0x98, 0x87, 0x66, 0x54, +0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x34, 0x45, 0x66, 0x78, 0x89, 0x99, 0xbb, 0xba, 0x99, +0x99, 0x98, 0x88, 0x88, 0x66, 0x66, 0x65, 0x56, 0x66, 0x66, 0x78, +0x88, 0x89, 0x99, 0x99, 0xab, 0xbb, 0xa9, 0x98, 0x87, 0x66, 0x54, +0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x34, 0x44, 0x66, 0x78, 0x89, 0x99, 0xbb, 0xa9, 0x99, +0x98, 0x88, 0x88, 0x76, 0x66, 0x66, 0x44, 0x44, 0x56, 0x66, 0x67, +0x88, 0x88, 0x89, 0x99, 0x99, 0xab, 0x99, 0x98, 0x87, 0x66, 0x54, +0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x34, 0x44, 0x66, 0x78, 0x89, 0x99, 0xa9, 0x99, 0x99, +0x88, 0x88, 0x86, 0x66, 0x66, 0x54, 0x44, 0x44, 0x45, 0x66, 0x66, +0x67, 0x88, 0x88, 0x99, 0x99, 0x9a, 0x99, 0x98, 0x87, 0x66, 0x44, +0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x23, 0x34, 0x44, 0x66, 0x68, 0x89, 0x99, 0x99, 0x99, 0x88, +0x88, 0x87, 0x66, 0x66, 0x64, 0x44, 0x44, 0x44, 0x44, 0x45, 0x66, +0x66, 0x78, 0x88, 0x88, 0x99, 0x99, 0x99, 0x98, 0x87, 0x66, 0x44, +0x43, 0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x13, 0x34, 0x44, 0x66, 0x68, 0x88, 0x99, 0x99, 0x98, 0x88, +0x87, 0x66, 0x66, 0x65, 0x44, 0x44, 0x33, 0x33, 0x44, 0x44, 0x56, +0x66, 0x66, 0x78, 0x88, 0x88, 0x99, 0x99, 0x98, 0x86, 0x66, 0x44, +0x43, 0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x13, 0x33, 0x44, 0x66, 0x68, 0x88, 0x99, 0x98, 0x88, 0x88, +0x76, 0x66, 0x65, 0x44, 0x44, 0x43, 0x33, 0x33, 0x34, 0x44, 0x44, +0x56, 0x66, 0x67, 0x88, 0x88, 0x89, 0x99, 0x88, 0x86, 0x66, 0x44, +0x43, 0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x13, 0x33, 0x44, 0x66, 0x68, 0x88, 0x99, 0x88, 0x88, 0x76, +0x66, 0x66, 0x54, 0x44, 0x43, 0x33, 0x33, 0x33, 0x33, 0x34, 0x44, +0x45, 0x66, 0x66, 0x67, 0x88, 0x88, 0x89, 0x88, 0x86, 0x66, 0x44, +0x33, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x13, 0x33, 0x44, 0x56, 0x68, 0x88, 0x88, 0x88, 0x87, 0x66, +0x66, 0x54, 0x44, 0x44, 0x33, 0x33, 0x21, 0x12, 0x33, 0x33, 0x44, +0x44, 0x45, 0x66, 0x66, 0x78, 0x88, 0x88, 0x88, 0x86, 0x65, 0x44, +0x33, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x13, 0x33, 0x44, 0x56, 0x67, 0x88, 0x88, 0x87, 0x66, 0x66, +0x65, 0x44, 0x44, 0x33, 0x33, 0x32, 0x11, 0x11, 0x23, 0x33, 0x33, +0x44, 0x44, 0x46, 0x66, 0x66, 0x78, 0x88, 0x88, 0x86, 0x65, 0x44, +0x33, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x56, 0x67, 0x88, 0x88, 0x76, 0x66, 0x65, +0x44, 0x44, 0x43, 0x33, 0x32, 0x11, 0x11, 0x11, 0x11, 0x23, 0x33, +0x34, 0x44, 0x44, 0x56, 0x66, 0x66, 0x88, 0x88, 0x76, 0x65, 0x44, +0x33, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x56, 0x67, 0x88, 0x76, 0x66, 0x66, 0x54, +0x44, 0x43, 0x33, 0x33, 0x21, 0x11, 0x10, 0x01, 0x11, 0x12, 0x33, +0x33, 0x34, 0x44, 0x44, 0x66, 0x66, 0x67, 0x88, 0x76, 0x65, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x56, 0x67, 0x87, 0x66, 0x66, 0x54, 0x44, +0x44, 0x33, 0x33, 0x21, 0x11, 0x11, 0x00, 0x00, 0x11, 0x11, 0x12, +0x33, 0x33, 0x34, 0x44, 0x45, 0x66, 0x66, 0x67, 0x76, 0x65, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x66, 0x66, 0x65, 0x44, 0x44, +0x33, 0x33, 0x32, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, +0x13, 0x33, 0x33, 0x44, 0x44, 0x45, 0x66, 0x66, 0x66, 0x65, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x12, 0x33, 0x44, 0x46, 0x66, 0x66, 0x65, 0x44, 0x44, 0x43, +0x33, 0x32, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, +0x11, 0x23, 0x33, 0x33, 0x44, 0x44, 0x56, 0x66, 0x66, 0x64, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x33, 0x44, 0x46, 0x66, 0x66, 0x44, 0x44, 0x43, 0x33, +0x33, 0x21, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x11, 0x33, 0x33, 0x34, 0x44, 0x44, 0x56, 0x66, 0x64, 0x44, +0x33, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x33, 0x34, 0x46, 0x66, 0x54, 0x44, 0x44, 0x33, 0x33, +0x21, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x11, 0x12, 0x33, 0x33, 0x34, 0x44, 0x45, 0x66, 0x64, 0x44, +0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x33, 0x34, 0x46, 0x64, 0x44, 0x44, 0x33, 0x33, 0x32, +0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x11, 0x13, 0x33, 0x33, 0x44, 0x44, 0x45, 0x64, 0x43, +0x33, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x33, 0x34, 0x44, 0x44, 0x44, 0x43, 0x33, 0x32, 0x11, +0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x11, 0x11, 0x23, 0x33, 0x33, 0x44, 0x44, 0x44, 0x43, +0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x33, 0x34, 0x44, 0x44, 0x43, 0x33, 0x33, 0x21, 0x11, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x11, 0x11, 0x23, 0x33, 0x34, 0x44, 0x44, 0x43, +0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x34, 0x44, 0x43, 0x33, 0x33, 0x21, 0x11, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x11, 0x12, 0x33, 0x33, 0x34, 0x44, 0x43, +0x33, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x34, 0x44, 0x33, 0x33, 0x31, 0x11, 0x11, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x12, 0x33, 0x33, 0x44, 0x43, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x34, 0x33, 0x33, 0x32, 0x11, 0x11, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x23, 0x33, 0x33, 0x43, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x33, 0x33, 0x33, 0x11, 0x11, 0x10, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x11, 0x23, 0x33, 0x33, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x23, 0x33, 0x33, 0x21, 0x11, 0x10, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x12, 0x33, 0x33, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x13, 0x33, 0x31, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x12, 0x33, +0x32, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x13, 0x32, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x13, +0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x11, 0x12, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x11, +0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x11, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, +0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00}; \ No newline at end of file diff --git a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino index ab823ea..7530051 100644 --- a/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino +++ b/examples/Test and diagnostics/Read_User_Setup/Read_User_Setup.ino @@ -61,6 +61,7 @@ if (user.tft_driver != 0xE9D) // For ePaper displays the size is defined in the Serial.print("Display driver = "); Serial.println(user.tft_driver, HEX); // Hexadecimal code Serial.print("Display width = "); Serial.println(user.tft_width); // Rotation 0 width and height Serial.print("Display height = "); Serial.println(user.tft_height); + Serial.println(); } else if (user.tft_driver == 0xE9D) Serial.println("Display driver = ePaper\n"); @@ -97,6 +98,10 @@ if (user.overlap == true) } #endif String pinNameRef = "GPIO "; +#ifdef ESP8266 + pinNameRef = "PIN_D"; +#endif + if (user.esp == 0x32F) { Serial.println("\n>>>>> Note: STM32 pin references above D15 may not reflect board markings <<<<<"); pinNameRef = "D"; @@ -119,6 +124,15 @@ if (user.pin_tft_d5 != -1) { Serial.print("TFT_D5 = " + pinNameRef); Serial.pr if (user.pin_tft_d6 != -1) { Serial.print("TFT_D6 = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d6)); } if (user.pin_tft_d7 != -1) { Serial.print("TFT_D7 = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d7)); } +#if defined (TFT_BL) + Serial.print("\nTFT_BL = " + pinNameRef); Serial.println(getPinName(user.pin_tft_led)); + #if defined (TFT_BACKLIGHT_ON) + Serial.print("TFT_BACKLIGHT_ON = "); Serial.println(user.pin_tft_led_on == HIGH ? "HIGH" : "LOW"); + #endif +#endif + +Serial.println(); + uint16_t fonts = tft.fontsLoaded(); if (fonts & (1 << 1)) Serial.print("Font GLCD loaded\n"); if (fonts & (1 << 2)) Serial.print("Font 2 loaded\n"); diff --git a/keywords.txt b/keywords.txt index 2debf03..41b73b6 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,27 +1,34 @@ +# TFT_eSPI core library + TFT_eSPI KEYWORD1 +begin KEYWORD2 init KEYWORD2 drawPixel KEYWORD2 drawChar KEYWORD2 -setAddrWindow KEYWORD2 -setWindow KEYWORD2 -startWrite KEYWORD2 -writeColor KEYWORD2 -endWrite KEYWORD2 -pushColor KEYWORD2 -pushColors KEYWORD2 -fillScreen KEYWORD2 -writeBegin KEYWORD2 -writeEnd KEYWORD2 drawLine KEYWORD2 drawFastVLine KEYWORD2 drawFastHLine KEYWORD2 -drawRect KEYWORD2 fillRect KEYWORD2 +height KEYWORD2 +width KEYWORD2 +setRotation KEYWORD2 +getRotation KEYWORD2 +invertDisplay KEYWORD2 +setAddrWindow KEYWORD2 +setWindow KEYWORD2 +pushColor KEYWORD2 +pushColors KEYWORD2 +pushBlock KEYWORD2 +pushPixels KEYWORD2 +readPixel KEYWORD2 +tft_Read_8 KEYWORD2 +begin_SDA_Read KEYWORD2 +end_SDA_Read KEYWORD2 +fillScreen KEYWORD2 +drawRect KEYWORD2 drawRoundRect KEYWORD2 fillRoundRect KEYWORD2 -setRotation KEYWORD2 -invertDisplay KEYWORD2 drawCircle KEYWORD2 drawCircleHelper KEYWORD2 fillCircle KEYWORD2 @@ -30,18 +37,40 @@ drawEllipse KEYWORD2 fillEllipse KEYWORD2 drawTriangle KEYWORD2 fillTriangle KEYWORD2 +setSwapBytes KEYWORD2 +getSwapBytes KEYWORD2 drawBitmap KEYWORD2 drawXBitmap KEYWORD2 +setPivot KEYWORD2 +getPivotX KEYWORD2 +getPivotY KEYWORD2 +readRect KEYWORD2 +pushRect KEYWORD2 +pushImage KEYWORD2 +readRectRGB KEYWORD2 +drawNumber KEYWORD2 +drawFloat KEYWORD2 +drawString KEYWORD2 +drawCentreString KEYWORD2 +drawRightString KEYWORD2 setCursor KEYWORD2 getCursorX KEYWORD2 getCursorY KEYWORD2 setTextColor KEYWORD2 setTextSize KEYWORD2 -setTextFont KEYWORD2 -setFreeFont KEYWORD2 setTextWrap KEYWORD2 setTextDatum KEYWORD2 +getTextDatum KEYWORD2 setTextPadding KEYWORD2 +getTextPadding KEYWORD2 +setFreeFont KEYWORD2 +setTextFont KEYWORD2 +textWidth KEYWORD2 +fontHeight KEYWORD2 +decodeUTF8 KEYWORD2 +write KEYWORD2 +setCallback KEYWORD2 +fontsLoaded KEYWORD2 spiwrite KEYWORD2 writecommand KEYWORD2 writedata KEYWORD2 @@ -49,56 +78,51 @@ commandList KEYWORD2 readcommand8 KEYWORD2 readcommand16 KEYWORD2 readcommand32 KEYWORD2 -readPixel KEYWORD2 -readRect KEYWORD2 -pushRect KEYWORD2 -pushImage KEYWORD2 -setSwapBytes KEYWORD2 -getSwapBytes KEYWORD2 -readRectRGB KEYWORD2 -getRotation KEYWORD2 -getTextDatum KEYWORD2 -fontsLoaded KEYWORD2 color565 KEYWORD2 -color16to8 KEYWORD2 color8to16 KEYWORD2 -drawNumber KEYWORD2 -drawFloat KEYWORD2 -drawString KEYWORD2 -drawCentreString KEYWORD2 -drawRightString KEYWORD2 -height KEYWORD2 -width KEYWORD2 -textWidth KEYWORD2 -fontHeight KEYWORD2 -getSetup KEYWORD2 -setAttribute KEYWORD2 -getAttribute KEYWORD2 +color16to8 KEYWORD2 +color16to24 KEYWORD2 +color24to16 KEYWORD2 alphaBlend KEYWORD2 - -getSPIinstance KEYWORD2 -pushBlock KEYWORD2 -pushPixels KEYWORD2 - +alphaBlend24 KEYWORD2 initDMA KEYWORD2 deInitDMA KEYWORD2 pushImageDMA KEYWORD2 -pushBlockDMA KEYWORD2 pushPixelsDMA KEYWORD2 dmaBusy KEYWORD2 dmaWait KEYWORD2 +startWrite KEYWORD2 +writeColor KEYWORD2 +endWrite KEYWORD2 +setAttribute KEYWORD2 +getAttribute KEYWORD2 +getSetup KEYWORD2 +getSPIinstance KEYWORD2 + + +# Touch functions getTouchRaw KEYWORD2 -convertRawXY KEYWORD2 getTouchRawZ KEYWORD2 +convertRawXY KEYWORD2 getTouch KEYWORD2 calibrateTouch KEYWORD2 setTouch KEYWORD2 + +# Smooth font functions + +loadFont KEYWORD2 +unloadFont KEYWORD2 +getUnicodeIndex KEYWORD2 +showFont KEYWORD2 + + +# Button class + TFT_eSPI_Button KEYWORD1 initButton KEYWORD2 -textcolor KEYWORD2 initButtonUL KEYWORD2 setLabelDatum KEYWORD2 drawButton KEYWORD2 @@ -109,31 +133,31 @@ justPressed KEYWORD2 justReleased KEYWORD2 +# Sprite class + TFT_eSprite KEYWORD1 createSprite KEYWORD2 -createPalette KEYWORD2 +getPointer KEYWORD2 +created KEYWORD2 +deleteSprite KEYWORD2 +frameBuffer KEYWORD2 setColorDepth KEYWORD2 getColorDepth KEYWORD2 -deleteSprite KEYWORD2 +createPalette KEYWORD2 +setPaletteColor KEYWORD2 +getPaletteColor KEYWORD2 +setBitmapColor KEYWORD2 +fillSprite KEYWORD2 +setScrollRect KEYWORD2 +scroll KEYWORD2 pushRotated KEYWORD2 -pushRotatedHP KEYWORD2 -rotatedBounds KEYWORD2 setPivot KEYWORD2 getPivotX KEYWORD2 getPivotY KEYWORD2 -fillSprite KEYWORD2 -pushBitmap KEYWORD2 -pushSprite KEYWORD2 -setScrollRect KEYWORD2 -scroll KEYWORD2 -printToSprite KEYWORD2 -frameBuffer KEYWORD2 -setBitmapColor KEYWORD2 - -showFont KEYWORD2 -loadFont KEYWORD2 -unloadFont KEYWORD2 -getUnicodeIndex KEYWORD2 -decodeUTF8 KEYWORD2 +getRotatedBounds KEYWORD2 +readPixelValue KEYWORD2 +pushToSprite KEYWORD2 drawGlyph KEYWORD2 +printToSprite KEYWORD2 +pushSprite KEYWORD2 diff --git a/library.json b/library.json index 3bed7b0..07bd791 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "2.2.4", + "version": "2.2.23", "keywords": "Arduino, tft, ePaper, display, STM32, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9486, ST7789, RM68140", "description": "A TFT and ePaper SPI graphics library with optimisation for ESP8266, ESP32 and STM32", "repository": diff --git a/library.properties b/library.properties index 972e24a..caef235 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=2.2.4 +version=2.2.23 author=Bodmer maintainer=Bodmer sentence=TFT graphics library for Arduino processors with performance optimisation for STM32, ESP8266 and ESP32