Last active
September 10, 2018 23:59
-
-
Save city41/9f057bb5e27836a787694d50d4a546f3 to your computer and use it in GitHub Desktop.
Arduboy2's Sprite and ArdBitmap primary drawBitmap methods combined into one
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "drawBitmap.h" | |
#include <Arduboy2.h> | |
/** | |
* draw a bitmap with an optional mask, optional mirroring and optional inverting | |
* | |
* This code is a combination of Arduboy2's Sprite::drawExternalMask() and Ardbitmap's drawBitmap() | |
* | |
* This method can accomplish the same effect as most of Sprite's methods: | |
* drawOverwrite: pass in a mask of NULL | |
* drawExternalMask: pass in a separate mask | |
* drawPlusMask: pass in data that is <an entire sprite frame>, <that frames entire mask>, ... | |
* --- NOTE: this is different from Sprite::drawPlusMask(), which expects the bytes to be <frame byte>, <mask byte>, ... | |
* drawSelfMasked: pass in the same pointer for bitmap and mask | |
* | |
* To mirror the sprite, pass in MIRROR_HORIZONTAL or MIRROR_VERTICAL as the mirror parameter. | |
* To mirrow both ways at once, pass in MIRROR_HORIZONTAL | MIRROR_VERTICAL as the parameter | |
* | |
* setting invert to true causes pixels that would have been black to be white, and white pixels to be black | |
*/ | |
void drawBitmap(int16_t x, int16_t y, const uint8_t* bitmap, const uint8_t* mask, bool plusMask, uint8_t frame, uint8_t maskFrame, uint8_t mirror, bool invert) { | |
if (bitmap == NULL) | |
return; | |
uint8_t w = pgm_read_byte(bitmap++); | |
uint8_t h = pgm_read_byte(bitmap++); | |
// no need to draw at all if we're offscreen | |
if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1) | |
return; | |
if (plusMask) { | |
mask = bitmap; | |
} | |
const boolean hasMask = mask != NULL; | |
uint16_t frame_offset = (w * ( h / 8 + ( h % 8 == 0 ? 0 : 1))); | |
if (frame > 0) { | |
mask += maskFrame * frame_offset; | |
bitmap += frame * frame_offset; | |
// plusMask means the sprite is frame,mask,frame,mask | |
// jump ahead one more time to get to the correct frame | |
if (plusMask) { | |
mask += maskFrame * frame_offset; | |
bitmap += frame * frame_offset; | |
} | |
} | |
if (plusMask) { | |
mask += frame_offset; | |
} | |
// 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 > WIDTH - 1) { | |
rendered_width = ((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 > (HEIGHT/8)-1) | |
if (sRow + loop_h > (HEIGHT / 8)) { | |
loop_h = (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 * WIDTH) + x + xOffset; | |
const uint8_t *bofs = bitmap + (start_h * w) + xOffset; | |
const uint8_t *mask_ofs = mask + (start_h * w) + xOffset; | |
if (mirror & MIRROR_HORIZONTAL) { | |
bofs += rendered_width - 1; | |
mask_ofs += rendered_width - 1; | |
if (x < 0){ | |
bofs -= w - rendered_width; | |
mask_ofs -= w - rendered_width; | |
} else{ | |
bofs += w - rendered_width; | |
mask_ofs += w - rendered_width; | |
} | |
} | |
if (mirror & MIRROR_VERTICAL) { | |
bofs += (loop_h - 1) * w; | |
mask_ofs += (loop_h - 1) * w; | |
if (y < 0) { | |
bofs -= (start_h * w); | |
mask_ofs -= (start_h * w); | |
} | |
} | |
uint8_t data; | |
uint8_t mul_amt = 1 << yOffset; | |
uint16_t bitmap_data; | |
uint16_t mask_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); | |
mask_data = hasMask ? pgm_read_byte(mask_ofs) : 0xFF; | |
if (invert) { | |
data = ~data & mask_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; | |
mask_data = (mask_data & 0xF0) >> 4 | (mask_data & 0x0F) << 4; | |
mask_data = (mask_data & 0xCC) >> 2 | (mask_data & 0x33) << 2; | |
mask_data = (mask_data & 0xAA) >> 1 | (mask_data & 0x55) << 1; | |
} | |
bitmap_data = data * mul_amt; | |
mask_data = ~(mask_data * mul_amt); | |
if (sRow >= 0) { | |
data = Arduboy2Base::sBuffer[ofs]; | |
data &= (uint8_t)(mask_data); | |
data |= (uint8_t)(bitmap_data); | |
Arduboy2Base::sBuffer[ofs] = data; | |
} | |
if (yOffset != 0 && sRow < 7) { | |
data = Arduboy2Base::sBuffer[ofs + WIDTH]; | |
data &= (*((unsigned char *) (&mask_data) + 1)); | |
data |= (*((unsigned char *) (&bitmap_data) + 1)); | |
Arduboy2Base::sBuffer[ofs + WIDTH] = data; | |
} | |
ofs++; | |
if (mirror & MIRROR_HORIZONTAL) { | |
bofs--; | |
mask_ofs--; | |
} else{ | |
bofs++; | |
mask_ofs++; | |
} | |
} | |
sRow++; | |
if (mirror & MIRROR_HORIZONTAL) { | |
bofs += w + rendered_width; | |
mask_ofs += w + rendered_width; | |
} else{ | |
bofs += w - rendered_width; | |
mask_ofs += w - rendered_width; | |
} | |
if (mirror & MIRROR_VERTICAL) { | |
bofs -= 2 * w; | |
mask_ofs -= 2 * w; | |
} | |
ofs += WIDTH - rendered_width; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment