aboutsummaryrefslogtreecommitdiff
path: root/src/Bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Bitmap.c')
-rw-r--r--src/Bitmap.c636
1 files changed, 636 insertions, 0 deletions
diff --git a/src/Bitmap.c b/src/Bitmap.c
new file mode 100644
index 0000000..2b908ad
--- /dev/null
+++ b/src/Bitmap.c
@@ -0,0 +1,636 @@
+#include <BH/Bitmap.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+
+struct BH_Bitmap
+{
+ int width;
+ int height;
+ int format;
+ int flags;
+ size_t step;
+ size_t stride;
+ uint8_t *data;
+ uint32_t *palette;
+};
+
+
+static size_t stepFromFormat(int format)
+{
+ switch (format & 0x8FFF)
+ {
+ case BH_BITMAP_INDEX8:
+ case BH_BITMAP_GRAY8:
+ return 1;
+
+ case BH_BITMAP_GRAY16:
+ case BH_BITMAP_RGB565:
+ return 2;
+
+ case BH_BITMAP_RGB888:
+ return 3;
+
+ case BH_BITMAP_RGBA32:
+ case BH_BITMAP_RGBA8888:
+ case BH_BITMAP_RGBA1010102:
+ return 4;
+
+ case BH_BITMAP_RGB161616:
+ return 6;
+
+ case BH_BITMAP_RGBA64:
+ case BH_BITMAP_RGBA16161616:
+ return 8;
+ }
+
+ return 0;
+}
+
+
+BH_Bitmap *BH_BitmapNew(int width,
+ int height,
+ int format,
+ int flags,
+ void *data,
+ void *palette)
+{
+ BH_Bitmap *result;
+ int needPalette;
+ size_t header, body, trailer, stride, step;
+
+ /* Unset internal flags */
+ flags &= ~BH_BITMAP_FLAG_EXT_DATA;
+ flags &= ~BH_BITMAP_FLAG_EXT_PALETTE;
+
+ /* Calculate size of the bitmap with headers */
+ needPalette = (format & 0x8FFF) == BH_BITMAP_INDEX8;
+ step = stepFromFormat(format);
+ stride = step * width;
+ if (flags & BH_BITMAP_FLAG_ALIGN32)
+ stride = (stride + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
+
+ header = sizeof(*result);
+ header = (header + (sizeof(uint64_t) - 1)) & ~(sizeof(uint64_t) - 1);
+ body = (stride * height + (sizeof(uint32_t) - 1)) & ~(sizeof(uint32_t) - 1);
+ trailer = sizeof(uint32_t) * 256;
+
+ /* Adjust body and trailer size if ext data or palette is provided */
+ if (!needPalette)
+ trailer = 0;
+
+ if (palette)
+ {
+ flags |= BH_BITMAP_FLAG_EXT_PALETTE;
+ trailer = 0;
+ }
+
+ if (data)
+ {
+ flags |= BH_BITMAP_FLAG_EXT_DATA;
+ body = 0;
+ }
+
+ /* Allocate and setup bitmap data */
+ result = malloc(header + body + trailer);
+ if (!result)
+ return NULL;
+
+ if (!data)
+ data = (uint8_t *)result + header;
+
+ if (!palette && needPalette)
+ palette = (uint8_t *)result + header + body;
+
+ result->width = width;
+ result->height = height;
+ result->format = format;
+ result->flags = flags;
+ result->step = step;
+ result->stride = stride;
+ result->data = data;
+ result->palette = palette;
+
+ return result;
+}
+
+
+void BH_BitmapFree(BH_Bitmap *bitmap)
+{
+ free(bitmap);
+}
+
+
+static const size_t bitOrder565[][3] =
+{
+ {11, 5, 0},
+ {0, 5, 11},
+};
+
+
+static const size_t bitOrder1010102[][4] =
+{
+ {0, 10, 20, 30},
+ {20, 10, 0, 30},
+};
+
+
+static const size_t unitOrderRGB[][3] =
+{
+ {0, 1, 2},
+ {2, 1, 0},
+};
+
+
+static const size_t unitOrderRGBA[][4] =
+{
+ {0, 1, 2, 3},
+ {2, 1, 0, 3},
+};
+
+
+static void color(void *data,
+ void *palette,
+ int format,
+ BH_Color *value)
+{
+ switch (format & 0x8FFF)
+ {
+ case BH_BITMAP_INDEX8:
+ {
+ uint32_t color = ((uint32_t *)palette)[*(uint8_t *)data];
+ BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
+ color & 0xFF, color >> 24 & 0xFF);
+ }
+ break;
+
+ case BH_BITMAP_GRAY8:
+ {
+ uint8_t color = *(uint8_t *)data;
+ BH_ColorSetRGBA8(value, color, color, color, 0xFF);
+ }
+ break;
+
+ case BH_BITMAP_GRAY16:
+ {
+ uint16_t color = *(uint16_t *)data;
+ BH_ColorSetRGBA16(value, color, color, color, 0xFFFF);
+ }
+ break;
+
+ case BH_BITMAP_RGBA32:
+ {
+ uint32_t color = *(uint32_t *)data;
+ BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
+ color & 0xFF, color >> 24 & 0xFF);
+ }
+ break;
+
+ case BH_BITMAP_RGBA64:
+ {
+ uint64_t color = *(uint64_t *)data;
+ BH_ColorSetRGBA16(value, color >> 32 & 0xFFFF, color >> 16 & 0xFFFF,
+ color & 0xFFFF, color >> 48 & 0xFFFF);
+ }
+ break;
+
+ case BH_BITMAP_RGB565:
+ {
+ uint8_t r, g, b;
+ uint16_t color = *(uint16_t *)data;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ r = color >> bitOrder565[orderSelector][0] & 0x1F;
+ g = color >> bitOrder565[orderSelector][1] & 0x3F;
+ b = color >> bitOrder565[orderSelector][2] & 0x1F;
+
+ r = r << 3 | r >> 2;
+ g = g << 2 | g >> 4;
+ b = b << 3 | b >> 2;
+ BH_ColorSetRGBA8(value, r, g, b, 0xFF);
+ }
+ break;
+
+ case BH_BITMAP_RGB888:
+ {
+ uint8_t r, g, b;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ r = ((uint8_t *)data)[unitOrderRGB[orderSelector][0]];
+ g = ((uint8_t *)data)[unitOrderRGB[orderSelector][1]];
+ b = ((uint8_t *)data)[unitOrderRGB[orderSelector][2]];
+ BH_ColorSetRGBA8(value, r, g, b, 0xFF);
+ }
+ break;
+
+ case BH_BITMAP_RGBA8888:
+ {
+ uint8_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ r = ((uint8_t *)data)[unitOrderRGBA[orderSelector][0]];
+ g = ((uint8_t *)data)[unitOrderRGBA[orderSelector][1]];
+ b = ((uint8_t *)data)[unitOrderRGBA[orderSelector][2]];
+ a = ((uint8_t *)data)[unitOrderRGBA[orderSelector][3]];
+ BH_ColorSetRGBA8(value, r, g, b, a);
+ }
+ break;
+
+ case BH_BITMAP_RGB161616:
+ {
+ uint16_t r, g, b;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ r = ((uint16_t *)data)[unitOrderRGB[orderSelector][0]];
+ g = ((uint16_t *)data)[unitOrderRGB[orderSelector][1]];
+ b = ((uint16_t *)data)[unitOrderRGB[orderSelector][2]];
+ BH_ColorSetRGBA16(value, r, g, b, 0xFFFF);
+ }
+ break;
+
+ case BH_BITMAP_RGBA16161616:
+ {
+ uint16_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ r = ((uint16_t *)data)[unitOrderRGBA[orderSelector][0]];
+ g = ((uint16_t *)data)[unitOrderRGBA[orderSelector][1]];
+ b = ((uint16_t *)data)[unitOrderRGBA[orderSelector][2]];
+ a = ((uint16_t *)data)[unitOrderRGBA[orderSelector][3]];
+ BH_ColorSetRGBA16(value, r, g, b, a);
+ }
+ break;
+
+ case BH_BITMAP_RGBA1010102:
+ {
+ uint16_t r, g, b, a;
+ uint32_t color = *(uint32_t *)data;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ r = color >> bitOrder1010102[orderSelector][0] & 0x3FF;
+ g = color >> bitOrder1010102[orderSelector][1] & 0x3FF;
+ b = color >> bitOrder1010102[orderSelector][2] & 0x3FF;
+ a = color >> bitOrder1010102[orderSelector][3] & 0x3;
+
+ r = r << 6 | r >> 4;
+ g = g << 6 | g >> 4;
+ b = b << 6 | b >> 4;
+ a = a << 2 | a;
+ a = a << 4 | a;
+ a = a << 8 | a;
+ BH_ColorSetRGBA16(value, r, g, b, a);
+ }
+ break;
+ }
+
+ /* Fix alpha, if format doesn't support alpha channel */
+ if (format & BH_BITMAP_NOALPHA)
+ value->data.rgba.a = 0xFFFF;
+}
+
+
+static size_t bestIndex(uint8_t r,
+ uint8_t g,
+ uint8_t b,
+ uint8_t a,
+ const uint32_t *palette)
+{
+ uint32_t bestError = -1;
+ size_t i, bestIndex = 0;
+
+ for (i = 0; i < 256; ++i) {
+ int32_t dr, dg, db, da;
+ uint32_t currentError;
+
+ dr = (int32_t)r - (palette[i] >> 16 & 0xFF);
+ dg = (int32_t)g - (palette[i] >> 8 & 0xFF);
+ db = (int32_t)b - (palette[i] & 0xFF);
+ da = (int32_t)a - (palette[i] >> 24 & 0xFF);
+
+ currentError = dr * dr + dg * dg + db * db + da * da;
+ if (currentError < bestError)
+ {
+ bestError = currentError;
+ bestIndex = i;
+ }
+ }
+
+ return bestIndex;
+}
+
+
+static void setColor(void *data,
+ void *palette,
+ int format,
+ const BH_Color *value)
+{
+ switch (format & 0x8FFF)
+ {
+ case BH_BITMAP_INDEX8:
+ {
+ uint8_t r, g, b, a;
+
+ BH_ColorRGBA8(value, &r, &g, &b, &a);
+ *(uint8_t *)data = bestIndex(r, g, b, a, palette);
+ }
+ break;
+
+ case BH_BITMAP_GRAY8:
+ {
+ uint8_t r, g, b, a;
+
+ BH_ColorRGBA8(value, &r, &g, &b, &a);
+ *(uint8_t *)data = r;
+ }
+ break;
+
+ case BH_BITMAP_GRAY16:
+ {
+ uint16_t r, g, b, a;
+
+ BH_ColorRGBA16(value, &r, &g, &b, &a);
+ *(uint16_t *)data = r;
+ }
+ break;
+
+ case BH_BITMAP_RGBA32:
+ {
+ uint8_t r, g, b, a;
+
+ BH_ColorRGBA8(value, &r, &g, &b, &a);
+ if (format & BH_BITMAP_NOALPHA)
+ a = 0xFF;
+
+ *(uint32_t *)data = (uint32_t)r << 16 | (uint32_t)g << 8 |
+ (uint32_t)b | (uint32_t)a << 24;
+ }
+ break;
+
+ case BH_BITMAP_RGBA64:
+ {
+ uint16_t r, g, b, a;
+
+ BH_ColorRGBA16(value, &r, &g, &b, &a);
+ if (format & BH_BITMAP_NOALPHA)
+ a = 0xFFFF;
+
+ *(uint64_t *)data = (uint64_t)r << 32 | (uint64_t)g << 16 |
+ (uint64_t)b | (uint64_t)a << 48;
+ }
+ break;
+
+ case BH_BITMAP_RGB565:
+ {
+ uint8_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+ uint16_t color;
+
+ color = 0;
+ BH_ColorRGBA8(value, &r, &g, &b, &a);
+ color |= (r >> 3) << bitOrder565[orderSelector][0];
+ color |= (g >> 2) << bitOrder565[orderSelector][1];
+ color |= (b >> 3) << bitOrder565[orderSelector][2];
+
+ *(uint16_t *)(data) = color;
+ }
+ break;
+
+ case BH_BITMAP_RGB888:
+ {
+ uint8_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ BH_ColorRGBA8(value, &r, &g, &b, &a);
+ ((uint8_t *)data)[unitOrderRGB[orderSelector][0]] = r;
+ ((uint8_t *)data)[unitOrderRGB[orderSelector][1]] = g;
+ ((uint8_t *)data)[unitOrderRGB[orderSelector][2]] = b;
+ }
+ break;
+
+ case BH_BITMAP_RGBA8888:
+ {
+ uint8_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ BH_ColorRGBA8(value, &r, &g, &b, &a);
+ if (format & BH_BITMAP_NOALPHA)
+ a = 0xFF;
+
+ ((uint8_t *)data)[unitOrderRGBA[orderSelector][0]] = r;
+ ((uint8_t *)data)[unitOrderRGBA[orderSelector][1]] = g;
+ ((uint8_t *)data)[unitOrderRGBA[orderSelector][2]] = b;
+ ((uint8_t *)data)[unitOrderRGBA[orderSelector][3]] = a;
+ }
+ break;
+
+ case BH_BITMAP_RGB161616:
+ {
+ uint16_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ BH_ColorRGBA16(value, &r, &g, &b, &a);
+ ((uint16_t *)data)[unitOrderRGB[orderSelector][0]] = r;
+ ((uint16_t *)data)[unitOrderRGB[orderSelector][1]] = g;
+ ((uint16_t *)data)[unitOrderRGB[orderSelector][2]] = b;
+ }
+ break;
+
+ case BH_BITMAP_RGBA16161616:
+ {
+ uint16_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+
+ BH_ColorRGBA16(value, &r, &g, &b, &a);
+ if (format & BH_BITMAP_NOALPHA)
+ a = 0xFFFF;
+
+ ((uint16_t *)data)[unitOrderRGBA[orderSelector][0]] = r;
+ ((uint16_t *)data)[unitOrderRGBA[orderSelector][1]] = g;
+ ((uint16_t *)data)[unitOrderRGBA[orderSelector][2]] = b;
+ ((uint16_t *)data)[unitOrderRGBA[orderSelector][3]] = a;
+ }
+ break;
+
+ case BH_BITMAP_RGBA1010102:
+ {
+ uint16_t r, g, b, a;
+ int orderSelector = (format & BH_BITMAP_BGR) > 0;
+ uint32_t color;
+
+ BH_ColorRGBA16(value, &r, &g, &b, &a);
+ if (format & BH_BITMAP_NOALPHA)
+ a = 0xFFFF;
+
+ color = 0;
+ color |= (r >> 6) << bitOrder1010102[orderSelector][0];
+ color |= (g >> 6) << bitOrder1010102[orderSelector][1];
+ color |= (b >> 6) << bitOrder1010102[orderSelector][2];
+ color |= (a >> 10) << bitOrder1010102[orderSelector][3];
+
+ *(uint32_t *)data = color;
+ }
+ break;
+ }
+}
+
+
+void BH_BitmapColor(const BH_Bitmap *bitmap,
+ int x,
+ int y,
+ BH_Color *value)
+{
+ color(BH_BitmapAt(bitmap, x, y), bitmap->palette, bitmap->format, value);
+}
+
+void BH_BitmapSetColor(BH_Bitmap *bitmap,
+ int x,
+ int y,
+ const BH_Color *value)
+{
+ setColor(BH_BitmapAt(bitmap, x, y), bitmap->palette, bitmap->format, value);
+}
+
+BH_Bitmap *BH_BitmapCopy(BH_Bitmap *bitmap,
+ int x,
+ int y,
+ int width,
+ int height,
+ int shallow)
+{
+ BH_Bitmap *result;
+ int i, j;
+
+ /* Check for bogus width and height */
+ if (width < 0 || height < 0)
+ return NULL;
+
+ /* Shallow copy can't go out of bounds of the source bitmap */
+ if (shallow)
+ {
+ if (x < 0 || x > bitmap->width || y < 0 || y > bitmap->height)
+ return NULL;
+
+ if (width < 0 || width > bitmap->width - x ||
+ height < 0 || height > bitmap->height - y)
+ return NULL;
+
+ result = BH_BitmapNew(width, height, bitmap->format, bitmap->flags,
+ BH_BitmapAt(bitmap, x, y), bitmap->palette);
+ result->stride = bitmap->stride;
+
+ return result;
+ }
+
+ /* Deep copy of the bitmap */
+ result = BH_BitmapNew(width, height, bitmap->format, bitmap->flags, NULL, NULL);
+ if (!result)
+ return NULL;
+
+ /* Copy the palette if neccesery */
+ if (result->palette)
+ memcpy(result->palette, bitmap->palette, sizeof(uint32_t) * 256);
+
+ for (j = 0; j < height; ++j)
+ {
+ for (i = 0; i < width; ++i)
+ {
+ BH_Color color;
+ memset(&color, 0, sizeof(color));
+
+
+ if (i + x >= 0 && i + x < bitmap->width &&
+ j + y >= 0 && j + y < bitmap->height)
+ BH_BitmapColor(bitmap, i + x, j + y, &color);
+
+ BH_BitmapSetColor(result, i, j, &color);
+ }
+ }
+
+ return result;
+}
+
+
+void *BH_BitmapScanline(const BH_Bitmap *bitmap,
+ int y)
+{
+ return bitmap->data + y * bitmap->stride;
+}
+
+
+void *BH_BitmapAt(const BH_Bitmap *bitmap,
+ int x,
+ int y)
+{
+ return BH_BitmapScanline(bitmap, y) + bitmap->step * x;
+}
+
+
+int BH_BitmapWidth(BH_Bitmap *bitmap)
+{
+ return bitmap->width;
+}
+
+
+int BH_BitmapHeight(BH_Bitmap *bitmap)
+{
+ return bitmap->height;
+}
+
+
+int BH_BitmapFormat(BH_Bitmap *bitmap)
+{
+ return bitmap->format;
+}
+
+
+size_t BH_BitmapStride(BH_Bitmap *bitmap)
+{
+ return bitmap->stride;
+}
+
+
+void *BH_BitmapData(BH_Bitmap *bitmap)
+{
+ return bitmap->data;
+}
+
+
+void *BH_BitmapPalette(BH_Bitmap *bitmap)
+{
+ return bitmap->palette;
+}
+
+
+int BH_BitmapFlags(BH_Bitmap *bitmap)
+{
+ return bitmap->flags;
+}
+
+
+void BH_BitmapConvertRow(void *src,
+ int srcFormat,
+ void *srcPalette,
+ void *dest,
+ int destFormat,
+ void *destPalette,
+ size_t count)
+{
+ size_t srcStep, destStep;
+
+ srcStep = stepFromFormat(srcFormat);
+ destStep = stepFromFormat(destFormat);
+
+ for (; count; --count)
+ {
+ BH_Color data;
+
+ color(src, srcPalette, srcFormat, &data);
+ setColor(dest, destPalette, destFormat, &data);
+ src = (uint8_t*)src + srcStep;
+ dest = (uint8_t*)dest + destStep;
+ }
+}