diff options
| author | Mikhail Romanko <me@blankhex.com> | 2025-08-02 20:47:40 +0300 |
|---|---|---|
| committer | Mikhail Romanko <me@blankhex.com> | 2025-08-03 19:23:41 +0300 |
| commit | f9feb23d4dae1287e21d45ab074c484f86d4cb1c (patch) | |
| tree | d682d962ad99fb2af62687096870e960307219fd | |
| parent | 3cfb61b6171beb567fab85157004659e12feb4b9 (diff) | |
| download | bhlib-f9feb23d4dae1287e21d45ab074c484f86d4cb1c.tar.gz | |
Add bitmap support
| -rw-r--r-- | doc/Manual/en/BH_Bitmap.pod | 290 | ||||
| -rw-r--r-- | doc/Manual/en/Makefile | 2 | ||||
| -rw-r--r-- | doc/Manual/ru/BH_Bitmap.pod | 297 | ||||
| -rw-r--r-- | doc/Manual/ru/Makefile | 2 | ||||
| -rw-r--r-- | include/BH/Bitmap.h | 105 | ||||
| -rw-r--r-- | include/BH/Info | 7 | ||||
| -rw-r--r-- | src/Bitmap.c | 636 | ||||
| -rw-r--r-- | test/src/TestBitmap.c | 97 |
8 files changed, 1436 insertions, 0 deletions
diff --git a/doc/Manual/en/BH_Bitmap.pod b/doc/Manual/en/BH_Bitmap.pod new file mode 100644 index 0000000..1dd603b --- /dev/null +++ b/doc/Manual/en/BH_Bitmap.pod @@ -0,0 +1,290 @@ +=encoding UTF-8 + + +=head1 NAME + +BH_Bitmap - bitmap/image access + + +=head1 SYNTAX + + #include <BH/Bitmap.h> + + cc prog.c -o prog -lbh + + +=head1 DESCRIPTION + +The BH_Bitmap module provides methods for accessing the pixel data of a bitmap +(image) and for converting between different pixel formats. + + +=head1 FORMATS + +Currently, the following pixel formats are supported: + +=over + +=item B<BH_BITMAP_INDEX8> + +8-bit indexed/paletted + +=item B<BH_BITMAP_GRAY8> + +8-bit grayscale + +=item B<BH_BITMAP_GRAY16> + +16-bit grayscale + +=item B<BH_BITMAP_RGBA32> + +32-bit RGB with alpha represented in uint32_t value. The layout is: 0xAARRGGBB + +=item B<BH_BITMAP_RGBA64> + +64-bit RGB with alpha represented in uint64_t value. The layout is: +0xAAAARRRRGGGGBBBB + +=item B<BH_BITMAP_RGB565> + +16-bit RGB + +=item B<BH_BITMAP_RGB888> + +24-bit RGB + +=item B<BH_BITMAP_RGBA8888> + +32-bit RGB with alpha + +=item B<BH_BITMAP_RGB161616> + +48-bit RGB + +=item B<BH_BITMAP_RGBA16161616> + +64-bit RGB with alpha + +=item B<BH_BITMAP_RGBA1010102> + +32-bit RGB with alpha + +=back + +All pixel formats use the current machine endianness. + +The flag I<BH_BITMAP_BGR> can be used to change the order of the color channels +(RGB -> BGR). The flag has no effect on the following pixel formats: +I<BH_BITMAP_INDEX8>, I<BH_BITMAP_GRAY8>, I<BH_BITMAP_GRAY16>, +I<BH_BITMAP_RGBA32> and I<BH_BITMAP_RGBA64>. + +The flag I<BH_BITMAP_NOALPHA> can be used to indicate that the alpha channel is +not used and should always be set to the maximum value (255 for 8-bit and 65535 +for 16-bit). + +The flag I<BH_BITMAP_PREMULT> can be used to indicate that color values are in +premultiplied form. + +The color palette is assumed to contain exactly 256 colors and is stored in the +I<BH_BITMAP_RGBA32> pixel format. + + +=head1 API CALLS + + +=head2 BH_BitmapNew + + BH_Bitmap *BH_BitmapNew(int width, + int height, + int format, + int flags, + void *data, + void *palette); + +Creates the bitmap with the specified I<width>, I<height> and pixel I<format>. + +The I<flags> parameter can take a combination of the following values: + +=over + +=item B<BH_BITMAP_FLAG_ALIGN32> + +Rows are aligned to 32-bit boundary + +=back + +The optional I<data> parameter specifies pointer to the existing data. + +The optional I<palette> parameter specifies pointer to the existing palette. + +This function returns a pointer to a new BH_Bitmap object or NULL. + + +=head2 BH_BitmapFree + + void BH_BitmapFree(BH_Bitmap *bitmap); + +Destroys the bitmap object. + + +=head2 BH_BitmapColor + + void BH_BitmapColor(const BH_Bitmap *bitmap, + int x, + int y, + BH_Color *value); + +Reads color value of the pixel at specified position. + +The I<x> and I<y> parameters specify a position on the bitmap. + + +=head2 BH_BitmapSetColor + + void BH_BitmapSetColor(BH_Bitmap *bitmap, + int x, + int y, + const BH_Color *value); + +Writes color value of the pixel at specified position. + +The I<x> and I<y> parameters specify a position on the bitmap. + + +=head2 BH_BitmapCopy + + BH_Bitmap *BH_BitmapCopy(BH_Bitmap *bitmap, + int x, + int y, + int width, + int height, + int shallow); + +Creates a copy of the bitmap region by the given position and size. + +The I<x> and I<y> parameters specify a position on the bitmap. + +The I<width> and I<height> parameters specify size of the new bitmap. + +The I<shallow> parameter specifies whether the new bitmap is a shallow copy (or +a view) of the existing bitmap or a deep copy. For the shallow copy to work, the +region should be within the existing bitmap. + +This function returns a pointer to a new BH_Bitmap object or NULL. + + +=head2 BH_BitmapScanline + + void *BH_BitmapScanline(const BH_Bitmap *bitmap, + int y); + +Returns address of the scanline in the bitmap. + + +=head2 BH_BitmapAt + + void *BH_BitmapAt(const BH_Bitmap *bitmap, + int x, + int y); + +Returns address of the pixel in the bitmap. + + +=head2 BH_BitmapWidth + + int BH_BitmapWidth(BH_Bitmap *bitmap); + +Returns width of the bitmap. + + +=head2 BH_BitmapHeight + + int BH_BitmapHeight(BH_Bitmap *bitmap); + +Returns height of the bitmap. + + +=head2 BH_BitmapFormat + + int BH_BitmapFormat(BH_Bitmap *bitmap); + +Returns pixel format of the bitmap. + + +=head2 BH_BitmapStride + + size_t BH_BitmapStride(BH_Bitmap *bitmap); + +Returns row stride of the bitmap. + + +=head2 BH_BitmapData + + void *BH_BitmapData(BH_Bitmap *bitmap); + +Returns pointer to the pixel data of the bitmap. + + +=head2 BH_BitmapPalette + + void *BH_BitmapPalette(BH_Bitmap *bitmap); + +Returns pointer to the bitmap's palette. + + +=head2 BH_BitmapFlags + + int BH_BitmapFlags(BH_Bitmap *bitmap); + +Returns bitmap's flags. + +The result can be a combination of the following values: + +=over + +=item B<BH_BITMAP_FLAG_ALIGN32> + +Rows are aligned to 32-bit boundary + +=item B<BH_BITMAP_FLAG_EXT_DATA> + +Bitmap doesn't own the pixel data + +=item B<BH_BITMAP_FLAG_EXT_PALETTE> + +Bitmap doesn't own palette data + +=back + + +=head2 BH_BitmapConvertRow + + void BH_BitmapConvertRow(void *src, + int srcFormat, + void *srcPalette, + void *dest, + int destFormat, + void *destPalette, + size_t count); + +Converts a row of source data from one pixel format to another pixel format. + +The parameter I<src> and I<srcFormat> specify the source of the data and its +pixel format. + +The parameter I<srcPalette> specify the source palette (if required by the pixel +format). + +The parameter I<dest> and I<destFormat> specify the destination of the data and +its pixel format. + +The parameter I<destPalette> specify the destination palette (if required by the +pixel format). + +The parameter I<count> specifies the number of pixel for conversion. + + +=head1 SEE ALSO + +L<BH> diff --git a/doc/Manual/en/Makefile b/doc/Manual/en/Makefile index 6175844..f5ff1f7 100644 --- a/doc/Manual/en/Makefile +++ b/doc/Manual/en/Makefile @@ -3,6 +3,7 @@ POD2MAN = pod2man HTMLS = BH_Algo.html \ BH_Args.html \ + BH_Bitmap.html \ BH_Box2f.html \ BH_Box3f.html \ BH_Color.html \ @@ -30,6 +31,7 @@ HTMLS = BH_Algo.html \ MANS = BH_Algo.3 \ BH_Args.3 \ + BH_Bitmap.3 \ BH_Box2f.3 \ BH_Box3f.3 \ BH_Color.3 \ diff --git a/doc/Manual/ru/BH_Bitmap.pod b/doc/Manual/ru/BH_Bitmap.pod new file mode 100644 index 0000000..566f14f --- /dev/null +++ b/doc/Manual/ru/BH_Bitmap.pod @@ -0,0 +1,297 @@ +=encoding UTF-8 + + +=head1 НАИМЕНОВАНИЕ + +BH_Bitmap - доступ к растровому изображению/картинке с изображением + + +=head1 СИНТАКСИС + + #include <BH/Bitmap.h> + + cc prog.c -o prog -lbh + + +=head1 ОПИСАНИЕ + +Модуль BH_Bitmap предоставляет методы для доступа к пиксельным данным растрового +изображения и для преобразования между различными пиксельными форматами. + + +=head1 ФОРМАТЫ + +В настоящее время поддерживаются следующие пиксельные форматы: + +=over + +=item B<BH_BITMAP_INDEX8> + +8-разрядный индексированный/палитрированный + +=item B<BH_BITMAP_GRAY8> + +8-разрядные оттенки серого + +=item B<BH_BITMAP_GRAY16> + +16-битные оттенки серого + +=item B<BH_BITMAP_RGBA32> + +32-разрядный RGB с прозрачностью, представленный в виде uint32_t. +Формат: 0xAARRGGBB + +=item B<BH_BITMAP_RGBA64> + +64-разрядный RGB с прозрачностью, представленный в виде uint64_t. +Формат: 0xAAAARRRRGGGGBBBB + +=item B<BH_BITMAP_RGB565> + +16-разрядный RGB + +=item B<BH_BITMAP_RGB888> + +24-разрядный RGB + +=item B<BH_BITMAP_RGBA8888> + +32-разрядный RGB с прозрачностью + +=item B<BH_BITMAP_RGB161616> + +48-разрядный RGB + +=item B<BH_BITMAP_RGBA16161616> + +64-разрядный RGB с прозрачностью + +=item B<BH_BITMAP_RGBA1010102> + +32-разрядный RGB с прозрачностью + +=back + +Во всех форматах пикселей используется нативный порядок байт. + +Флаг I<BH_BITMAP_BGR> может использоваться для изменения порядка цветовых +каналов (RGB -> BGR). Этот флаг не влияет на следующие пиксельные форматы: +I<BH_BITMAP_INDEX8>, I<BH_BITMAP_GRAY8>, I<BH_BITMAP_GRAY16>, +I<BH_BITMAP_RGBA32> и I<BH_BITMAP_RGBA64>. + +Флаг I<BH_BITMAP_NOALPHA> может использоваться для указания того, +что альфа-канал не используется и всегда должен быть установлен в максимальное +значение (255 для 8-разрядных и 65535 для 16-разрядных). + +Флаг I<BH_BITMAP_PREMULT> может использоваться для указания того, что значения +цвета представлены в предварительно умноженном виде. + +Предполагается, что цветовая палитра содержит ровно 256 цветов и хранится в +пиксельном формате I<BH_BITMAP_RGBA32>. + + +=head1 ВЫЗОВЫ API + + +=head2 BH_BitmapNew + + BH_Bitmap *BH_BitmapNew(int width, + int height, + int format, + int flags, + void *data, + void *palette); + +Создает растровое изображение с указанной шириной, высотой и пиксельным +форматом. + +Параметры I<width> и I<height> указывают размеры изображения. + +Параметр I<format> указывает используемый пиксельный формат изображения. + +Параметр I<flags> может принимать комбинацию из следующих значений: + +=over + +=item B<BH_BITMAP_FLAG_ALIGN32> + +Строки выравниваются по 32-разрядной границе + +=back + +Необязательный параметр I<data> указывает на существующие данные. + +Необязательный параметр I<palette> указывает на существующую палитру. + +Эта функция возвращает указатель на новый объект BH_Bitmap или значение NULL. + + +=head2 BH_BitmapFree + + void BH_BitmapFree(BH_Bitmap *bitmap); + +Уничтожает растровый объект. + + +=head2 BH_BitmapColor + + void BH_BitmapColor(const BH_Bitmap *bitmap, + int x, + int y, + BH_Color *value); + +Считывает значение цвета пикселя в указанной позиции. + +Параметры I<x> и I<y> определяют положение на растровом изображении. + + +=head2 BH_BitmapSetColor + + void BH_BitmapSetColor(BH_Bitmap *bitmap, + int x, + int y, + const BH_Color *value); + +Записывает значение цвета пикселя в указанной позиции. + +Параметры I<x> и I<y> определяют положение на растровой карте. + + +=head2 BH_BitmapCopy + + BH_Bitmap *BH_BitmapCopy(BH_Bitmap *bitmap, + int x, + int y, + int width, + int height, + int shallow); + +Создает копию области растрового изображения с заданным положением и размером. + +Параметры I<x> и I<y> задают положение на растровом изображении. + +Параметры I<width> и I<height> задают размер нового растрового изображения. + +Параметр I<shallow> указывает, является ли новое растровое изображение +поверхностной копией (или отображением) существующего растрового изображения или +его глубокой копией. Для работы с поверхностной копией область должна находиться +в пределах существующего растрового изображения. + +Эта функция возвращает указатель на новый объект BH_Bitmap или NULL. + + +=head2 BH_BitmapScanline + + void *BH_BitmapScanline(const BH_Bitmap *bitmap, + int y); + +Возвращает адрес строки сканирования в растровом изображении. + + +=head2 BH_BitmapAt + + void *BH_BitmapAt(const BH_Bitmap *bitmap, + int x, + int y); + +Возвращает адрес пикселя в растровом изображении. + + +=head2 BH_BitmapWidth + + int BH_BitmapWidth(BH_Bitmap *bitmap); + +Возвращает ширину растрового изображения. + + +=head2 BH_BitmapHeight + + int BH_BitmapHeight(BH_Bitmap *bitmap); + +Возвращает высоту растрового изображения. + + +=head2 BH_BitmapFormat + + int BH_BitmapFormat(BH_Bitmap *bitmap); + +Возвращает пиксельный формат растрового изображения. + + +=head2 BH_BitmapStride + + size_t BH_BitmapStride(BH_Bitmap *bitmap); + +Возвращает шаг строки растрового изображения. + + +=head2 BH_BitmapData + + void *BH_BitmapData(BH_Bitmap *bitmap); + +Возвращает указатель на пиксельные данные растрового изображения. + + +=head2 BH_BitmapPalette + + void *BH_BitmapPalette(BH_Bitmap *bitmap); + +Возвращает указатель на палитру растрового изображения. + + +=head2 BH_BitmapFlags + + int BH_BitmapFlags(BH_Bitmap *bitmap); + +Возвращает флаги растрового изображения. + +Результатом может быть комбинация следующих значений: + +=over + +=item B<BH_BITMAP_FLAG_ALIGN32> + +Строки выравниваются по 32-битной границе + +=item B<BH_BITMAP_FLAG_EXT_DATA> + +Растровому изображению не принадлежат пиксельные данные + +=item B<BH_BITMAP_FLAG_EXT_PALETTE> + +Растровому изображению не принадлежат данные палитры + +=back + + +=head2 BH_BitmapConvertRow + + void BH_BitmapConvertRow(void *src, + int srcFormat, + void *srcPalette, + void *dest, + int destFormat, + void *destPalette, + size_t count); + +Преобразует строку исходных данных из одного пиксельного формата в другой пиксельный формат. + +Параметры I<src> и I<srcFormat> указывают источник данных и его +формат в пикселях. + +Параметр I<srcPalette> указывает исходную палитру (если это требуется +для формата в пикселях). + +Параметры I<dest> и I<destFormat> указывают назначение данных и +их формат в пикселях. + +Параметр I<destPalette> определяет целевую палитру (если этого требует формат +пикселя). + +Параметр I<count> определяет количество пикселей для преобразования. + + +=head1 СМ. ТАКЖЕ + +L<BH> diff --git a/doc/Manual/ru/Makefile b/doc/Manual/ru/Makefile index 6175844..f5ff1f7 100644 --- a/doc/Manual/ru/Makefile +++ b/doc/Manual/ru/Makefile @@ -3,6 +3,7 @@ POD2MAN = pod2man HTMLS = BH_Algo.html \ BH_Args.html \ + BH_Bitmap.html \ BH_Box2f.html \ BH_Box3f.html \ BH_Color.html \ @@ -30,6 +31,7 @@ HTMLS = BH_Algo.html \ MANS = BH_Algo.3 \ BH_Args.3 \ + BH_Bitmap.3 \ BH_Box2f.3 \ BH_Box3f.3 \ BH_Color.3 \ diff --git a/include/BH/Bitmap.h b/include/BH/Bitmap.h new file mode 100644 index 0000000..b1ec661 --- /dev/null +++ b/include/BH/Bitmap.h @@ -0,0 +1,105 @@ +#ifndef BH_BITMAP_H +#define BH_BITMAP_H + + +#include "Common.h" +#include "Color.h" + + +typedef struct BH_Bitmap BH_Bitmap; + + +#define BH_BITMAP_PREMULT 0x1000 +#define BH_BITMAP_NOALPHA 0x2000 +#define BH_BITMAP_BGR 0x4000 + + +#define BH_BITMAP_INDEX8 0x0000 +#define BH_BITMAP_GRAY8 0x0001 +#define BH_BITMAP_GRAY16 0x0002 +#define BH_BITMAP_RGBA32 0x0003 +#define BH_BITMAP_RGBA64 0x0004 +#define BH_BITMAP_RGB565 0x8000 +#define BH_BITMAP_RGB888 0x8001 +#define BH_BITMAP_RGBA8888 0x8002 +#define BH_BITMAP_RGB161616 0x8003 +#define BH_BITMAP_RGBA16161616 0x8004 +#define BH_BITMAP_RGBA1010102 0x8005 + + +#define BH_BITMAP_FLAG_ALIGN32 0x0001 +#define BH_BITMAP_FLAG_EXT_DATA 0x0002 +#define BH_BITMAP_FLAG_EXT_PALETTE 0x0004 + + +BH_Bitmap *BH_BitmapNew(int width, + int height, + int format, + int flags, + void *data, + void *palette); + + +void BH_BitmapFree(BH_Bitmap *bitmap); + + +void BH_BitmapColor(const BH_Bitmap *bitmap, + int x, + int y, + BH_Color *value); + + +void BH_BitmapSetColor(BH_Bitmap *bitmap, + int x, + int y, + const BH_Color *value); + + +BH_Bitmap *BH_BitmapCopy(BH_Bitmap *bitmap, + int x, + int y, + int width, + int height, + int shallow); + + +void *BH_BitmapScanline(const BH_Bitmap *bitmap, + int y); + + +void *BH_BitmapAt(const BH_Bitmap *bitmap, + int x, + int y); + + +int BH_BitmapWidth(BH_Bitmap *bitmap); + + +int BH_BitmapHeight(BH_Bitmap *bitmap); + + +int BH_BitmapFormat(BH_Bitmap *bitmap); + + +size_t BH_BitmapStride(BH_Bitmap *bitmap); + + +void *BH_BitmapData(BH_Bitmap *bitmap); + + +void *BH_BitmapPalette(BH_Bitmap *bitmap); + + +int BH_BitmapFlags(BH_Bitmap *bitmap); + + +void BH_BitmapConvertRow(void *src, + int srcFormat, + void *srcPalette, + void *dest, + int destFormat, + void *destPalette, + size_t count); + + +#endif /* BH_BITMAP_H */ diff --git a/include/BH/Info b/include/BH/Info new file mode 100644 index 0000000..6b34345 --- /dev/null +++ b/include/BH/Info @@ -0,0 +1,7 @@ +12345678901234567890123456789012 +RRRR + GGGG + BBBB + AAAA + FFFF + 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; + } +} diff --git a/test/src/TestBitmap.c b/test/src/TestBitmap.c new file mode 100644 index 0000000..349a157 --- /dev/null +++ b/test/src/TestBitmap.c @@ -0,0 +1,97 @@ +#include <BH/Bitmap.h> +#include <BH/Color.h> +#include <BH/IO.h> +#include <BH/Unit.h> + + +BH_UNIT_TEST(RoundTrip) +{ + size_t i, flag, format; + + uint16_t data[][4] = + { + {0, 0, 0, 65535}, + {0, 0, 65535, 65535}, + {0, 65535, 0, 65535}, + {65535, 0, 0, 65535}, + {0, 65535, 65535, 65535}, + {65535, 0, 65535, 65535}, + {65535, 65535, 0, 65535}, + {65535, 65535, 65535, 65535}, + {0, 0, 0, 0}, + {0, 0, 65535, 0}, + {0, 65535, 0, 0}, + {65535, 0, 0, 0}, + {0, 65535, 65535, 0}, + {65535, 0, 65535, 0}, + {65535, 65535, 0, 0}, + {65535, 65535, 65535, 0}, + }; + + uint16_t flags[] = + { + 0, + BH_BITMAP_BGR, + }; + + uint16_t formats[] = + { + BH_BITMAP_RGBA32, + BH_BITMAP_RGBA64, + BH_BITMAP_RGB888, + BH_BITMAP_RGBA8888, + BH_BITMAP_RGB161616, + BH_BITMAP_RGBA16161616, + BH_BITMAP_RGB565, + BH_BITMAP_RGBA1010102, + BH_BITMAP_RGBA8888 | BH_BITMAP_NOALPHA, + BH_BITMAP_RGBA16161616 | BH_BITMAP_NOALPHA, + BH_BITMAP_RGBA1010102 | BH_BITMAP_NOALPHA, + }; + + for (flag = 0; flag < sizeof(flags) / sizeof(uint16_t); ++flag) + { + for (format = 0; format < sizeof(formats) / sizeof(uint16_t); ++format) + { + for (i = 0; i < 16; ++i) + { + BH_Color source, destination; + uint64_t temp; + + BH_ColorSetRGBA16(&source, data[i][0], data[i][1], data[i][2], + data[i][3]); + BH_BitmapConvertRow(&source.data, BH_BITMAP_RGBA16161616, NULL, + &temp, formats[format] | flags[flag], NULL, + 1); + BH_BitmapConvertRow(&temp, formats[format] | flags[flag], NULL, + &destination.data, BH_BITMAP_RGBA16161616, + NULL, 1); + + BH_VERIFY(source.data.rgba.r == destination.data.rgba.r); + BH_VERIFY(source.data.rgba.g == destination.data.rgba.g); + BH_VERIFY(source.data.rgba.b == destination.data.rgba.b); + + if (formats[format] == BH_BITMAP_RGB888 || + formats[format] == BH_BITMAP_RGB161616 || + formats[format] == BH_BITMAP_RGB565 || + (formats[format] | flags[flag]) & BH_BITMAP_NOALPHA) + BH_VERIFY(destination.data.rgba.a == 65535); + else + BH_VERIFY(source.data.rgba.a == destination.data.rgba.a); + } + } + } + + return 0; +} + + +int main(int argc, char **argv) +{ + BH_UNUSED(argc); + BH_UNUSED(argv); + + BH_UNIT_ADD(RoundTrip); + + return BH_UnitRun(); +} |
