#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; } }