#include #include #include #include #include #include "studio/bitmap.h" static inline size_t area(const struct bitmap bitmap) { assert(bitmap.width); assert(bitmap.height); assert(bitmap.width <= SIZE_MAX / bitmap.height); return bitmap.width * bitmap.height; } static inline size_t size(const struct bitmap bitmap) { size_t size = area(bitmap); switch (bitmap.depth) { case mono1: { assert(size <= SIZE_MAX - 8); size = (size + 8) / 8; } case grey8: case index8: { break; } case rgb24: { assert(size <= SIZE_MAX / rgb); size *= rgb; break; } case rgba32: case cmyk32: { static_assert(sizeof (colour){0}.rgba == sizeof (colour){0}.cmyk, ""); assert(size <= SIZE_MAX / rgba); size *= rgba; break; } default: { assert(0); } } return size; } struct bitmap *createbitmap(const struct bitmap new) { struct bitmap * const bitmap = calloc(1, (sizeof *bitmap) + size(new)); if (bitmap) *bitmap = new; return bitmap; } struct bitmap *clearbitmap(struct bitmap * const bitmap, colour value) { assert(bitmap); size_t size = area(*bitmap); switch (bitmap->depth) { case mono1: { assert(size <= SIZE_MAX - 8); size = (size + 8) / 8; value.grey[level] = value.grey[level] ? 255 : 0; } case grey8: case index8: { for (size_t position = 0; position < size; ++position) bitmap->data[position] = value.grey[level]; break; } case rgb24: { uint8_t (* const pixel)[rgb] = (void *)bitmap->data; for (size_t position = 0; position < size; ++position) { pixel[position][red] = value.rgb[red]; pixel[position][green] = value.rgb[green]; pixel[position][blue] = value.rgb[blue]; } break; } case rgba32: { uint8_t (* const pixel)[rgba] = (void *)bitmap->data; for (size_t position = 0; position < size; ++position) { pixel[position][red] = value.rgba[red]; pixel[position][green] = value.rgba[green]; pixel[position][blue] = value.rgba[blue]; pixel[position][alpha] = value.rgba[alpha]; } break; } case cmyk32: { uint8_t (* const pixel)[cmyk] = (void *)bitmap->data; for (size_t position = 0; position < size; ++position) { pixel[position][cyan] = value.cmyk[cyan]; pixel[position][magenta] = value.cmyk[magenta]; pixel[position][yellow] = value.cmyk[yellow]; pixel[position][black] = value.cmyk[black]; } } default: { assert(0); } } bitmap->clear = value; return bitmap; } static inline bool clip(const struct bitmap bitmap, const size_t x, const size_t y) { assert(bitmap.width); assert(bitmap.height); assert(bitmap.width <= SIZE_MAX / bitmap.height); return (x >= bitmap.width || y >= bitmap.height); } colour getbitmap(const struct bitmap * const bitmap, const size_t x, const size_t y) { assert(bitmap); if (clip(*bitmap, x, y)) return (colour){0}; const size_t position = x + (y * bitmap->width); switch (bitmap->depth) { case mono1: { return (colour){ .grey[level] = ( bitmap->data[position / 8] & (128u >> (position & 7)) ) ? 255 : 0 }; } case grey8: case index8: { return (colour){.grey[level] = bitmap->data[position]}; } case rgb24: { const uint8_t (* const pixel)[rgb] = (void *)bitmap->data; return (colour){ .rgb[red] = pixel[position][red], .rgb[green] = pixel[position][green], .rgb[blue] = pixel[position][blue] }; } case rgba32: { const uint8_t (* const pixel)[rgba] = (void *)bitmap->data; return (colour){ .rgba[red] = pixel[position][red], .rgba[green] = pixel[position][green], .rgba[blue] = pixel[position][blue], .rgba[alpha] = pixel[position][alpha] }; } case cmyk32: { const uint8_t (* const pixel)[cmyk] = (void *)bitmap->data; return (colour){ .cmyk[cyan] = pixel[position][cyan], .cmyk[magenta] = pixel[position][magenta], .cmyk[yellow] = pixel[position][yellow], .cmyk[black] = pixel[position][black] }; } default: { assert(0); } } } struct bitmap *setbitmap(struct bitmap * const bitmap, const size_t x, const size_t y, const colour value) { assert(bitmap); if (clip(*bitmap, x, y)) return bitmap; const size_t position = x + (y * bitmap->width); switch (bitmap->depth) { case mono1: { const uint8_t bit = 128u >> (position & 7); if (value.grey[level]) bitmap->data[position / 8] |= bit; else bitmap->data[position / 8] &= bit ^ 255; break; } case grey8: case index8: { bitmap->data[position] = value.grey[level]; break; } case rgb24: { uint8_t (* const pixel)[rgb] = (void *)bitmap->data; pixel[position][red] = value.rgb[red]; pixel[position][green] = value.rgb[green]; pixel[position][blue] = value.rgb[blue]; break; } case rgba32: { uint8_t (* const pixel)[rgba] = (void *)bitmap->data; pixel[position][red] = value.rgba[red]; pixel[position][green] = value.rgba[green]; pixel[position][blue] = value.rgba[blue]; pixel[position][alpha] = value.rgba[alpha]; break; } case cmyk32: { uint8_t (* const pixel)[cmyk] = (void *)bitmap->data; pixel[position][cyan] = value.cmyk[cyan]; pixel[position][magenta] = value.cmyk[magenta]; pixel[position][yellow] = value.cmyk[yellow]; pixel[position][black] = value.cmyk[black]; break; } default: { assert(0); } } return bitmap; } struct memory exportbitmap(struct bitmap * const bitmap) { assert(bitmap); return (struct memory){ .size = size(*bitmap), .bytes = bitmap->data }; } struct bitmap *destroybitmap(struct bitmap * const bitmap) { if (bitmap) { *bitmap = (struct bitmap){0}; free(bitmap); } return 0; }