Initial commit
This commit is contained in:
commit
1929eebc23
11 files changed
+1604
No files matched your search
@@ -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
@@ -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
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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 */
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user