Initial commit

This commit is contained in:
blankhex committed 2026-07-21 18:31:09 +03:00
commit 1929eebc23
11 files changed
+1604

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
name: CI
on:
push:
branches: [trunk]
pull_request:
branches: [trunk]
jobs:
build-and-analyze:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install tools
run: |
sudo apt-get update -qq
sudo apt-get install -y build-essential clang clang-tools clang-tidy cppcheck
- name: Build with GCC
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=gcc CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with Clang
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=clang CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Linting checks
run: |
echo "Running all linting checks..."
FAILED=0
# cppcheck
echo "=== Check 1/4: cppcheck ==="
if make -f Makefile.lint cppcheck; then
echo "cppcheck: PASSED"
else
echo "cppcheck: FAILED"
FAILED=1
fi
# clang-tidy
echo "=== Check 2/4: clang-tidy ==="
if make -f Makefile.lint clang-tidy; then
echo "clang-tidy: PASSED"
else
echo "clang-tidy: FAILED"
FAILED=1
fi
# scan-build
echo "=== Check 3/4: scan-build ==="
if make -f Makefile.lint scan-build; then
echo "scan-build: PASSED"
else
echo "scan-build: FAILED"
FAILED=1
fi
# security-check
echo "=== Check 4/4: security-check ==="
if make -f Makefile.lint security-check; then
echo "security-check: PASSED"
else
echo "security-check: FAILED"
FAILED=1
fi
# Final result
if [ $FAILED -ne 0 ]; then
echo "One or more linting checks failed."
exit 1
else
echo "All linting checks passed."
fi
+68
View File
@@ -0,0 +1,68 @@
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json
+26
View File
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.10)
project(CgePix LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(SOURCES
Color.c
Pix.c
)
set(HEADERS
CgePix.h
)
add_library(CgePix STATIC ${SOURCES} ${HEADERS})
target_include_directories(CgePix PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS CgePix
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES ${HEADERS} DESTINATION include)
+173
View File
@@ -0,0 +1,173 @@
#ifndef CGE_PIX_H
#define CGE_PIX_H
#include <stdint.h>
#include <stddef.h>
enum CgeColorType {
CGE_COLOR_RGBA,
CGE_COLOR_HSVA,
CGE_COLOR_HSLA
};
enum CgePixFormat {
CGE_PIX_UNKNOWN,
CGE_PIX_INDEX1,
CGE_PIX_INDEX1_LSB,
CGE_PIX_INDEX2,
CGE_PIX_INDEX2_LSB,
CGE_PIX_INDEX4,
CGE_PIX_INDEX4_LSB,
CGE_PIX_INDEX8,
CGE_PIX_R8,
CGE_PIX_R16,
CGE_PIX_R32F,
CGE_PIX_RGB8,
CGE_PIX_BGR8,
CGE_PIX_RGB16,
CGE_PIX_BGR16,
CGE_PIX_RGB32F,
CGE_PIX_BGR32F,
CGE_PIX_RGBA8,
CGE_PIX_BGRA8,
CGE_PIX_ARGB8,
CGE_PIX_ABGR8,
CGE_PIX_RGBA16,
CGE_PIX_BGRA16,
CGE_PIX_ARGB16,
CGE_PIX_ABGR16,
CGE_PIX_RGBA32F,
CGE_PIX_BGRA32F,
CGE_PIX_ARGB32F,
CGE_PIX_ABGR32F,
CGE_PIX_RGB565,
CGE_PIX_BGR565,
CGE_PIX_RGBA5551,
CGE_PIX_BGRA5551,
CGE_PIX_ARGB1555,
CGE_PIX_ABGR1555,
CGE_PIX_RGBA1010102,
CGE_PIX_BGRA1010102,
CGE_PIX_ARGB2101010,
CGE_PIX_ABGR2101010,
};
enum CgePixFlags {
CGE_PIX_PREMULTIPLIED = (1 << 0),
CGE_PIX_NOALPHA = (1 << 1),
CGE_PIX_ALIGN32 = (1 << 2),
};
typedef struct CgeColor {
union {
struct {
uint16_t r;
uint16_t g;
uint16_t b;
uint16_t a;
} rgba;
struct {
uint16_t h;
uint16_t s;
uint16_t v;
uint16_t a;
} hsva;
struct {
uint16_t h;
uint16_t s;
uint16_t l;
uint16_t a;
} hsla;
uint16_t channel[4];
} data;
int type;
} CgeColor;
typedef struct CgePix
{
uint32_t width;
uint32_t height;
int format;
int flags;
size_t stride;
void *data;
CgeColor *palette;
} CgePix;
void CgeColorRGBA8(const CgeColor *color, uint8_t *r, uint8_t *g, uint8_t *b,
uint8_t *a);
void CgeColorRGBA16(const CgeColor *color, uint16_t *r, uint16_t *g,
uint16_t *b, uint16_t *a);
void CgeColorRGBAf(const CgeColor *color, float *r, float *g, float *b,
float *a);
void CgeColorSetRGBA8(CgeColor *color, uint8_t r, uint8_t g, uint8_t b,
uint8_t a);
void CgeColorSetRGBA16(CgeColor *color, uint16_t r, uint16_t g, uint16_t b,
uint16_t a);
void CgeColorSetRGBAf(CgeColor *color, float r, float g, float b, float a);
void CgeColorHSVAf(const CgeColor *color, float *h, float *s, float *v,
float *a);
void CgeColorSetHSVAf(CgeColor *color, float h, float s, float v, float a);
void CgeColorHSLAf(const CgeColor *color, float *h, float *s, float *l,
float *a);
void CgeColorSetHSLAf(CgeColor *color, float h, float s, float l, float a);
void CgeColorToRGBA(const CgeColor *color, CgeColor *out);
void CgeColorToHSVA(const CgeColor *color, CgeColor *out);
void CgeColorToHSLA(const CgeColor *color, CgeColor *out);
void CgeColorPremultiply(const CgeColor *color, CgeColor *out);
void CgeColorUnpremultiply(const CgeColor *color, CgeColor *out);
void CgeColorBlendNormal(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendMultiply(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendScreen(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendOverlay(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendDarken(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendLighten(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendColorDodge(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendColorBurn(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendLinearDodge(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendLinearBurn(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendHardLight(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendSoftLight(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendDifference(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendExclusion(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendHue(const CgeColor *fg, const CgeColor *bg, CgeColor *out);
void CgeColorBlendSaturation(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
void CgeColorBlendColor(const CgeColor *fg, const CgeColor *bg, CgeColor *out);
void CgeColorBlendLuminosity(const CgeColor *fg, const CgeColor *bg,
CgeColor *out);
int CgePixFormatIs32Native(int format);
size_t CgePixInitLayout(uint32_t width, uint32_t height, int format, int flags,
CgePix *out);
void CgePixView(CgePix *pix, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, CgePix *out);
size_t CgePixSize(const CgePix *pix);
void *CgePixScanline(const CgePix *pix, uint32_t y);
void *CgePixAt(const CgePix *pix, uint32_t x, uint32_t y);
void CgePixColor(const CgePix *pix, uint32_t x, uint32_t y, CgeColor *value);
void CgePixSetColor(CgePix *pix, uint32_t x, uint32_t y, const CgeColor *value);
uint8_t CgePixIndex(const CgePix *pix, uint32_t x, uint32_t y);
void CgePixSetIndex(CgePix *pix, uint32_t x, uint32_t y, uint8_t index);
void CgePixConvertRow(void *src, int srcFormat, int srcFlags,
const CgeColor *srcPalette, void *dest, int destFormat,
int destFlags, const CgeColor *destPalette, size_t count);
#endif /* CGE_PIX_H */
+628
View File
@@ -0,0 +1,628 @@
#include "CgePix.h"
#include <math.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
/* Conversion between RGB <-> HSL/HSV has error of around ~ 0.009% */
#define DEGREE_MULT 1.8204444e02f
void CgeColorRGBA8(const CgeColor *color, uint8_t *r, uint8_t *g, uint8_t *b,
uint8_t *a) {
CgeColor tmp;
CgeColorToRGBA(color, &tmp);
*r = tmp.data.rgba.r >> 8;
*g = tmp.data.rgba.g >> 8;
*b = tmp.data.rgba.b >> 8;
*a = tmp.data.rgba.a >> 8;
}
void CgeColorRGBA16(const CgeColor *color, uint16_t *r, uint16_t *g,
uint16_t *b, uint16_t *a) {
CgeColor tmp;
CgeColorToRGBA(color, &tmp);
*r = tmp.data.rgba.r;
*g = tmp.data.rgba.g;
*b = tmp.data.rgba.b;
*a = tmp.data.rgba.a;
}
void CgeColorRGBAf(const CgeColor *color, float *r, float *g, float *b,
float *a) {
CgeColor tmp;
CgeColorToRGBA(color, &tmp);
*r = tmp.data.rgba.r / 65535.0f;
*g = tmp.data.rgba.g / 65535.0f;
*b = tmp.data.rgba.b / 65535.0f;
*a = tmp.data.rgba.a / 65535.0f;
}
void CgeColorSetRGBA8(CgeColor *color, uint8_t r, uint8_t g, uint8_t b,
uint8_t a) {
color->type = CGE_COLOR_RGBA;
color->data.rgba.r = (uint16_t)r * 0x0101;
color->data.rgba.g = (uint16_t)g * 0x0101;
color->data.rgba.b = (uint16_t)b * 0x0101;
color->data.rgba.a = (uint16_t)a * 0x0101;
}
void CgeColorSetRGBA16(CgeColor *color, uint16_t r, uint16_t g, uint16_t b,
uint16_t a) {
color->type = CGE_COLOR_RGBA;
color->data.rgba.r = r;
color->data.rgba.g = g;
color->data.rgba.b = b;
color->data.rgba.a = a;
}
void CgeColorSetRGBAf(CgeColor *color, float r, float g, float b, float a) {
r = MAX(MIN(1.0f, r), 0.0f);
g = MAX(MIN(1.0f, g), 0.0f);
b = MAX(MIN(1.0f, b), 0.0f);
a = MAX(MIN(1.0f, a), 0.0f);
color->type = CGE_COLOR_RGBA;
color->data.rgba.r = r * 65535.0f;
color->data.rgba.g = g * 65535.0f;
color->data.rgba.b = b * 65535.0f;
color->data.rgba.a = a * 65535.0f;
}
void CgeColorHSVAf(const CgeColor *color, float *h, float *s, float *v,
float *a) {
CgeColor tmp;
CgeColorToHSVA(color, &tmp);
*h = tmp.data.hsva.h / DEGREE_MULT;
*s = tmp.data.hsva.s / 65535.0f;
*v = tmp.data.hsva.v / 65535.0f;
*a = tmp.data.hsva.a / 65535.0f;
}
void CgeColorSetHSVAf(CgeColor *color, float h, float s, float v, float a) {
while (h >= 360.0f)
h -= 360.0f;
while (h < 0.0f)
h += 360.0f;
s = MAX(MIN(1.0f, s), 0.0f);
v = MAX(MIN(1.0f, v), 0.0f);
color->type = CGE_COLOR_HSVA;
color->data.hsva.h = h * DEGREE_MULT;
color->data.hsva.s = s * 65535.0f;
color->data.hsva.v = v * 65535.0f;
color->data.hsva.a = a * 65535.0f;
}
void CgeColorHSLAf(const CgeColor *color, float *h, float *s, float *l,
float *a) {
CgeColor tmp;
CgeColorToHSLA(color, &tmp);
*h = tmp.data.hsla.h / DEGREE_MULT;
*s = tmp.data.hsla.s / 65535.0f;
*l = tmp.data.hsla.l / 65535.0f;
*a = tmp.data.hsla.a / 65535.0f;
}
void CgeColorSetHSLAf(CgeColor *color, float h, float s, float l, float a) {
while (h >= 360.0f)
h -= 360.0f;
while (h < 0.0f)
h += 360.0f;
s = MAX(MIN(1.0f, s), 0.0f);
l = MAX(MIN(1.0f, l), 0.0f);
color->type = CGE_COLOR_HSLA;
color->data.hsla.h = h * DEGREE_MULT;
color->data.hsla.s = s * 65535;
color->data.hsla.l = l * 65535;
color->data.hsla.a = a * 65535;
}
/*
* Transformations between color models are based on:
* Computer Graphics and Geometric Modeling by Max K. Agoston
*/
static void rgbToHsv(const CgeColor *color, CgeColor *out) {
float min, max, d, r, g, b, a, h, s, v;
CgeColorRGBAf(color, &r, &g, &b, &a);
max = MAX(MAX(r, g), b);
min = MIN(MIN(r, g), b);
v = max;
s = 0.0f;
h = 0.0f;
d = max - min;
if (max)
s = d / max;
if (s) {
if (r == max)
h = (g - b) / d;
else if (g == max)
h = 2 + (b - r) / d;
else
h = 4 + (r - g) / d;
}
CgeColorSetHSVAf(out, h * 60.0f, s, v, a);
}
static void hsvToRgb(const CgeColor *color, CgeColor *out) {
float h, s, v, a, r, g, b, fract, p, q, t;
int sextant;
CgeColorHSVAf(color, &h, &s, &v, &a);
r = v;
g = v;
b = v;
if (s) {
h /= 60.0f;
sextant = (int)h;
fract = h - sextant;
p = v * (1 - s);
q = v * (1 - (s * fract));
t = v * (1 - (s * (1 - fract)));
switch (sextant) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
}
CgeColorSetRGBAf(out, r, g, b, a);
}
static void rgbToHsl(const CgeColor *color, CgeColor *out) {
float min, max, d, r, g, b, a, h, s, l;
CgeColorRGBAf(color, &r, &g, &b, &a);
max = MAX(MAX(r, g), b);
min = MIN(MIN(r, g), b);
l = (max + min) / 2.0;
s = 0.0f;
h = 0.0f;
d = max - min;
if (max != min) {
if (l <= 0.5f)
s = d / (max + min);
else
s = d / (2.0f - max - min);
if (r == max)
h = (g - b) / d;
else if (g == max)
h = 2 + (b - r) / d;
else
h = 4 + (r - g) / d;
}
CgeColorSetHSLAf(out, h * 60.0f, s, l, a);
}
static void hslToHsv(const CgeColor *color, CgeColor *out) {
float h, s, l, v, a;
CgeColorHSLAf(color, &h, &s, &l, &a);
v = s * MIN(l, 1.0f - l) + l;
s = (v) ? (2.0f - 2.0f * l / v) : (0.0f);
CgeColorSetHSVAf(out, h, s, v, a);
}
static void hsvToHsl(const CgeColor *color, CgeColor *out) {
float h, s, v, l, a, m;
CgeColorHSVAf(color, &h, &s, &v, &a);
l = v - v * s / 2.0f;
m = MIN(l, 1.0f - l);
s = (m) ? ((v - l) / m) : (0);
CgeColorSetHSLAf(out, h, s, l, a);
}
void CgeColorToRGBA(const CgeColor *color, CgeColor *out) {
switch (color->type) {
case CGE_COLOR_RGBA:
*out = *color;
break;
case CGE_COLOR_HSVA:
hsvToRgb(color, out);
break;
case CGE_COLOR_HSLA:
hslToHsv(color, out);
hsvToRgb(out, out);
break;
}
}
void CgeColorToHSVA(const CgeColor *color, CgeColor *out) {
switch (color->type) {
case CGE_COLOR_RGBA:
rgbToHsv(color, out);
break;
case CGE_COLOR_HSVA:
*out = *color;
break;
case CGE_COLOR_HSLA:
hslToHsv(color, out);
break;
}
}
void CgeColorToHSLA(const CgeColor *color, CgeColor *out) {
switch (color->type) {
case CGE_COLOR_RGBA:
rgbToHsl(color, out);
break;
case CGE_COLOR_HSVA:
hsvToHsl(color, out);
break;
case CGE_COLOR_HSLA:
*out = *color;
break;
}
}
static uint16_t mulDiv16(uint16_t x, uint16_t y) {
uint32_t product = (uint32_t)x * (uint32_t)y + 32767u;
return (uint16_t)((product + (product >> 16)) >> 16);
}
static uint16_t divScale16(uint16_t x, uint16_t denom) {
return (uint16_t)(((uint32_t)x * 65535u + denom/2 ) / denom);
}
void CgeColorPremultiply(const CgeColor *color, CgeColor *out) {
uint16_t r, g, b, a;
CgeColorRGBA16(color, &r, &g, &b, &a);
if (a) {
out->data.channel[0] = mulDiv16(r, a);
out->data.channel[1] = mulDiv16(g, a);
out->data.channel[2] = mulDiv16(b, a);
} else {
out->data.channel[0] = 0;
out->data.channel[1] = 0;
out->data.channel[2] = 0;
}
out->data.channel[3] = a;
out->type = CGE_COLOR_RGBA;
}
void CgeColorUnpremultiply(const CgeColor *color, CgeColor *out) {
uint16_t r, g, b, a;
CgeColorRGBA16(color, &r, &g, &b, &a);
if (a) {
out->data.channel[0] = divScale16(r, a);
out->data.channel[1] = divScale16(g, a);
out->data.channel[2] = divScale16(b, a);
} else {
out->data.channel[0] = 0;
out->data.channel[1] = 0;
out->data.channel[2] = 0;
}
out->data.channel[3] = a;
out->type = CGE_COLOR_RGBA;
}
static float blendMultiply(float fg, float bg) {
return fg * bg;
}
static float blendScreen(float fg, float bg) {
return fg + bg - (fg * bg);
}
static float blendDarken(float fg, float bg) {
return MIN(fg, bg);
}
static float blendLighten(float fg, float bg) {
return MAX(fg, bg);
}
static float blendColorDodge(float fg, float bg) {
if (fg != 1.0f)
return MIN(1.0f, bg / (1.0f - fg));
return fg;
}
static float blendColorBurn(float fg, float bg) {
if (fg != 0.0f)
return 1.0f - MIN(1.0f, (1.0f - bg) / fg);
return fg;
}
static float blendLinearDodge(float fg, float bg) {
return fg + bg;
}
static float blendLinearBurn(float fg, float bg) {
return fg + bg - 1.0f;
}
static float blendDifference(float fg, float bg) {
return fabs(bg - fg);
}
static float blendExclusion(float fg, float bg) {
return fg + bg - (2.0f * fg * bg);
}
static float blendHardLight(float fg, float bg) {
if (fg <= 0.5f)
return blendMultiply(2.0f * fg, bg);
else
return blendScreen(2.0f * fg - 1.0f, bg);
}
static float blendSoftLight(float fg, float bg) {
return (1.0f - 2.0f * fg) * bg * bg + 2.0f * fg * bg;
}
static float blendOverlay(float fg, float bg) {
return blendHardLight(bg, fg);
}
void CgeColorBlendNormal(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
/* Unused */
(void)bg;
*out = *fg;
}
void CgeColorBlendMultiply(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendMultiply(fgr, bgr);
fgg = blendMultiply(fgg, bgg);
fgb = blendMultiply(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendScreen(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendScreen(fgr, bgr);
fgg = blendScreen(fgg, bgg);
fgb = blendScreen(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendOverlay(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendOverlay(fgr, bgr);
fgg = blendOverlay(fgg, bgg);
fgb = blendOverlay(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendDarken(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendDarken(fgr, bgr);
fgg = blendDarken(fgg, bgg);
fgb = blendDarken(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendLighten(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendLighten(fgr, bgr);
fgg = blendLighten(fgg, bgg);
fgb = blendLighten(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendColorDodge(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendColorDodge(fgr, bgr);
fgg = blendColorDodge(fgg, bgg);
fgb = blendColorDodge(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendColorBurn(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendColorBurn(fgr, bgr);
fgg = blendColorBurn(fgg, bgg);
fgb = blendColorBurn(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendLinearDodge(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendLinearDodge(fgr, bgr);
fgg = blendLinearDodge(fgg, bgg);
fgb = blendLinearDodge(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendLinearBurn(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendLinearBurn(fgr, bgr);
fgg = blendLinearBurn(fgg, bgg);
fgb = blendLinearBurn(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendHardLight(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendHardLight(fgr, bgr);
fgg = blendHardLight(fgg, bgg);
fgb = blendHardLight(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendSoftLight(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendSoftLight(fgr, bgr);
fgg = blendSoftLight(fgg, bgg);
fgb = blendSoftLight(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendDifference(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendDifference(fgr, bgr);
fgg = blendDifference(fgg, bgg);
fgb = blendDifference(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendExclusion(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
fgr = blendExclusion(fgr, bgr);
fgg = blendExclusion(fgg, bgg);
fgb = blendExclusion(fgb, bgb);
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
}
void CgeColorBlendHue(const CgeColor *fg, const CgeColor *bg, CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
CgeColorSetHSLAf(out, fgr, bgg, bgb, fga);
}
void CgeColorBlendSaturation(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
CgeColorSetHSLAf(out, bgr, fgg, bgb, fga);
}
void CgeColorBlendColor(const CgeColor *fg, const CgeColor *bg, CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
CgeColorSetHSLAf(out, fgr, fgg, bgb, fga);
}
void CgeColorBlendLuminosity(const CgeColor *fg, const CgeColor *bg,
CgeColor *out) {
float bgr, bgg, bgb, bga;
float fgr, fgg, fgb, fga;
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
CgeColorSetHSLAf(out, bgr, bgg, fgb, fga);
}
+12
View File
@@ -0,0 +1,12 @@
Copyright (C) 2026 by blankhex me@blankhex.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
+29
View File
@@ -0,0 +1,29 @@
# Makefile.lint - Individual linting targets
.PHONY: all
all: cppcheck clang-tidy scan-build security-check
@echo "All linting scheduled"
.PHONY: cppcheck
cppcheck:
@echo "Running cppcheck..."
@cppcheck --enable=warning,performance,portability --std=c99 --quiet -Iinclude -I. .
.PHONY: clang-tidy
clang-tidy:
@echo "Running clang-tidy..."
@for file in $$(find . -name "*.c" -type f); do \
clang-tidy "$$file" -- -I. -Iinclude -std=c99; \
done
.PHONY: scan-build
scan-build:
@echo "Running scan-build..."
@scan-build --status-bugs make -f Makefile.posix clean all CC=clang
.PHONY: security-check
security-check:
@echo "Scanning for unsafe C functions..."
@grep -rnE '\b(strcpy|strcat|sprintf|gets|scanf|sscanf|realpath|mktemp|tempnam|tmpnam|getwd|getlogin)\b' \
. --include="*.c" --include="*.h" | grep -v "^Binary file" && \
exit 1 || exit 0
+23
View File
@@ -0,0 +1,23 @@
# MinGW Makefile for CgePix
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra
ARFLAGS = rcs
TARGET = libCgePix.a
SOURCES = Color.c Pix.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgePix.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
del $(OBJECTS) $(TARGET) 2>nul || exit 0
+33
View File
@@ -0,0 +1,33 @@
# POSIX Makefile for CgePix
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra -fPIC
ARFLAGS = rcs
TARGET = libCgePix.a
SOURCES = Color.c Pix.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean install
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgePix.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/lib/
cp CgePix.h /usr/local/include/
ldconfig || echo "Run ldconfig manually if needed"
uninstall:
rm -f /usr/local/lib/libCgePix.a
rm -f /usr/local/include/CgePix.h
ldconfig || true
+23
View File
@@ -0,0 +1,23 @@
# Makefile.win32 for MSVC (NMake)
# Usage: Open "x86 Native Tools Command Prompt", then:
# nmake -f Makefile.win32
CC = cl
LIB = lib
CFLAGS = /c /nologo /W3 /O2
LIBFLAGS = /nologo
TARGET = CgePix.lib
SOURCES = Color.c Pix.c
OBJECTS = $(SOURCES:.c=.obj)
$(TARGET): $(OBJECTS)
$(LIB) $(LIBFLAGS) /OUT:$(TARGET) $(OBJECTS)
{.}.c{}.obj:
$(CC) $(CFLAGS) /Fo$@ $<
clean:
del $(OBJECTS) $(TARGET) 2>nul
.PHONY: clean
+510
View File
@@ -0,0 +1,510 @@
#include "CgePix.h"
#include <string.h>
enum FormatType {
TYPE_UNKNOWN,
TYPE_INDEX,
TYPE_IARRAY,
TYPE_FARRAY,
TYPE_PACKED
};
struct FormatInfo {
int type;
size_t bytesPerPixel;
size_t bitsPerChannel;
size_t channels;
uint16_t masks[4];
uint8_t shifts[8];
};
static struct FormatInfo globalInfo[] = {
/* CGE_PIX_UNKNOWN */
{ TYPE_UNKNOWN, 0, 0, 0, {}, {}},
/* CGE_PIX_INDEX1 */
{ TYPE_INDEX, 0, 1, 8, {0x1}, {7, 6, 5, 4, 3, 2, 1, 0,}},
/* CGE_PIX_INDEX1_LSB */
{ TYPE_INDEX, 0, 1, 8, {0x1}, {0, 1, 2, 3, 4, 5, 6, 7,}},
/* CGE_PIX_INDEX2 */
{ TYPE_INDEX, 0, 2, 4, {0x3}, {6, 4, 2, 0,}},
/* CGE_PIX_INDEX2_LSB */
{ TYPE_INDEX, 0, 2, 4, {0x3}, {0, 2, 4, 6,}},
/* CGE_PIX_INDEX4 */
{ TYPE_INDEX, 0, 4, 2, {0xF}, {4, 0,}},
/* CGE_PIX_INDEX4_LSB */
{ TYPE_INDEX, 0, 4, 2, {0xF}, {0, 4,}},
/* CGE_PIX_INDEX8 */
{ TYPE_INDEX, 1, 8, 1, {0xFF}, {0,}},
/* CGE_PIX_R8 */
{ TYPE_IARRAY, 1, 8, 1, {0xFF}, {0,}},
/* CGE_PIX_R16 */
{ TYPE_IARRAY, 2, 16, 1, {0xFFFF}, {0,}},
/* CGE_PIX_R32F */
{ TYPE_FARRAY, 4, 32, 1, {}, {0,}},
/* CGE_PIX_RGB8 */
{ TYPE_IARRAY, 3, 8, 3, {0xFF, 0xFF, 0xFF}, {0, 1, 2,}},
/* CGE_PIX_BGR8 */
{ TYPE_IARRAY, 3, 8, 3, {0xFF, 0xFF, 0xFF}, {2, 1, 0,}},
/* CGE_PIX_RGB16 */
{ TYPE_IARRAY, 6, 16, 3, {0xFFFF, 0xFFFF, 0xFFFF}, {0, 1, 2,}},
/* CGE_PIX_BGR16 */
{ TYPE_IARRAY, 6, 16, 3, {0xFFFF, 0xFFFF, 0xFFFF}, {2, 1, 0,}},
/* CGE_PIX_RGB32F */
{ TYPE_FARRAY, 12, 32, 3, {}, {0, 1, 2,}},
/* CGE_PIX_BGR32F */
{ TYPE_FARRAY, 12, 32, 3, {}, {2, 1, 0,}},
/* CGE_PIX_RGBA8 */
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {0, 1, 2, 3,}},
/* CGE_PIX_BGRA8 */
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {2, 1, 0, 3,}},
/* CGE_PIX_ARGB8 */
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {1, 2, 3, 0,}},
/* CGE_PIX_ABGR8 */
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {3, 2, 1, 0,}},
/* CGE_PIX_RGBA16 */
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {0, 1, 2, 3,}},
/* CGE_PIX_BGRA16 */
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {2, 1, 0, 3,}},
/* CGE_PIX_ARGB16 */
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {1, 2, 3, 0,}},
/* CGE_PIX_ABGR16 */
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {3, 2, 1, 0,}},
/* CGE_PIX_RGBA32F */
{ TYPE_FARRAY, 16, 32, 4, {}, {0, 1, 2, 3,}},
/* CGE_PIX_BGRA32F */
{ TYPE_FARRAY, 16, 32, 4, {}, {2, 1, 0, 3,}},
/* CGE_PIX_ARGB32F */
{ TYPE_FARRAY, 16, 32, 4, {}, {1, 2, 3, 0,}},
/* CGE_PIX_ABGR32F */
{ TYPE_FARRAY, 16, 32, 4, {}, {3, 2, 1, 0,}},
/* CGE_PIX_RGB565 */
{ TYPE_PACKED, 2, 16, 3, {0x1F, 0x3F, 0x1F,}, {11, 5, 0,}},
/* CGE_PIX_BGR565 */
{ TYPE_PACKED, 2, 16, 3, {0x1F, 0x3F, 0x1F,}, {0, 5, 11,}},
/* CGE_PIX_RGBA5551 */
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {11, 6, 1, 0,}},
/* CGE_PIX_BGRA5551 */
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {1, 6, 11, 0,}},
/* CGE_PIX_ARGB1555 */
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {10, 5, 0, 15,}},
/* CGE_PIX_ABGR1555 */
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {0, 5, 10, 15,}},
/* CGE_PIX_RGBA1010102 */
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {22, 12, 2, 0,}},
/* CGE_PIX_BGRA1010102 */
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {2, 12, 22, 0,}},
/* CGE_PIX_ARGB2101010 */
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {20, 10, 0, 30,}},
/* CGE_PIX_ABGR2101010 */
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {0, 10, 20, 30,}},
};
static uint16_t normilize(uint16_t mask, uint16_t value) {
return ((uint32_t)(value & mask) * 65535) / mask;
}
static uint16_t denormilize(uint16_t mask, uint16_t value) {
return (((uint32_t)(value * mask)) / 65535) & mask;
}
static void fixColor(CgeColor *color, const struct FormatInfo *format, int flags) {
if (format->type == TYPE_INDEX)
return;
if (flags & CGE_PIX_NOALPHA)
color->data.channel[3] = 65535;
if (format->type != TYPE_INDEX) {
switch (format->channels) {
case 3:
color->data.channel[3] = 65535;
break;
case 1:
color->data.channel[3] = color->data.channel[0];
color->data.channel[2] = color->data.channel[0];
color->data.channel[1] = color->data.channel[0];
break;
}
}
}
static size_t bestPaletteIndex(CgeColor *color, const CgeColor *palette,
const struct FormatInfo *format) {
uint32_t bestError = -1;
size_t i, bestIndex = 0, maxIndex;
CgeColor tmpColor;
maxIndex = (1 << format->bitsPerChannel);
for (i = 0; i < maxIndex; ++i) {
uint32_t currentError;
int32_t delta[4];
switch (palette[i].type) {
case CGE_COLOR_HSLA: CgeColorToHSLA(color, &tmpColor); break;
case CGE_COLOR_HSVA: CgeColorToHSVA(color, &tmpColor); break;
case CGE_COLOR_RGBA: CgeColorToRGBA(color, &tmpColor); break;
}
delta[0] = (int32_t)tmpColor.data.channel[0] - palette[i].data.channel[0];
delta[1] = (int32_t)tmpColor.data.channel[1] - palette[i].data.channel[1];
delta[2] = (int32_t)tmpColor.data.channel[2] - palette[i].data.channel[2];
delta[3] = (int32_t)tmpColor.data.channel[3] - palette[i].data.channel[3];
currentError = delta[0] * delta[0] + delta[1] * delta[1] +
delta[2] * delta[2] + delta[3] * delta[3];
if (currentError < bestError) {
bestError = currentError;
bestIndex = i;
}
}
return bestIndex;
}
static void readPacked(const struct FormatInfo *format, void *data,
CgeColor *value) {
uint32_t raw;
switch (format->bitsPerChannel) {
default: return;
case 16: raw = *(uint16_t *)data; break;
case 32: raw = *(uint32_t *)data; break;
}
switch (format->channels) {
case 4: value->data.channel[3] = normilize(format->masks[3], raw >> format->shifts[3]);
/* fallthrough */
case 3: value->data.channel[2] = normilize(format->masks[2], raw >> format->shifts[2]);
/* fallthrough */
case 2: value->data.channel[1] = normilize(format->masks[1], raw >> format->shifts[1]);
/* fallthrough */
case 1: value->data.channel[0] = normilize(format->masks[0], raw >> format->shifts[0]);
/* fallthrough */
}
}
static void writePacked(const struct FormatInfo *format, void *data,
const CgeColor *value) {
uint32_t raw = 0;
switch (format->channels) {
case 4: raw |= (uint32_t)denormilize(format->masks[3], value->data.channel[3]) << format->shifts[3];
/* fallthrough */
case 3: raw |= (uint32_t)denormilize(format->masks[2], value->data.channel[2]) << format->shifts[2];
/* fallthrough */
case 2: raw |= (uint32_t)denormilize(format->masks[1], value->data.channel[1]) << format->shifts[1];
/* fallthrough */
case 1: raw |= (uint32_t)denormilize(format->masks[0], value->data.channel[0]) << format->shifts[0];
/* fallthrough */
}
switch (format->bitsPerChannel) {
case 16: *(uint16_t *)data = raw; break;
case 32: *(uint32_t *)data = raw; break;
}
}
static void readFloatArray32(const struct FormatInfo *format, float *data,
CgeColor *value) {
float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
switch (format->channels) {
case 4: a = data[format->shifts[3]];
/* fallthrough */
case 3: b = data[format->shifts[2]];
/* fallthrough */
case 2: g = data[format->shifts[1]];
/* fallthrough */
case 1: r = data[format->shifts[0]];
/* fallthrough */
}
CgeColorSetRGBAf(value, r, g, b, a);
}
static void readFloatArray(const struct FormatInfo *format, void *data,
CgeColor *value) {
switch (format->bitsPerChannel) {
default: return;
case 32: readFloatArray32(format, (float *)data, value); break;
}
}
static void writeFloatArray32(const struct FormatInfo *format, float *data,
const CgeColor *value) {
float r, g, b, a;
CgeColorRGBAf(value, &r, &g, &b, &a);
switch (format->channels) {
case 4: data[format->shifts[3]] = a;
/* fallthrough */
case 3: data[format->shifts[2]] = b;
/* fallthrough */
case 2: data[format->shifts[1]] = g;
/* fallthrough */
case 1: data[format->shifts[0]] = r;
/* fallthrough */
}
}
static void writeFloatArray(const struct FormatInfo *format, void *data,
const CgeColor *value) {
switch (format->bitsPerChannel) {
default: return;
case 32: writeFloatArray32(format, (float *)data, value); break;
}
}
static void readIntArray8(const struct FormatInfo *format, uint8_t *data,
CgeColor *value) {
switch (format->channels) {
case 4: value->data.channel[3] = normilize(format->masks[3], data[format->shifts[3]]);
/* fallthrough */
case 3: value->data.channel[2] = normilize(format->masks[2], data[format->shifts[2]]);
/* fallthrough */
case 2: value->data.channel[1] = normilize(format->masks[1], data[format->shifts[1]]);
/* fallthrough */
case 1: value->data.channel[0] = normilize(format->masks[0], data[format->shifts[0]]);
/* fallthrough */
}
}
static void readIntArray16(const struct FormatInfo *format, uint16_t *data,
CgeColor *value) {
switch (format->channels) {
case 4: value->data.channel[3] = normilize(format->masks[3], data[format->shifts[3]]);
/* fallthrough */
case 3: value->data.channel[2] = normilize(format->masks[2], data[format->shifts[2]]);
/* fallthrough */
case 2: value->data.channel[1] = normilize(format->masks[1], data[format->shifts[1]]);
/* fallthrough */
case 1: value->data.channel[0] = normilize(format->masks[0], data[format->shifts[0]]);
/* fallthrough */
}
}
static void readIntArray(const struct FormatInfo *format, void *data,
CgeColor *value) {
switch (format->bitsPerChannel) {
default: return;
case 8: readIntArray8(format, (uint8_t *)data, value); break;
case 16: readIntArray16(format, (uint16_t *)data, value); break;
}
}
static void writeIntArray8(const struct FormatInfo *format, uint8_t *data,
const CgeColor *value) {
switch (format->channels) {
case 4: data[format->shifts[3]] = denormilize(format->masks[3], value->data.channel[3]);
/* fallthrough */
case 3: data[format->shifts[2]] = denormilize(format->masks[2], value->data.channel[2]);
/* fallthrough */
case 2: data[format->shifts[1]] = denormilize(format->masks[1], value->data.channel[1]);
/* fallthrough */
case 1: data[format->shifts[0]] = denormilize(format->masks[0], value->data.channel[0]);
/* fallthrough */
}
}
static void writeIntArray16(const struct FormatInfo *format, uint16_t *data,
const CgeColor *value) {
switch (format->channels) {
case 4: data[format->shifts[3]] = denormilize(format->masks[3], value->data.channel[3]);
/* fallthrough */
case 3: data[format->shifts[2]] = denormilize(format->masks[2], value->data.channel[2]);
/* fallthrough */
case 2: data[format->shifts[1]] = denormilize(format->masks[1], value->data.channel[1]);
/* fallthrough */
case 1: data[format->shifts[0]] = denormilize(format->masks[0], value->data.channel[0]);
/* fallthrough */
}
}
static void writeIntArray(const struct FormatInfo *format, void *data,
const CgeColor *value) {
switch (format->bitsPerChannel) {
default: return;
case 8: writeIntArray8(format, (uint8_t *)data, value); break;
case 16: writeIntArray16(format, (uint16_t *)data, value); break;
}
}
static uint8_t readIndex(const struct FormatInfo *format, uint8_t *data,
uint32_t x) {
return (*data >> format->shifts[x & (format->channels - 1)]) & format->masks[0];
}
static void writeIndex(const struct FormatInfo *format, uint8_t *data, uint32_t x,
uint8_t index) {
uint8_t mask, value;
mask = ~(format->masks[0] << format->shifts[x & (format->channels - 1)]);
value = (index & format->masks[0]) << format->shifts[x & (format->channels - 1)];
*data = (*data & mask) | value;
}
static int isLittleEndian(void) {
uint16_t number = 0x0001;
return (*((uint8_t *)&number) == 0x01);
}
static void readData(const struct FormatInfo *format, void *data, uint32_t x,
CgeColor *value, const CgeColor *palette, int flags) {
switch (format->type) {
case TYPE_INDEX:
*value = palette[readIndex(format, data, x)];
break;
case TYPE_IARRAY:
readIntArray(format, data, value);
value->type = CGE_COLOR_RGBA;
break;
case TYPE_FARRAY:
readFloatArray(format, data, value);
value->type = CGE_COLOR_RGBA;
break;
case TYPE_PACKED:
readPacked(format, data, value);
value->type = CGE_COLOR_RGBA;
break;
}
fixColor(value, format, flags);
}
static void writeData(const struct FormatInfo *format, void *data, uint32_t x,
const CgeColor *value, const CgeColor *palette) {
CgeColor tmp;
CgeColorToRGBA(value, &tmp);
switch (format->type) {
case TYPE_INDEX:
writeIndex(format, data, x, bestPaletteIndex(&tmp, palette, format));
break;
case TYPE_IARRAY:
writeIntArray(format, data, &tmp);
break;
case TYPE_FARRAY:
writeFloatArray(format, data, &tmp);
break;
case TYPE_PACKED:
writePacked(format, data, &tmp);
break;
}
}
int CgePixFormatIs32Native(int format) {
switch (format) {
case CGE_PIX_BGRA8: return isLittleEndian();
case CGE_PIX_ARGB8: return !isLittleEndian();
}
return 0;
}
size_t CgePixInitLayout(uint32_t width, uint32_t height, int format, int flags,
CgePix *out) {
struct FormatInfo *lookup = globalInfo + format;
out->width = width;
out->height = height;
out->format = format;
out->flags = flags;
out->data = NULL;
out->palette = NULL;
if (lookup->type == TYPE_INDEX)
out->stride = (lookup->bitsPerChannel * width + 7) >> 3;
else
out->stride = lookup->bytesPerPixel * width;
if (flags & CGE_PIX_ALIGN32)
out->stride = (out->stride + 3) & ~(size_t)0x3;
return CgePixSize(out);
}
void CgePixView(CgePix *pix, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, CgePix *out) {
memcpy(out, pix, sizeof(*pix));
if (x > pix->width)
x = pix->width;
if (y > pix->height)
y = pix->height;
if (width > pix->width - x)
width = pix->width - x;
if (height > pix->height - y)
height = pix->height - y;
out->data = CgePixAt(pix, x, y);
out->width = width;
out->height = height;
}
size_t CgePixSize(const CgePix *pix) {
return pix->stride * pix->height;
}
void *CgePixScanline(const CgePix *pix, uint32_t y) {
return (char *)(pix->data) + y * pix->stride;
}
static void *rowAt(const struct FormatInfo *format, void *data, uint32_t x) {
if (format->type == TYPE_INDEX)
return (char *)data + ((x * format->bitsPerChannel) >> 3);
return (char *)data + x * format->bytesPerPixel;
}
void *CgePixAt(const CgePix *pix, uint32_t x, uint32_t y) {
struct FormatInfo *lookup = globalInfo + pix->format;
return rowAt(lookup, CgePixScanline(pix, y), x);
}
void CgePixColor(const CgePix *pix, uint32_t x, uint32_t y, CgeColor *value) {
struct FormatInfo *lookup = globalInfo + pix->format;
readData(lookup, CgePixAt(pix, x, y), x, value, pix->palette, pix->flags);
}
void CgePixSetColor(CgePix *pix, uint32_t x, uint32_t y, const CgeColor *value) {
struct FormatInfo *lookup = globalInfo + pix->format;
writeData(lookup, CgePixAt(pix, x, y), x, value, pix->palette);
}
uint8_t CgePixIndex(const CgePix *pix, uint32_t x, uint32_t y) {
struct FormatInfo *lookup = globalInfo + pix->format;
return readIndex(lookup, CgePixAt(pix, x, y), x);
}
void CgePixSetIndex(CgePix *pix, uint32_t x, uint32_t y, uint8_t index) {
struct FormatInfo *lookup = globalInfo + pix->format;
writeIndex(lookup, CgePixAt(pix, x, y), x, index);
}
void CgePixConvertRow(void *src, int srcFormat, int srcFlags,
const CgeColor *srcPalette, void *dest, int destFormat,
int destFlags, const CgeColor *destPalette, size_t count) {
const struct FormatInfo *srcInfo, *destInfo;
size_t x;
/* Unused */
(void)destFlags;
srcInfo = globalInfo + srcFormat;
destInfo = globalInfo + destFormat;
for (x = 0; count; --count, ++x) {
void *srcAt, *destAt;
CgeColor data;
srcAt = rowAt(srcInfo, src, x);
destAt = rowAt(destInfo, dest, x);
readData(srcInfo, srcAt, x, &data, srcPalette, srcFlags);
writeData(destInfo, destAt, x, &data, destPalette);
}
}