+
+/* -------------------------------------------------------------------------------------------------
+ * RLE STUFF
+ * -------------------------------------------------------------------------------------------------
+ */
+/* Limit streak count per scanline so we can directly jump to specific scanline */
+#define RLE_STREAKS_PER_SCANLINE 4
+/* Every streak is encoded by 2 bytes: offset and count of black pixels in the streak */
+#define RLE_BYTES_PER_SCANLINE RLE_STREAKS_PER_SCANLINE * 2
+#define RLE_FILL_COLOR 0
+#define RLE_FILL_COLOR_32 ((RLE_FILL_COLOR << 16) | RLE_FILL_COLOR)
+
+#define RLE_FIXED_BITS 16
+
+static RLEBitmap rleCreate(unsigned int w, unsigned int h) {
+ RLEBitmap ret;
+ ret.w = w;
+ ret.h = h;
+
+ /* Add some padding at the end of the buffer, with the worst case for a scanline (w/2 streaks) */
+ ret.scans = (unsigned char*) calloc(h * RLE_BYTES_PER_SCANLINE + w, 1);
+
+ return ret;
+}
+
+static void rleDestroy(RLEBitmap b) {
+ free(b.scans);
+}
+
+static RLEBitmap rleEncode(unsigned char *pixels, unsigned int w, unsigned int h) {
+ int scanline;
+ int i;
+ int penActive = 0;
+ int counter = 0;
+ int accum = 0;
+ RLEBitmap ret;
+ unsigned char *output;
+
+ /* https://www.youtube.com/watch?v=RKMR02o1I88&feature=youtu.be&t=55 */
+ ret = rleCreate(w, h);
+
+ for (scanline = 0; scanline < h; scanline++) {
+ output = ret.scans + scanline * RLE_BYTES_PER_SCANLINE;
+ accum = 0;
+ for (i = 0; i < w; i++) {
+ if (*pixels++) {
+ if (penActive) {
+ if (counter >= PIXEL_PADDING) {
+ *output++ = (unsigned char) counter;
+ counter = 0;
+ *output++ = (unsigned char)accum;
+ }
+ counter++;
+ accum++;
+ } else {
+ *output++ = (unsigned char)accum;
+ counter = 1;
+ accum++;
+ penActive = 1;
+ }
+ } else {
+ if (penActive) {
+ *output++ = (unsigned char)counter;
+ counter = 1;
+ accum++;
+ penActive = 0;
+ } else {
+ counter++;
+ accum++;
+ }
+ }
+ }
+
+ if (penActive) {
+ *output++ = (unsigned char)counter;
+ }
+ penActive = 0;
+ counter = 0;
+ }
+
+ return ret;
+}
+
+static void rleBlit(unsigned short *dst, int dstW, int dstH, int dstStride,
+ RLEBitmap bitmap, int blitX, int blitY)
+{
+ int scanline = 0;
+ int streakPos = 0;
+ int streakLength = 0;
+ int streak = 0;
+ unsigned char *input = bitmap.scans;
+ unsigned short *output;
+ unsigned int *output32;
+
+ dst += blitX + blitY * dstStride;
+
+ for (scanline = blitY; scanline < blitY + bitmap.h; scanline++) {
+ if (scanline < 0 || scanline >= dstH) continue;
+ for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) {
+ streakPos = *input++;
+ streakLength = *input++;
+
+ if ((streakPos + blitX) <= 0) continue;
+
+ output = dst + streakPos;
+
+ /* Check if we need to write the first pixel as 16bit */
+ if (streakLength % 2) {
+ *output++ = RLE_FILL_COLOR;
+ }
+
+ /* Then, write 2 pixels at a time */
+ streakLength >>= 1;
+ output32 = (unsigned int*) output;
+ while (streakLength--) {
+ *output32++ = RLE_FILL_COLOR_32;
+ }
+ }
+
+ dst += dstStride;
+ }
+}
+
+static void interpolateScan(unsigned char *output, unsigned char *a, unsigned char *b, float t) {
+ static int div = 1 << 23;
+ int ti, i;
+
+ t += 1.0f;
+ ti = (*((unsigned int*)&t)) & 0x7FFFFF;
+
+ for (i = 0; i < RLE_BYTES_PER_SCANLINE; i++) {
+ if (*a == 0) {
+ *output++ = *b++;
+ a++;
+ } else {
+ if (*b == 0) {
+ *output++ = *a++;
+ b++;
+ } else {
+ *output++ = ((*b++ * ti) + (*a++ * (div - ti))) >> 23;
+ }
+ }
+ }
+}
+
+static void rleBlitScale(unsigned short *dst, int dstW, int dstH, int dstStride,
+ RLEBitmap bitmap, int blitX, int blitY, float scaleX, float scaleY)
+{
+ int scanline = 0;
+ int streakPos = 0;
+ int streakLength = 0;
+ int streak = 0;
+ unsigned short *output;
+ unsigned int *output32;
+ unsigned char *input;
+ int scanlineCounter = 0;
+ static unsigned char scan[512];
+
+ int blitW = (int) (bitmap.w * scaleX + 0.5f);
+ int blitH = (int)(bitmap.h * scaleY + 0.5f);
+
+ /* From this point on, scaleY will be inverted */
+ scaleY = 1.0f / scaleY;
+
+ int scaleXFixed = (int)(scaleX * (float)(1 << RLE_FIXED_BITS) + 0.5f);
+
+ dst += blitX + blitY * dstStride;
+
+ for (scanline = blitY; scanline < blitY + blitH; scanline++) {
+ float normalScan = scanlineCounter * scaleY; /* ScaleY is inverted */
+ unsigned char *scan0 = bitmap.scans + RLE_BYTES_PER_SCANLINE * (int)normalScan;
+ unsigned char *scan1 = scan0 + RLE_BYTES_PER_SCANLINE;
+ normalScan -= (int)normalScan;
+ interpolateScan(scan, scan0, scan1, normalScan);
+ input = scan;
+ scanlineCounter++;
+
+ if (scanline < 0 || scanline >= dstH) continue;
+ for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) {
+ streakPos = (*input++ * scaleXFixed) >> RLE_FIXED_BITS;
+ streakLength = (*input++ * scaleXFixed) >> RLE_FIXED_BITS;
+
+ if ((streakPos + blitX) <= 0) continue;
+
+ output = dst + streakPos;
+
+ /* Check if we need to write the first pixel as 16bit */
+ if (streakLength % 2) {
+ *output++ = RLE_FILL_COLOR;
+ }
+
+ /* Then, write 2 pixels at a time */
+ streakLength >>= 1;
+ output32 = (unsigned int*)output;
+ while (streakLength--) {
+ *output32++ = RLE_FILL_COLOR_32;
+ }
+ }
+
+ dst += dstStride;
+ }
+}
+
+
+
+static void rleBlitScaleInv(unsigned short *dst, int dstW, int dstH, int dstStride,
+ RLEBitmap bitmap, int blitX, int blitY, float scaleX, float scaleY)
+{
+ int scanline = 0;
+ int streakPos = 0;
+ int streakLength = 0;
+ int streak = 0;
+ unsigned short *output;
+ unsigned int *output32;
+ unsigned char *input;
+ int scanlineCounter = 0;
+ static unsigned char scan[512];
+
+ int blitW = (int)(bitmap.w * scaleX + 0.5f);
+ int blitH = (int)(bitmap.h * scaleY + 0.5f);
+
+ /* From this point on, scaleY will be inverted */
+ scaleY = 1.0f / scaleY;
+
+ int scaleXFixed = (int)(scaleX * (float)(1 << RLE_FIXED_BITS) + 0.5f);
+
+ dst += blitX + blitY * dstStride;
+
+ for (scanline = blitY; scanline > blitY - blitH; scanline--) {
+ float normalScan = scanlineCounter * scaleY; /* ScaleY is inverted */
+ unsigned char *scan0 = bitmap.scans + RLE_BYTES_PER_SCANLINE * (int)normalScan;
+ unsigned char *scan1 = scan0 + RLE_BYTES_PER_SCANLINE;
+ normalScan -= (int)normalScan;
+ interpolateScan(scan, scan0, scan1, normalScan);
+ input = scan;
+ scanlineCounter++;
+
+ if (scanline < 0 || scanline >= dstH) continue;
+ for (streak = 0; streak < RLE_STREAKS_PER_SCANLINE; streak++) {
+ streakPos = (*input++ * scaleXFixed) >> RLE_FIXED_BITS;
+ streakLength = (*input++ * scaleXFixed) >> RLE_FIXED_BITS;
+
+ if ((streakPos + blitX) <= 0) continue;
+
+ output = dst + streakPos;
+
+ /* Check if we need to write the first pixel as 16bit */
+ if (streakLength % 2) {
+ *output++ = RLE_FILL_COLOR;
+ }
+
+ /* Then, write 2 pixels at a time */
+ streakLength >>= 1;
+ output32 = (unsigned int*)output;
+ while (streakLength--) {
+ *output32++ = RLE_FILL_COLOR_32;
+ }
+ }
+
+ dst -= dstStride;
+ }
+}
\ No newline at end of file