aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/Manual/en/BH_Bitmap.pod290
-rw-r--r--doc/Manual/en/Makefile2
-rw-r--r--doc/Manual/ru/BH_Bitmap.pod297
-rw-r--r--doc/Manual/ru/Makefile2
-rw-r--r--include/BH/Bitmap.h105
-rw-r--r--include/BH/Info7
-rw-r--r--src/Bitmap.c636
-rw-r--r--test/src/TestBitmap.c97
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();
+}