807 lines
21 KiB
C
807 lines
21 KiB
C
|
/*
|
||
|
Copyright (C) 2016 Ignacio Vina (@igvina)
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
*/
|
||
|
|
||
|
// ArdBitmap: version 2.0.3
|
||
|
|
||
|
#ifndef ARDBITMAP_H
|
||
|
#define ARDBITMAP_H
|
||
|
|
||
|
//Uncomment NO_SPEED_HACK if speed is not important (reduce ~100 bytes)
|
||
|
//#define NO_SPEED_HACK
|
||
|
|
||
|
//Uncomment RESIZE_HACK for fast drawResized with resize >= 1.0
|
||
|
//#define RESIZE_HACK
|
||
|
|
||
|
#include <Arduino.h>
|
||
|
|
||
|
#define ALIGN_H_LEFT 0b00000000
|
||
|
#define ALIGN_H_RIGHT 0b00000001
|
||
|
#define ALIGN_H_CENTER 0b00000010
|
||
|
#define ALIGN_V_TOP 0b00000000
|
||
|
#define ALIGN_V_BOTTOM 0b00000100
|
||
|
#define ALIGN_V_CENTER 0b00001000
|
||
|
#define ALIGN_CENTER 0b00001010
|
||
|
#define ALIGN_NONE 0b00000000
|
||
|
|
||
|
#define MIRROR_NONE 0b00000000
|
||
|
#define MIRROR_HORIZONTAL 0b00000001
|
||
|
#define MIRROR_VERTICAL 0b00000010
|
||
|
#define MIRROR_HOR_VER 0b00000011
|
||
|
|
||
|
static const uint8_t BIT_SHIFT[8] = {
|
||
|
0b00000001,
|
||
|
0b00000010,
|
||
|
0b00000100,
|
||
|
0b00001000,
|
||
|
0b00010000,
|
||
|
0b00100000,
|
||
|
0b01000000,
|
||
|
0b10000000,
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
static const uint8_t REVERSE_16[16] = { 0, 8, 4, 12,
|
||
|
2, 10, 6, 14 ,
|
||
|
1, 9, 5, 13,
|
||
|
3, 11, 7, 15 };
|
||
|
|
||
|
static const uint8_t REVERSE_256[256] = {
|
||
|
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
|
||
|
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
|
||
|
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
|
||
|
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
|
||
|
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
|
||
|
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
|
||
|
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
|
||
|
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
|
||
|
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
|
||
|
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
|
||
|
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
|
||
|
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
|
||
|
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
|
||
|
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
|
||
|
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
|
||
|
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
|
||
|
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
|
||
|
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
|
||
|
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
|
||
|
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
|
||
|
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
|
||
|
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
||
|
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
|
||
|
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
|
||
|
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
|
||
|
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
|
||
|
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
|
||
|
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
|
||
|
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
|
||
|
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
|
||
|
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
|
||
|
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
|
||
|
};
|
||
|
*/
|
||
|
|
||
|
template<int16_t SB_WIDTH, int16_t SB_HEIGHT> class ArdBitmap
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
void drawCompressed(int16_t sx, int16_t sy, const uint8_t *compBitmap, uint8_t color, uint8_t align, uint8_t mirror);
|
||
|
void drawCompressedResized(int16_t sx, int16_t sy, const uint8_t *compBitmap, uint8_t color,uint8_t align, uint8_t mirror, float resize);
|
||
|
|
||
|
void drawBitmap(int16_t sx, int16_t sy, const uint8_t *bitmap,uint8_t w, uint8_t h, uint8_t color, uint8_t align, uint8_t mirror);
|
||
|
void drawBitmapResized(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t w,uint8_t h, uint8_t color,uint8_t align, uint8_t mirror, float resize);
|
||
|
};
|
||
|
|
||
|
////////////////////////
|
||
|
// COMPRESSED BITMAPS //
|
||
|
////////////////////////
|
||
|
|
||
|
template<int16_t SB_WIDTH, int16_t SB_HEIGHT>
|
||
|
void ArdBitmap<SB_WIDTH, SB_HEIGHT>::drawCompressed(int16_t sx, int16_t sy, const uint8_t *compBitmap, uint8_t color, uint8_t align, uint8_t mirror)
|
||
|
{
|
||
|
//TODO: check why int16_t sizeCounter is a bit faster than uint16_t sizeCounter
|
||
|
int16_t sizeCounter;
|
||
|
uint16_t len;
|
||
|
int a, iCol;
|
||
|
uint8_t decByte;
|
||
|
uint8_t w, h;
|
||
|
uint8_t col;
|
||
|
boolean scanMode, scanZigZag;
|
||
|
uint16_t encoderPos;
|
||
|
uint8_t characterPos;
|
||
|
|
||
|
|
||
|
// Read size from header (Max image size = 128 x 64)
|
||
|
|
||
|
uint8_t byte0 = pgm_read_byte(&compBitmap[0]);
|
||
|
uint8_t byte1 = pgm_read_byte(&compBitmap[1]);
|
||
|
|
||
|
w = (byte0 & 0b01111111) + 1;
|
||
|
h = (byte1 & 0b00111111) + 1;
|
||
|
|
||
|
// Move positions to match alignment
|
||
|
|
||
|
if (align & ALIGN_H_CENTER) {
|
||
|
sx -= (w / 2);
|
||
|
} else if (align & ALIGN_H_RIGHT) {
|
||
|
sx -= w;
|
||
|
}
|
||
|
|
||
|
if (align & ALIGN_V_CENTER) {
|
||
|
sy -= (h / 2);
|
||
|
} else if (align & ALIGN_V_BOTTOM) {
|
||
|
sy -= h;
|
||
|
}
|
||
|
|
||
|
// No need to draw at all if we're offscreen
|
||
|
if (sx + w < 0 || sx > SB_WIDTH - 1 || sy + h < 0 || sy > SB_HEIGHT - 1)
|
||
|
return;
|
||
|
|
||
|
col = (byte0 >> 7) & 0x01;
|
||
|
scanMode = ((byte1 >> 6) & 0x01) > 0;
|
||
|
scanZigZag = ((byte1 >> 7) & 0x01) > 0;
|
||
|
|
||
|
int yOffset = abs(sy) % 8;
|
||
|
int sRow = sy / 8;
|
||
|
if (sy < 0 && yOffset > 0) {
|
||
|
sRow--;
|
||
|
yOffset = 8 - yOffset;
|
||
|
}
|
||
|
|
||
|
uint8_t data;
|
||
|
uint16_t bitmap_data;
|
||
|
uint8_t mul_amt = 1 << yOffset;
|
||
|
|
||
|
//uint16_t boffs;
|
||
|
|
||
|
int8_t rows = h / 8;
|
||
|
if (h % 8 != 0) rows++;
|
||
|
|
||
|
// Init values
|
||
|
iCol = 0;
|
||
|
decByte = 0;
|
||
|
encoderPos = 16;
|
||
|
characterPos = 7;
|
||
|
a = 0;
|
||
|
|
||
|
if (mirror & MIRROR_VERTICAL) {
|
||
|
a = rows - 1;
|
||
|
scanMode = !scanMode;
|
||
|
}
|
||
|
|
||
|
int iColMod = (mirror & MIRROR_HORIZONTAL) ? w - 1 : 0;
|
||
|
while (a < rows && a > -1) {
|
||
|
|
||
|
sizeCounter = 1;
|
||
|
while (((pgm_read_byte(&compBitmap[encoderPos / 8]) >> (encoderPos % 8)) & 0x01) == 1) {
|
||
|
sizeCounter ++;
|
||
|
encoderPos++;
|
||
|
}
|
||
|
encoderPos ++;
|
||
|
|
||
|
if (sizeCounter == 1) {
|
||
|
len = 1 + ((pgm_read_byte(&compBitmap[encoderPos / 8]) >> (encoderPos % 8)) & 0x01);
|
||
|
encoderPos++;
|
||
|
} else {
|
||
|
len = (1 << (sizeCounter - 1)) + 1 ;
|
||
|
|
||
|
//TODO: check why int j is faster than uint16_t j
|
||
|
for (int j = 0; j < sizeCounter - 1; j++) {
|
||
|
if (((pgm_read_byte(&compBitmap[encoderPos / 8]) >> (encoderPos % 8)) & 0x01) == 1) {
|
||
|
len += (1 << j);
|
||
|
}
|
||
|
encoderPos++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (uint16_t i = 0; i < len; i++)
|
||
|
{
|
||
|
|
||
|
#ifndef NO_SPEED_HACK
|
||
|
if (col == 0) {
|
||
|
if (len - i > characterPos) {
|
||
|
i += characterPos;
|
||
|
characterPos = 0;
|
||
|
} else {
|
||
|
characterPos -= (len - i - 1);
|
||
|
i = len;
|
||
|
}
|
||
|
} else if (len - i > characterPos) {
|
||
|
if (characterPos == 7) {
|
||
|
decByte = 0xFF;
|
||
|
} else {
|
||
|
decByte |= scanMode ? 0xFF >> (7 - characterPos) : (0xFF80 >> characterPos);
|
||
|
}
|
||
|
i += characterPos;
|
||
|
characterPos = 0;
|
||
|
} else {
|
||
|
decByte |= scanMode ? BIT_SHIFT[characterPos] : BIT_SHIFT[7 - characterPos];
|
||
|
}
|
||
|
#else
|
||
|
if (col) {
|
||
|
decByte |= scanMode ? BIT_SHIFT[characterPos] : BIT_SHIFT[7 - characterPos];
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
characterPos--;
|
||
|
|
||
|
if (characterPos == 0xFF){
|
||
|
|
||
|
//Paint decoded byte
|
||
|
int8_t bRow = sRow + a;
|
||
|
|
||
|
if (decByte && bRow < (SB_HEIGHT / 8) && iColMod + sx < SB_WIDTH && iColMod + sx >= 0){
|
||
|
|
||
|
bitmap_data = decByte * mul_amt;
|
||
|
|
||
|
if (bRow >= 0) {
|
||
|
|
||
|
data = ARDBITMAP_SBUF[(bRow * SB_WIDTH) + sx + iColMod];
|
||
|
if (color) {
|
||
|
data |= bitmap_data & 0xFF;
|
||
|
}else {
|
||
|
data &= ~(bitmap_data & 0xFF);
|
||
|
}
|
||
|
ARDBITMAP_SBUF[(bRow * SB_WIDTH) + sx + iColMod] = data;
|
||
|
}
|
||
|
|
||
|
if (yOffset && bRow < (SB_HEIGHT / 8) - 1 && bRow > -2) {
|
||
|
|
||
|
data = ARDBITMAP_SBUF[((bRow + 1) * SB_WIDTH) + sx + iColMod];
|
||
|
if (color) {
|
||
|
data |= ((bitmap_data >> 8) & 0xFF);
|
||
|
} else {
|
||
|
data &= ~(((bitmap_data >> 8) & 0xFF));
|
||
|
}
|
||
|
ARDBITMAP_SBUF[((bRow + 1)*SB_WIDTH) + sx + iColMod] = data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Iterate next column-byte
|
||
|
|
||
|
if (scanZigZag) {
|
||
|
scanMode = !scanMode;
|
||
|
}
|
||
|
|
||
|
iCol++;
|
||
|
|
||
|
if(mirror & MIRROR_HORIZONTAL){
|
||
|
iColMod--;
|
||
|
}else{
|
||
|
iColMod++;
|
||
|
}
|
||
|
if (iCol >= w){
|
||
|
|
||
|
iCol = 0;
|
||
|
if (mirror & MIRROR_VERTICAL) {
|
||
|
a--;
|
||
|
} else {
|
||
|
a++;
|
||
|
}
|
||
|
|
||
|
iColMod = (mirror & MIRROR_HORIZONTAL) ? w - 1 : 0;
|
||
|
}
|
||
|
|
||
|
// Reset decoded byte
|
||
|
decByte = 0;
|
||
|
characterPos = 7;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Toggle color for next span
|
||
|
col = 1 - col;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
template<int16_t SB_WIDTH, int16_t SB_HEIGHT>
|
||
|
void ArdBitmap<SB_WIDTH, SB_HEIGHT>::drawCompressedResized(int16_t sx, int16_t sy, const uint8_t *compBitmap, uint8_t color,uint8_t align, uint8_t mirror, float resize)
|
||
|
{
|
||
|
|
||
|
//TODO: check if this can be done in a better way
|
||
|
#ifdef RESIZE_HACK
|
||
|
if (resize >= 1.0){
|
||
|
return drawCompressed(sx, sy, compBitmap, color, align, mirror);
|
||
|
}
|
||
|
#else
|
||
|
if (resize > 1.0){
|
||
|
resize = 1.0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//TODO: check why int16_t sizeCounter is a bit faster than uint16_t sizeCounter
|
||
|
int16_t sizeCounter;
|
||
|
uint16_t len;
|
||
|
uint8_t a, iCol;
|
||
|
uint8_t decByte;
|
||
|
uint8_t w, wRes, h, hRes;
|
||
|
uint8_t col;
|
||
|
boolean scanMode, scanZigZag;
|
||
|
uint16_t encoderPos;
|
||
|
uint8_t characterPos;
|
||
|
|
||
|
// Read size from header (Max image size = 128 x 64)
|
||
|
|
||
|
uint8_t byte0 = pgm_read_byte(&compBitmap[0]);
|
||
|
uint8_t byte1 = pgm_read_byte(&compBitmap[1]);
|
||
|
|
||
|
w = (byte0 & 0b01111111) + 1;
|
||
|
h = (byte1 & 0b00111111) + 1;
|
||
|
|
||
|
wRes = (uint8_t)(w * resize);
|
||
|
hRes = (uint8_t)(h * resize);
|
||
|
|
||
|
if (align & ALIGN_H_CENTER) {
|
||
|
sx -= (wRes / 2);
|
||
|
} else if (align & ALIGN_H_RIGHT) {
|
||
|
sx -= wRes;
|
||
|
}
|
||
|
|
||
|
if (align & ALIGN_V_CENTER) {
|
||
|
sy -= (hRes / 2);
|
||
|
} else if (align & ALIGN_V_BOTTOM) {
|
||
|
sy -= hRes;
|
||
|
}
|
||
|
|
||
|
// No need to draw at all if we're offscreen
|
||
|
if (sx + wRes < 0 || sx > SB_WIDTH - 1 || sy + hRes < 0 || sy > SB_HEIGHT - 1)
|
||
|
return;
|
||
|
|
||
|
col = (byte0 >> 7) & 0x01;
|
||
|
scanMode = ((byte1 >> 6) & 0x01) > 0;
|
||
|
scanZigZag = ((byte1 >> 7) & 0x01) > 0;
|
||
|
|
||
|
int yOffset = abs(sy) % 8;
|
||
|
int sRow = sy / 8;
|
||
|
if (sy < 0) {
|
||
|
sRow--;
|
||
|
yOffset = 8 - yOffset;
|
||
|
}
|
||
|
|
||
|
uint8_t data;
|
||
|
uint16_t bitmap_data;
|
||
|
uint8_t mul_amt = 1 << yOffset;
|
||
|
|
||
|
int rows = h / 8;
|
||
|
if (h % 8 != 0) rows++;
|
||
|
|
||
|
uint8_t rowsRes = hRes / 8;
|
||
|
if (hRes % 8 != 0) rowsRes++;
|
||
|
|
||
|
// Init values
|
||
|
iCol = 0;
|
||
|
decByte = 0;
|
||
|
encoderPos = 16;
|
||
|
characterPos = 7;
|
||
|
a = 0;
|
||
|
|
||
|
// Create Lookup tables to speed up drawing
|
||
|
|
||
|
uint8_t x_LUT[w];
|
||
|
|
||
|
for (uint8_t i=0 ; i < w; i++){
|
||
|
x_LUT[i] = 0xFF;
|
||
|
}
|
||
|
// Precalculate column translation (0xFF if skipped)
|
||
|
for (uint8_t i=0 ; i < wRes; i++){
|
||
|
x_LUT[((uint16_t)i * w) / wRes] = (mirror & MIRROR_HORIZONTAL) ? wRes - 1 - i : i;
|
||
|
}
|
||
|
|
||
|
uint8_t y_LUT[h];
|
||
|
|
||
|
for (uint8_t i=0 ; i < h; i++){
|
||
|
y_LUT[i] = 0xFF;
|
||
|
}
|
||
|
|
||
|
for (uint8_t i=0 ; i < hRes; i++){
|
||
|
y_LUT[((uint16_t)i * h) / hRes] = (mirror & MIRROR_VERTICAL) ? hRes - 1 - i : i;
|
||
|
}
|
||
|
|
||
|
while (a < rows && /*a > -1*/ a != 0xFF) {
|
||
|
|
||
|
sizeCounter = 1;
|
||
|
while (((pgm_read_byte(&compBitmap[encoderPos / 8]) >> (encoderPos % 8)) & 0x01) == 1) {
|
||
|
sizeCounter ++;
|
||
|
encoderPos++;
|
||
|
}
|
||
|
encoderPos ++;
|
||
|
|
||
|
if (sizeCounter == 1) {
|
||
|
len = 1 + ((pgm_read_byte(&compBitmap[encoderPos / 8]) >> (encoderPos % 8)) & 0x01);
|
||
|
encoderPos++;
|
||
|
} else {
|
||
|
len = (1 << (sizeCounter - 1)) + 1 ;
|
||
|
|
||
|
//TODO: check why int j is faster than uint16_t j
|
||
|
for (int j = 0; j < sizeCounter - 1; j++) {
|
||
|
if (((pgm_read_byte(&compBitmap[encoderPos / 8]) >> (encoderPos % 8)) & 0x01) == 1) {
|
||
|
len += (1 << j);
|
||
|
}
|
||
|
encoderPos++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (uint16_t i = 0; i < len; i++)
|
||
|
{
|
||
|
|
||
|
#ifndef NO_SPEED_HACK
|
||
|
if (col == 0) {
|
||
|
if (len - i > characterPos) {
|
||
|
i += characterPos;
|
||
|
characterPos = 0;
|
||
|
} else {
|
||
|
characterPos -= (len - i - 1);
|
||
|
i = len;
|
||
|
}
|
||
|
} else if (len - i > characterPos) {
|
||
|
if (characterPos == 7) {
|
||
|
decByte = 0xFF;
|
||
|
} else {
|
||
|
decByte |= scanMode ? 0xFF >> (7 - characterPos) : (0xFF80 >> characterPos);
|
||
|
}
|
||
|
i += characterPos;
|
||
|
characterPos = 0;
|
||
|
} else {
|
||
|
decByte |= scanMode ? BIT_SHIFT[characterPos] : BIT_SHIFT[7 - characterPos];
|
||
|
}
|
||
|
#else
|
||
|
if (col) {
|
||
|
decByte |= scanMode ? BIT_SHIFT[characterPos] : BIT_SHIFT[7 - characterPos];
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
characterPos--;
|
||
|
|
||
|
if (characterPos == 0xFF){
|
||
|
|
||
|
//Paint decoded byte
|
||
|
int aRow8 = a * 8;
|
||
|
int16_t iColMod = x_LUT[iCol] + sx;
|
||
|
|
||
|
// Skip if column not needed
|
||
|
if (x_LUT[iCol] != 0xFF && iColMod < SB_WIDTH && iColMod >= 0){
|
||
|
|
||
|
for (uint8_t s = 0; s < 8 ;s++){
|
||
|
|
||
|
if (y_LUT[aRow8+s] != 0xFF && decByte & BIT_SHIFT[s]){
|
||
|
|
||
|
//TODO: CHECK LIMITS ON LUT?
|
||
|
uint8_t row = (uint8_t)(y_LUT[aRow8+s]+sy) / 8;
|
||
|
|
||
|
if (row < (SB_HEIGHT / 8)) {
|
||
|
|
||
|
if (color) {
|
||
|
ARDBITMAP_SBUF[(row*SB_WIDTH) + (uint8_t)iColMod] |= BIT_SHIFT[((uint8_t)(y_LUT[aRow8+s]+sy) % 8)];
|
||
|
} else {
|
||
|
ARDBITMAP_SBUF[(row*SB_WIDTH) + (uint8_t)iColMod] &= ~ BIT_SHIFT[((uint8_t)(y_LUT[aRow8+s]+sy) % 8)];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Iterate next column-byte
|
||
|
|
||
|
if (scanZigZag) {
|
||
|
scanMode = !scanMode;
|
||
|
}
|
||
|
|
||
|
iCol++;
|
||
|
if (iCol >= w){
|
||
|
|
||
|
iCol = 0;
|
||
|
a++;
|
||
|
}
|
||
|
|
||
|
// Reset decoded byte
|
||
|
decByte = 0;
|
||
|
characterPos = 7;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
col = 1 - col; // toggle colour for next span
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//////////////////////////
|
||
|
// UNCOMPRESSED BITMAPS //
|
||
|
//////////////////////////
|
||
|
|
||
|
|
||
|
template<int16_t SB_WIDTH, int16_t SB_HEIGHT>
|
||
|
void ArdBitmap<SB_WIDTH, SB_HEIGHT>::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color, uint8_t align, uint8_t mirror)
|
||
|
{
|
||
|
|
||
|
// Move positions to match alignment
|
||
|
|
||
|
if (align & ALIGN_H_CENTER) {
|
||
|
x -= (w / 2);
|
||
|
} else if (align & ALIGN_H_RIGHT) {
|
||
|
x -= w;
|
||
|
}
|
||
|
|
||
|
if (align & ALIGN_V_CENTER) {
|
||
|
y -= (h / 2);
|
||
|
} else if (align & ALIGN_V_BOTTOM) {
|
||
|
y -= h;
|
||
|
}
|
||
|
|
||
|
// no need to draw at all of we're offscreen
|
||
|
if (x + w <= 0 || x > SB_WIDTH - 1 || y + h <= 0 || y > SB_HEIGHT - 1)
|
||
|
return;
|
||
|
|
||
|
if (bitmap == NULL)
|
||
|
return;
|
||
|
|
||
|
// xOffset technically doesn't need to be 16 bit but the math operations
|
||
|
// are measurably faster if it is
|
||
|
uint16_t xOffset, ofs;
|
||
|
int8_t yOffset = abs(y) % 8;
|
||
|
int8_t sRow = y / 8;
|
||
|
uint8_t loop_h, start_h, rendered_width;
|
||
|
|
||
|
if (y < 0 && yOffset > 0) {
|
||
|
sRow--;
|
||
|
yOffset = 8 - yOffset;
|
||
|
}
|
||
|
|
||
|
// if the left side of the render is offscreen skip those loops
|
||
|
if (x < 0) {
|
||
|
xOffset = abs(x);
|
||
|
} else {
|
||
|
xOffset = 0;
|
||
|
}
|
||
|
|
||
|
// if the right side of the render is offscreen skip those loops
|
||
|
if (x + w > SB_WIDTH - 1) {
|
||
|
rendered_width = ((SB_WIDTH - x) - xOffset);
|
||
|
} else {
|
||
|
rendered_width = (w - xOffset);
|
||
|
}
|
||
|
|
||
|
// if the top side of the render is offscreen skip those loops
|
||
|
if (sRow < -1) {
|
||
|
start_h = abs(sRow) - 1;
|
||
|
} else {
|
||
|
start_h = 0;
|
||
|
}
|
||
|
|
||
|
loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
|
||
|
|
||
|
// if (sRow + loop_h - 1 > (SB_HEIGHT/8)-1)
|
||
|
if (sRow + loop_h > (SB_HEIGHT / 8)) {
|
||
|
loop_h = (SB_HEIGHT / 8) - sRow;
|
||
|
}
|
||
|
|
||
|
// prepare variables for loops later so we can compare with 0
|
||
|
// instead of comparing two variables
|
||
|
loop_h -= start_h;
|
||
|
|
||
|
sRow += start_h;
|
||
|
ofs = (sRow * SB_WIDTH) + x + xOffset;
|
||
|
|
||
|
uint8_t *bofs = (uint8_t *)bitmap + (start_h * w) + xOffset;
|
||
|
|
||
|
if (mirror & MIRROR_HORIZONTAL) {
|
||
|
bofs += rendered_width - 1;
|
||
|
if (x < 0){
|
||
|
bofs -= w - rendered_width;
|
||
|
} else{
|
||
|
bofs += w - rendered_width;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mirror & MIRROR_VERTICAL) {
|
||
|
bofs += (loop_h - 1) * w;
|
||
|
if (y < 0){
|
||
|
bofs -= (start_h * w);
|
||
|
} else {
|
||
|
bofs += (sRow * w);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint8_t data;
|
||
|
uint8_t mul_amt = 1 << yOffset;
|
||
|
uint16_t bitmap_data;
|
||
|
|
||
|
// really if yOffset = 0 you have a faster case here that could be
|
||
|
// optimized
|
||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||
|
data = pgm_read_byte(bofs);
|
||
|
if(data) {
|
||
|
if (mirror & MIRROR_VERTICAL){
|
||
|
//reverse bits
|
||
|
data = (data & 0xF0) >> 4 | (data & 0x0F) << 4;
|
||
|
data = (data & 0xCC) >> 2 | (data & 0x33) << 2;
|
||
|
data = (data & 0xAA) >> 1 | (data & 0x55) << 1;
|
||
|
|
||
|
//LUT - No speed improvement and more mem
|
||
|
//data = (((REVERSE_16[(data & 0x0F)]) << 4) + REVERSE_16[((data & 0xF0) >> 4)]);
|
||
|
|
||
|
//Fast but too much mem
|
||
|
//data = REVERSE_256[data];
|
||
|
}
|
||
|
|
||
|
bitmap_data = data * mul_amt;
|
||
|
if (sRow >= 0) {
|
||
|
data = ARDBITMAP_SBUF[ofs];
|
||
|
if (color){
|
||
|
data |= bitmap_data & 0xFF;
|
||
|
} else {
|
||
|
data &= ~(bitmap_data & 0xFF);
|
||
|
}
|
||
|
ARDBITMAP_SBUF[ofs] = data;
|
||
|
}
|
||
|
|
||
|
if (yOffset != 0 && sRow < 7) {
|
||
|
data = ARDBITMAP_SBUF[ofs + SB_WIDTH];
|
||
|
if (color){
|
||
|
data |= (bitmap_data >> 8) & 0xFF;
|
||
|
} else{
|
||
|
data &= ~((bitmap_data >> 8) & 0xFF);
|
||
|
}
|
||
|
ARDBITMAP_SBUF[ofs + SB_WIDTH] = data;
|
||
|
}
|
||
|
}
|
||
|
ofs++;
|
||
|
|
||
|
if (mirror & MIRROR_HORIZONTAL){
|
||
|
bofs--;
|
||
|
} else{
|
||
|
bofs++;
|
||
|
}
|
||
|
}
|
||
|
sRow++;
|
||
|
|
||
|
if (mirror & MIRROR_HORIZONTAL){
|
||
|
bofs += w + rendered_width;
|
||
|
} else{
|
||
|
bofs += w - rendered_width;
|
||
|
}
|
||
|
|
||
|
if (mirror & MIRROR_VERTICAL){
|
||
|
bofs -= 2 * w;
|
||
|
}
|
||
|
ofs += SB_WIDTH - rendered_width;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
template<int16_t SB_WIDTH, int16_t SB_HEIGHT>
|
||
|
void ArdBitmap<SB_WIDTH, SB_HEIGHT>::drawBitmapResized(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t w,uint8_t h, uint8_t color,uint8_t align, uint8_t mirror, float resize)
|
||
|
{
|
||
|
|
||
|
//TODO: check if this can be done in a better way
|
||
|
#ifdef RESIZE_HACK
|
||
|
if (resize >= 1.0){
|
||
|
return drawBitmap(sx, sy, bitmap,w, h, color, align, mirror);
|
||
|
}
|
||
|
#else
|
||
|
if (resize > 1.0){
|
||
|
resize = 1.0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//TODO: check why int16_t sizeCounter is a bit faster than uint16_t sizeCounter
|
||
|
int16_t sizeCounter;
|
||
|
uint16_t len;
|
||
|
uint8_t a, iCol;
|
||
|
uint8_t data;
|
||
|
uint8_t wRes, hRes;
|
||
|
uint8_t col;
|
||
|
|
||
|
wRes = (uint8_t)(w * resize);
|
||
|
hRes = (uint8_t)(h * resize);
|
||
|
|
||
|
|
||
|
// Move positions to match alignment
|
||
|
if (align & ALIGN_H_CENTER) {
|
||
|
sx -= (wRes / 2);
|
||
|
} else if (align & ALIGN_H_RIGHT) {
|
||
|
sx -= wRes;
|
||
|
}
|
||
|
|
||
|
if (align & ALIGN_V_CENTER) {
|
||
|
sy -= (hRes / 2);
|
||
|
} else if (align & ALIGN_V_BOTTOM) {
|
||
|
sy -= hRes;
|
||
|
}
|
||
|
|
||
|
// No need to draw at all if we're offscreen
|
||
|
if (sx + wRes < 0 || sx > SB_WIDTH - 1 || sy + hRes < 0 || sy > SB_HEIGHT - 1)
|
||
|
return;
|
||
|
|
||
|
int yOffset = abs(sy) % 8;
|
||
|
int sRow = sy / 8;
|
||
|
if (sy < 0) {
|
||
|
sRow--;
|
||
|
yOffset = 8 - yOffset;
|
||
|
}
|
||
|
|
||
|
int rows = h / 8;
|
||
|
if (h % 8 != 0) rows++;
|
||
|
|
||
|
uint8_t rowsRes = hRes / 8;
|
||
|
if (hRes % 8 != 0) rowsRes++;
|
||
|
|
||
|
// Init values
|
||
|
iCol = 0;
|
||
|
a = 0;
|
||
|
|
||
|
// Create Lookup tables to speed up drawing
|
||
|
|
||
|
uint8_t x_LUT[w];
|
||
|
|
||
|
for (uint8_t i=0 ; i < w; i++){
|
||
|
x_LUT[i] = 0xFF;
|
||
|
}
|
||
|
// Precalculate column translation (0xFF if skipped)
|
||
|
for (uint8_t i=0 ; i < wRes; i++){
|
||
|
x_LUT[((uint16_t)i * w) / wRes] = (mirror & MIRROR_HORIZONTAL) ? wRes - 1 - i : i;
|
||
|
}
|
||
|
|
||
|
uint8_t y_LUT[h];
|
||
|
|
||
|
for (uint8_t i=0 ; i < h; i++){
|
||
|
y_LUT[i] = 0xFF;
|
||
|
}
|
||
|
|
||
|
for (uint8_t i=0 ; i < hRes; i++){
|
||
|
y_LUT[((uint16_t)i * h) / hRes] = (mirror & MIRROR_VERTICAL) ? hRes - 1 - i : i;
|
||
|
}
|
||
|
|
||
|
len = w * rows;
|
||
|
|
||
|
for (uint16_t i = 0; i < len ; i++){
|
||
|
|
||
|
data = pgm_read_byte(&bitmap[i]);
|
||
|
int aRow8 = a * 8;
|
||
|
int16_t iColMod = x_LUT[iCol] + sx;
|
||
|
|
||
|
// Skip if column not needed
|
||
|
if (x_LUT[iCol] != 0xFF && iColMod < SB_WIDTH && iColMod >= 0){
|
||
|
for (uint8_t s = 0; s < 8 ;s++){
|
||
|
if (y_LUT[aRow8+s] != 0xFF && data & BIT_SHIFT[s]){
|
||
|
//TODO: CHECK LIMITS ON LUT?
|
||
|
uint8_t row = (uint8_t)(y_LUT[aRow8+s]+sy) / 8;
|
||
|
|
||
|
if (row < (SB_HEIGHT / 8)) {
|
||
|
if (color) {
|
||
|
ARDBITMAP_SBUF[(row*SB_WIDTH) + (uint8_t)iColMod] |= BIT_SHIFT[((uint8_t)(y_LUT[aRow8+s]+sy) % 8)];
|
||
|
} else {
|
||
|
ARDBITMAP_SBUF[(row*SB_WIDTH) + (uint8_t)iColMod] &= ~ BIT_SHIFT[((uint8_t)(y_LUT[aRow8+s]+sy) % 8)];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iCol++;
|
||
|
if (iCol >= w){
|
||
|
iCol = 0;
|
||
|
a++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|