2025-08-02 20:47:40 +03:00
|
|
|
#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
|
|
|
|
|
{
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t width;
|
|
|
|
|
uint32_t height;
|
2025-08-02 20:47:40 +03:00
|
|
|
int format;
|
|
|
|
|
int flags;
|
|
|
|
|
size_t step;
|
|
|
|
|
size_t stride;
|
|
|
|
|
uint8_t *data;
|
|
|
|
|
uint32_t *palette;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
/* Bit and unit order arrays for working with RGB/BGR and MSB/LSB */
|
|
|
|
|
static const size_t bitOrder1[][8] =
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
{7, 6, 5, 4, 3, 2, 1, 0},
|
|
|
|
|
{0, 1, 2, 3, 4, 5, 6, 7},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const size_t bitMask1[][8] =
|
|
|
|
|
{
|
|
|
|
|
{0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01},
|
|
|
|
|
{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const size_t bitOrder2[][4] =
|
|
|
|
|
{
|
|
|
|
|
{6, 4, 2, 0},
|
|
|
|
|
{0, 2, 4, 6},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const size_t bitMask2[][4] =
|
|
|
|
|
{
|
|
|
|
|
{0xC0, 0x30, 0x0C, 0x03},
|
|
|
|
|
{0x03, 0x0C, 0x30, 0xC0},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const size_t bitOrder4[][2] =
|
|
|
|
|
{
|
|
|
|
|
{4, 0},
|
|
|
|
|
{0, 4},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const size_t bitMask4[][2] =
|
|
|
|
|
{
|
|
|
|
|
{0xF0, 0x0F},
|
|
|
|
|
{0x0F, 0xF0},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Private functions */
|
|
|
|
|
static size_t calculateStep(int format)
|
|
|
|
|
{
|
|
|
|
|
switch (format & 0x0FFF)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
return 0;
|
|
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
static uint8_t bitmapIndex(void *data,
|
|
|
|
|
int format,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
int isLSB;
|
|
|
|
|
uint8_t octet;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isLSB = (format & BH_BITMAP_LSB) > 0;
|
|
|
|
|
switch (format & 0x0FFF)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
octet = (*(uint8_t *)data) & bitMask1[isLSB][x & 7];
|
|
|
|
|
return octet >> bitOrder1[isLSB][x & 7];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
octet = (*(uint8_t *)data) & bitMask2[isLSB][x & 3];
|
|
|
|
|
return octet >> bitOrder2[isLSB][x & 3];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
octet = (*(uint8_t *)data) & bitMask4[isLSB][x & 1];
|
|
|
|
|
return octet >> bitOrder4[isLSB][x & 1];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX8:
|
|
|
|
|
return *(uint8_t *)data;
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
return 0;
|
|
|
|
|
}
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
static void setBitmapIndex(void *data,
|
|
|
|
|
int format,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
2025-08-04 12:33:44 +03:00
|
|
|
uint8_t index)
|
|
|
|
|
{
|
|
|
|
|
int isLSB;
|
|
|
|
|
uint8_t octet;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isLSB = (format & BH_BITMAP_LSB) > 0;
|
|
|
|
|
switch (format & 0x0FFF)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
octet = (*(uint8_t *)data) & ~bitMask1[isLSB][x & 7];
|
|
|
|
|
octet |= (index & 0x1) << bitOrder1[isLSB][x & 7];
|
|
|
|
|
*(uint8_t *)data = octet;
|
|
|
|
|
break;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
octet = (*(uint8_t *)data) & ~bitMask2[isLSB][x & 3];
|
|
|
|
|
octet |= (index & 0x3) << bitOrder2[isLSB][x & 3];
|
|
|
|
|
*(uint8_t *)data = octet;
|
|
|
|
|
break;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
octet = (*(uint8_t *)data) & ~bitMask4[isLSB][x & 1];
|
|
|
|
|
octet |= (index & 0xF) << bitOrder4[isLSB][x & 1];
|
|
|
|
|
*(uint8_t *)data = octet;
|
|
|
|
|
break;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX8:
|
|
|
|
|
*(uint8_t *)data = index;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
static void bitmapColor(void *data,
|
|
|
|
|
void *palette,
|
|
|
|
|
int format,
|
|
|
|
|
BH_Color *value,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
switch (format & 0x0FFF)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
{
|
|
|
|
|
uint32_t color;
|
|
|
|
|
color = ((uint32_t *)palette)[bitmapIndex(data, format, x)];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
|
|
|
|
|
color & 0xFF, color >> 24 & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
{
|
|
|
|
|
uint32_t color;
|
|
|
|
|
color = ((uint32_t *)palette)[bitmapIndex(data, format, x)];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
|
|
|
|
|
color & 0xFF, color >> 24 & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
{
|
|
|
|
|
uint32_t color;
|
|
|
|
|
color = ((uint32_t *)palette)[bitmapIndex(data, format, x)];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
|
|
|
|
|
color & 0xFF, color >> 24 & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
case BH_BITMAP_INDEX8:
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
uint32_t color;
|
|
|
|
|
color = ((uint32_t *)palette)[bitmapIndex(data, format, x)];
|
|
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
|
|
|
|
|
color & 0xFF, color >> 24 & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_GRAY8:
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
uint8_t color;
|
|
|
|
|
|
|
|
|
|
color = *(uint8_t *)data;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorSetRGBA8(value, color, color, color, 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_GRAY16:
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
uint16_t color;
|
|
|
|
|
|
|
|
|
|
color = *(uint16_t *)data;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorSetRGBA16(value, color, color, color, 0xFFFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA32:
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
uint32_t color;
|
|
|
|
|
|
|
|
|
|
color = *(uint32_t *)data;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorSetRGBA8(value, color >> 16 & 0xFF, color >> 8 & 0xFF,
|
|
|
|
|
color & 0xFF, color >> 24 & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA64:
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
uint64_t color;
|
|
|
|
|
|
|
|
|
|
color = *(uint64_t *)data;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorSetRGBA16(value, color >> 32 & 0xFFFF, color >> 16 & 0xFFFF,
|
|
|
|
|
color & 0xFFFF, color >> 48 & 0xFFFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGB565:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b;
|
2025-08-04 12:33:44 +03:00
|
|
|
uint16_t color;
|
|
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
color = *(uint16_t *)data;
|
|
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
|
|
|
|
r = color >> bitOrder565[isBGR][0] & 0x1F;
|
|
|
|
|
g = color >> bitOrder565[isBGR][1] & 0x3F;
|
|
|
|
|
b = color >> bitOrder565[isBGR][2] & 0x1F;
|
2025-08-02 20:47:40 +03:00
|
|
|
r = r << 3 | r >> 2;
|
|
|
|
|
g = g << 2 | g >> 4;
|
|
|
|
|
b = b << 3 | b >> 2;
|
2025-08-04 12:33:44 +03:00
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorSetRGBA8(value, r, g, b, 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGB888:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
|
|
|
|
|
|
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
|
|
|
|
r = ((uint8_t *)data)[unitOrderRGB[isBGR][0]];
|
|
|
|
|
g = ((uint8_t *)data)[unitOrderRGB[isBGR][1]];
|
|
|
|
|
b = ((uint8_t *)data)[unitOrderRGB[isBGR][2]];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
BH_ColorSetRGBA8(value, r, g, b, 0xFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA8888:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
|
|
|
|
|
|
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
|
|
|
|
r = ((uint8_t *)data)[unitOrderRGBA[isBGR][0]];
|
|
|
|
|
g = ((uint8_t *)data)[unitOrderRGBA[isBGR][1]];
|
|
|
|
|
b = ((uint8_t *)data)[unitOrderRGBA[isBGR][2]];
|
|
|
|
|
a = ((uint8_t *)data)[unitOrderRGBA[isBGR][3]];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
BH_ColorSetRGBA8(value, r, g, b, a);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGB161616:
|
|
|
|
|
{
|
|
|
|
|
uint16_t r, g, b;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
|
|
|
|
|
|
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
|
|
|
|
r = ((uint16_t *)data)[unitOrderRGB[isBGR][0]];
|
|
|
|
|
g = ((uint16_t *)data)[unitOrderRGB[isBGR][1]];
|
|
|
|
|
b = ((uint16_t *)data)[unitOrderRGB[isBGR][2]];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
BH_ColorSetRGBA16(value, r, g, b, 0xFFFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA16161616:
|
|
|
|
|
{
|
|
|
|
|
uint16_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
|
|
|
|
|
|
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
|
|
|
|
r = ((uint16_t *)data)[unitOrderRGBA[isBGR][0]];
|
|
|
|
|
g = ((uint16_t *)data)[unitOrderRGBA[isBGR][1]];
|
|
|
|
|
b = ((uint16_t *)data)[unitOrderRGBA[isBGR][2]];
|
|
|
|
|
a = ((uint16_t *)data)[unitOrderRGBA[isBGR][3]];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
BH_ColorSetRGBA16(value, r, g, b, a);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA1010102:
|
|
|
|
|
{
|
|
|
|
|
uint16_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
uint32_t color;
|
|
|
|
|
int isBGR;
|
|
|
|
|
|
|
|
|
|
color = *(uint32_t *)data;
|
|
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
|
|
|
|
r = color >> bitOrder1010102[isBGR][0] & 0x3FF;
|
|
|
|
|
g = color >> bitOrder1010102[isBGR][1] & 0x3FF;
|
|
|
|
|
b = color >> bitOrder1010102[isBGR][2] & 0x3FF;
|
|
|
|
|
a = color >> bitOrder1010102[isBGR][3] & 0x3;
|
2025-08-02 20:47:40 +03:00
|
|
|
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;
|
2025-08-04 12:33:44 +03:00
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
static size_t bestPaletteIndex(uint8_t r,
|
|
|
|
|
uint8_t g,
|
|
|
|
|
uint8_t b,
|
|
|
|
|
uint8_t a,
|
|
|
|
|
const uint32_t *palette,
|
|
|
|
|
size_t paletteSize)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
|
|
|
|
uint32_t bestError = -1;
|
|
|
|
|
size_t i, bestIndex = 0;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
for (i = 0; i < paletteSize; ++i) {
|
2025-08-02 20:47:40 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
static void setBitmapColor(void *data,
|
|
|
|
|
void *palette,
|
|
|
|
|
int format,
|
|
|
|
|
const BH_Color *value,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
switch (format & 0x0FFF)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
|
|
|
|
|
|
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
|
|
|
|
setBitmapIndex(data, format, x, bestPaletteIndex(r, g, b, a, palette, 1));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
|
|
|
|
|
|
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
|
|
|
|
setBitmapIndex(data, format, x, bestPaletteIndex(r, g, b, a, palette, 4));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
|
|
|
|
|
|
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
|
|
|
|
setBitmapIndex(data, format, x, bestPaletteIndex(r, g, b, a, palette, 16));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
case BH_BITMAP_INDEX8:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
|
|
|
|
|
|
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
2025-08-04 12:33:44 +03:00
|
|
|
setBitmapIndex(data, format, x, bestPaletteIndex(r, g, b, a, palette, 256));
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
uint16_t color;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
2025-08-04 12:33:44 +03:00
|
|
|
color = 0;
|
|
|
|
|
color |= (r >> 3) << bitOrder565[isBGR][0];
|
|
|
|
|
color |= (g >> 2) << bitOrder565[isBGR][1];
|
|
|
|
|
color |= (b >> 3) << bitOrder565[isBGR][2];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
*(uint16_t *)(data) = color;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGB888:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
2025-08-04 12:33:44 +03:00
|
|
|
((uint8_t *)data)[unitOrderRGB[isBGR][0]] = r;
|
|
|
|
|
((uint8_t *)data)[unitOrderRGB[isBGR][1]] = g;
|
|
|
|
|
((uint8_t *)data)[unitOrderRGB[isBGR][2]] = b;
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA8888:
|
|
|
|
|
{
|
|
|
|
|
uint8_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorRGBA8(value, &r, &g, &b, &a);
|
|
|
|
|
if (format & BH_BITMAP_NOALPHA)
|
|
|
|
|
a = 0xFF;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
((uint8_t *)data)[unitOrderRGBA[isBGR][0]] = r;
|
|
|
|
|
((uint8_t *)data)[unitOrderRGBA[isBGR][1]] = g;
|
|
|
|
|
((uint8_t *)data)[unitOrderRGBA[isBGR][2]] = b;
|
|
|
|
|
((uint8_t *)data)[unitOrderRGBA[isBGR][3]] = a;
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGB161616:
|
|
|
|
|
{
|
|
|
|
|
uint16_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorRGBA16(value, &r, &g, &b, &a);
|
2025-08-04 12:33:44 +03:00
|
|
|
((uint16_t *)data)[unitOrderRGB[isBGR][0]] = r;
|
|
|
|
|
((uint16_t *)data)[unitOrderRGB[isBGR][1]] = g;
|
|
|
|
|
((uint16_t *)data)[unitOrderRGB[isBGR][2]] = b;
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA16161616:
|
|
|
|
|
{
|
|
|
|
|
uint16_t r, g, b, a;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorRGBA16(value, &r, &g, &b, &a);
|
|
|
|
|
if (format & BH_BITMAP_NOALPHA)
|
|
|
|
|
a = 0xFFFF;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
((uint16_t *)data)[unitOrderRGBA[isBGR][0]] = r;
|
|
|
|
|
((uint16_t *)data)[unitOrderRGBA[isBGR][1]] = g;
|
|
|
|
|
((uint16_t *)data)[unitOrderRGBA[isBGR][2]] = b;
|
|
|
|
|
((uint16_t *)data)[unitOrderRGBA[isBGR][3]] = a;
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_RGBA1010102:
|
|
|
|
|
{
|
|
|
|
|
uint16_t r, g, b, a;
|
|
|
|
|
uint32_t color;
|
2025-08-04 12:33:44 +03:00
|
|
|
int isBGR;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
isBGR = (format & BH_BITMAP_BGR) > 0;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_ColorRGBA16(value, &r, &g, &b, &a);
|
|
|
|
|
if (format & BH_BITMAP_NOALPHA)
|
|
|
|
|
a = 0xFFFF;
|
|
|
|
|
|
|
|
|
|
color = 0;
|
2025-08-04 12:33:44 +03:00
|
|
|
color |= (r >> 6) << bitOrder1010102[isBGR][0];
|
|
|
|
|
color |= (g >> 6) << bitOrder1010102[isBGR][1];
|
|
|
|
|
color |= (b >> 6) << bitOrder1010102[isBGR][2];
|
|
|
|
|
color |= (a >> 10) << bitOrder1010102[isBGR][3];
|
2025-08-02 20:47:40 +03:00
|
|
|
|
|
|
|
|
*(uint32_t *)data = color;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
static void *rowAt(void *data,
|
|
|
|
|
int format,
|
|
|
|
|
int step,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x)
|
2025-08-04 12:33:44 +03:00
|
|
|
{
|
|
|
|
|
switch (format & 0x0FFF)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
return (uint8_t *)data + x / 8;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
return (uint8_t *)data + x / 4;
|
|
|
|
|
|
|
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
return (uint8_t *)data + x / 2;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return (uint8_t *)data + step * x;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static size_t calculateStride(int format,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t width)
|
2025-08-04 12:33:44 +03:00
|
|
|
{
|
2025-08-04 20:35:19 +03:00
|
|
|
size_t step;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
switch (format & 0x0FFF)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1: return (width + 7) / 8;
|
|
|
|
|
case BH_BITMAP_INDEX2: return (width + 3) / 4;
|
|
|
|
|
case BH_BITMAP_INDEX4: return (width + 1) / 2;
|
|
|
|
|
}
|
2025-08-04 20:35:19 +03:00
|
|
|
|
|
|
|
|
step = calculateStep(format);
|
|
|
|
|
if (BH_CHECK_UMUL_WRAP(step, width, size_t))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return step * width;
|
2025-08-04 12:33:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int isPaletteNeeded(int format)
|
|
|
|
|
{
|
|
|
|
|
switch (format)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1:
|
|
|
|
|
case BH_BITMAP_INDEX2:
|
|
|
|
|
case BH_BITMAP_INDEX4:
|
|
|
|
|
case BH_BITMAP_INDEX8:
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static size_t calculateTrailer(int format)
|
|
|
|
|
{
|
|
|
|
|
switch (format & 0x0FFF)
|
|
|
|
|
{
|
|
|
|
|
case BH_BITMAP_INDEX1: return sizeof(uint32_t) * 2;
|
|
|
|
|
case BH_BITMAP_INDEX2: return sizeof(uint32_t) * 4;
|
|
|
|
|
case BH_BITMAP_INDEX4: return sizeof(uint32_t) * 16;
|
|
|
|
|
case BH_BITMAP_INDEX8: return sizeof(uint32_t) * 256;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Public functions */
|
2025-08-04 20:35:19 +03:00
|
|
|
BH_Bitmap *BH_BitmapNew(uint32_t width,
|
|
|
|
|
uint32_t height,
|
2025-08-04 12:33:44 +03:00
|
|
|
int format,
|
|
|
|
|
int flags,
|
|
|
|
|
void *data,
|
|
|
|
|
void *palette)
|
|
|
|
|
{
|
|
|
|
|
BH_Bitmap *result;
|
|
|
|
|
int needPalette;
|
2025-08-04 20:35:19 +03:00
|
|
|
size_t header, body, allocSize, stride, step;
|
|
|
|
|
|
|
|
|
|
/* Basic sanity check */
|
|
|
|
|
if (width == 0 || width >= 0x7FFFFFFF ||
|
|
|
|
|
height == 0 || height >= 0x7FFFFFFF)
|
|
|
|
|
return NULL;
|
2025-08-04 12:33:44 +03:00
|
|
|
|
|
|
|
|
/* Unset internal flags */
|
|
|
|
|
flags &= ~BH_BITMAP_FLAG_EXT_DATA;
|
|
|
|
|
flags &= ~BH_BITMAP_FLAG_EXT_PALETTE;
|
|
|
|
|
|
|
|
|
|
/* Calculate size of the bitmap with headers */
|
|
|
|
|
step = calculateStep(format);
|
|
|
|
|
stride = calculateStride(format, width);
|
|
|
|
|
needPalette = isPaletteNeeded(format);
|
|
|
|
|
|
2025-08-04 20:35:19 +03:00
|
|
|
/* Check stride overflow */
|
|
|
|
|
if (!stride)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
if (flags & BH_BITMAP_FLAG_ALIGN32)
|
2025-08-04 20:35:19 +03:00
|
|
|
{
|
|
|
|
|
if (BH_CHECK_UADD_WRAP(stride, sizeof(uint32_t) - 1, size_t))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
stride = (stride + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
|
2025-08-04 20:35:19 +03:00
|
|
|
}
|
2025-08-04 12:33:44 +03:00
|
|
|
|
|
|
|
|
header = sizeof(*result);
|
|
|
|
|
header = (header + (sizeof(uint64_t) - 1)) & ~(sizeof(uint64_t) - 1);
|
2025-08-04 20:35:19 +03:00
|
|
|
allocSize = calculateTrailer(format);
|
|
|
|
|
|
|
|
|
|
body = stride * height;
|
|
|
|
|
if (BH_CHECK_UMUL_WRAP(stride, height, size_t))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (BH_CHECK_UADD_WRAP(body, sizeof(uint32_t) - 1, size_t))
|
|
|
|
|
return NULL;
|
|
|
|
|
body = (body + (sizeof(uint32_t) - 1)) & ~(sizeof(uint32_t) - 1);
|
2025-08-04 12:33:44 +03:00
|
|
|
|
|
|
|
|
/* Adjust body and trailer size if ext data or palette is provided */
|
|
|
|
|
if (palette)
|
|
|
|
|
{
|
|
|
|
|
flags |= BH_BITMAP_FLAG_EXT_PALETTE;
|
2025-08-04 20:35:19 +03:00
|
|
|
allocSize = 0;
|
2025-08-04 12:33:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data)
|
|
|
|
|
{
|
|
|
|
|
flags |= BH_BITMAP_FLAG_EXT_DATA;
|
|
|
|
|
body = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 20:35:19 +03:00
|
|
|
if (BH_CHECK_UADD_WRAP(allocSize, header, size_t))
|
|
|
|
|
return NULL;
|
|
|
|
|
allocSize += header;
|
|
|
|
|
|
|
|
|
|
if (BH_CHECK_UADD_WRAP(allocSize, body, size_t))
|
|
|
|
|
return NULL;
|
|
|
|
|
allocSize += body;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
/* Allocate and setup bitmap data */
|
2025-08-04 20:35:19 +03:00
|
|
|
result = malloc(allocSize);
|
2025-08-04 12:33:44 +03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
void BH_BitmapColor(const BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
|
|
|
|
uint32_t y,
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_Color *value)
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
bitmapColor(BH_BitmapAt(bitmap, x, y), bitmap->palette, bitmap->format, value, x);
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BH_BitmapSetColor(BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
|
|
|
|
uint32_t y,
|
2025-08-02 20:47:40 +03:00
|
|
|
const BH_Color *value)
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
setBitmapColor(BH_BitmapAt(bitmap, x, y), bitmap->palette, bitmap->format, value, x);
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_Bitmap *BH_BitmapCopy(BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
|
|
|
|
uint32_t y,
|
|
|
|
|
uint32_t width,
|
|
|
|
|
uint32_t height,
|
2025-08-02 20:47:40 +03:00
|
|
|
int shallow)
|
|
|
|
|
{
|
|
|
|
|
BH_Bitmap *result;
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t i, j;
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 20:35:19 +03:00
|
|
|
/* Sanity checks */
|
|
|
|
|
if (!width || !height || x >= bitmap->width || y >= bitmap->height)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (width < bitmap->width - x || height < bitmap->height - y)
|
2025-08-02 20:47:40 +03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Shallow copy can't go out of bounds of the source bitmap */
|
|
|
|
|
if (shallow)
|
|
|
|
|
{
|
|
|
|
|
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;
|
2025-08-04 20:35:19 +03:00
|
|
|
BH_BitmapColor(bitmap, i + x, j + y, &color);
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_BitmapSetColor(result, i, j, &color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
uint8_t BH_BitmapIndex(const BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
|
|
|
|
uint32_t y)
|
2025-08-04 12:33:44 +03:00
|
|
|
{
|
|
|
|
|
return bitmapIndex(BH_BitmapAt(bitmap, x, y), bitmap->format, x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BH_BitmapSetIndex(BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
|
|
|
|
uint32_t y,
|
2025-08-04 12:33:44 +03:00
|
|
|
uint8_t index)
|
|
|
|
|
{
|
|
|
|
|
setBitmapIndex(BH_BitmapAt(bitmap, x, y), bitmap->format, x, index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-02 20:47:40 +03:00
|
|
|
void *BH_BitmapScanline(const BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t y)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
|
|
|
|
return bitmap->data + y * bitmap->stride;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *BH_BitmapAt(const BH_Bitmap *bitmap,
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t x,
|
|
|
|
|
uint32_t y)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
return rowAt(BH_BitmapScanline(bitmap, y), bitmap->format, bitmap->step, x);
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t BH_BitmapWidth(BH_Bitmap *bitmap)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
|
|
|
|
return bitmap->width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-08-04 20:35:19 +03:00
|
|
|
uint32_t BH_BitmapHeight(BH_Bitmap *bitmap)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
size_t srcStep, destStep, x;
|
|
|
|
|
srcStep = calculateStep(srcFormat);
|
|
|
|
|
destStep = calculateStep(destFormat);
|
2025-08-02 20:47:40 +03:00
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
for (x = 0; count; --count, ++x)
|
2025-08-02 20:47:40 +03:00
|
|
|
{
|
2025-08-04 12:33:44 +03:00
|
|
|
void *srcAt, *destAt;
|
2025-08-02 20:47:40 +03:00
|
|
|
BH_Color data;
|
|
|
|
|
|
2025-08-04 12:33:44 +03:00
|
|
|
srcAt = rowAt(src, srcFormat, srcStep, x);
|
|
|
|
|
destAt = rowAt(dest, destFormat, destStep, x);
|
|
|
|
|
bitmapColor(srcAt, srcPalette, srcFormat, &data, x);
|
|
|
|
|
setBitmapColor(destAt, destPalette, destFormat, &data, x);
|
2025-08-02 20:47:40 +03:00
|
|
|
}
|
|
|
|
|
}
|