From 742be08ccce58399da08b52141b2f6d1e9554baf Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 01:49:46 -0400 Subject: [PATCH 01/64] added current state of aabb --- CMakeLists.txt | 2 + include/lexlib/aabb.h | 61 ++++++++++++++++++++++++ src/aabb.c | 106 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 include/lexlib/aabb.h create mode 100644 src/aabb.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fc61fe..25b2048 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project("lexlib" VERSION 1.4.0) # lexlib itself and src add_library(${PROJECT_NAME} STATIC) target_sources(${PROJECT_NAME} PRIVATE + src/color/aabb.c src/color/color.c src/color/colorflt.c src/file.c @@ -29,6 +30,7 @@ set_source_files_properties(src/path.c PROPERTIES COMPILE_FLAGS -Wdeprecated-dec # explicitly define public headers target_sources(${PROJECT_NAME} PUBLIC FILE_SET HEADERS FILES + include/lexlib/aabb.h include/lexlib/color.h include/lexlib/common.h include/lexlib/defines.h diff --git a/include/lexlib/aabb.h b/include/lexlib/aabb.h new file mode 100644 index 0000000..7bfa659 --- /dev/null +++ b/include/lexlib/aabb.h @@ -0,0 +1,61 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + +#ifndef lexlib_aabb_h +#define lexlib_aabb_h +#include"common.h" + +#include + +// NOTE s +// the length extends across the axis in the +. + +// structs // + +// the [0] of each component is the pos and [1] is the length. +struct LexlibAABB { + float x[2]; + float y[2]; + float z[2]; +}; + +// ..structs.. // + + + +// typedefs // +#ifndef __cplusplus +typedef struct LexlibAABB LexlibAABB; +#endif +// ..typedefs.. // + + + +// creation // + +LEXLIB_EXTERN LexlibAABB lexlibAABBNewUni3(float x, float y, float z, float size); + +// ..creation.. // + + + +// intersects // + +LEXLIB_EXTERN uint8_t lexlibAABBIntersect(const LexlibAABB* a, const LexlibAABB* b); + +// ..intersects.. // + + + +// misc // +#ifdef LEXLIB_EXPERIMENTAL + +// gets the aabb as world vertex data. +// the verts and indices can be optional, the indices are always the same. +// the format is x,y,z. +LEXLIB_EXTERN void lexlibAABBVertexData(const LexlibAABB* aabb, float verts[24], unsigned int indices[24]); + +#endif +// ..misc.. // + +#endif \ No newline at end of file diff --git a/src/aabb.c b/src/aabb.c new file mode 100644 index 0000000..e4baecb --- /dev/null +++ b/src/aabb.c @@ -0,0 +1,106 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + +#include +#define false 0 +#define true 1 + +// creation // + +LEXLIB_EXTERN LexlibAABB lexlibAABBNewUni3(float x, float y, float z, float size){ + return (LexlibAABB){ + {x, size}, + {y, size}, + {z, size} + }; +} + +// ..creation.. // + + + +// intersects // + +uint8_t lexlibAABBIntersect(const LexlibAABB* a, const LexlibAABB* b){ + LexlibAABB A = { + {a->x[0], a->x[0] + a->x[1]}, + {a->y[0], a->y[0] + a->y[1]}, + {a->z[0], a->z[0] + a->z[1]}, + }; + LexlibAABB B = { + {b->x[0], b->x[0] + b->x[1]}, + {b->y[0], b->y[0] + b->y[1]}, + {b->z[0], b->z[0] + b->z[1]}, + }; + + if(A.x[1] > B.x[0]) + return true; + + return false; +} + +// ..intersects.. // + + + +// misc // + +void lexlibAABBVertexData(const LexlibAABB* aabb, float verts[24], unsigned int indices[24]){ + if(verts){ + // bottom + verts[0 ] = aabb->x[0]; + verts[1 ] = aabb->y[0]; + verts[2 ] = aabb->z[0]; + verts[3 ] = aabb->x[0] + aabb->x[1]; + verts[4 ] = aabb->y[0]; + verts[5 ] = aabb->z[0]; + verts[6 ] = aabb->x[0]; + verts[7 ] = aabb->y[0]; + verts[8 ] = aabb->z[0] + aabb->z[1]; + verts[9 ] = aabb->x[0] + aabb->x[1]; + verts[10] = aabb->y[0]; + verts[11] = aabb->z[0] + aabb->z[1]; + // top + verts[12] = aabb->x[0]; + verts[13] = aabb->y[0] + aabb->y[1]; + verts[14] = aabb->z[0]; + verts[15] = aabb->x[0] + aabb->x[1]; + verts[16] = aabb->y[0] + aabb->y[1]; + verts[17] = aabb->z[0]; + verts[18] = aabb->x[0]; + verts[19] = aabb->y[0] + aabb->y[1]; + verts[20] = aabb->z[0] + aabb->z[1]; + verts[21] = aabb->x[0] + aabb->x[1]; + verts[22] = aabb->y[0] + aabb->y[1]; + verts[23] = aabb->z[0] + aabb->z[1]; + } + + if(indices){ + indices[0 ] = 0; + indices[1 ] = 1; + indices[2 ] = 0; + indices[3 ] = 2; + indices[4 ] = 1; + indices[5 ] = 3; + indices[6 ] = 2; + indices[7 ] = 3; + indices[8 ] = 4; + indices[9 ] = 5; + indices[10] = 4; + indices[11] = 6; + indices[12] = 5; + indices[13] = 7; + indices[14] = 6; + indices[15] = 7; + indices[16] = 0; + indices[17] = 4; + indices[18] = 1; + indices[19] = 5; + indices[20] = 2; + indices[21] = 6; + indices[22] = 3; + indices[23] = 7; + } +} + +// ..misc.. // \ No newline at end of file -- GitLab From 83e373b6f70b1871621d2dc0d637322e945d7043 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 01:52:02 -0400 Subject: [PATCH 02/64] set version number to 1.5.0 --- CMakeLists.txt | 2 +- linux.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25b2048..a2f9921 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.26.0) -project("lexlib" VERSION 1.4.0) +project("lexlib" VERSION 1.5.0) # NOTE s # don't forget to change the version number in linux.sh too. diff --git a/linux.sh b/linux.sh index 278078f..24a36f0 100755 --- a/linux.sh +++ b/linux.sh @@ -6,7 +6,7 @@ mingw32dir=/usr/i686-w64-mingw32 mingw64dir=/usr/x86_64-w64-mingw32 # lexlib vars, do not touch. -version="1.4.0" +version="1.5.0" function message () { echo -e "[\e[1;38;5;243mlinux.sh\e[0m]: $@\e[0m" -- GitLab From c93e16aedb1dbc2fb1e25a2efd96e0a40ad59fb5 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 01:53:29 -0400 Subject: [PATCH 03/64] fixed miscopied src/aabb.c --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a2f9921..7e40916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ project("lexlib" VERSION 1.5.0) # lexlib itself and src add_library(${PROJECT_NAME} STATIC) target_sources(${PROJECT_NAME} PRIVATE - src/color/aabb.c + src/aabb.c src/color/color.c src/color/colorflt.c src/file.c -- GitLab From 1089c7af797a64831e37f3d68cbc9bdd638dadef Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 02:50:53 -0400 Subject: [PATCH 04/64] added the short license notice to src color --- src/color/color.c | 3 +++ src/color/colorflt.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/color/color.c b/src/color/color.c index 63157b4..8cd2648 100644 --- a/src/color/color.c +++ b/src/color/color.c @@ -1,3 +1,6 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + #include #include #include diff --git a/src/color/colorflt.c b/src/color/colorflt.c index 6248fb7..28f9187 100644 --- a/src/color/colorflt.c +++ b/src/color/colorflt.c @@ -1,3 +1,6 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + #include LexlibColorFlt lexlibColorFltBlend(const LexlibColorFlt dst, const LexlibColorFlt src, uint8_t mode){ -- GitLab From 192b390d7da6ec992db2007418f673bc1cb6bdb5 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 14:01:11 -0400 Subject: [PATCH 05/64] macro to check for endianness --- include/lexlib/common.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/lexlib/common.h b/include/lexlib/common.h index c6dd7f8..2927c87 100644 --- a/include/lexlib/common.h +++ b/include/lexlib/common.h @@ -5,7 +5,8 @@ #define lexlib_common_h /* this header is not documented since its not -suposed to be used externally of lexlib */ +suposed to be used externally of lexlib +and any changes may break api */ #include"defines.h" @@ -25,6 +26,20 @@ suposed to be used externally of lexlib */ # define LEXLIB_DEPRECATED #endif +#ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define LEXLIB_LITTLE_ENDIAN LEXLIB_FALSE +# define LEXLIB_BIG_ENDIAN LEXLIB_TRUE +# else /* if its not big endian or can't be detected fallback to little endian. */ +# define LEXLIB_LITTLE_ENDIAN LEXLIB_TRUE +# define LEXLIB_BIG_ENDIAN LEXLIB_FALSE +# endif +#else +# warning lexlib could not detected endianness, __BYTE_ORDER__ not defined, fallback to little +# define LEXLIB_LITTLE_ENDIAN LEXLIB_TRUE +# define LEXLIB_BIG_ENDIAN LEXLIB_FALSE +#endif + #define LEXLIB_BUFFER_SIZE 256 #endif \ No newline at end of file -- GitLab From d08f818a3ecc458c62caf560d9bc32731c167339 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 15:06:06 -0400 Subject: [PATCH 06/64] color16 blending --- CMakeLists.txt | 1 + include/lexlib/color.h | 1 + src/color/color16.c | 60 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/color/color16.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e40916..6720ba0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(${PROJECT_NAME} STATIC) target_sources(${PROJECT_NAME} PRIVATE src/aabb.c src/color/color.c + src/color/color16.c src/color/colorflt.c src/file.c src/image.c diff --git a/include/lexlib/color.h b/include/lexlib/color.h index 08e864e..dfae622 100644 --- a/include/lexlib/color.h +++ b/include/lexlib/color.h @@ -77,6 +77,7 @@ typedef struct LexlibColor16 LexlibColor16; // performs a color blend depending on the mode // if mode is invalid the default (LEXLIB_ADD) will be done. LEXLIB_EXTERN LexlibColor lexlibColorBlend(const LexlibColor dst, const LexlibColor src, uint8_t mode); +LEXLIB_EXTERN LexlibColor16 lexlibColor16Blend(const LexlibColor16 dst, const LexlibColor16 src, uint8_t mode); LEXLIB_EXTERN LexlibColorFlt lexlibColorFltBlend(const LexlibColorFlt dst, const LexlibColorFlt src, uint8_t mode); diff --git a/src/color/color16.c b/src/color/color16.c new file mode 100644 index 0000000..b63fbe9 --- /dev/null +++ b/src/color/color16.c @@ -0,0 +1,60 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + +#include +#include +#include +#include + +LexlibColor16 lexlibColor16Blend(const LexlibColor16 dst, const LexlibColor16 src, uint8_t mode){ + LexlibColor16 color; + + switch(mode){ + case LEXLIB_NONE: + return src; + case LEXLIB_ADD:{ + float alpha = (float)src.a / .0f; + color.r = lexlibClampi(dst.r + rintf(src.r * alpha), 0, 65535); + color.g = lexlibClampi(dst.g + rintf(src.g * alpha), 0, 65535); + color.b = lexlibClampi(dst.b + rintf(src.b * alpha), 0, 65535); + color.a = dst.a; + return color; + } + case LEXLIB_SUB:{ + float alpha = (float)src.a / 65535.0f; + color.r = dst.r - (src.r * alpha); + color.g = dst.g - (src.g * alpha); + color.b = dst.b - (src.b * alpha); + color.a = dst.a; + return color; + } + case LEXLIB_MUL:{ + LexlibColor16 d = lexlibColor16Premultiply(dst); + LexlibColorFlt dstf = lexlibColor16ToFlt(d); + float alpha = (float)src.a / 65535.0f; + float alphainv = 1.0f - alpha; + color.r = lexlibClampi(rintf((src.r * dstf.r) + (dst.r * alphainv)), 0, 65535); + color.g = lexlibClampi(rintf((src.g * dstf.g) + (dst.g * alphainv)), 0, 65535); + color.b = lexlibClampi(rintf((src.b * dstf.b) + (dst.b * alphainv)), 0, 65535); + color.a = lexlibClampi(rintf(((alpha * dstf.a) + (dstf.a * alphainv))) * 65535.0f, 0, 65535); + return color; + } + case LEXLIB_MOD: + color.r = rintf(src.r * (dst.r / 65535.0f)); + color.g = rintf(src.g * (dst.g / 65535.0f)); + color.b = rintf(src.b * (dst.b / 65535.0f)); + color.a = dst.a; + return color; + case LEXLIB_BLEND:{ + float alpha = (float)src.a / 65535.0f; + float alphainv = 1.0f - alpha; + color.r = rintf((src.r * alpha) + (dst.r * alphainv)); + color.g = rintf((src.g * alpha) + (dst.g * alphainv)); + color.b = rintf((src.b * alpha) + (dst.b * alphainv)); + color.a = (alpha + (((float)dst.a / 65535.0f) * alphainv)) * 65535.0f; + return color; + } + default: + return lexlibColor16Blend(dst, src, LEXLIB_ADD); + } +} \ No newline at end of file -- GitLab From 7bc913c9d8dccde1bb63dcd8b6024109e919d0f3 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 15:07:37 -0400 Subject: [PATCH 07/64] fixed a little error in color16 blending --- src/color/color16.c | 2 +- src/color/colorflt.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/color/color16.c b/src/color/color16.c index b63fbe9..f3bc57c 100644 --- a/src/color/color16.c +++ b/src/color/color16.c @@ -13,7 +13,7 @@ LexlibColor16 lexlibColor16Blend(const LexlibColor16 dst, const LexlibColor16 sr case LEXLIB_NONE: return src; case LEXLIB_ADD:{ - float alpha = (float)src.a / .0f; + float alpha = (float)src.a / 65535.0f; color.r = lexlibClampi(dst.r + rintf(src.r * alpha), 0, 65535); color.g = lexlibClampi(dst.g + rintf(src.g * alpha), 0, 65535); color.b = lexlibClampi(dst.b + rintf(src.b * alpha), 0, 65535); diff --git a/src/color/colorflt.c b/src/color/colorflt.c index 28f9187..b03eefa 100644 --- a/src/color/colorflt.c +++ b/src/color/colorflt.c @@ -2,6 +2,7 @@ // licensed under the MIT or Apache 2.0 (at your option) #include +#include LexlibColorFlt lexlibColorFltBlend(const LexlibColorFlt dst, const LexlibColorFlt src, uint8_t mode){ LexlibColorFlt color; -- GitLab From 8cce6887cab915cc616c9172a4e07c636ccb475f Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 16:42:01 -0400 Subject: [PATCH 08/64] finished imagePixelSet() --- CHANGELOG.md | 4 ++ include/lexlib/image.h | 5 ++ src/image.c | 119 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 123 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af324a7..edd2c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.0 (under development) ++ function lexlibColor16Blend(). ++ function lexlibImagePixelSet(). + ## 1.4.0 + function lexlibStrCopy(). + removed path.h from default includes. diff --git a/include/lexlib/image.h b/include/lexlib/image.h index d9057ab..0524afd 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -8,6 +8,11 @@ #include #include"color.h" +/* NOTE s + images with more bitdepth than 8 are bigendian. +*/ + + struct LexlibImage { uint8_t* data; uint32_t width; diff --git a/src/image.c b/src/image.c index a36314d..3c37aca 100644 --- a/src/image.c +++ b/src/image.c @@ -175,12 +175,121 @@ uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, Le size_t rowsize = image->width * pixelsize; size_t offset = (y * rowsize) + (x * pixelsize); - image->data[offset+0] = color.r; - image->data[offset+1] = color.g; - image->data[offset+2] = color.b; - image->data[offset+3] = color.a; + if(image->depth == 8){ + if(image->channels == 1){ + image->data[offset] = lexlibColorGray(color); + return LEXLIB_OK; + } + if(image->channels == 3){ + LexlibColor pixel = *((LexlibColor*)image->data+offset); + pixel.a = -1; + pixel = lexlibColorPremultiply(lexlibColorBlend(pixel, color, blendmode)); + image->data[offset+0] = pixel.r; + image->data[offset+1] = pixel.g; + image->data[offset+2] = pixel.b; + return LEXLIB_OK; + } + if(image->channels == 4){ + *(LexlibColor*)(image->data+offset) = lexlibColorBlend(*(LexlibColor*)(image->data+offset), color, blendmode); + return LEXLIB_OK; + } + return LEXLIB_INVALID_VALUE; + } - return LEXLIB_OK; + if(image->depth == 16){ +#if LEXLIB_BIG_ENDIAN + if(image->channels == 1){ + uint16_t col = lexlibColor16Gray(lexlibColorTo16(color)); + image->data[offset+0] = ((uint8_t*)&col)[0]; + image->data[offset+1] = ((uint8_t*)&col)[1]; + return LEXLIB_OK; + } + if(image->channels == 3){ + LexlibColor16 color16 = lexlibColorTo16(color); + LexlibColor16 pixel16 = *((LexlibColor16*)image->data+offset); + pixel16.a = -1; + pixel16 = lexlibColor16Premultiply(lexlibColor16Blend(pixel16, color16, blendmode)); + image->data[offset+0] = ((uint8_t*)&pixel16)[0]; + image->data[offset+1] = ((uint8_t*)&pixel16)[1]; + image->data[offset+2] = ((uint8_t*)&pixel16)[2]; + image->data[offset+3] = ((uint8_t*)&pixel16)[3]; + image->data[offset+4] = ((uint8_t*)&pixel16)[4]; + image->data[offset+5] = ((uint8_t*)&pixel16)[5]; + return LEXLIB_OK; + } + + if(image->channels == 4){ + *(LexlibColor16*)(image->data+offset) = lexlibColor16Blend(*(LexlibColor16*)(image->data+offset), lexlibColorTo16(color), blendmode); + return LEXLIB_OK; + } +#else /* little endian */ + if(image->channels == 1){ + uint16_t col = lexlibColor16Gray(lexlibColorTo16(color)); + image->data[offset+0] = ((uint8_t*)&col)[1]; + image->data[offset+1] = ((uint8_t*)&col)[0]; + return LEXLIB_OK; + } + + if(image->channels == 3){ + LexlibColor16 color16 = lexlibColorTo16(color); + LexlibColor16 pixel16; + uint8_t* color16raw = (uint8_t*)&color16; + uint8_t* pixel16raw = (uint8_t*)&pixel16; + + pixel16raw[0] = image->data[offset+1]; + pixel16raw[1] = image->data[offset+0]; + pixel16raw[2] = image->data[offset+3]; + pixel16raw[3] = image->data[offset+2]; + pixel16raw[4] = image->data[offset+5]; + pixel16raw[5] = image->data[offset+4]; + pixel16.a = -1; + + color16 = lexlibColor16Blend(pixel16, color16, blendmode); + color16 = lexlibColor16Premultiply(color16); + + image->data[offset+0] = color16raw[1]; + image->data[offset+1] = color16raw[0]; + image->data[offset+2] = color16raw[3]; + image->data[offset+3] = color16raw[2]; + image->data[offset+4] = color16raw[5]; + image->data[offset+5] = color16raw[4]; + + return LEXLIB_OK; + } + + if(image->channels == 4){ + LexlibColor16 color16 = lexlibColorTo16(color); + LexlibColor16 pixel16; + uint8_t* color16raw = (uint8_t*)&color16; + uint8_t* pixel16raw = (uint8_t*)&pixel16; + + pixel16raw[0] = image->data[offset+1]; + pixel16raw[1] = image->data[offset+0]; + pixel16raw[2] = image->data[offset+3]; + pixel16raw[3] = image->data[offset+2]; + pixel16raw[4] = image->data[offset+5]; + pixel16raw[5] = image->data[offset+4]; + pixel16raw[6] = image->data[offset+7]; + pixel16raw[7] = image->data[offset+6]; + + color16 = lexlibColor16Blend(pixel16, color16, blendmode); + + image->data[offset+0] = color16raw[1]; + image->data[offset+1] = color16raw[0]; + image->data[offset+2] = color16raw[3]; + image->data[offset+3] = color16raw[2]; + image->data[offset+4] = color16raw[5]; + image->data[offset+5] = color16raw[4]; + image->data[offset+6] = color16raw[7]; + image->data[offset+7] = color16raw[6]; + + return LEXLIB_OK; + } +#endif + return LEXLIB_INVALID_VALUE; + } + + return LEXLIB_ERROR; } LexlibImage lexlibImageLoad(const char* filename){ -- GitLab From 26094243ad13502298fc010cd12986afa357f8a4 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 16:56:11 -0400 Subject: [PATCH 09/64] moved image png functions to its own src file; related changes --- CMakeLists.txt | 1 + include/lexlib/image.h | 3 + src/image.c | 163 +--------------------------------------- src/image/png.c | 166 +++++++++++++++++++++++++++++++++++++++++ test/colorblend.c | 3 +- 5 files changed, 173 insertions(+), 163 deletions(-) create mode 100644 src/image/png.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6720ba0..d65c666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources(${PROJECT_NAME} PRIVATE src/color/colorflt.c src/file.c src/image.c + src/image/png.c src/io.c src/lexlib.c src/linkedList.c diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 0524afd..0379a9f 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -34,6 +34,9 @@ typedef struct LexlibImage LexlibImage; #define LEXLIB_FLIP_X 0x01 #define LEXLIB_FLIP_Y 0x02 +// "constants" +#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0}) + // creates a new LexlibImage and allocates its memory. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_OUT_OF_MEMORY) LEXLIB_EXTERN LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uint8_t depth); diff --git a/src/image.c b/src/image.c index 3c37aca..e17fb2b 100644 --- a/src/image.c +++ b/src/image.c @@ -8,8 +8,6 @@ #include #include -#define imageZero {NULL, 0, 0, 0, 0} - LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uint8_t depth){ LexlibImage image = {NULL, width, height, 0, depth}; @@ -293,7 +291,7 @@ uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, Le } LexlibImage lexlibImageLoad(const char* filename){ - LexlibImage image = imageZero; + LexlibImage image = LEXLIB_IMAGE_ZERO; char* offset = strrchr(filename, '.'); if(offset){ @@ -315,163 +313,4 @@ uint8_t lexlibImageSave(const LexlibImage* image, const char* filename){ } return LEXLIB_INVALID_FILENAME; -} - -LexlibImage lexlibImageLoadPng(const char* filename){ - LexlibImage image = imageZero; - - FILE* file = fopen(filename, "rb"); - if(!file){ - image.data = NULL; - image.depth = LEXLIB_CANT_OPEN; - return image; - } - - {// check if its a png - uint8_t head[8]; - fread(head, 1, 8, file); - bool ispng = !png_sig_cmp(head, 0, 8); - if(!ispng){ - fclose(file); - image.data = NULL; - image.depth = LEXLIB_INVALID_OPERATION; - return image; - } - } - - // allocate libpng structs - png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if(!pngPtr){ - fclose(file); - image.data = NULL; - image.depth = LEXLIB_OUT_OF_MEMORY; - return image; - } - png_infop infoPtr = png_create_info_struct(pngPtr); - if(!infoPtr){ - fclose(file); - png_destroy_read_struct(&pngPtr, NULL, NULL); - image.data = NULL; - image.depth = LEXLIB_OUT_OF_MEMORY; - return image; - } - png_infop endInfo = png_create_info_struct(pngPtr); - if(!endInfo){ - fclose(file); - png_destroy_read_struct(&pngPtr, &infoPtr, NULL); - image.data = NULL; - image.depth = LEXLIB_OUT_OF_MEMORY; - return image; - } - - // initial read - png_init_io(pngPtr, file); - png_set_sig_bytes(pngPtr, 8); - png_read_png(pngPtr, infoPtr, 0, NULL); - - // read info - image.width = png_get_image_width(pngPtr, infoPtr); - image.height = png_get_image_height(pngPtr, infoPtr); - image.depth = png_get_bit_depth(pngPtr, infoPtr); - image.channels = png_get_channels(pngPtr, infoPtr);; - image = lexlibImageNew(image.width, image.height, image.channels, image.depth); - - // check for errors - if(image.data == NULL){ - switch(image.depth){ - case LEXLIB_OUT_OF_MEMORY: - break; - default: - image.depth = LEXLIB_ERROR; - break; - } - fclose(file); - png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo); - return image; - } - - // read data - png_byte** rowPointers = png_get_rows(pngPtr, infoPtr); - size_t rowSize = image.width * image.channels * (image.depth / 8); - size_t offset = 0; - for(size_t y = 0; y < image.height; y++){ - memcpy(image.data+offset, rowPointers[y], rowSize * sizeof(uint8_t)); - offset += rowSize; - } - - // cleanup and return - png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo); - fclose(file); - return image; -} - -uint8_t lexlibImageSavePng(const LexlibImage* image, const char* filename){ - // image checks - if(image->data == NULL) - return LEXLIB_INVALID_VALUE; - if(image->width == 0) - return LEXLIB_INVALID_VALUE; - if(image->height == 0) - return LEXLIB_INVALID_VALUE; - if(image->channels == 0) - return LEXLIB_INVALID_VALUE; - if(image->depth == 0) - return LEXLIB_INVALID_VALUE; - if((image->depth % 8) != 0) - return LEXLIB_INVALID_VALUE; - - // create file - FILE* file = fopen(filename, "wb"); - if(!file) - return LEXLIB_CANT_WRITE; - - // allocate libpng structs - png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if(!pngPtr) - return LEXLIB_OUT_OF_MEMORY; - png_infop infoPtr = png_create_info_struct(pngPtr); - if(!infoPtr){ - png_destroy_write_struct(&pngPtr, NULL); - return LEXLIB_OUT_OF_MEMORY; - } - - // get color type - int colorType = 0; - switch(image->channels){ - case 1: - colorType = PNG_COLOR_TYPE_GRAY; - break; - case 3: - colorType = PNG_COLOR_TYPE_RGB; - break; - case 4: - colorType = PNG_COLOR_TYPE_RGB_ALPHA; - break; - default: - colorType = PNG_COLOR_TYPE_GRAY; - } - - // set png info - png_set_IHDR(pngPtr, infoPtr, image->width, image->height, image->depth, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_set_compression_level(pngPtr, 9); - - // write png - png_init_io(pngPtr, file); - png_write_info(pngPtr, infoPtr); - png_write_flush(pngPtr); - - // write data - size_t rowSize = image->width * image->channels * (image->depth / 8); - uint32_t offset = 0; - for(uint32_t y = 0; y < image->height; y++){ - png_write_row(pngPtr, image->data+offset); - offset += rowSize; - } - - png_write_end(pngPtr, infoPtr); - - // cleanup - png_destroy_write_struct(&pngPtr, &infoPtr); - fclose(file); - return LEXLIB_OK; } \ No newline at end of file diff --git a/src/image/png.c b/src/image/png.c new file mode 100644 index 0000000..9d13c8e --- /dev/null +++ b/src/image/png.c @@ -0,0 +1,166 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + +#include +#include +#include +#include + +LexlibImage lexlibImageLoadPng(const char* filename){ + LexlibImage image = LEXLIB_IMAGE_ZERO; + + FILE* file = fopen(filename, "rb"); + if(!file){ + image.data = NULL; + image.depth = LEXLIB_CANT_OPEN; + return image; + } + + {// check if its a png + uint8_t head[8]; + fread(head, 1, 8, file); + bool ispng = !png_sig_cmp(head, 0, 8); + if(!ispng){ + fclose(file); + image.data = NULL; + image.depth = LEXLIB_INVALID_OPERATION; + return image; + } + } + + // allocate libpng structs + png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if(!pngPtr){ + fclose(file); + image.data = NULL; + image.depth = LEXLIB_OUT_OF_MEMORY; + return image; + } + png_infop infoPtr = png_create_info_struct(pngPtr); + if(!infoPtr){ + fclose(file); + png_destroy_read_struct(&pngPtr, NULL, NULL); + image.data = NULL; + image.depth = LEXLIB_OUT_OF_MEMORY; + return image; + } + png_infop endInfo = png_create_info_struct(pngPtr); + if(!endInfo){ + fclose(file); + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + image.data = NULL; + image.depth = LEXLIB_OUT_OF_MEMORY; + return image; + } + + // initial read + png_init_io(pngPtr, file); + png_set_sig_bytes(pngPtr, 8); + png_read_png(pngPtr, infoPtr, 0, NULL); + + // read info + image.width = png_get_image_width(pngPtr, infoPtr); + image.height = png_get_image_height(pngPtr, infoPtr); + image.depth = png_get_bit_depth(pngPtr, infoPtr); + image.channels = png_get_channels(pngPtr, infoPtr);; + image = lexlibImageNew(image.width, image.height, image.channels, image.depth); + + // check for errors + if(image.data == NULL){ + switch(image.depth){ + case LEXLIB_OUT_OF_MEMORY: + break; + default: + image.depth = LEXLIB_ERROR; + break; + } + fclose(file); + png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo); + return image; + } + + // read data + png_byte** rowPointers = png_get_rows(pngPtr, infoPtr); + size_t rowSize = image.width * image.channels * (image.depth / 8); + size_t offset = 0; + for(size_t y = 0; y < image.height; y++){ + memcpy(image.data+offset, rowPointers[y], rowSize * sizeof(uint8_t)); + offset += rowSize; + } + + // cleanup and return + png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo); + fclose(file); + return image; +} + +uint8_t lexlibImageSavePng(const LexlibImage* image, const char* filename){ + // image checks + if(image->data == NULL) + return LEXLIB_INVALID_VALUE; + if(image->width == 0) + return LEXLIB_INVALID_VALUE; + if(image->height == 0) + return LEXLIB_INVALID_VALUE; + if(image->channels == 0) + return LEXLIB_INVALID_VALUE; + if(image->depth == 0) + return LEXLIB_INVALID_VALUE; + if((image->depth % 8) != 0) + return LEXLIB_INVALID_VALUE; + + // create file + FILE* file = fopen(filename, "wb"); + if(!file) + return LEXLIB_CANT_WRITE; + + // allocate libpng structs + png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if(!pngPtr) + return LEXLIB_OUT_OF_MEMORY; + png_infop infoPtr = png_create_info_struct(pngPtr); + if(!infoPtr){ + png_destroy_write_struct(&pngPtr, NULL); + return LEXLIB_OUT_OF_MEMORY; + } + + // get color type + int colorType = 0; + switch(image->channels){ + case 1: + colorType = PNG_COLOR_TYPE_GRAY; + break; + case 3: + colorType = PNG_COLOR_TYPE_RGB; + break; + case 4: + colorType = PNG_COLOR_TYPE_RGB_ALPHA; + break; + default: + colorType = PNG_COLOR_TYPE_GRAY; + } + + // set png info + png_set_IHDR(pngPtr, infoPtr, image->width, image->height, image->depth, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_compression_level(pngPtr, 9); + + // write png + png_init_io(pngPtr, file); + png_write_info(pngPtr, infoPtr); + png_write_flush(pngPtr); + + // write data + size_t rowSize = image->width * image->channels * (image->depth / 8); + uint32_t offset = 0; + for(uint32_t y = 0; y < image->height; y++){ + png_write_row(pngPtr, image->data+offset); + offset += rowSize; + } + + png_write_end(pngPtr, infoPtr); + + // cleanup + png_destroy_write_struct(&pngPtr, &infoPtr); + fclose(file); + return LEXLIB_OK; +} \ No newline at end of file diff --git a/test/colorblend.c b/test/colorblend.c index f75c9c0..4d59fc9 100644 --- a/test/colorblend.c +++ b/test/colorblend.c @@ -9,7 +9,7 @@ void testColorBlend(void){ const char* errmsg = NULL; #ifdef TEST_PNG - LexlibImage image = lexlibImageNew(41, 3, LEXLIB_RGBA, 8); + LexlibImage image = lexlibImageNew(41, 3, LEXLIB_RGB, 16); LexlibColor color1 = {0xE5, 0xA4, 0x7C, 0xFF}; LexlibColor color2 = {0x00, 0x00, 0x00, 0xCC}; LexlibColor colorR = {0xFF, 0x00, 0x00, 178}; @@ -24,6 +24,7 @@ void testColorBlend(void){ // reference // add lexlibImagePixelSet(&image, col++, row, (LexlibColor){0xB2, 0x00, 0x00, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, col-1, row, (LexlibColor){0x00, 0xFF, 0x00, 178}, LEXLIB_BLEND); lexlibImagePixelSet(&image, col++, row, (LexlibColor){0x00, 0xB2, 0x00, 0xFF}, LEXLIB_NONE); lexlibImagePixelSet(&image, col++, row, (LexlibColor){0x00, 0x00, 0xB2, 0xFF}, LEXLIB_NONE); lexlibImagePixelSet(&image, col++, row, (LexlibColor){0xB2, 0xB2, 0x00, 0xFF}, LEXLIB_NONE); -- GitLab From 91a1d64a15ce71b80df535631c963b41a3ea8dc8 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 17:20:52 -0400 Subject: [PATCH 10/64] added .bpp element to image --- include/lexlib/image.h | 5 +++-- src/image.c | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 0379a9f..3720635 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -18,7 +18,8 @@ struct LexlibImage { uint32_t width; uint32_t height; uint8_t channels; - uint8_t depth; + uint8_t depth; /* bits per color */ + uint8_t bpp; /* bits per pixel */ }; #ifndef __cplusplus @@ -35,7 +36,7 @@ typedef struct LexlibImage LexlibImage; #define LEXLIB_FLIP_Y 0x02 // "constants" -#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0}) +#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0}) // creates a new LexlibImage and allocates its memory. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_OUT_OF_MEMORY) diff --git a/src/image.c b/src/image.c index e17fb2b..a16d7c3 100644 --- a/src/image.c +++ b/src/image.c @@ -9,7 +9,7 @@ #include LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uint8_t depth){ - LexlibImage image = {NULL, width, height, 0, depth}; + LexlibImage image = {NULL, width, height, 0, depth, 0}; // corrections if(image.depth == 0) @@ -41,10 +41,10 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin break; } - // size of the data - size_t size = image.depth / 8; - size *= (image.width * image.height) * image.channels; - image.data = calloc(size, sizeof(uint8_t)); + image.bpp = image.channels * image.depth; + + // allocate data + image.data = calloc((image.width * image.height) * (image.bpp / 8), sizeof(uint8_t)); if(!image.data){ image.data = NULL; image.depth = LEXLIB_OUT_OF_MEMORY; -- GitLab From 3b7130b5a18ba122924cef61933351957d30c09e Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 17:39:39 -0400 Subject: [PATCH 11/64] 2 new defines and one deprecated deprecated "LEXLIB_INVALID_FILENAME" in favor of LEXLIB_INVALID_FILE_NAME --- include/lexlib/defines.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/lexlib/defines.h b/include/lexlib/defines.h index a98f5e8..f9fab0e 100644 --- a/include/lexlib/defines.h +++ b/include/lexlib/defines.h @@ -17,6 +17,8 @@ #define LEXLIB_CANT_OPEN 0x83 #define LEXLIB_CANT_WRITE 0x84 #define LEXLIB_PARTIAL_WRITE 0x85 -#define LEXLIB_INVALID_FILENAME 0x86 +#define LEXLIB_INVALID_FILENAME 0x86 /* DEPRECATED, use LEXLIB_INVALID_FILE_NAME instead */ +#define LEXLIB_INVALID_FILE_NAME 0x86 +#define LEXLIB_INVALID_FILE_TYPE 0x87 #endif \ No newline at end of file -- GitLab From 51ee95531c14062d39effb7cbe1812ef03428221 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 22:33:57 -0400 Subject: [PATCH 12/64] lexlibFileBytes() --- CHANGELOG.md | 1 + include/lexlib/file.h | 14 ++++++++++++++ src/file.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edd2c60..56e9ed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 1.5.0 (under development) + function lexlibColor16Blend(). + function lexlibImagePixelSet(). ++ function lexlibFileBytes(): ## 1.4.0 + function lexlibStrCopy(). diff --git a/include/lexlib/file.h b/include/lexlib/file.h index f6555d7..ba0b0c2 100644 --- a/include/lexlib/file.h +++ b/include/lexlib/file.h @@ -5,6 +5,16 @@ #define lexlib_file_h #include"common.h" +#include +#include + +struct LexlibBytes { + uint8_t* data; + size_t count; +}; + +typedef struct LexlibBytes LexlibBytes; + /// reads a file into a null terminated heap allocated string. /// returns a valid string on success, NULL on error. LEXLIB_EXTERN char* lexlibFileToString(const char* filename); @@ -12,5 +22,9 @@ LEXLIB_EXTERN char* lexlibFileToString(const char* filename); /// writes a null terminated string into a file. /// returns LEXLIB_OK on success, LEXLIB_CANT_WRITE, LEXLIB_PARTIAL_WRITE on error. LEXLIB_EXTERN unsigned int lexlibStringToFile(const char* string, const char* filename); + +// reads a file as bytes +// on error bytes.data will be NULL and the error code is placed in bytes.count (LEXLIB_CANT_OPEN, LEXLIB_ERROR, LEXLIB_OUT_OF_MEMORY, LEXLIB_PARTIAL_READ). +LEXLIB_EXTERN LexlibBytes lexlibFileBytes(const char* filename); #endif \ No newline at end of file diff --git a/src/file.c b/src/file.c index 0848e47..c11f94f 100644 --- a/src/file.c +++ b/src/file.c @@ -50,4 +50,45 @@ unsigned int lexlibStringToFile(const char* string, const char* filename){ fclose(file); return LEXLIB_OK; -} \ No newline at end of file +} + +LexlibBytes lexlibFileBytes(const char* filename){ + LexlibBytes bytes = {NULL, 0}; + + FILE* file = fopen(filename, "rb"); + if(!file){ + bytes.count = LEXLIB_CANT_OPEN; + return bytes; + } + + if(fseek(file, 0, SEEK_END) != 0){ + bytes.count = LEXLIB_ERROR; + fclose(file); + return bytes; + } + bytes.count = ftell(file); + + if(fseek(file, 0, SEEK_SET) != 0){ + bytes.count = LEXLIB_ERROR; + fclose(file); + return bytes; + }; + + bytes.data = malloc(bytes.count); + if(!bytes.data){ + bytes.count = LEXLIB_OUT_OF_MEMORY; + fclose(file); + return bytes; + } + + if(fread(bytes.data, 1, bytes.count, file) != bytes.count){ + free(bytes.data); + fclose(file); + bytes.data = NULL; + bytes.count = LEXLIB_PARTIAL_READ; + return bytes; + } + + fclose(file); + return bytes; +} -- GitLab From a3937bf8b2ff1c47732b2927ee7e9a02fdaf4116 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 11 May 2023 22:36:41 -0400 Subject: [PATCH 13/64] initial support for loading bmp files --- .gitignore | 1 + CMakeLists.txt | 1 + include/lexlib/defines.h | 2 + include/lexlib/image.h | 4 ++ include/lexlibinternal/bmp.h | 108 +++++++++++++++++++++++++++++++++ resources/plasma16.bmp | Bin 0 -> 582 bytes resources/plasma24.bmp | Bin 0 -> 822 bytes resources/plasma32.bmp | Bin 0 -> 1094 bytes src/image/bmp.c | 113 +++++++++++++++++++++++++++++++++++ test/test.c | 21 ++++++- 10 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 include/lexlibinternal/bmp.h create mode 100644 resources/plasma16.bmp create mode 100644 resources/plasma24.bmp create mode 100644 resources/plasma32.bmp create mode 100644 src/image/bmp.c diff --git a/.gitignore b/.gitignore index e83bc2c..c22f3de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ build packages resources/colorblendtest.png +resources/out resources/textFileWrite.txt resources/pixelDucksOut.png resources/archPackage/pkg diff --git a/CMakeLists.txt b/CMakeLists.txt index d65c666..a5af99d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources(${PROJECT_NAME} PRIVATE src/color/colorflt.c src/file.c src/image.c + src/image/bmp.c src/image/png.c src/io.c src/lexlib.c diff --git a/include/lexlib/defines.h b/include/lexlib/defines.h index f9fab0e..9d0e0f0 100644 --- a/include/lexlib/defines.h +++ b/include/lexlib/defines.h @@ -20,5 +20,7 @@ #define LEXLIB_INVALID_FILENAME 0x86 /* DEPRECATED, use LEXLIB_INVALID_FILE_NAME instead */ #define LEXLIB_INVALID_FILE_NAME 0x86 #define LEXLIB_INVALID_FILE_TYPE 0x87 +#define LEXLIB_INVALID_FILE_DATA 0x88 +#define LEXLIB_PARTIAL_READ 0x89 #endif \ No newline at end of file diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 3720635..b0a90cd 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -73,6 +73,10 @@ LEXLIB_EXTERN LexlibImage lexlibImageLoad(const char* filename); // returns LEXLIB_OK on success, nonzero on error. (LEXLIB_INVALID_FILENAME, ... *errors sujected to the actual saver*) LEXLIB_EXTERN uint8_t lexlibImageSave(const LexlibImage* image, const char* filename); +// loads a bmp into LexlibImage +// on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_OPERATION, LEXLIB_OUT_OF_MEMORY, LEXLIB_ERROR). +LEXLIB_EXTERN LexlibImage lexlibImageLoadBmp(const char* filename); + // loads a png into LexlibImage // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_OPERATION, LEXLIB_OUT_OF_MEMORY, LEXLIB_ERROR). LEXLIB_EXTERN LexlibImage lexlibImageLoadPng(const char* filename); diff --git a/include/lexlibinternal/bmp.h b/include/lexlibinternal/bmp.h new file mode 100644 index 0000000..02332e1 --- /dev/null +++ b/include/lexlibinternal/bmp.h @@ -0,0 +1,108 @@ +#ifndef lexlib_internal_bmpheaders_h +#define lexlib_internal_bmpheaders_h + +#include + +// "constants" // +#define BMP_RGB 0 +#define BMP_RLE8 1 +#define BMP_RLE4 2 +#define BMP_BITFIELDS 3 +#define BMP_JPEG 4 +#define BMP_PNG 5 +#define BMP_ALPHABITFIELDS 6 +#define BMP_CMYK 11 +#define BMP_CMYKRLE8 12 +#define BMP_CMYKRLE4 13 + +// structs // + +#pragma pack(push, 1) +typedef struct BmpFileHeader { + uint16_t type; + uint32_t size; + uint16_t reserved1; + uint16_t reserved2; + uint32_t start; +} BmpFileHeader; +#pragma pack(pop) + +// windows 2.0 or later +typedef struct BmpCoreHeader { + uint32_t size; + uint16_t width; + uint16_t height; + uint16_t planes; + uint16_t bitdepth; /* bits per pixel */ +} BmpCoreHeader; + +// windows nt, 3.1x or later +typedef struct BmpInfoHeader { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bitdepth; + uint32_t compression; + uint32_t imagesize; + int32_t xresm; + int32_t yresm; + uint32_t colorpalette; + uint32_t colorimportant; +} BmpInfoHeader; + +// header 2 and 3 are not documented + +// windows nt 4.0, 95 +typedef struct BmpInfoHeader4 { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bitdepth; + uint32_t compression; + uint32_t imagesize; + int32_t xresm; + int32_t yresm; + uint32_t colorpalette; + uint32_t colorimportant; + uint32_t redmask; + uint32_t greenmask; + uint32_t bluemask; + uint32_t alphamask; + uint32_t cstype; + int32_t endpoint[3][3];// ?? + uint32_t redgamma; + uint32_t greengamma; + uint32_t bluegamma; +} BmpInfoHeader4; + +// windows nt 5.0, 98 +typedef struct BmpInfoHeader5 { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bitdepth; + uint32_t compression; + uint32_t imagesize; + int32_t xresm; + int32_t yresm; + uint32_t colorpalette; + uint32_t colorimportant; + uint32_t redmask; + uint32_t greenmask; + uint32_t bluemask; + uint32_t alphamask; + uint32_t cstype; + int32_t endpoint[3][3];// ?? + uint32_t redgamma; + uint32_t greengamma; + uint32_t bluegamma; + uint32_t intent; + uint32_t profiledata; + uint32_t profilesize; + uint32_t reserved; +} BmpInfoHeader5; + +#endif \ No newline at end of file diff --git a/resources/plasma16.bmp b/resources/plasma16.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f47a579a85e5ad87dad67ea87fdfa7a861570335 GIT binary patch literal 582 zcmZ?rbz@=x12+Z+1`7rT1_3B$WDsCr2FWlnFevLmFo+ArKNuJuurn~ogE~g?$YLR#D&?_ z(KW$!a?qxri$Max`oX@zJ;AGkPX=>@sE4?Qw1g}UIT*qaDwHgkY>@1ioSWQ}+?~8C z`9$*bWVRI66qOXGl*E*VlnKTC#eK!S#cPUJ7q2Q_S$w?sSn<)~BgK!49~D0=W+>rn z;cek*;cj`={IdB)^YiB8&BvOLHXmtT+`OoHVRJ)s&y?;dSyQ~GXinjn!an8MKfHGPgWmU-Mrd)HPh-ts~T51u3}jAZ11wYse4uTKH1Z<$7K)mp2fS9 zb~Eihw5xHK;;sif8+N9gQ#p6|Z1P#4(DV{obGT|h{$w&8^ z?kU|{bl36j!#j<481F2+t$6$3Eyr6AZYJDhxY=+c;r)Ym3GW`fO?b=jw&4xKn}*j6 SuNz)5ylQyK@Ur0r!wUd{XYgqN literal 0 HcmV?d00001 diff --git a/resources/plasma24.bmp b/resources/plasma24.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a788b4fff2154420f165bcae8a214b286f1e351e GIT binary patch literal 822 zcmZ?rHDhJ~12YB&1`P%V1_3B$WRL)hGcYqSDCT`BXC4RV2g#o3KxMHAp#u< z0u==UIW+oVj3DkY8rfM8eD1`Y-$=TDjEzb8Z;^z6e=1dN*V-88aPTC z7z!E^CQOK#Fd?L8LO{m^kCq7z4HIk{CYaPrFsPWIQ8GcHV1i7+1d*Hx0vQuHQYJ7Y zObA%8z-P_^j~NRbCM>Y&Szytzz@TA)PR#<1iUkS<3uJN@NMtM!NLauVvw$OF0Ykt7 zhYcHS)@-m?vcY7|27@UZbb2;uv}{nR*`QFdL8f4XM8*b@gbe}_8+ZaXaCmHBaM)mQ z;DEu713DWHXe>FPGUtH8gaZm42V@!!NR%89$vGg9a6lm908hXH4wnNgHU}6C4k%o> zpm5@X%$^G}8!kvJxgas)f=JH=k%kKbB^LxTF7U)$;0d_E;c|h);sT4#1qOu+0uLSt zTzMdH;(@@92Rv&Y@XUF@GvNVG!vl_z2OKF6I3gZ!csyXSdBCFcfJNZ}D2;yj!0_M$ w!-WqF2R<-t_`tB>1H*(53=JO`3O+C-d|(Lpz~JzK!Qcaf!UqO{4-5<+02U!Hd;kCd literal 0 HcmV?d00001 diff --git a/resources/plasma32.bmp b/resources/plasma32.bmp new file mode 100644 index 0000000000000000000000000000000000000000..52f2755b2aded28d3b38533d0c4372028f263c16 GIT binary patch literal 1094 zcmZ?rbz@-w12+Z+1`7rT1_3B$WKdvW2Fb86FevLmFo+Am{}~t{2+aM!z`*c8fPvwH z0t3SV0|tf-4h#$n0vH%3Brq^E6fiIpG%zqEOkiLLSir#Guz`WW-~a=I!UYBffd>o> z3?D#x1sJZ#2rwMc5MbD1A;7T0MSx*OhyX)Jf&fEBfdE5JjQ~STj{t+u3;_n46#@(f zTLc&sjtDRaTo3?hX1JiBz;HxEfnkS<0>cUi1%^333Jg6F3JeV?3Je7W3JfVV3Jf6~ z3Je}o6c{WPC@|=(QDBhSp}-(;L;>vR0|o{RTPzG1RyY_i%<(Z`m=I#X(2!ujP?BN5 zkWpa35L08o5YS@4;4r~}!DNO3gT?{_2AMSm3<6sWz#iV<;J~oL#erdtj|0Pm5C?{q z7zc)m6bFW!90!Jk5(kEm3I_&{1_uV44hIH<2@VV@GaMKs7C12QtZ)Da)`EZlh8ZCN z3_TG63=IhZ3>7H>3^^GA3<(7R3=t&(3_cYB3=Rze3>GZ`3_2YF3 z00;Dhgan3;gan3$lmv#7j0A?9oCJo1f&_+$f&_+uk^}~qiUbCmngj-eh6DzUh6DzM zmIMZojsymto&<0tH53#uR1_326ciLNWE2!IB$N~|M3fXT1e6poc$5?{*i;lSm{b%n z=u{Lis8kd%$kY@th}0A?@YEE5Be0h~AzCQM+6nJ|GNq-O#{K*t0IkCq7x z4h<6+Y#JsonAA*QFsPWopiwe`L7`v*gG|8$29cZz3<4Pw7&uZUfD=~0f&~mda~3do z%viwSFku0MP0s=bi;e{h1`P`sbZQnbXjCj*;) zz=_gf!v+SMH5(W#mTX`!nX`exV9Evtot_O08Z8?bRBARbC{%1+RCzzN#mzySt>9S0b6HXLBkSaN_tWzGQxg$V~36gmzt$TS>akSIC8 zAd+){K_KA(gFwUq2A+Te3>+>87+7o$fHQ-_g$oP{CoV9^?76@ov*7}R#F7gP5;HC^ zi1b`w5NWu;AW(9FK_KG-15eBa2A+Tm3>+>O7&t60FtF%c0B0nD2M-tot~_86IPri% zV8;Uno;42`c;-A{;F<7%fv4dC14qdN29A^m3>*;;7&ts0FtFG>U|`XCz`&yL0Fntm zfHL6+28IhC7#I$GU|`trfq`Mc2L^@-9~c-KJ}@v8d|+Tm_`tvr@PUEB;R6GM!3PEg Og%1o20-&7#fdK$tmNK>g literal 0 HcmV?d00001 diff --git a/src/image/bmp.c b/src/image/bmp.c new file mode 100644 index 0000000..c647df3 --- /dev/null +++ b/src/image/bmp.c @@ -0,0 +1,113 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + +> pixel formats + 8bpp: index of table 256. + 16bpp: ? + 24bpp: b,g,r. + 32bpp: ? +*/ + +LexlibImage lexlibImageLoadBmp(const char* filename){ + LexlibImage image = LEXLIB_IMAGE_ZERO; + LexlibBytes bmp = lexlibFileBytes(filename); + if(!bmp.data){ + printf("bmp: no data.\n"); + image.depth = bmp.count; + return image; + } + printf("bmp.count: %zu\n", bmp.count); + + printf("BmpFileHeader size: %zu\n", sizeof(BmpFileHeader)); + printf("BmpCoreHeader size: %zu\n", sizeof(BmpCoreHeader)); + printf("BmpInfoHeader size: %zu\n", sizeof(BmpInfoHeader)); + printf("BmpInfoHeader4 size: %zu\n", sizeof(BmpInfoHeader4)); + printf("BmpInfoHeader5 size: %zu\n", sizeof(BmpInfoHeader5)); + printf("\n"); + + BmpFileHeader* fileheader = (BmpFileHeader*)bmp.data; + BmpCoreHeader* coreheader = (BmpCoreHeader*)(bmp.data+14); + + printf("fileheader.type: 0x%X\n", fileheader->type); + printf("fileheader.size: %d\n", fileheader->size); + printf("fileheader.res1: %d\n", fileheader->reserved1); + printf("fileheader.res2: %d\n", fileheader->reserved2); + printf("fileheader.stat: %d\n", fileheader->start); + printf("\n"); + + printf("coreheader might be wrong.\n"); + printf("coreheader.size : %u\n", coreheader->size); + printf("coreheader.width : %d\n", coreheader->width); + printf("coreheader.height : %d\n", coreheader->height); + printf("coreheader.planes : %d\n", coreheader->planes); + printf("coreheader.bitdep : %d\n", coreheader->bitdepth); + printf("\n"); + + /* check if file is bmp */ + if(fileheader->type != 0x4D42){ + printf("bmp: no\n"); + image.depth = LEXLIB_INVALID_FILE_TYPE; + goto bmploadend; + } + + // info header + if(coreheader->size == 40){ + BmpInfoHeader* infoheader = (BmpInfoHeader*)coreheader; + printf("infoheader.size : %u\n", infoheader->size); + printf("infoheader.width : %d\n", infoheader->width); + printf("infoheader.height : %d\n", infoheader->height); + printf("infoheader.planes : %u\n", infoheader->planes); + printf("infoheader.bitdep : %u\n", infoheader->bitdepth); + printf("infoheader.compre : %u\n", infoheader->compression); + printf("infoheader.imgsiz : %u\n", infoheader->imagesize); + printf("infoheader.xresm : %u\n", infoheader->xresm); + printf("infoheader.yresm : %u\n", infoheader->yresm); + printf("infoheader.clrpa : %u\n", infoheader->colorpalette); + printf("infoheader.clrim : %u\n", infoheader->colorimportant); + + uint8_t profile = LEXLIB_RGB; + switch(infoheader->bitdepth){ + case 8: + profile = LEXLIB_GRAY; + break; + case 24: + profile = LEXLIB_RGB; + break; + case 32: + profile = LEXLIB_RGBA; + break; + default: + break; + } + + image = lexlibImageNew(infoheader->width, infoheader->height, profile, infoheader->bitdepth / 8); + size_t bmpoffset = fileheader->start; + size_t imgoffset = 0; + while(imgoffset < infoheader->imagesize){ + image.data[imgoffset+0] = bmp.data[bmpoffset+2]; + image.data[imgoffset+1] = bmp.data[bmpoffset+1]; + image.data[imgoffset+2] = bmp.data[bmpoffset+0]; + bmpoffset+=image.channels; + imgoffset+=image.channels; + } + } + + LEXLIB_UNREACHABLE + + bmploadend: + if(bmp.data) + free(bmp.data); + return image; +} \ No newline at end of file diff --git a/test/test.c b/test/test.c index 405951c..e9eaaba 100644 --- a/test/test.c +++ b/test/test.c @@ -3,6 +3,7 @@ #include #include +#include int main(void){ #ifdef __unix__ @@ -27,7 +28,7 @@ int main(void){ testImagePng(); testColorBlend(); testColorGray(); - testColorFlt(); + // testColorFlt(); // end unsigned int total = testCountFailed + testCountSuccess; @@ -41,6 +42,24 @@ int main(void){ } printf("all tests succeed.\n"); + {// temp bmp test + mkdir("resources/out", 0777); + LexlibImage image16 = lexlibImageLoadBmp("resources/plasma16.bmp"); + if(image16.channels == 0) printf("bmp16 ewwor :(\n"); + lexlibImageFlip(&image16, LEXLIB_FLIP_Y); + lexlibImageSavePng(&image16, "resources/out/plasma16.png"); + + LexlibImage image24 = lexlibImageLoadBmp("resources/plasma24.bmp"); + if(image24.channels == 0) printf("bmp24 ewwor :(\n"); + lexlibImageFlip(&image24, LEXLIB_FLIP_Y); + lexlibImageSavePng(&image24, "resources/out/plasma24.png"); + + LexlibImage image32 = lexlibImageLoadBmp("resources/plasma32.bmp"); + if(image24.channels == 0) printf("bmp32 ewwor :(\n"); + lexlibImageFlip(&image32, LEXLIB_FLIP_Y); + lexlibImageSavePng(&image24, "resources/out/plasma32.png"); + } + return 0; } -- GitLab From 9221d4cca0ee221d98607eb7bc7ecc25558bf31d Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 01:17:07 -0400 Subject: [PATCH 14/64] bmp info header v3 --- include/lexlibinternal/bmp.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/include/lexlibinternal/bmp.h b/include/lexlibinternal/bmp.h index 02332e1..dbeee2c 100644 --- a/include/lexlibinternal/bmp.h +++ b/include/lexlibinternal/bmp.h @@ -51,7 +51,26 @@ typedef struct BmpInfoHeader { uint32_t colorimportant; } BmpInfoHeader; -// header 2 and 3 are not documented +// header 2 is not documented + +// https://web.archive.org/web/20150127132443/https://forums.adobe.com/message/3272950#3272950 +typedef struct BmpInfoHeader3 { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bitdepth; + uint32_t compression; + uint32_t imagesize; + int32_t xresm; + int32_t yresm; + uint32_t colorpalette; + uint32_t colorimportant; + uint32_t redbitmask; + uint32_t greenbitmask; + uint32_t bluebitmask; + uint32_t alphabitmask; +} BmpInfoHeader3; // windows nt 4.0, 95 typedef struct BmpInfoHeader4 { -- GitLab From 4b8e29f4731765fb31dc88ac4297345cf2e75a33 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 02:43:04 -0400 Subject: [PATCH 15/64] working on bmp support --- include/lexlib/image.h | 10 +++-- src/image.c | 7 ++-- src/image/bmp.c | 90 +++++++++++++++++++++++++++++++++++++++++- test/test.c | 10 ++++- 4 files changed, 105 insertions(+), 12 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index b0a90cd..eadb25a 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -20,6 +20,7 @@ struct LexlibImage { uint8_t channels; uint8_t depth; /* bits per color */ uint8_t bpp; /* bits per pixel */ + uint8_t profile; }; #ifndef __cplusplus @@ -27,16 +28,17 @@ typedef struct LexlibImage LexlibImage; #endif // profiles -#define LEXLIB_GRAY 0x01 -#define LEXLIB_RGB 0x03 -#define LEXLIB_RGBA 0x04 +#define LEXLIB_GRAY 0x01 /* Gray scale */ +#define LEXLIB_BW 0x02 /* Black White */ +#define LEXLIB_RGB 0x03 /* Red Green Blue */ +#define LEXLIB_RGBA 0x04 /* Red Green Blue Alpha */ // flags #define LEXLIB_FLIP_X 0x01 #define LEXLIB_FLIP_Y 0x02 // "constants" -#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0}) +#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0, 0}) // creates a new LexlibImage and allocates its memory. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_OUT_OF_MEMORY) diff --git a/src/image.c b/src/image.c index a16d7c3..b02256b 100644 --- a/src/image.c +++ b/src/image.c @@ -9,7 +9,7 @@ #include LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uint8_t depth){ - LexlibImage image = {NULL, width, height, 0, depth, 0}; + LexlibImage image = {NULL, width, height, 0, depth, 0, profile}; // corrections if(image.depth == 0) @@ -24,9 +24,7 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin image.height = 16; // set profile settings. - if(profile == 0) - profile = LEXLIB_RGB; - switch(profile){ + switch(image.profile){ case LEXLIB_GRAY: image.channels = 1; break; @@ -37,6 +35,7 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin image.channels = 4; break; default: + image.profile = LEXLIB_RGB; image.channels = 3; break; } diff --git a/src/image/bmp.c b/src/image/bmp.c index c647df3..edb2c9b 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -33,6 +33,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("BmpFileHeader size: %zu\n", sizeof(BmpFileHeader)); printf("BmpCoreHeader size: %zu\n", sizeof(BmpCoreHeader)); printf("BmpInfoHeader size: %zu\n", sizeof(BmpInfoHeader)); + printf("BmpInfoHeader3 size: %zu\n", sizeof(BmpInfoHeader3)); printf("BmpInfoHeader4 size: %zu\n", sizeof(BmpInfoHeader4)); printf("BmpInfoHeader5 size: %zu\n", sizeof(BmpInfoHeader5)); printf("\n"); @@ -81,20 +82,25 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ switch(infoheader->bitdepth){ case 8: profile = LEXLIB_GRAY; + image.depth = 8; break; case 24: profile = LEXLIB_RGB; + image.depth = 8; break; case 32: profile = LEXLIB_RGBA; + image.depth = 8; break; default: break; } - image = lexlibImageNew(infoheader->width, infoheader->height, profile, infoheader->bitdepth / 8); + image = lexlibImageNew(infoheader->width, infoheader->height, profile, image.depth); size_t bmpoffset = fileheader->start; size_t imgoffset = 0; + + if(image.profile == LEXLIB_RGB) while(imgoffset < infoheader->imagesize){ image.data[imgoffset+0] = bmp.data[bmpoffset+2]; image.data[imgoffset+1] = bmp.data[bmpoffset+1]; @@ -102,9 +108,89 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ bmpoffset+=image.channels; imgoffset+=image.channels; } + + if(image.profile == LEXLIB_RGBA) + while(imgoffset < infoheader->imagesize){ + image.data[imgoffset+0] = bmp.data[bmpoffset+3]; + image.data[imgoffset+1] = bmp.data[bmpoffset+2]; + image.data[imgoffset+2] = bmp.data[bmpoffset+1]; + image.data[imgoffset+3] = bmp.data[bmpoffset+0]; + bmpoffset+=image.channels; + imgoffset+=image.channels; + } + + } + + if(coreheader->size == 56){ + BmpInfoHeader3* infoheader3 = (BmpInfoHeader3*)coreheader; + printf("infoheader3.size : %u\n", infoheader3->size); + printf("infoheader3.width : %d\n", infoheader3->width); + printf("infoheader3.height : %d\n", infoheader3->height); + printf("infoheader3.planes : %u\n", infoheader3->planes); + printf("infoheader3.bitdep : %u\n", infoheader3->bitdepth); + printf("infoheader3.compre : %u\n", infoheader3->compression); + printf("infoheader3.imgsiz : %u\n", infoheader3->imagesize); + printf("infoheader3.xresm : %u\n", infoheader3->xresm); + printf("infoheader3.yresm : %u\n", infoheader3->yresm); + printf("infoheader3.clrpa : %u\n", infoheader3->colorpalette); + printf("infoheader3.clrim : %u\n", infoheader3->colorimportant); + printf("infoheader3.redbitmask : 0x%.8X\n", infoheader3->redbitmask); + printf("infoheader3.greenbitmask : 0x%.8X\n", infoheader3->greenbitmask); + printf("infoheader3.bluebitmask : 0x%.8X\n", infoheader3->bluebitmask); + printf("infoheader3.alphabitmask : 0x%.8X\n", infoheader3->alphabitmask); + + uint8_t profile = LEXLIB_RGB; + uint8_t bmpoffsetextra = 0; + switch(infoheader3->bitdepth){ + case 8: + profile = LEXLIB_GRAY; + image.depth = 8; + break; + case 24: + profile = LEXLIB_RGB; + image.depth = 8; + break; + case 32: + image.depth = 8; + if(infoheader3->alphabitmask){ + profile = LEXLIB_RGBA; + } else { + profile = LEXLIB_RGB; + // infoheader3->imagesize /= 3; + infoheader3->imagesize = (infoheader3->width * infoheader3->height) * (24 / 8); + printf("infoheader3.imgsiz : %u\n", infoheader3->imagesize); + bmpoffsetextra = 1; + } + break; + default: + break; + } + + image = lexlibImageNew(infoheader3->width, infoheader3->height, profile, image.depth); + size_t bmpoffset = fileheader->start; + size_t imgoffset = 0; + + if(image.profile == LEXLIB_RGB) + while(imgoffset < infoheader3->imagesize){ + image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; + image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; + image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); + bmpoffset+=image.channels+bmpoffsetextra; + imgoffset+=image.channels; + } + + if(image.profile == LEXLIB_RGBA) + while(imgoffset < infoheader3->imagesize){ + image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; + image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; + image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); + image.data[imgoffset+3] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->alphabitmask) >> 24; + bmpoffset+=image.channels; + imgoffset+=image.channels; + } } - LEXLIB_UNREACHABLE + // LEXLIB_UNREACHABLE bmploadend: if(bmp.data) diff --git a/test/test.c b/test/test.c index e9eaaba..4846b44 100644 --- a/test/test.c +++ b/test/test.c @@ -44,20 +44,26 @@ int main(void){ {// temp bmp test mkdir("resources/out", 0777); + printf("\n[plasma16.bmp]\n"); LexlibImage image16 = lexlibImageLoadBmp("resources/plasma16.bmp"); if(image16.channels == 0) printf("bmp16 ewwor :(\n"); lexlibImageFlip(&image16, LEXLIB_FLIP_Y); lexlibImageSavePng(&image16, "resources/out/plasma16.png"); + lexlibImageDelete(&image16); + printf("\n[plasma24.bmp]\n"); LexlibImage image24 = lexlibImageLoadBmp("resources/plasma24.bmp"); if(image24.channels == 0) printf("bmp24 ewwor :(\n"); lexlibImageFlip(&image24, LEXLIB_FLIP_Y); lexlibImageSavePng(&image24, "resources/out/plasma24.png"); + lexlibImageDelete(&image24); + printf("\n[plasma32.bmp]\n"); LexlibImage image32 = lexlibImageLoadBmp("resources/plasma32.bmp"); - if(image24.channels == 0) printf("bmp32 ewwor :(\n"); + if(image32.channels == 0) printf("bmp32 ewwor :(\n"); lexlibImageFlip(&image32, LEXLIB_FLIP_Y); - lexlibImageSavePng(&image24, "resources/out/plasma32.png"); + lexlibImageSavePng(&image32, "resources/out/plasma32.png"); + lexlibImageDelete(&image32); } return 0; -- GitLab From e601aa8b912a79dc19ff633c9ba05c2f31717e39 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 04:01:12 -0400 Subject: [PATCH 16/64] documentation in bmp source --- src/image/bmp.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index edb2c9b..5e79483 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -12,14 +12,50 @@ #include /* - +> BMP + data is stored as little endian. + +> BMP structure + File Header | contains information about the file itsef + Info Header | contains Info about the bitmap + Extra bit masks | defines pixel format | optional + Color Table | defines colors used by the bitmap | semi-optional (not if bpp is less than 8) + Gap1 | alignment | optional + Pixel Array | actual pixel data + Gap2 | alignment | optional + ICC color profile | defines color profile | optional + +> BMP headers + all bmp files start with "BM" (0x4D42 in hex 16bits) which + is part of a file header (BmpFileHeader). + + information headers. + L S + ◯ ◯ BmpCoreHeader | 1..24 bpp + ◇ ◇ ?? | ?..?? bpp | not sure about which one it is + ● ◯ BmpInfoHeader | 1..32 bpp + ◇ ◇ BmpInfoHeader2 | ?..?? bpp | not documented | no struct exist + ● ◯ BmpInfoHeader3 | ?..?? bpp | not officially documented + ◯ ◯ BmpInfoHeader4 | 0..32 bpp + ◯ ◯ BmpInfoHeader5 | 0..32 bpp + +> Color Table + usually the order is bgr0. + 4-byte per entry for rgba format. + 3-byte per entry for rgb format + > pixel formats - 8bpp: index of table 256. - 16bpp: ? - 24bpp: b,g,r. - 32bpp: ? + left-most pixel in the most-significant bit of the first byte. + ◯ 1bpp : 2 colors | table + ◯ 2bpp : 4 colors | table + ◯ 4bpp : 16 colors | table + ◯ 8bpp : 256 colors | table + ◯ 16bpp: 65536 colors | ? + ● 24bpp: 16777216 colors | b,g,r. + ● 32bpp: 4294967296 colors | a,b,g,r. */ +// TODO probably needs big endian transforms LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; LexlibBytes bmp = lexlibFileBytes(filename); -- GitLab From 807c1bc0bf741f1503ea87f48df5b1c369317eff Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 13:38:43 -0400 Subject: [PATCH 17/64] added element image.datasize --- include/lexlib/image.h | 3 ++- src/image.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index eadb25a..f558310 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -15,6 +15,7 @@ struct LexlibImage { uint8_t* data; + size_t datasize; uint32_t width; uint32_t height; uint8_t channels; @@ -38,7 +39,7 @@ typedef struct LexlibImage LexlibImage; #define LEXLIB_FLIP_Y 0x02 // "constants" -#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0, 0}) +#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0, 0, 0}) // creates a new LexlibImage and allocates its memory. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_OUT_OF_MEMORY) diff --git a/src/image.c b/src/image.c index b02256b..c91aaaf 100644 --- a/src/image.c +++ b/src/image.c @@ -9,7 +9,16 @@ #include LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uint8_t depth){ - LexlibImage image = {NULL, width, height, 0, depth, 0, profile}; + LexlibImage image = { + .data = NULL, + .datasize = 0, + .width = width, + .height = height, + .channels = 0, + .depth = depth, + .bpp = 0, + .profile = profile + }; // corrections if(image.depth == 0) @@ -40,10 +49,11 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin break; } + image.datasize = (image.width * image.height) * (image.bpp / 8); image.bpp = image.channels * image.depth; // allocate data - image.data = calloc((image.width * image.height) * (image.bpp / 8), sizeof(uint8_t)); + image.data = calloc(image.datasize, sizeof(uint8_t)); if(!image.data){ image.data = NULL; image.depth = LEXLIB_OUT_OF_MEMORY; -- GitLab From 20c6cf73a3b178f7d75f33d7c56eeb33d7de4109 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 15:09:22 -0400 Subject: [PATCH 18/64] fixed image.datasize calculation --- src/image.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/image.c b/src/image.c index c91aaaf..df7f216 100644 --- a/src/image.c +++ b/src/image.c @@ -49,8 +49,8 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin break; } - image.datasize = (image.width * image.height) * (image.bpp / 8); image.bpp = image.channels * image.depth; + image.datasize = (image.width * image.height) * (image.bpp / 8); // allocate data image.data = calloc(image.datasize, sizeof(uint8_t)); -- GitLab From 7f51c6b8678294e23a09e783684743273c6e5f6e Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 15:19:43 -0400 Subject: [PATCH 19/64] better bmp rgba32 support --- resources/macintosh.bmp | Bin 0 -> 822 bytes resources/macintosh_alpha70.bmp | Bin 0 -> 1094 bytes src/image/bmp.c | 68 ++++++++++++++++++-------------- test/test.c | 9 ++++- 4 files changed, 47 insertions(+), 30 deletions(-) create mode 100644 resources/macintosh.bmp create mode 100644 resources/macintosh_alpha70.bmp diff --git a/resources/macintosh.bmp b/resources/macintosh.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4360108dfc205bba092a8c9d596236a40f212289 GIT binary patch literal 822 zcmZ?rHDhJ~12YB&1`P%V1_3B$WRL)hGcYqSDCzp?TEG~@p>jsKr9 z1}S1l`){1~KP~P5%(VY!(m;9`X8tz@nR&+e%$c+^XJ(!Of&XXDFo1NNNdwt=<_yTx zGYky>jT!!@G5nv&@ITG?%uM4mXN*BkVMse;oOUKH?aa(HkP-jW&M?e8V?6Ur+RQUE zXM#-p4>AGV{!TkY^Ye&KNVCNn6rDMoiWG& zhM5cuU^^MWu0~bCz`($b#{h^l12;Ffl9G~(i%V!|XnA>gM@Pr1RjYRI-hKD(-FNTa Hp&0-G;;&+k literal 0 HcmV?d00001 diff --git a/resources/macintosh_alpha70.bmp b/resources/macintosh_alpha70.bmp new file mode 100644 index 0000000000000000000000000000000000000000..348142c192a671bab9b4b0054b20fb8cb75b8bbe GIT binary patch literal 1094 zcmZ?rbz@-w12+Z+1`7rT1_3B$WKdvW2Fb86FevLmFo+Am{}~t{2*mxLHuL}HnKS=y zJ~Q+G=KnMQZ)Q02f3xwK|C`g!{NFtD%>T`2&ivo}|IGi*4FCUcHva#AbK3v^n`i$2 zzxmAn|C|4VXomlr|1Sq zwEvrDrv2Z1Chh;`|7rg>GtB(I*%)l!na##$&TLLQb7u3*GiNrRIdf+7|1)PcGyFfZ z+4%pN&1wJ7Y@Yf5%;q!y&usqx|IB6vhX0$58UAlhWB9*$CWtmZvw5cRnayX6&usp0 zd}cF4+L_J9X=gU4rJdP4GwsagGihfw|4%!!nPKLc&Bim&Y)+ebX7kLMXEvXid1mu} z5PfFm=Kp7AZf5vDbF=aPnVZx8&)huo|IE#2{?FX}|NqR*3=C&B8#A2QoW^iw^Gt>_ zo6j(u+5Dg3%w`7TGn~`JcA=|Npej3=A_j z8#Bz@oW?M7^Gt@Bo6j)J-29(m=4J-tnVXG`XKqe2p1FCZ@yyLBkXnYQ`N%(Tt_XQpjtIFq*7_)Oa7v@>a&XP!yheCABr=Kp8X zHZ%NB+iVQBFKx3iL)zvvhP2Hy8PYbNVMyEjpCN5CgK^qsW8<{VX~t=rXBww%K4YA= z`M+`6W`?x1&BkeIo72+LHqQjnGmSUToN2uI%uM6W|7RL+W;kQK+4zj{=Cm`$n`fRe z-hAea@#g<$j5jmU^WS*$e-O=Jy!k(a@n!~NcaBPD^9hJTr}9^O-b;&HvLFHZ#m* z*lawLVRPC{hRri)GHgCGlVS7!nIQ9&HZw4|Y-V5x-ORvHzL|lcV>1K8s?7`xyEij1 z+}+H;@NTm)1HhTROC8SXM{W_Sm(n|m_@1H)!+ zZtl%WN=lnuTwFGXhK6n~FE8KR(b2Ja)v8sSckkZ4`R?7jo8P^Ax0#!PVY3ng!)6x- PhRvZ244cat7&Zd{ceWYU literal 0 HcmV?d00001 diff --git a/src/image/bmp.c b/src/image/bmp.c index 5e79483..d7fe000 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -55,7 +55,7 @@ ● 32bpp: 4294967296 colors | a,b,g,r. */ -// TODO probably needs big endian transforms +// TODO probably needs big endian to little transforms on big endian machines LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; LexlibBytes bmp = lexlibFileBytes(filename); @@ -84,14 +84,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("fileheader.stat: %d\n", fileheader->start); printf("\n"); - printf("coreheader might be wrong.\n"); - printf("coreheader.size : %u\n", coreheader->size); - printf("coreheader.width : %d\n", coreheader->width); - printf("coreheader.height : %d\n", coreheader->height); - printf("coreheader.planes : %d\n", coreheader->planes); - printf("coreheader.bitdep : %d\n", coreheader->bitdepth); - printf("\n"); - /* check if file is bmp */ if(fileheader->type != 0x4D42){ printf("bmp: no\n"); @@ -174,48 +166,66 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("infoheader3.greenbitmask : 0x%.8X\n", infoheader3->greenbitmask); printf("infoheader3.bluebitmask : 0x%.8X\n", infoheader3->bluebitmask); printf("infoheader3.alphabitmask : 0x%.8X\n", infoheader3->alphabitmask); + printf("\n"); - uint8_t profile = LEXLIB_RGB; - uint8_t bmpoffsetextra = 0; - switch(infoheader3->bitdepth){ + image.width = infoheader3->width; + image.height = infoheader3->height; + switch(infoheader3->bitdepth){ /* image.profile & depth */ case 8: - profile = LEXLIB_GRAY; + image.profile = LEXLIB_GRAY; image.depth = 8; break; case 24: - profile = LEXLIB_RGB; + image.profile = LEXLIB_RGB; image.depth = 8; break; case 32: image.depth = 8; - if(infoheader3->alphabitmask){ - profile = LEXLIB_RGBA; - } else { - profile = LEXLIB_RGB; - // infoheader3->imagesize /= 3; - infoheader3->imagesize = (infoheader3->width * infoheader3->height) * (24 / 8); - printf("infoheader3.imgsiz : %u\n", infoheader3->imagesize); - bmpoffsetextra = 1; - } + image.profile = LEXLIB_RGBA; break; default: break; } - image = lexlibImageNew(infoheader3->width, infoheader3->height, profile, image.depth); + image = lexlibImageNew(image.width, image.height, image.profile, image.depth); + size_t bmpoffset = fileheader->start; size_t imgoffset = 0; + uint64_t pixelcount = image.width * image.height; + uint8_t profile = image.profile; - if(image.profile == LEXLIB_RGB) - while(imgoffset < infoheader3->imagesize){ + if(profile == LEXLIB_RGBA){ + if(infoheader3->alphabitmask){ + profile = LEXLIB_RGBA; + } else { + profile = LEXLIB_RGB; + } + } + + printf("profile: %u\n", profile); + printf("pixelco: %zu\n", pixelcount); + printf("\n"); + + printf("image.datasize: %zu\n", image.datasize); + printf("image.width : %u\n", image.width); + printf("image.height : %u\n", image.height); + printf("image.channels: %u\n", image.channels); + printf("image.depth : %u\n", image.depth); + printf("image.bpp : %u\n", image.bpp); + printf("image.profile : %u\n", image.profile); + printf("\n"); + + if(profile == LEXLIB_RGB) + for(uint64_t i = 0; i < pixelcount; i++){ image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); - bmpoffset+=image.channels+bmpoffsetextra; - imgoffset+=image.channels; + if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + bmpoffset += image.channels; + imgoffset += image.channels; } - if(image.profile == LEXLIB_RGBA) + if(profile == LEXLIB_RGBA) while(imgoffset < infoheader3->imagesize){ image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; diff --git a/test/test.c b/test/test.c index 4846b44..a472430 100644 --- a/test/test.c +++ b/test/test.c @@ -64,8 +64,15 @@ int main(void){ lexlibImageFlip(&image32, LEXLIB_FLIP_Y); lexlibImageSavePng(&image32, "resources/out/plasma32.png"); lexlibImageDelete(&image32); - } + printf("\n[macintosh_a70.bmp]\n"); + image32 = lexlibImageLoadBmp("resources/macintosh_alpha70.bmp"); + if(image32.channels == 0) printf("mac_a32 ewwor :(\n"); + lexlibImageFlip(&image32, LEXLIB_FLIP_Y); + lexlibImageSavePng(&image32, "resources/out/macintosh_alpha70.png"); + lexlibImageDelete(&image32); + } + printf("s: %zu\n",sizeof(LexlibImage)); return 0; } -- GitLab From c1edb584cc1d6bbe16bf0de13a4c0292acc8fb60 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 19:04:58 -0400 Subject: [PATCH 20/64] bmp bit masks and shifts detection --- include/lexlib/image.h | 24 ++++++++ src/image/bmp.c | 121 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 139 insertions(+), 6 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index f558310..e7cf228 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -10,6 +10,25 @@ /* NOTE s images with more bitdepth than 8 are bigendian. + +> Image file formats + L S M + ● ◯ ◆ bmp + ◯ ◯ ◯ gif + ◯ ◯ ◯ jpg + ● ● ◯ png | libpng and zlib required + ◯ ◯ ◯ webp + +> Color profiles + lexlib images work directly only with certain color profiles in the R.G.B.A order. + any loading or saving with a profile the image doesn't work directly results in a conversion. + ● GRAY + ◯ BW (black white) + ● RGB + ● RGBA + ● RGB24 + ▽ RGB555 + ▽ RGB565 */ @@ -34,6 +53,11 @@ typedef struct LexlibImage LexlibImage; #define LEXLIB_RGB 0x03 /* Red Green Blue */ #define LEXLIB_RGBA 0x04 /* Red Green Blue Alpha */ +#define LEXLIB_RGB24 0x03 /* R.G.B.A.X: 8.8.8.0.0 */ +#define LEXLIB_RGB555 0x05 /* R.G.B.A.X: 5.5.5.0.1 */ +#define LEXLIB_RGB565 0x06 /* R.G.B.A.X: 5.6.5.0.0 */ + + // flags #define LEXLIB_FLIP_X 0x01 #define LEXLIB_FLIP_Y 0x02 diff --git a/src/image/bmp.c b/src/image/bmp.c index d7fe000..61020f0 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -55,6 +57,25 @@ ● 32bpp: 4294967296 colors | a,b,g,r. */ +// get an individual bit +LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index); + +// to store bitmasks and shifts +typedef struct ColorBitMask { + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t alpha; + uint32_t redbit; + uint32_t greenbit; + uint32_t bluebit; + uint32_t alphabit; + uint8_t redshift; + uint8_t greenshift; + uint8_t blueshift; + uint8_t alphashift; +} ColorBitMask; + // TODO probably needs big endian to little transforms on big endian machines LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; @@ -168,6 +189,52 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("infoheader3.alphabitmask : 0x%.8X\n", infoheader3->alphabitmask); printf("\n"); + size_t bmpoffset = fileheader->start; + size_t imgoffset = 0; + uint64_t pixelcount = 0; + uint8_t profile = 0; + ColorBitMask colormask = { + .red = 0, + .green = 0, + .blue = 0, + .alpha = 0, + .redbit = infoheader3->redbitmask, + .greenbit = infoheader3->greenbitmask, + .bluebit = infoheader3->bluebitmask, + .alphabit = infoheader3->alphabitmask, + .redshift = 0, + .greenshift = 0, + .blueshift = 0, + .alphashift = 0, + }; + + for(uint8_t i = 0; i < 32; i++){ + colormask.red += bitGet(colormask.redbit, i); + colormask.green += bitGet(colormask.greenbit, i); + colormask.blue += bitGet(colormask.bluebit, i); + colormask.alpha += bitGet(colormask.alphabit, i); + } + + // get bit shifts + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colormask.redbit, i) == 0) + colormask.redshift++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colormask.greenbit, i) == 0) + colormask.greenshift++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colormask.bluebit, i) == 0) + colormask.blueshift++; + else + break; + } + image.width = infoheader3->width; image.height = infoheader3->height; switch(infoheader3->bitdepth){ /* image.profile & depth */ @@ -175,9 +242,19 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.profile = LEXLIB_GRAY; image.depth = 8; break; + case 16: + image.depth = 8; + if(infoheader3->alphabitmask){ + image.profile = LEXLIB_RGBA; + profile = LEXLIB_RGB555; + } else { + image.profile = LEXLIB_RGB; + profile = LEXLIB_RGB555; + } + break; case 24: - image.profile = LEXLIB_RGB; image.depth = 8; + image.profile = LEXLIB_RGB; break; case 32: image.depth = 8; @@ -189,10 +266,9 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image = lexlibImageNew(image.width, image.height, image.profile, image.depth); - size_t bmpoffset = fileheader->start; - size_t imgoffset = 0; - uint64_t pixelcount = image.width * image.height; - uint8_t profile = image.profile; + pixelcount = image.width * image.height; + if(!profile) + profile = image.profile; if(profile == LEXLIB_RGBA){ if(infoheader3->alphabitmask){ @@ -204,6 +280,13 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("profile: %u\n", profile); printf("pixelco: %zu\n", pixelcount); + printf("colormask.red: %u\n", colormask.red); + printf("colormask.green: %u\n", colormask.green); + printf("colormask.blue: %u\n", colormask.blue); + printf("colormask.alpha: %u\n", colormask.alpha); + printf("colormask.redshift : %u\n", colormask.redshift); + printf("colormask.greenshift: %u\n", colormask.greenshift); + printf("colormask.blueshift : %u\n", colormask.blueshift); printf("\n"); printf("image.datasize: %zu\n", image.datasize); @@ -215,6 +298,25 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("image.profile : %u\n", image.profile); printf("\n"); + unsigned yes = 0; + for(unsigned i = 0; i < 32; i++){ + yes += bitGet(infoheader3->bluebitmask, i); + } + printf("yes: %d\n", yes); + + if(profile == LEXLIB_RGB555) + for(uint64_t i = 0; i < pixelcount; i++){ + uint16_t bmppixel = *((uint16_t*)(bmp.data+bmpoffset)); + + image.data[imgoffset+0] = rintf(( ((bmppixel & colormask.redbit) >> colormask.redshift) / 31.0f) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colormask.greenbit) >> colormask.greenshift) / 63.0f) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colormask.bluebit) >> colormask.blueshift) / 31.0f) * 255.0f); + + bmpoffset += 2; + imgoffset += image.channels; + } + + if(profile == LEXLIB_RGB) for(uint64_t i = 0; i < pixelcount; i++){ image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; @@ -242,4 +344,11 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ if(bmp.data) free(bmp.data); return image; -} \ No newline at end of file +} + + +#define UNSIGNED_BIT_WIDTH (CHAR_BIT * sizeof(unsigned)) +LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index){ + index %= UNSIGNED_BIT_WIDTH; + return (x >> index) & 1; +} -- GitLab From 61dc3065389669f1f761f74b03525dd54cb4bfba Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 19:59:22 -0400 Subject: [PATCH 21/64] bmp 16bit RGB555 and RGB565 support --- include/lexlib/defines.h | 1 + resources/macintosh_8bpp.bmp | Bin 0 -> 1334 bytes src/image/bmp.c | 99 ++++++++++++++++++----------------- test/test.c | 9 +++- 4 files changed, 60 insertions(+), 49 deletions(-) create mode 100644 resources/macintosh_8bpp.bmp diff --git a/include/lexlib/defines.h b/include/lexlib/defines.h index 9d0e0f0..be6a49e 100644 --- a/include/lexlib/defines.h +++ b/include/lexlib/defines.h @@ -22,5 +22,6 @@ #define LEXLIB_INVALID_FILE_TYPE 0x87 #define LEXLIB_INVALID_FILE_DATA 0x88 #define LEXLIB_PARTIAL_READ 0x89 +#define LEXLIB_UNSUPORTED 0x8A #endif \ No newline at end of file diff --git a/resources/macintosh_8bpp.bmp b/resources/macintosh_8bpp.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e2f88c6f29e89bb8e4339ef3db0bb3c0d6681240 GIT binary patch literal 1334 zcmZ?rHDhG}12Yx|1`P%V1_3B$WZ(dcGcYnRDCC1H%~x28RD2JB%3^jExx>(u^4xW*Rdv zoH1r#_zzWRoW{VAmd3y^GmU}aOd12jf2d~TnG6hRGZ`3W&SYRXGn0YgKh!|uGYkxA zXBZe}o?&1(bB2N8KhzTA{|pRi{}~u&{%2q~^Php?Kh$Dl24jXa24jYq48{y+7>pVI zGe8_;Y;4SsW^Bwb)7Y5djIlApf2aeE(~KF?(u^5qrWrGwNi$~n4|TNhOk;+$nZ^t= zXBsn{nQ6@MALW*Vn4oH0&g_zw*j_zw+0<1=XtX=lPGn3&zG%}6<&typZKa*kR|CtPD{?BCi4~<|@EP+A>@8mOvo`a?O8GiZ=ewU~K%KAAg zpW#0=Q5w(u&yY6rKf}zK{~6BA{Lk!9$0S96fgY#K}{q&zwDXe#6F1o40J;wtdIWUAy<} z-M4?i!bOXhEM2yI#mZHy*Q{N)e!|2_lc!9bHhsp-S+nQNoj1Rsv8lPGwXMCQv#YzO zx39mTu&B7Cw5+_MvZ}hKwyr)QF)2AEH7z|OGb=kMH!nXRFeo@AG%P$KGAcSIHZI=5 z(aAZ()y>_*)63h(*U#U;(8$=t)Xdz%(#qP#*3Mo*QAt@vRZU$(Q%hS%S5IF+P)Jxr fR7_k#Qc7AzR!*LQk%^gwm5rT)lZ%^&myaI+hZ*81 literal 0 HcmV?d00001 diff --git a/src/image/bmp.c b/src/image/bmp.c index 61020f0..161f65f 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -87,14 +87,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ } printf("bmp.count: %zu\n", bmp.count); - printf("BmpFileHeader size: %zu\n", sizeof(BmpFileHeader)); - printf("BmpCoreHeader size: %zu\n", sizeof(BmpCoreHeader)); - printf("BmpInfoHeader size: %zu\n", sizeof(BmpInfoHeader)); - printf("BmpInfoHeader3 size: %zu\n", sizeof(BmpInfoHeader3)); - printf("BmpInfoHeader4 size: %zu\n", sizeof(BmpInfoHeader4)); - printf("BmpInfoHeader5 size: %zu\n", sizeof(BmpInfoHeader5)); - printf("\n"); - BmpFileHeader* fileheader = (BmpFileHeader*)bmp.data; BmpCoreHeader* coreheader = (BmpCoreHeader*)(bmp.data+14); @@ -207,7 +199,14 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ .blueshift = 0, .alphashift = 0, }; + LexlibColorFlt colormax = { + .r = 255.0f, + .g = 255.0f, + .b = 255.0f, + .a = 255.0f, + }; + // get R.G.B.A for(uint8_t i = 0; i < 32; i++){ colormask.red += bitGet(colormask.redbit, i); colormask.green += bitGet(colormask.greenbit, i); @@ -235,41 +234,48 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ break; } - image.width = infoheader3->width; - image.height = infoheader3->height; - switch(infoheader3->bitdepth){ /* image.profile & depth */ - case 8: - image.profile = LEXLIB_GRAY; - image.depth = 8; - break; - case 16: - image.depth = 8; - if(infoheader3->alphabitmask){ - image.profile = LEXLIB_RGBA; - profile = LEXLIB_RGB555; - } else { - image.profile = LEXLIB_RGB; - profile = LEXLIB_RGB555; - } - break; - case 24: - image.depth = 8; - image.profile = LEXLIB_RGB; - break; - case 32: - image.depth = 8; + // set the color profile + if(infoheader3->bitdepth == 8){ + image.profile = LEXLIB_GRAY; + image.depth = 8; + profile = LEXLIB_GRAY; + } + if(infoheader3->bitdepth == 16){ + image.depth = 8; + if(colormask.alpha) image.profile = LEXLIB_RGBA; - break; - default: - break; + if(colormask.green == 5){ + profile = LEXLIB_RGB555; + colormax.r = 31.0f; + colormax.g = 31.0f; + colormax.b = 31.0f; + colormax.a = 0.0f; + } + if(colormask.green == 6){ + profile = LEXLIB_RGB565; + colormax.r = 31.0f; + colormax.g = 63.0f; + colormax.b = 31.0f; + colormax.a = 0.0f; + } + } + if(infoheader3->bitdepth == 24){ + image.depth = 8; + image.profile = LEXLIB_RGB; + profile = LEXLIB_RGB; + } + if(infoheader3->bitdepth == 32){ + image.depth = 8; + image.profile = LEXLIB_RGBA; + profile = LEXLIB_RGBA; } - image = lexlibImageNew(image.width, image.height, image.profile, image.depth); - + image = lexlibImageNew(infoheader3->width, infoheader3->height, image.profile, image.depth); pixelcount = image.width * image.height; + + // check for the correct bmp profile if(!profile) profile = image.profile; - if(profile == LEXLIB_RGBA){ if(infoheader3->alphabitmask){ profile = LEXLIB_RGBA; @@ -298,24 +304,19 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("image.profile : %u\n", image.profile); printf("\n"); - unsigned yes = 0; - for(unsigned i = 0; i < 32; i++){ - yes += bitGet(infoheader3->bluebitmask, i); - } - printf("yes: %d\n", yes); - - if(profile == LEXLIB_RGB555) + if(profile == LEXLIB_RGB555 || profile == LEXLIB_RGB565) for(uint64_t i = 0; i < pixelcount; i++){ uint16_t bmppixel = *((uint16_t*)(bmp.data+bmpoffset)); - image.data[imgoffset+0] = rintf(( ((bmppixel & colormask.redbit) >> colormask.redshift) / 31.0f) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colormask.greenbit) >> colormask.greenshift) / 63.0f) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colormask.bluebit) >> colormask.blueshift) / 31.0f) * 255.0f); + image.data[imgoffset+0] = rintf(( ((bmppixel & colormask.redbit) >> colormask.redshift) / colormax.r) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colormask.greenbit) >> colormask.greenshift) / colormax.g) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colormask.bluebit) >> colormask.blueshift) / colormax.b) * 255.0f); + if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } bmpoffset += 2; imgoffset += image.channels; } - + if(profile == LEXLIB_RGB) for(uint64_t i = 0; i < pixelcount; i++){ @@ -323,6 +324,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + bmpoffset += image.channels; imgoffset += image.channels; } @@ -333,6 +335,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); image.data[imgoffset+3] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->alphabitmask) >> 24; + bmpoffset+=image.channels; imgoffset+=image.channels; } diff --git a/test/test.c b/test/test.c index a472430..25a1010 100644 --- a/test/test.c +++ b/test/test.c @@ -71,8 +71,15 @@ int main(void){ lexlibImageFlip(&image32, LEXLIB_FLIP_Y); lexlibImageSavePng(&image32, "resources/out/macintosh_alpha70.png"); lexlibImageDelete(&image32); + + printf("\n[macintosh_8bpp.bmp]\n"); + LexlibImage image8 = lexlibImageLoadBmp("resources/macintosh_8bpp.bmp"); + if(image8.channels == 0) printf("mac_bpp8 ewwor :(\n"); + lexlibImageFlip(&image8, LEXLIB_FLIP_Y); + lexlibImageSavePng(&image8, "resources/out/macintosh_8bpp.png"); + lexlibImageDelete(&image8); } - printf("s: %zu\n",sizeof(LexlibImage)); + // printf("s: %zu\n",sizeof(LexlibImage)); return 0; } -- GitLab From ba86c5ea332f01b7dd2a33ce6c1172c46ae711b6 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 22:57:05 -0400 Subject: [PATCH 22/64] added credits for plasma.bmp and macintosh.bmp --- CREDITS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CREDITS b/CREDITS index 642947d..b6d42f3 100644 --- a/CREDITS +++ b/CREDITS @@ -17,6 +17,14 @@ Credits for the libraries/resources/tools used by lexlib. [notes]: lexlib does not provide any code from libpng thus it needs to be linked separately. [used]: for optionally supporting the png image format. +> macintosh.bmp (resource) +[description]: The default System palette stored in the ROM BIOS of various classic Macintosh computer systems. +[source]: https://lospec.com/palette-list/macintosh-8-bit-system-palette +[notes]: the colors in the file were taken from the color palette in lospec published by vga256. + +> plasma.bmp (resource) +[source]: the default "plasma" GIMP color palette. + > zlib (lib) [license]: https://www.zlib.net/zlib_license.html [copyright]: (C) 1995-2022 Jean-loup Gailly and Mark Adler -- GitLab From 522cb67b5010a0c5de213b3039134c5b7822a260 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 23:44:31 -0400 Subject: [PATCH 23/64] added comments to bmp headers --- include/lexlibinternal/bmp.h | 107 +++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/include/lexlibinternal/bmp.h b/include/lexlibinternal/bmp.h index dbeee2c..7b238e2 100644 --- a/include/lexlibinternal/bmp.h +++ b/include/lexlibinternal/bmp.h @@ -1,3 +1,7 @@ +// 2023 alexevier +// This is free and unencumbered software released into the public domain. +// free to copy, modify, publish, use, compile, sell, or distribute for any purpose, commercial or non-commercial. + #ifndef lexlib_internal_bmpheaders_h #define lexlib_internal_bmpheaders_h @@ -19,35 +23,57 @@ #pragma pack(push, 1) typedef struct BmpFileHeader { + // file type, should be "BM" uint16_t type; + // size in bytes of the file uint32_t size; + // set'd by generating program, generally 0 uint16_t reserved1; uint16_t reserved2; + // offset in bytes where the bitmap data starts uint32_t start; + // used for reading the size of the info header; this element is not official in the spec + uint32_t infoHeaderSize; } BmpFileHeader; #pragma pack(pop) // windows 2.0 or later typedef struct BmpCoreHeader { + // size of the header uint32_t size; + // bitmap width in pixels uint16_t width; + // bitmap height in pixels uint16_t height; + // number of color planes (must be 1) uint16_t planes; - uint16_t bitdepth; /* bits per pixel */ + // number of bits per pixel + uint16_t bitdepth; } BmpCoreHeader; // windows nt, 3.1x or later typedef struct BmpInfoHeader { + // size of the header uint32_t size; + // bitmap width in pixels int32_t width; + // bitmap height in pixels int32_t height; + // number of color planes (must be 1) uint16_t planes; + // number of bits per pixel uint16_t bitdepth; + // compression method uint32_t compression; + // raw size of the bitmap data uint32_t imagesize; + // horizontal resolution, in pixels per meter int32_t xresm; + // vertical resolution, in pixels per meter int32_t yresm; + // number of color indices in the color table uint32_t colorpalette; + // number of color indices that are considered important, if zero all are important uint32_t colorimportant; } BmpInfoHeader; @@ -55,72 +81,131 @@ typedef struct BmpInfoHeader { // https://web.archive.org/web/20150127132443/https://forums.adobe.com/message/3272950#3272950 typedef struct BmpInfoHeader3 { + // size of the header uint32_t size; + // bitmap width in pixels int32_t width; + // bitmap height in pixels int32_t height; + // number of color planes (must be 1) uint16_t planes; + // number of bits per pixel uint16_t bitdepth; + // compression method uint32_t compression; + // raw size of the bitmap data uint32_t imagesize; + // horizontal resolution, in pixels per meter int32_t xresm; + // vertical resolution, in pixels per meter int32_t yresm; + // number of color indices in the color table uint32_t colorpalette; + // number of color indices that are considered important, if zero all are important uint32_t colorimportant; + // red color per pixel bit mask uint32_t redbitmask; + // green color per pixel bit mask uint32_t greenbitmask; + // blue color per pixel bit mask uint32_t bluebitmask; + // alpha color per pixel bit mask uint32_t alphabitmask; } BmpInfoHeader3; // windows nt 4.0, 95 typedef struct BmpInfoHeader4 { + // size of the header uint32_t size; + // bitmap width in pixels int32_t width; + // bitmap height in pixels int32_t height; + // number of color planes (must be 1) uint16_t planes; + // number of bits per pixel uint16_t bitdepth; + // compression method uint32_t compression; + // raw size of the bitmap data uint32_t imagesize; + // horizontal resolution, in pixels per meter int32_t xresm; + // vertical resolution, in pixels per meter int32_t yresm; + // number of color indices in the color table uint32_t colorpalette; + // number of color indices that are considered important, if zero all are important. uint32_t colorimportant; - uint32_t redmask; - uint32_t greenmask; - uint32_t bluemask; - uint32_t alphamask; + // red color per pixel bit mask + uint32_t redbitmask; + // green color per pixel bit mask + uint32_t greenbitmask; + // blue color per pixel bit mask + uint32_t bluebitmask; + // alpha color per pixel bit mask + uint32_t alphabitmask; + // color space uint32_t cstype; - int32_t endpoint[3][3];// ?? + // specifies the x, y, and z coordinates of the three colors that correspond to the red, green, and blue endpoints + int32_t endpoint[3][3]; /* this type is wrong */ + // Tone response curve for red uint32_t redgamma; + // Tone response curve for green uint32_t greengamma; + // Tone response curve for blue uint32_t bluegamma; } BmpInfoHeader4; // windows nt 5.0, 98 typedef struct BmpInfoHeader5 { + // size of the header uint32_t size; + // bitmap width in pixels int32_t width; + // bitmap height in pixels int32_t height; + // number of color planes (must be 1) uint16_t planes; + // number of bits per pixel uint16_t bitdepth; + // compression method uint32_t compression; + // raw size of the bitmap data uint32_t imagesize; + // horizontal resolution, in pixels per meter int32_t xresm; + // vertical resolution, in pixels per meter int32_t yresm; + // number of color indices in the color table uint32_t colorpalette; + // number of color indices that are considered important, if zero all are important uint32_t colorimportant; - uint32_t redmask; - uint32_t greenmask; - uint32_t bluemask; - uint32_t alphamask; + // red color per pixel bit mask + uint32_t redbitmask; + // green color per pixel bit mask + uint32_t greenbitmask; + // blue color per pixel bit mask + uint32_t bluebitmask; + // alpha color per pixel bit mask + uint32_t alphabitmask; + // color space uint32_t cstype; - int32_t endpoint[3][3];// ?? + // specifies the x, y, and z coordinates of the three colors that correspond to the red, green, and blue endpoints + int32_t endpoint[3][3]; /* this type is wrong */ + // Tone response curve for red. uint32_t redgamma; + // Tone response curve for green uint32_t greengamma; + // Tone response curve for blue uint32_t bluegamma; + // Rendering intent for bitmap uint32_t intent; + // offset in bytes from the beginning of the header to the start of profile data uint32_t profiledata; + // size of the embedded profile data uint32_t profilesize; + // value should be set to zero uint32_t reserved; } BmpInfoHeader5; -- GitLab From 312b910f9765df439370f74a0aa9670cdac08360 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 12 May 2023 23:45:21 -0400 Subject: [PATCH 24/64] using BmpFileHeader.infoHeaderSize --- src/image/bmp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 161f65f..c5cd029 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -88,13 +88,13 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("bmp.count: %zu\n", bmp.count); BmpFileHeader* fileheader = (BmpFileHeader*)bmp.data; - BmpCoreHeader* coreheader = (BmpCoreHeader*)(bmp.data+14); printf("fileheader.type: 0x%X\n", fileheader->type); printf("fileheader.size: %d\n", fileheader->size); printf("fileheader.res1: %d\n", fileheader->reserved1); printf("fileheader.res2: %d\n", fileheader->reserved2); printf("fileheader.stat: %d\n", fileheader->start); + printf("fileheader.ihds: %d\n", fileheader->infoHeaderSize); printf("\n"); /* check if file is bmp */ @@ -105,8 +105,8 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ } // info header - if(coreheader->size == 40){ - BmpInfoHeader* infoheader = (BmpInfoHeader*)coreheader; + if(fileheader->infoHeaderSize == 40){ + BmpInfoHeader* infoheader = (BmpInfoHeader*)(bmp.data+14); printf("infoheader.size : %u\n", infoheader->size); printf("infoheader.width : %d\n", infoheader->width); printf("infoheader.height : %d\n", infoheader->height); @@ -162,8 +162,8 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ } - if(coreheader->size == 56){ - BmpInfoHeader3* infoheader3 = (BmpInfoHeader3*)coreheader; + if(fileheader->infoHeaderSize == 56){ + BmpInfoHeader3* infoheader3 = (BmpInfoHeader3*)(bmp.data+14); printf("infoheader3.size : %u\n", infoheader3->size); printf("infoheader3.width : %d\n", infoheader3->width); printf("infoheader3.height : %d\n", infoheader3->height); @@ -244,6 +244,8 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.depth = 8; if(colormask.alpha) image.profile = LEXLIB_RGBA; + else + image.profile = LEXLIB_RGB; if(colormask.green == 5){ profile = LEXLIB_RGB555; colormax.r = 31.0f; -- GitLab From ddc1ed3960371b5b723ff02ceaf25866343f2b81 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sat, 13 May 2023 00:55:29 -0400 Subject: [PATCH 25/64] new LICENSE file --- LICENSE | 19 +++++++++++++++++++ LICENSE-UNLICENSE.txt | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 LICENSE create mode 100644 LICENSE-UNLICENSE.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d6cdc71 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +LexLib is dual licensed under the MIT and Apache 2.0 +some parts are released under the Unlicense (aka. Public Domain) + +for the full licenses check the LICENSE-*** files. + +> In short +You are free to use, modify, redistribute, sublicense for any purpose, commercial or non-commercial free of charge. +Copyright holders are not liable for any claim, damage or other. + +to not clobber source files with a awfully long license notice, short ones are used. + +> Short MIT and Apache 2.0 license notice +Copyright [year*] [Copyright Holder*] [] +licensed under the MIT or Apache 2.0 (at your option) + +> Short Unlicense license notice +[year] [writer] +This is free and unencumbered software released into the public domain. +free to copy, modify, publish, use, compile, sell, or distribute for any purpose, commercial or non-commercial. \ No newline at end of file diff --git a/LICENSE-UNLICENSE.txt b/LICENSE-UNLICENSE.txt new file mode 100644 index 0000000..3c577b0 --- /dev/null +++ b/LICENSE-UNLICENSE.txt @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to \ No newline at end of file -- GitLab From 67a0142b9b177e252ada9e75ca27c9d3c6a7cf3f Mon Sep 17 00:00:00 2001 From: alexevier Date: Sat, 13 May 2023 22:51:11 -0400 Subject: [PATCH 26/64] checks for bmp compression support --- include/lexlibinternal/bmp.h | 2 +- src/image/bmp.c | 61 ++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/include/lexlibinternal/bmp.h b/include/lexlibinternal/bmp.h index 7b238e2..59fb673 100644 --- a/include/lexlibinternal/bmp.h +++ b/include/lexlibinternal/bmp.h @@ -32,7 +32,7 @@ typedef struct BmpFileHeader { uint16_t reserved2; // offset in bytes where the bitmap data starts uint32_t start; - // used for reading the size of the info header; this element is not official in the spec + // used for reading the size of the info header; WARNING this element is not official in the spec uint32_t infoHeaderSize; } BmpFileHeader; #pragma pack(pop) diff --git a/src/image/bmp.c b/src/image/bmp.c index c5cd029..04d2374 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -19,7 +19,7 @@ > BMP structure File Header | contains information about the file itsef - Info Header | contains Info about the bitmap + Info Header | contains Info about the bitmap | 7 different versions Extra bit masks | defines pixel format | optional Color Table | defines colors used by the bitmap | semi-optional (not if bpp is less than 8) Gap1 | alignment | optional @@ -41,6 +41,18 @@ ◯ ◯ BmpInfoHeader4 | 0..32 bpp ◯ ◯ BmpInfoHeader5 | 0..32 bpp +> Compression + ● RGB | none | most common + ◯ RLE8 | RLE 8bit | only with 8-bit/pixel + ◯ RLE4 | RLE 4bit | only with 4-bit/pixel + ● BITFIELDS | RGB/A bit field masks + ◯ JPEG | ? + ◯ PNG | ? + ◯ ALPHABITFIELDS | RGBA bit field masks + ◯ CMYK | none + ◯ CMYKRLE8 | RLE-8 | only windows metafile + ◯ CMYKRLE4 | RLE-4 | only windows metafile + > Color Table usually the order is bgr0. 4-byte per entry for rgba format. @@ -55,6 +67,7 @@ ◯ 16bpp: 65536 colors | ? ● 24bpp: 16777216 colors | b,g,r. ● 32bpp: 4294967296 colors | a,b,g,r. + */ // get an individual bit @@ -99,12 +112,12 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ /* check if file is bmp */ if(fileheader->type != 0x4D42){ - printf("bmp: no\n"); image.depth = LEXLIB_INVALID_FILE_TYPE; - goto bmploadend; + free(bmp.data); + return image; } - // info header + /* info header */ if(fileheader->infoHeaderSize == 40){ BmpInfoHeader* infoheader = (BmpInfoHeader*)(bmp.data+14); printf("infoheader.size : %u\n", infoheader->size); @@ -119,6 +132,18 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("infoheader.clrpa : %u\n", infoheader->colorpalette); printf("infoheader.clrim : %u\n", infoheader->colorimportant); + // check if compression is supported + switch(infoheader->compression){ + case BMP_RGB: + break; + default: // unsuported + free(bmp.data); + lexlibImageDelete(&image); + image.depth = LEXLIB_UNSUPORTED; + return image; + break; + } + uint8_t profile = LEXLIB_RGB; switch(infoheader->bitdepth){ case 8: @@ -160,8 +185,11 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ imgoffset+=image.channels; } - } + free(bmp.data); + return image; + }/* ..info header.. */ + /* info header v3 */ if(fileheader->infoHeaderSize == 56){ BmpInfoHeader3* infoheader3 = (BmpInfoHeader3*)(bmp.data+14); printf("infoheader3.size : %u\n", infoheader3->size); @@ -181,6 +209,19 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("infoheader3.alphabitmask : 0x%.8X\n", infoheader3->alphabitmask); printf("\n"); + // check if compression is supported + switch(infoheader3->compression){ + case BMP_RGB: + case BMP_BITFIELDS: + break; + default: // unsuported + free(bmp.data); + lexlibImageDelete(&image); + image.depth = LEXLIB_UNSUPORTED; + return image; + break; + } + size_t bmpoffset = fileheader->start; size_t imgoffset = 0; uint64_t pixelcount = 0; @@ -341,13 +382,15 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ bmpoffset+=image.channels; imgoffset+=image.channels; } - } - - // LEXLIB_UNREACHABLE + + free(bmp.data); + return image; + }/* ..info header v3.. */ - bmploadend: + LEXLIB_UNREACHABLE if(bmp.data) free(bmp.data); + return image; } -- GitLab From 2b327b605067e797c0d036e4710eb24025e3822d Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 02:51:24 -0400 Subject: [PATCH 27/64] gnu attrib macro: LEXLIB_FALLTHROUGH --- include/lexlib/common.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/lexlib/common.h b/include/lexlib/common.h index 2927c87..686a525 100644 --- a/include/lexlib/common.h +++ b/include/lexlib/common.h @@ -21,9 +21,11 @@ and any changes may break api */ #ifdef __GNUC__ # define LEXLIB_INLINE static inline __attribute__((always_inline)) # define LEXLIB_DEPRECATED __attribute__((deprecated)) +# define LEXLIB_FALLTHROUGH __attribute__((fallthrough)) #else # define LEXLIB_INLINE static inline # define LEXLIB_DEPRECATED +# define LEXLIB_FALLTHROUGH #endif #ifdef __BYTE_ORDER__ -- GitLab From 97ff4bda7b0ec70daf5decdbe3f4b5e312b88435 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 03:02:15 -0400 Subject: [PATCH 28/64] lexlibPowu() --- CHANGELOG.md | 7 ++++--- documentation/math.html | 23 +++++++++++++++++++++++ include/lexlib/math.h | 9 +++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e9ed2..e1e3fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ## 1.5.0 (under development) -+ function lexlibColor16Blend(). -+ function lexlibImagePixelSet(). -+ function lexlibFileBytes(): ++ lexlibColor16Blend(). ++ lexlibImagePixelSet(). ++ lexlibFileBytes(): ++ lexlibPowu(); ## 1.4.0 + function lexlibStrCopy(). diff --git a/documentation/math.html b/documentation/math.html index 0dcf3ab..f7ef727 100644 --- a/documentation/math.html +++ b/documentation/math.html @@ -19,6 +19,7 @@ lexlibDegToRadf() lexlibRadToDeg() lexlibRadToDegf() + lexlibPowu() lexlibVec#New() lexlibVec#NewZero() lexlibVec#Clone() @@ -250,6 +251,28 @@

+

+


+

lexlibPowu()

+

+ calculates base to the power of exp. (baseexp) +

+

Function
+

    + unsigned int lexlibPowu(unsigned int base, unsigned int exp); +
+

+

Argument
+

unsigned int base : base. +
unsigned int exp : exponent. +
+

+

Return
+

    + unsigned int : result of baseexp. +
+

+


lexlibVec#New()

diff --git a/include/lexlib/math.h b/include/lexlib/math.h index a3c4c28..5318884 100644 --- a/include/lexlib/math.h +++ b/include/lexlib/math.h @@ -149,6 +149,15 @@ LEXLIB_INLINE unsigned int lexlibClampu(unsigned int num, unsigned int min, unsi return (num > max) ? max : (num < min) ? min : num; } +LEXLIB_INLINE unsigned int lexlibPowu(unsigned int base, unsigned int exp){ + unsigned int result = 1; + while(exp != 0){ + result *= base; + --exp; + } + return result; +} + // ..misc.. // -- GitLab From 9717a7e27749f7772e7117796f05ef92f99484ae Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 03:54:08 -0400 Subject: [PATCH 29/64] bmp info header bpp16 support --- src/image/bmp.c | 188 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 160 insertions(+), 28 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 04d2374..2e34bc2 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -42,16 +42,17 @@ ◯ ◯ BmpInfoHeader5 | 0..32 bpp > Compression - ● RGB | none | most common - ◯ RLE8 | RLE 8bit | only with 8-bit/pixel - ◯ RLE4 | RLE 4bit | only with 4-bit/pixel - ● BITFIELDS | RGB/A bit field masks - ◯ JPEG | ? - ◯ PNG | ? - ◯ ALPHABITFIELDS | RGBA bit field masks - ◯ CMYK | none - ◯ CMYKRLE8 | RLE-8 | only windows metafile - ◯ CMYKRLE4 | RLE-4 | only windows metafile + C I 2 3 4 5 + ◯ ● ◯ ◯ ◯ ◯ RGB | none | most common + ◯ ◯ ◯ ◯ ◯ ◯ RLE8 | RLE 8bit | only with 8-bit/pixel + ◯ ◯ ◯ ◯ ◯ ◯ RLE4 | RLE 4bit | only with 4-bit/pixel + ◯ ● ◯ ◯ ◯ ◯ BITFIELDS | RGB bit field masks + ◯ ◯ ◯ ◯ ◯ ◯ JPEG | ? + ◯ ◯ ◯ ◯ ◯ ◯ PNG | ? + ◯ ● ◯ ◯ ◯ ◯ ALPHABITFIELDS | RGBA bit field masks + ◯ ◯ ◯ ◯ ◯ ◯ CMYK | none + ◯ ◯ ◯ ◯ ◯ ◯ CMYKRLE8 | RLE-8 | only windows metafile + ◯ ◯ ◯ ◯ ◯ ◯ CMYKRLE4 | RLE-4 | only windows metafile > Color Table usually the order is bgr0. @@ -60,13 +61,14 @@ > pixel formats left-most pixel in the most-significant bit of the first byte. - ◯ 1bpp : 2 colors | table - ◯ 2bpp : 4 colors | table - ◯ 4bpp : 16 colors | table - ◯ 8bpp : 256 colors | table - ◯ 16bpp: 65536 colors | ? - ● 24bpp: 16777216 colors | b,g,r. - ● 32bpp: 4294967296 colors | a,b,g,r. + C I 2 3 4 5 + ◯ ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table + ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table + ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table + ◯ ◯ ◯ ◯ ◯ ◯ 8bpp : 256 colors | table + ◯ ● ◯ ◯ ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. + ◯ ● ◯ ◯ ◯ ◯ 24bpp: 16777216 colors | b,g,r. + ◯ ● ◯ ◯ ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. */ @@ -89,6 +91,9 @@ typedef struct ColorBitMask { uint8_t alphashift; } ColorBitMask; +#define LEXLIB_RGB_BITFIELDS 0x0A +#define LEXLIB_RGB_ALPHABITFIELDS 0x0B + // TODO probably needs big endian to little transforms on big endian machines LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; @@ -132,10 +137,68 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("infoheader.clrpa : %u\n", infoheader->colorpalette); printf("infoheader.clrim : %u\n", infoheader->colorimportant); - // check if compression is supported + ColorBitMask colorMask = {0}; + LexlibColorFlt colorMax = {0}; + uint8_t profile = 0; + uint64_t pixelcount = 0; + + // check for compression switch(infoheader->compression){ case BMP_RGB: break; + case BMP_ALPHABITFIELDS: + /* the alpha bitfield is added on top on the rgb bitfields */ + colorMask.alphabit = (*(uint32_t*)(bmp.data+66)); + for(uint8_t i = 0; i < 32; i++){ + colorMask.alpha += bitGet(colorMask.alphabit, i); + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.alphabit, i) == 0) + colorMask.alphashift++; + else + break; + } + colorMax.a = lexlibPowu(2, colorMask.alpha) - 1; + profile = LEXLIB_RGB_ALPHABITFIELDS; + LEXLIB_FALLTHROUGH; + case BMP_BITFIELDS: + /* rgb bitfields*/ + // get bit masks + colorMask.redbit = (*(uint32_t*)(bmp.data+54)); + colorMask.greenbit = (*(uint32_t*)(bmp.data+58)); + colorMask.bluebit = (*(uint32_t*)(bmp.data+62)); + // get R.G.B + for(uint8_t i = 0; i < 32; i++){ + colorMask.red += bitGet(colorMask.redbit, i); + colorMask.green += bitGet(colorMask.greenbit, i); + colorMask.blue += bitGet(colorMask.bluebit, i); + } + // get bit shifts + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.redbit, i) == 0) + colorMask.redshift++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.greenbit, i) == 0) + colorMask.greenshift++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.bluebit, i) == 0) + colorMask.blueshift++; + else + break; + } + // calculate max color value + colorMax.r = lexlibPowu(2, colorMask.red) - 1; + colorMax.g = lexlibPowu(2, colorMask.green) - 1; + colorMax.b = lexlibPowu(2, colorMask.blue) - 1; + if(!profile) + profile = LEXLIB_RGB_BITFIELDS; + break; default: // unsuported free(bmp.data); lexlibImageDelete(&image); @@ -144,29 +207,99 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ break; } - uint8_t profile = LEXLIB_RGB; + // check for color profile switch(infoheader->bitdepth){ case 8: - profile = LEXLIB_GRAY; - image.depth = 8; + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; break; + case 16: + if(infoheader->compression == BMP_RGB){ + profile = LEXLIB_RGB555; + image.profile = LEXLIB_RGB; + colorMax.r = 31.0f; + colorMax.g = 31.0f; + colorMax.b = 31.0f; + break; + } + if(infoheader->compression == BMP_BITFIELDS){ + image.depth = 8; + if(colorMask.alpha) + image.profile = LEXLIB_RGBA; + else + image.profile = LEXLIB_RGB; + break; + } + free(bmp.data); + image.depth = LEXLIB_INVALID_FILE_DATA; + return image; case 24: - profile = LEXLIB_RGB; image.depth = 8; + image.profile = LEXLIB_RGB; + if(!profile) + profile = LEXLIB_RGB; break; case 32: - profile = LEXLIB_RGBA; image.depth = 8; + image.profile = LEXLIB_RGBA; + if(!profile) + profile = LEXLIB_RGBA; break; default: - break; + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; } - image = lexlibImageNew(infoheader->width, infoheader->height, profile, image.depth); + printf("profile: %u\n", profile); + printf("pixelco: %zu\n", pixelcount); + printf("colormax.r: %f\n", colorMax.r); + printf("colormax.g: %f\n", colorMax.g); + printf("colormax.b: %f\n", colorMax.b); + printf("colormax.a: %f\n", colorMax.a); + printf("colormask.red: %u\n", colorMask.red); + printf("colormask.green: %u\n", colorMask.green); + printf("colormask.blue: %u\n", colorMask.blue); + printf("colormask.alpha: %u\n", colorMask.alpha); + printf("colormask.redshift : %u\n", colorMask.redshift); + printf("colormask.greenshift: %u\n", colorMask.greenshift); + printf("colormask.blueshift : %u\n", colorMask.blueshift); + printf("colormask.alphashift : %u\n", colorMask.alphashift); + printf("\n"); + + image = lexlibImageNew(infoheader->width, infoheader->height, image.profile, image.depth); + pixelcount = image.width * image.height; size_t bmpoffset = fileheader->start; size_t imgoffset = 0; - if(image.profile == LEXLIB_RGB) + if(profile == LEXLIB_RGB_BITFIELDS) + for(uint64_t i = 0; i < pixelcount; i++){ + uint16_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); + + image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.redbit) >> colorMask.redshift) / colorMax.r) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.greenbit) >> colorMask.greenshift) / colorMax.g) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bluebit) >> colorMask.blueshift) / colorMax.b) * 255.0f); + if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + + bmpoffset += 2; + imgoffset += image.channels; + } + + if(profile == LEXLIB_RGB_ALPHABITFIELDS) + for(uint64_t i = 0; i < pixelcount; i++){ + uint16_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); + + image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.redbit) >> colorMask.redshift) / colorMax.r) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.greenbit) >> colorMask.greenshift) / colorMax.g) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bluebit) >> colorMask.blueshift) / colorMax.b) * 255.0f); + image.data[imgoffset+3] = rintf(( ((bmppixel & colorMask.alpha) >> colorMask.alphashift) / colorMax.a) * 255.0f); + + bmpoffset += 2; + imgoffset += image.channels; + } + + if(profile == LEXLIB_RGB) while(imgoffset < infoheader->imagesize){ image.data[imgoffset+0] = bmp.data[bmpoffset+2]; image.data[imgoffset+1] = bmp.data[bmpoffset+1]; @@ -175,7 +308,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ imgoffset+=image.channels; } - if(image.profile == LEXLIB_RGBA) + if(profile == LEXLIB_RGBA) while(imgoffset < infoheader->imagesize){ image.data[imgoffset+0] = bmp.data[bmpoffset+3]; image.data[imgoffset+1] = bmp.data[bmpoffset+2]; @@ -360,7 +493,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ imgoffset += image.channels; } - if(profile == LEXLIB_RGB) for(uint64_t i = 0; i < pixelcount; i++){ image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; -- GitLab From ba2e8a0ee2ced118d604287d1e6d6c678a49f4e1 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 17:09:38 -0400 Subject: [PATCH 30/64] bmp header 1 and 2 refactor --- src/image/bmp.c | 431 ++++++++++++++++++++++++------------------------ 1 file changed, 213 insertions(+), 218 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 2e34bc2..406e878 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -43,13 +43,13 @@ > Compression C I 2 3 4 5 - ◯ ● ◯ ◯ ◯ ◯ RGB | none | most common + ◯ ● ◯ ● ◯ ◯ RGB | none | most common ◯ ◯ ◯ ◯ ◯ ◯ RLE8 | RLE 8bit | only with 8-bit/pixel ◯ ◯ ◯ ◯ ◯ ◯ RLE4 | RLE 4bit | only with 4-bit/pixel - ◯ ● ◯ ◯ ◯ ◯ BITFIELDS | RGB bit field masks + ◯ ● ◯ ● ◯ ◯ BITFIELDS | RGB bit field masks ◯ ◯ ◯ ◯ ◯ ◯ JPEG | ? ◯ ◯ ◯ ◯ ◯ ◯ PNG | ? - ◯ ● ◯ ◯ ◯ ◯ ALPHABITFIELDS | RGBA bit field masks + ◯ ● ◯ ● ◯ ◯ ALPHABITFIELDS | RGBA bit field masks ◯ ◯ ◯ ◯ ◯ ◯ CMYK | none ◯ ◯ ◯ ◯ ◯ ◯ CMYKRLE8 | RLE-8 | only windows metafile ◯ ◯ ◯ ◯ ◯ ◯ CMYKRLE4 | RLE-4 | only windows metafile @@ -66,34 +66,36 @@ ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 8bpp : 256 colors | table - ◯ ● ◯ ◯ ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. - ◯ ● ◯ ◯ ◯ ◯ 24bpp: 16777216 colors | b,g,r. - ◯ ● ◯ ◯ ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. + ◯ ● ◯ ● ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. + ◯ ● ◯ ● ◯ ◯ 24bpp: 16777216 colors | b,g,r. + ◯ ● ◯ ● ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. */ -// get an individual bit -LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index); - // to store bitmasks and shifts typedef struct ColorBitMask { - uint8_t red; - uint8_t green; - uint8_t blue; - uint8_t alpha; - uint32_t redbit; - uint32_t greenbit; - uint32_t bluebit; - uint32_t alphabit; - uint8_t redshift; - uint8_t greenshift; - uint8_t blueshift; - uint8_t alphashift; + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + uint32_t rb; + uint32_t gb; + uint32_t bb; + uint32_t ab; + uint8_t rs; + uint8_t gs; + uint8_t bs; + uint8_t as; } ColorBitMask; +/* Static Functions */ +LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index); +static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields); + #define LEXLIB_RGB_BITFIELDS 0x0A -#define LEXLIB_RGB_ALPHABITFIELDS 0x0B +#define LEXLIB_RGBA_BITFIELDS 0x0B +// TODO image creation error checks // TODO probably needs big endian to little transforms on big endian machines LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; @@ -140,64 +142,28 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ ColorBitMask colorMask = {0}; LexlibColorFlt colorMax = {0}; uint8_t profile = 0; - uint64_t pixelcount = 0; + uint8_t bmpPixelSize = 0; // check for compression switch(infoheader->compression){ case BMP_RGB: break; - case BMP_ALPHABITFIELDS: - /* the alpha bitfield is added on top on the rgb bitfields */ - colorMask.alphabit = (*(uint32_t*)(bmp.data+66)); - for(uint8_t i = 0; i < 32; i++){ - colorMask.alpha += bitGet(colorMask.alphabit, i); - } - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colorMask.alphabit, i) == 0) - colorMask.alphashift++; - else - break; - } - colorMax.a = lexlibPowu(2, colorMask.alpha) - 1; - profile = LEXLIB_RGB_ALPHABITFIELDS; - LEXLIB_FALLTHROUGH; case BMP_BITFIELDS: - /* rgb bitfields*/ - // get bit masks - colorMask.redbit = (*(uint32_t*)(bmp.data+54)); - colorMask.greenbit = (*(uint32_t*)(bmp.data+58)); - colorMask.bluebit = (*(uint32_t*)(bmp.data+62)); - // get R.G.B - for(uint8_t i = 0; i < 32; i++){ - colorMask.red += bitGet(colorMask.redbit, i); - colorMask.green += bitGet(colorMask.greenbit, i); - colorMask.blue += bitGet(colorMask.bluebit, i); - } - // get bit shifts - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colorMask.redbit, i) == 0) - colorMask.redshift++; - else - break; - } - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colorMask.greenbit, i) == 0) - colorMask.greenshift++; - else - break; - } - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colorMask.bluebit, i) == 0) - colorMask.blueshift++; - else - break; - } + colorMask = colorBitMaskGet((uint32_t*)(bmp.data+54), LEXLIB_RGB_BITFIELDS); + profile = LEXLIB_RGB_BITFIELDS; // calculate max color value - colorMax.r = lexlibPowu(2, colorMask.red) - 1; - colorMax.g = lexlibPowu(2, colorMask.green) - 1; - colorMax.b = lexlibPowu(2, colorMask.blue) - 1; - if(!profile) - profile = LEXLIB_RGB_BITFIELDS; + colorMax.r = lexlibPowu(2, colorMask.r) - 1; + colorMax.g = lexlibPowu(2, colorMask.g) - 1; + colorMax.b = lexlibPowu(2, colorMask.b) - 1; + break; + case BMP_ALPHABITFIELDS: + colorMask = colorBitMaskGet((uint32_t*)(bmp.data+54), LEXLIB_RGBA_BITFIELDS); + profile = LEXLIB_RGB_BITFIELDS; + // calculate max color value + colorMax.r = lexlibPowu(2, colorMask.r) - 1; + colorMax.g = lexlibPowu(2, colorMask.g) - 1; + colorMax.b = lexlibPowu(2, colorMask.b) - 1; + colorMax.a = lexlibPowu(2, colorMask.a); break; default: // unsuported free(bmp.data); @@ -215,8 +181,10 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ return image; break; case 16: + bmpPixelSize = 2; if(infoheader->compression == BMP_RGB){ profile = LEXLIB_RGB555; + image.depth = 8; image.profile = LEXLIB_RGB; colorMax.r = 31.0f; colorMax.g = 31.0f; @@ -225,10 +193,12 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ } if(infoheader->compression == BMP_BITFIELDS){ image.depth = 8; - if(colorMask.alpha) - image.profile = LEXLIB_RGBA; - else - image.profile = LEXLIB_RGB; + image.profile = (colorMask.a) ? LEXLIB_RGBA : LEXLIB_RGB; + break; + } + if(infoheader->compression == BMP_ALPHABITFIELDS){ + image.depth = 8; + image.profile = LEXLIB_RGBA; break; } free(bmp.data); @@ -239,12 +209,14 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.profile = LEXLIB_RGB; if(!profile) profile = LEXLIB_RGB; + bmpPixelSize = 3; break; case 32: image.depth = 8; image.profile = LEXLIB_RGBA; if(!profile) profile = LEXLIB_RGBA; + bmpPixelSize = 4; break; default: free(bmp.data); @@ -253,68 +225,72 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ } printf("profile: %u\n", profile); - printf("pixelco: %zu\n", pixelcount); printf("colormax.r: %f\n", colorMax.r); printf("colormax.g: %f\n", colorMax.g); printf("colormax.b: %f\n", colorMax.b); printf("colormax.a: %f\n", colorMax.a); - printf("colormask.red: %u\n", colorMask.red); - printf("colormask.green: %u\n", colorMask.green); - printf("colormask.blue: %u\n", colorMask.blue); - printf("colormask.alpha: %u\n", colorMask.alpha); - printf("colormask.redshift : %u\n", colorMask.redshift); - printf("colormask.greenshift: %u\n", colorMask.greenshift); - printf("colormask.blueshift : %u\n", colorMask.blueshift); - printf("colormask.alphashift : %u\n", colorMask.alphashift); + printf("colormask.r: %u\n", colorMask.r); + printf("colormask.g: %u\n", colorMask.g); + printf("colormask.b: %u\n", colorMask.b); + printf("colormask.a: %u\n", colorMask.a); + printf("colormask.rs: %u\n", colorMask.rs); + printf("colormask.gs: %u\n", colorMask.gs); + printf("colormask.bs: %u\n", colorMask.bs); + printf("colormask.as: %u\n", colorMask.as); printf("\n"); + // create image and set offsets image = lexlibImageNew(infoheader->width, infoheader->height, image.profile, image.depth); - pixelcount = image.width * image.height; + uint64_t pixelCount = image.width * image.height; size_t bmpoffset = fileheader->start; size_t imgoffset = 0; + // load pixel data from bmp to lexlib image if(profile == LEXLIB_RGB_BITFIELDS) - for(uint64_t i = 0; i < pixelcount; i++){ + for(uint64_t i = 0; i < pixelCount; i++){ uint16_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); - image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.redbit) >> colorMask.redshift) / colorMax.r) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.greenbit) >> colorMask.greenshift) / colorMax.g) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bluebit) >> colorMask.blueshift) / colorMax.b) * 255.0f); - if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> colorMask.rs) / colorMax.r) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> colorMask.gs) / colorMax.g) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> colorMask.bs) / colorMax.b) * 255.0f); + image.data[imgoffset+3] = (image.profile == LEXLIB_RGBA) ? rintf(( ((bmppixel & colorMask.ab) >> colorMask.as) / colorMax.a) * 256.0f) - 1 : 0xFF; - bmpoffset += 2; + bmpoffset += bmpPixelSize; imgoffset += image.channels; } - if(profile == LEXLIB_RGB_ALPHABITFIELDS) - for(uint64_t i = 0; i < pixelcount; i++){ - uint16_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); + if(profile == LEXLIB_RGB555) + for(uint64_t i = 0; i < pixelCount; i++){ + uint16_t bmppixel = *((uint16_t*)(bmp.data+bmpoffset)); - image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.redbit) >> colorMask.redshift) / colorMax.r) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.greenbit) >> colorMask.greenshift) / colorMax.g) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bluebit) >> colorMask.blueshift) / colorMax.b) * 255.0f); - image.data[imgoffset+3] = rintf(( ((bmppixel & colorMask.alpha) >> colorMask.alphashift) / colorMax.a) * 255.0f); + image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> 10) / 31.0f) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> 5) / 31.0f) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> 0) / 31.0f) * 255.0f); + if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } bmpoffset += 2; imgoffset += image.channels; } if(profile == LEXLIB_RGB) - while(imgoffset < infoheader->imagesize){ + for(uint64_t i = 0; i < pixelCount; i++){ image.data[imgoffset+0] = bmp.data[bmpoffset+2]; image.data[imgoffset+1] = bmp.data[bmpoffset+1]; image.data[imgoffset+2] = bmp.data[bmpoffset+0]; - bmpoffset+=image.channels; + if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + + bmpoffset+=3; imgoffset+=image.channels; } if(profile == LEXLIB_RGBA) - while(imgoffset < infoheader->imagesize){ + for(uint64_t i = 0; i < pixelCount; i++){ image.data[imgoffset+0] = bmp.data[bmpoffset+3]; image.data[imgoffset+1] = bmp.data[bmpoffset+2]; image.data[imgoffset+2] = bmp.data[bmpoffset+1]; image.data[imgoffset+3] = bmp.data[bmpoffset+0]; - bmpoffset+=image.channels; + + bmpoffset+=4; imgoffset+=image.channels; } @@ -342,10 +318,24 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("infoheader3.alphabitmask : 0x%.8X\n", infoheader3->alphabitmask); printf("\n"); - // check if compression is supported + ColorBitMask colorMask = {0}; + LexlibColorFlt colorMax = {0}; + uint8_t profile = 0; + uint8_t bmpPixelSize = 0; + + // check for compression switch(infoheader3->compression){ case BMP_RGB: + break; case BMP_BITFIELDS: + case BMP_ALPHABITFIELDS: + colorMask = colorBitMaskGet((uint32_t*)(bmp.data+54), LEXLIB_RGBA_BITFIELDS); + profile = LEXLIB_RGB_BITFIELDS; + // calculate max color value + colorMax.r = lexlibPowu(2, colorMask.r) - 1; + colorMax.g = lexlibPowu(2, colorMask.g) - 1; + colorMax.b = lexlibPowu(2, colorMask.b) - 1; + colorMax.a = lexlibPowu(2, colorMask.a); break; default: // unsuported free(bmp.data); @@ -355,120 +345,57 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ break; } - size_t bmpoffset = fileheader->start; - size_t imgoffset = 0; - uint64_t pixelcount = 0; - uint8_t profile = 0; - ColorBitMask colormask = { - .red = 0, - .green = 0, - .blue = 0, - .alpha = 0, - .redbit = infoheader3->redbitmask, - .greenbit = infoheader3->greenbitmask, - .bluebit = infoheader3->bluebitmask, - .alphabit = infoheader3->alphabitmask, - .redshift = 0, - .greenshift = 0, - .blueshift = 0, - .alphashift = 0, - }; - LexlibColorFlt colormax = { - .r = 255.0f, - .g = 255.0f, - .b = 255.0f, - .a = 255.0f, - }; - - // get R.G.B.A - for(uint8_t i = 0; i < 32; i++){ - colormask.red += bitGet(colormask.redbit, i); - colormask.green += bitGet(colormask.greenbit, i); - colormask.blue += bitGet(colormask.bluebit, i); - colormask.alpha += bitGet(colormask.alphabit, i); - } - - // get bit shifts - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colormask.redbit, i) == 0) - colormask.redshift++; - else - break; - } - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colormask.greenbit, i) == 0) - colormask.greenshift++; - else - break; - } - for(uint8_t i = 0; i < 32; i++){ - if(bitGet(colormask.bluebit, i) == 0) - colormask.blueshift++; - else - break; - } - // set the color profile if(infoheader3->bitdepth == 8){ - image.profile = LEXLIB_GRAY; - image.depth = 8; - profile = LEXLIB_GRAY; + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; } if(infoheader3->bitdepth == 16){ image.depth = 8; - if(colormask.alpha) - image.profile = LEXLIB_RGBA; - else - image.profile = LEXLIB_RGB; - if(colormask.green == 5){ + image.profile = (colorMask.a) ? LEXLIB_RGBA : LEXLIB_RGB; + if(!profile) profile = LEXLIB_RGB555; - colormax.r = 31.0f; - colormax.g = 31.0f; - colormax.b = 31.0f; - colormax.a = 0.0f; - } - if(colormask.green == 6){ - profile = LEXLIB_RGB565; - colormax.r = 31.0f; - colormax.g = 63.0f; - colormax.b = 31.0f; - colormax.a = 0.0f; - } + bmpPixelSize = 2; } if(infoheader3->bitdepth == 24){ image.depth = 8; image.profile = LEXLIB_RGB; - profile = LEXLIB_RGB; + if(!profile) + profile = LEXLIB_RGB; + bmpPixelSize = 3; } if(infoheader3->bitdepth == 32){ image.depth = 8; image.profile = LEXLIB_RGBA; - profile = LEXLIB_RGBA; + if(!profile) + profile = LEXLIB_RGBA; + bmpPixelSize = 4; } + // create image and set offsets image = lexlibImageNew(infoheader3->width, infoheader3->height, image.profile, image.depth); - pixelcount = image.width * image.height; - - // check for the correct bmp profile - if(!profile) - profile = image.profile; - if(profile == LEXLIB_RGBA){ - if(infoheader3->alphabitmask){ - profile = LEXLIB_RGBA; - } else { - profile = LEXLIB_RGB; - } - } + uint64_t pixelCount = image.width * image.height; + size_t bmpoffset = fileheader->start; + size_t imgoffset = 0; printf("profile: %u\n", profile); - printf("pixelco: %zu\n", pixelcount); - printf("colormask.red: %u\n", colormask.red); - printf("colormask.green: %u\n", colormask.green); - printf("colormask.blue: %u\n", colormask.blue); - printf("colormask.alpha: %u\n", colormask.alpha); - printf("colormask.redshift : %u\n", colormask.redshift); - printf("colormask.greenshift: %u\n", colormask.greenshift); - printf("colormask.blueshift : %u\n", colormask.blueshift); + printf("colormax.r: %f\n", colorMax.r); + printf("colormax.g: %f\n", colorMax.g); + printf("colormax.b: %f\n", colorMax.b); + printf("colormax.a: %f\n", colorMax.a); + printf("colormask.r: %u\n", colorMask.r); + printf("colormask.g: %u\n", colorMask.g); + printf("colormask.b: %u\n", colorMask.b); + printf("colormask.a: %u\n", colorMask.a); + printf("colormask.rb: %u\n", colorMask.rb); + printf("colormask.gb: %u\n", colorMask.gb); + printf("colormask.bb: %u\n", colorMask.bb); + printf("colormask.ab: %u\n", colorMask.ab); + printf("colormask.rs: %u\n", colorMask.rs); + printf("colormask.gs: %u\n", colorMask.gs); + printf("colormask.bs: %u\n", colorMask.bs); + printf("colormask.as: %u\n", colorMask.as); printf("\n"); printf("image.datasize: %zu\n", image.datasize); @@ -480,38 +407,52 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ printf("image.profile : %u\n", image.profile); printf("\n"); - if(profile == LEXLIB_RGB555 || profile == LEXLIB_RGB565) - for(uint64_t i = 0; i < pixelcount; i++){ + // load pixel data from bmp to lexlib image + if(profile == LEXLIB_RGB_BITFIELDS) + for(uint64_t i = 0; i < pixelCount; i++){ + uint32_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); + + image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> colorMask.rs) / colorMax.r) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> colorMask.gs) / colorMax.g) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> colorMask.bs) / colorMax.b) * 255.0f); + image.data[imgoffset+3] = (image.profile == LEXLIB_RGBA) ? rintf(( ((bmppixel & colorMask.ab) >> colorMask.as) / colorMax.a) * 256.0f) - 1 : 0xFF; + + bmpoffset += bmpPixelSize; + imgoffset += image.channels; + } + + if(profile == LEXLIB_RGB555) + for(uint64_t i = 0; i < pixelCount; i++){ uint16_t bmppixel = *((uint16_t*)(bmp.data+bmpoffset)); - image.data[imgoffset+0] = rintf(( ((bmppixel & colormask.redbit) >> colormask.redshift) / colormax.r) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colormask.greenbit) >> colormask.greenshift) / colormax.g) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colormask.bluebit) >> colormask.blueshift) / colormax.b) * 255.0f); + image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> 10) / 31.0f) * 255.0f); + image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> 5) / 31.0f) * 255.0f); + image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> 0) / 31.0f) * 255.0f); if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } bmpoffset += 2; imgoffset += image.channels; } - + if(profile == LEXLIB_RGB) - for(uint64_t i = 0; i < pixelcount; i++){ - image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; - image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; - image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); + for(uint64_t i = 0; i < pixelCount; i++){ + image.data[imgoffset+0] = bmp.data[bmpoffset+2]; + image.data[imgoffset+1] = bmp.data[bmpoffset+1]; + image.data[imgoffset+2] = bmp.data[bmpoffset+0]; if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } - bmpoffset += image.channels; - imgoffset += image.channels; + bmpoffset+=3; + imgoffset+=image.channels; } if(profile == LEXLIB_RGBA) - while(imgoffset < infoheader3->imagesize){ - image.data[imgoffset+0] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->redbitmask) >> 16; - image.data[imgoffset+1] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->greenbitmask) >> 8; - image.data[imgoffset+2] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->bluebitmask); - image.data[imgoffset+3] = (*((uint32_t*)(bmp.data+bmpoffset)) & infoheader3->alphabitmask) >> 24; + for(uint64_t i = 0; i < pixelCount; i++){ + image.data[imgoffset+0] = bmp.data[bmpoffset+3]; + image.data[imgoffset+1] = bmp.data[bmpoffset+2]; + image.data[imgoffset+2] = bmp.data[bmpoffset+1]; + image.data[imgoffset+3] = bmp.data[bmpoffset+0]; - bmpoffset+=image.channels; + bmpoffset+=4; imgoffset+=image.channels; } @@ -527,8 +468,62 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ } +/* Static Functions */ #define UNSIGNED_BIT_WIDTH (CHAR_BIT * sizeof(unsigned)) LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index){ index %= UNSIGNED_BIT_WIDTH; return (x >> index) & 1; } + +static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields){ + ColorBitMask colorMask = {0}; + + // get bit masks + if(bitfields == LEXLIB_RGB_BITFIELDS){ + colorMask.rb = data[0]; + colorMask.gb = data[1]; + colorMask.bb = data[2]; + } + if(bitfields == LEXLIB_RGBA_BITFIELDS){ + colorMask.rb = data[0]; + colorMask.gb = data[1]; + colorMask.bb = data[2]; + colorMask.ab = data[3]; + } + + // get R.G.B.A + for(uint8_t i = 0; i < 32; i++){ + colorMask.r += bitGet(colorMask.rb, i); + colorMask.g += bitGet(colorMask.gb, i); + colorMask.b += bitGet(colorMask.bb, i); + colorMask.a += bitGet(colorMask.ab, i); + } + + // get bit shifts + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.rb, i) == 0) + colorMask.rs++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.gb, i) == 0) + colorMask.gs++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.bb, i) == 0) + colorMask.bs++; + else + break; + } + for(uint8_t i = 0; i < 32; i++){ + if(bitGet(colorMask.ab, i) == 0) + colorMask.as++; + else + break; + } + + return colorMask; +} \ No newline at end of file -- GitLab From d3d23bca8773a7c40cc5e46129c264b98f09bac5 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 17:51:48 -0400 Subject: [PATCH 31/64] bmp 8bpp table support --- src/image/bmp.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 406e878..58c72d4 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -65,7 +65,7 @@ ◯ ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table - ◯ ◯ ◯ ◯ ◯ ◯ 8bpp : 256 colors | table + ◯ ● ◯ ◯ ◯ ◯ 8bpp : 256 colors | table ◯ ● ◯ ● ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. ◯ ● ◯ ● ◯ ◯ 24bpp: 16777216 colors | b,g,r. ◯ ● ◯ ● ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. @@ -88,12 +88,19 @@ typedef struct ColorBitMask { uint8_t as; } ColorBitMask; +typedef struct ColorRGB { + uint8_t r; + uint8_t g; + uint8_t b; +} ColorRGB; + /* Static Functions */ LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index); static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields); #define LEXLIB_RGB_BITFIELDS 0x0A #define LEXLIB_RGBA_BITFIELDS 0x0B +#define LEXLIB_RGB_PALETTE 0x0C // TODO image creation error checks // TODO probably needs big endian to little transforms on big endian machines @@ -176,6 +183,26 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ // check for color profile switch(infoheader->bitdepth){ case 8: + bmpPixelSize = 1; + image.depth = 8; + image.profile = LEXLIB_RGB; + if(infoheader->colorpalette){ + profile = LEXLIB_RGB_PALETTE; + if(infoheader->compression){ + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; + } + break; + } + if(infoheader->compression == BMP_BITFIELDS){ + image.profile = (colorMask.a) ? LEXLIB_RGBA : LEXLIB_RGB; + break; + } + if(infoheader->compression == BMP_ALPHABITFIELDS){ + image.profile = LEXLIB_RGBA; + break; + } free(bmp.data); image.depth = LEXLIB_UNSUPORTED; return image; @@ -294,6 +321,19 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ imgoffset+=image.channels; } + if(profile == LEXLIB_RGB_PALETTE){ + LexlibColor8* palette = (LexlibColor8*)(bmp.data+54); + for(uint64_t i = 0; i < pixelCount; i++){ + + image.data[imgoffset+0] = palette[bmp.data[bmpoffset]].b; + image.data[imgoffset+1] = palette[bmp.data[bmpoffset]].g; + image.data[imgoffset+2] = palette[bmp.data[bmpoffset]].r; + if(image.profile == LEXLIB_RGBA) image.data[imgoffset+4] = palette[bmp.data[bmpoffset]].a; + + bmpoffset += bmpPixelSize; + imgoffset += image.channels; + }} + free(bmp.data); return image; }/* ..info header.. */ -- GitLab From 13116a27739ba5f286247589adc0909f07121655 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 17:54:30 -0400 Subject: [PATCH 32/64] bmp checks at image creation --- src/image/bmp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 58c72d4..0e597ec 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -102,7 +102,6 @@ static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields); #define LEXLIB_RGBA_BITFIELDS 0x0B #define LEXLIB_RGB_PALETTE 0x0C -// TODO image creation error checks // TODO probably needs big endian to little transforms on big endian machines LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; @@ -268,6 +267,10 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ // create image and set offsets image = lexlibImageNew(infoheader->width, infoheader->height, image.profile, image.depth); + if(!image.data){ + free(bmp.data); + return image; + } uint64_t pixelCount = image.width * image.height; size_t bmpoffset = fileheader->start; size_t imgoffset = 0; @@ -415,6 +418,10 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ // create image and set offsets image = lexlibImageNew(infoheader3->width, infoheader3->height, image.profile, image.depth); + if(!image.data){ + free(bmp.data); + return image; + } uint64_t pixelCount = image.width * image.height; size_t bmpoffset = fileheader->start; size_t imgoffset = 0; -- GitLab From d13db3f1e5a8ff6188139aba91128e22ba971a35 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 17:56:28 -0400 Subject: [PATCH 33/64] removed printf's from imageLoadBmp --- src/image/bmp.c | 84 +------------------------------------------------ 1 file changed, 1 insertion(+), 83 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 0e597ec..1ad68ab 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -4,15 +4,13 @@ #include #include #include +#include #include #include #include #include #include -#include -#include - /* > BMP data is stored as little endian. @@ -107,22 +105,12 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; LexlibBytes bmp = lexlibFileBytes(filename); if(!bmp.data){ - printf("bmp: no data.\n"); image.depth = bmp.count; return image; } - printf("bmp.count: %zu\n", bmp.count); BmpFileHeader* fileheader = (BmpFileHeader*)bmp.data; - printf("fileheader.type: 0x%X\n", fileheader->type); - printf("fileheader.size: %d\n", fileheader->size); - printf("fileheader.res1: %d\n", fileheader->reserved1); - printf("fileheader.res2: %d\n", fileheader->reserved2); - printf("fileheader.stat: %d\n", fileheader->start); - printf("fileheader.ihds: %d\n", fileheader->infoHeaderSize); - printf("\n"); - /* check if file is bmp */ if(fileheader->type != 0x4D42){ image.depth = LEXLIB_INVALID_FILE_TYPE; @@ -133,17 +121,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ /* info header */ if(fileheader->infoHeaderSize == 40){ BmpInfoHeader* infoheader = (BmpInfoHeader*)(bmp.data+14); - printf("infoheader.size : %u\n", infoheader->size); - printf("infoheader.width : %d\n", infoheader->width); - printf("infoheader.height : %d\n", infoheader->height); - printf("infoheader.planes : %u\n", infoheader->planes); - printf("infoheader.bitdep : %u\n", infoheader->bitdepth); - printf("infoheader.compre : %u\n", infoheader->compression); - printf("infoheader.imgsiz : %u\n", infoheader->imagesize); - printf("infoheader.xresm : %u\n", infoheader->xresm); - printf("infoheader.yresm : %u\n", infoheader->yresm); - printf("infoheader.clrpa : %u\n", infoheader->colorpalette); - printf("infoheader.clrim : %u\n", infoheader->colorimportant); ColorBitMask colorMask = {0}; LexlibColorFlt colorMax = {0}; @@ -250,21 +227,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ return image; } - printf("profile: %u\n", profile); - printf("colormax.r: %f\n", colorMax.r); - printf("colormax.g: %f\n", colorMax.g); - printf("colormax.b: %f\n", colorMax.b); - printf("colormax.a: %f\n", colorMax.a); - printf("colormask.r: %u\n", colorMask.r); - printf("colormask.g: %u\n", colorMask.g); - printf("colormask.b: %u\n", colorMask.b); - printf("colormask.a: %u\n", colorMask.a); - printf("colormask.rs: %u\n", colorMask.rs); - printf("colormask.gs: %u\n", colorMask.gs); - printf("colormask.bs: %u\n", colorMask.bs); - printf("colormask.as: %u\n", colorMask.as); - printf("\n"); - // create image and set offsets image = lexlibImageNew(infoheader->width, infoheader->height, image.profile, image.depth); if(!image.data){ @@ -344,22 +306,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ /* info header v3 */ if(fileheader->infoHeaderSize == 56){ BmpInfoHeader3* infoheader3 = (BmpInfoHeader3*)(bmp.data+14); - printf("infoheader3.size : %u\n", infoheader3->size); - printf("infoheader3.width : %d\n", infoheader3->width); - printf("infoheader3.height : %d\n", infoheader3->height); - printf("infoheader3.planes : %u\n", infoheader3->planes); - printf("infoheader3.bitdep : %u\n", infoheader3->bitdepth); - printf("infoheader3.compre : %u\n", infoheader3->compression); - printf("infoheader3.imgsiz : %u\n", infoheader3->imagesize); - printf("infoheader3.xresm : %u\n", infoheader3->xresm); - printf("infoheader3.yresm : %u\n", infoheader3->yresm); - printf("infoheader3.clrpa : %u\n", infoheader3->colorpalette); - printf("infoheader3.clrim : %u\n", infoheader3->colorimportant); - printf("infoheader3.redbitmask : 0x%.8X\n", infoheader3->redbitmask); - printf("infoheader3.greenbitmask : 0x%.8X\n", infoheader3->greenbitmask); - printf("infoheader3.bluebitmask : 0x%.8X\n", infoheader3->bluebitmask); - printf("infoheader3.alphabitmask : 0x%.8X\n", infoheader3->alphabitmask); - printf("\n"); ColorBitMask colorMask = {0}; LexlibColorFlt colorMax = {0}; @@ -426,34 +372,6 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ size_t bmpoffset = fileheader->start; size_t imgoffset = 0; - printf("profile: %u\n", profile); - printf("colormax.r: %f\n", colorMax.r); - printf("colormax.g: %f\n", colorMax.g); - printf("colormax.b: %f\n", colorMax.b); - printf("colormax.a: %f\n", colorMax.a); - printf("colormask.r: %u\n", colorMask.r); - printf("colormask.g: %u\n", colorMask.g); - printf("colormask.b: %u\n", colorMask.b); - printf("colormask.a: %u\n", colorMask.a); - printf("colormask.rb: %u\n", colorMask.rb); - printf("colormask.gb: %u\n", colorMask.gb); - printf("colormask.bb: %u\n", colorMask.bb); - printf("colormask.ab: %u\n", colorMask.ab); - printf("colormask.rs: %u\n", colorMask.rs); - printf("colormask.gs: %u\n", colorMask.gs); - printf("colormask.bs: %u\n", colorMask.bs); - printf("colormask.as: %u\n", colorMask.as); - printf("\n"); - - printf("image.datasize: %zu\n", image.datasize); - printf("image.width : %u\n", image.width); - printf("image.height : %u\n", image.height); - printf("image.channels: %u\n", image.channels); - printf("image.depth : %u\n", image.depth); - printf("image.bpp : %u\n", image.bpp); - printf("image.profile : %u\n", image.profile); - printf("\n"); - // load pixel data from bmp to lexlib image if(profile == LEXLIB_RGB_BITFIELDS) for(uint64_t i = 0; i < pixelCount; i++){ -- GitLab From 18f7a96055d13cb513812316266fd6c00ad223c4 Mon Sep 17 00:00:00 2001 From: alexevier Date: Sun, 14 May 2023 23:45:01 -0400 Subject: [PATCH 34/64] moved image resources to resources/image --- resources/{ => image}/macintosh.bmp | Bin resources/{ => image}/macintosh_8bpp.bmp | Bin resources/{ => image}/macintosh_alpha70.bmp | Bin resources/{ => image}/pixelDucks.png | Bin resources/{ => image}/plasma16.bmp | Bin resources/{ => image}/plasma24.bmp | Bin resources/{ => image}/plasma32.bmp | Bin resources/image/puro.bmp | Bin 0 -> 17154 bytes test/imagePng.c | 6 +++--- 9 files changed, 3 insertions(+), 3 deletions(-) rename resources/{ => image}/macintosh.bmp (100%) rename resources/{ => image}/macintosh_8bpp.bmp (100%) rename resources/{ => image}/macintosh_alpha70.bmp (100%) rename resources/{ => image}/pixelDucks.png (100%) rename resources/{ => image}/plasma16.bmp (100%) rename resources/{ => image}/plasma24.bmp (100%) rename resources/{ => image}/plasma32.bmp (100%) create mode 100644 resources/image/puro.bmp diff --git a/resources/macintosh.bmp b/resources/image/macintosh.bmp similarity index 100% rename from resources/macintosh.bmp rename to resources/image/macintosh.bmp diff --git a/resources/macintosh_8bpp.bmp b/resources/image/macintosh_8bpp.bmp similarity index 100% rename from resources/macintosh_8bpp.bmp rename to resources/image/macintosh_8bpp.bmp diff --git a/resources/macintosh_alpha70.bmp b/resources/image/macintosh_alpha70.bmp similarity index 100% rename from resources/macintosh_alpha70.bmp rename to resources/image/macintosh_alpha70.bmp diff --git a/resources/pixelDucks.png b/resources/image/pixelDucks.png similarity index 100% rename from resources/pixelDucks.png rename to resources/image/pixelDucks.png diff --git a/resources/plasma16.bmp b/resources/image/plasma16.bmp similarity index 100% rename from resources/plasma16.bmp rename to resources/image/plasma16.bmp diff --git a/resources/plasma24.bmp b/resources/image/plasma24.bmp similarity index 100% rename from resources/plasma24.bmp rename to resources/image/plasma24.bmp diff --git a/resources/plasma32.bmp b/resources/image/plasma32.bmp similarity index 100% rename from resources/plasma32.bmp rename to resources/image/plasma32.bmp diff --git a/resources/image/puro.bmp b/resources/image/puro.bmp new file mode 100644 index 0000000000000000000000000000000000000000..07317298cb2c4fbfbb3950a426887cf35fca0161 GIT binary patch literal 17154 zcmZ?rWpZW!12YB&1`P%V25%^4WRL)hpK)SfP}YNBumA%CgNaE$@yKFu0OB2NV$whA zN6g?F9MNcj=`cJZqw$CtIHU16I748Frd9lbiI)6E{fHJICMNy(18|VYqB(ms9?=4X zp7DrgHAyK1P4A$MM>Kbi`VlPz(86al9?=41G(Vyx7z(+V!HXQ;eTG8jr|92Vvt6F)GSJtiwf;WYK6mVg@5g0Z55rOskPa zNHTde9+86%!X_yIDNziu4i`z1$pqt(B-5y<7?2;e zD@Kz=i#BILjvKsK7I(6Tm~EE*Rx9?=vGpudoOPT3DcO zS{XcP6)nx6IU7G0O)nZ3&5vkYG{rPct7vxN_Y|5enqD+6ny1jXXo^SuNMU|N3s1C| z!p}w1i^fHZI5aMr;?a0S3lM_Qim4ut2qp1|W-NvAh-MlU9Y#f?M*T=IKaR#DT6#kZ z5d2&;y=YwgdeLO5n^w^bryCc|7x=kot|B$9qG_ia7r&>_WYJtT>PNHy89**tOyTFE JxpRcXBLFk8cc%aV literal 0 HcmV?d00001 diff --git a/test/imagePng.c b/test/imagePng.c index c16ae36..4604d24 100644 --- a/test/imagePng.c +++ b/test/imagePng.c @@ -9,9 +9,9 @@ void testImagePng(void){ #ifdef TEST_PNG testStart("png image"); - remove("resources/pixelDucksOut.png"); + remove("resources/out/pixelDucks.png"); - LexlibImage image = lexlibImageLoadPng("resources/pixelDucks.png"); + LexlibImage image = lexlibImageLoadPng("resources/image/pixelDucks.png"); if(!image.data){ switch(image.depth){ case LEXLIB_CANT_OPEN: @@ -43,7 +43,7 @@ void testImagePng(void){ return; } - switch(lexlibImageSave(&image, "resources/pixelDucksOut.png")){ + switch(lexlibImageSave(&image, "resources/out/pixelDucks.png")){ case 0: break; case LEXLIB_INVALID_VALUE: -- GitLab From 9961471f8a3b9d6b3ac00afaf1175e8b226d0198 Mon Sep 17 00:00:00 2001 From: alexevier Date: Mon, 15 May 2023 00:07:14 -0400 Subject: [PATCH 35/64] placed bmp in its own test file --- CMakeLists.txt | 1 + test/imageBmp.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ test/test.c | 40 +----------------- test/test.h | 1 + 4 files changed, 113 insertions(+), 39 deletions(-) create mode 100644 test/imageBmp.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a5af99d..ce0dec1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(test test/path.c test/linkedList.c test/strPath.c + test/imageBmp.c test/imagePng.c test/colorblend.c test/colorgray.c diff --git a/test/imageBmp.c b/test/imageBmp.c new file mode 100644 index 0000000..df15286 --- /dev/null +++ b/test/imageBmp.c @@ -0,0 +1,110 @@ +// Copyright 2023 alexevier +// licensed under the MIT or Apache 2.0 (at your option) + +#include +#include +#include + +void testImageBmp(void){ + + {testStart("bmp plasma16"); + LexlibImage image = lexlibImageLoadBmp("resources/image/plasma16.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/plasma16.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/plasma16.png"); + testEnd(1, NULL);} + + {testStart("bmp plasma24"); + LexlibImage image = lexlibImageLoadBmp("resources/image/plasma24.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/plasma24.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/plasma24.png"); + testEnd(1, NULL);} + + {testStart("bmp plasma32"); + LexlibImage image = lexlibImageLoadBmp("resources/image/plasma32.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/plasma32.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/plasma32.png"); + testEnd(1, NULL);} + + {testStart("bmp puro"); + LexlibImage image = lexlibImageLoadBmp("resources/image/puro.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/puro.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/puro.png"); + testEnd(1, NULL);} + +} \ No newline at end of file diff --git a/test/test.c b/test/test.c index 25a1010..34772d0 100644 --- a/test/test.c +++ b/test/test.c @@ -26,6 +26,7 @@ int main(void){ testLinkedList(); testStrPath(); testImagePng(); + testImageBmp(); testColorBlend(); testColorGray(); // testColorFlt(); @@ -41,45 +42,6 @@ int main(void){ return 1; } printf("all tests succeed.\n"); - - {// temp bmp test - mkdir("resources/out", 0777); - printf("\n[plasma16.bmp]\n"); - LexlibImage image16 = lexlibImageLoadBmp("resources/plasma16.bmp"); - if(image16.channels == 0) printf("bmp16 ewwor :(\n"); - lexlibImageFlip(&image16, LEXLIB_FLIP_Y); - lexlibImageSavePng(&image16, "resources/out/plasma16.png"); - lexlibImageDelete(&image16); - - printf("\n[plasma24.bmp]\n"); - LexlibImage image24 = lexlibImageLoadBmp("resources/plasma24.bmp"); - if(image24.channels == 0) printf("bmp24 ewwor :(\n"); - lexlibImageFlip(&image24, LEXLIB_FLIP_Y); - lexlibImageSavePng(&image24, "resources/out/plasma24.png"); - lexlibImageDelete(&image24); - - printf("\n[plasma32.bmp]\n"); - LexlibImage image32 = lexlibImageLoadBmp("resources/plasma32.bmp"); - if(image32.channels == 0) printf("bmp32 ewwor :(\n"); - lexlibImageFlip(&image32, LEXLIB_FLIP_Y); - lexlibImageSavePng(&image32, "resources/out/plasma32.png"); - lexlibImageDelete(&image32); - - printf("\n[macintosh_a70.bmp]\n"); - image32 = lexlibImageLoadBmp("resources/macintosh_alpha70.bmp"); - if(image32.channels == 0) printf("mac_a32 ewwor :(\n"); - lexlibImageFlip(&image32, LEXLIB_FLIP_Y); - lexlibImageSavePng(&image32, "resources/out/macintosh_alpha70.png"); - lexlibImageDelete(&image32); - - printf("\n[macintosh_8bpp.bmp]\n"); - LexlibImage image8 = lexlibImageLoadBmp("resources/macintosh_8bpp.bmp"); - if(image8.channels == 0) printf("mac_bpp8 ewwor :(\n"); - lexlibImageFlip(&image8, LEXLIB_FLIP_Y); - lexlibImageSavePng(&image8, "resources/out/macintosh_8bpp.png"); - lexlibImageDelete(&image8); - } - // printf("s: %zu\n",sizeof(LexlibImage)); return 0; } diff --git a/test/test.h b/test/test.h index 7b915ff..db188d7 100644 --- a/test/test.h +++ b/test/test.h @@ -23,6 +23,7 @@ void testCompatCglm(void); void testPath(void); void testLinkedList(void); void testStrPath(void); +void testImageBmp(void); void testImagePng(void); void testColorBlend(void); void testColorGray(void); -- GitLab From e736bd35f6c6d20d26b64a74b850ef2bff2c1372 Mon Sep 17 00:00:00 2001 From: alexevier Date: Mon, 15 May 2023 04:14:14 -0400 Subject: [PATCH 36/64] rewrote the bmp loader --- include/lexlibinternal/bmp.h | 22 +- src/image/bmp.c | 471 +++++++++++++++-------------------- test/imageBmp.c | 50 ++++ 3 files changed, 265 insertions(+), 278 deletions(-) diff --git a/include/lexlibinternal/bmp.h b/include/lexlibinternal/bmp.h index 59fb673..6f744ca 100644 --- a/include/lexlibinternal/bmp.h +++ b/include/lexlibinternal/bmp.h @@ -31,9 +31,7 @@ typedef struct BmpFileHeader { uint16_t reserved1; uint16_t reserved2; // offset in bytes where the bitmap data starts - uint32_t start; - // used for reading the size of the info header; WARNING this element is not official in the spec - uint32_t infoHeaderSize; + uint32_t offset; } BmpFileHeader; #pragma pack(pop) @@ -48,7 +46,7 @@ typedef struct BmpCoreHeader { // number of color planes (must be 1) uint16_t planes; // number of bits per pixel - uint16_t bitdepth; + uint16_t bitdepth; } BmpCoreHeader; // windows nt, 3.1x or later @@ -72,9 +70,9 @@ typedef struct BmpInfoHeader { // vertical resolution, in pixels per meter int32_t yresm; // number of color indices in the color table - uint32_t colorpalette; + uint32_t colorPalette; // number of color indices that are considered important, if zero all are important - uint32_t colorimportant; + uint32_t colorImportant; } BmpInfoHeader; // header 2 is not documented @@ -100,9 +98,9 @@ typedef struct BmpInfoHeader3 { // vertical resolution, in pixels per meter int32_t yresm; // number of color indices in the color table - uint32_t colorpalette; + uint32_t colorPalette; // number of color indices that are considered important, if zero all are important - uint32_t colorimportant; + uint32_t colorImportant; // red color per pixel bit mask uint32_t redbitmask; // green color per pixel bit mask @@ -134,9 +132,9 @@ typedef struct BmpInfoHeader4 { // vertical resolution, in pixels per meter int32_t yresm; // number of color indices in the color table - uint32_t colorpalette; + uint32_t colorPalette; // number of color indices that are considered important, if zero all are important. - uint32_t colorimportant; + uint32_t colorImportant; // red color per pixel bit mask uint32_t redbitmask; // green color per pixel bit mask @@ -178,9 +176,9 @@ typedef struct BmpInfoHeader5 { // vertical resolution, in pixels per meter int32_t yresm; // number of color indices in the color table - uint32_t colorpalette; + uint32_t colorPalette; // number of color indices that are considered important, if zero all are important - uint32_t colorimportant; + uint32_t colorImportant; // red color per pixel bit mask uint32_t redbitmask; // green color per pixel bit mask diff --git a/src/image/bmp.c b/src/image/bmp.c index 1ad68ab..02aa9e1 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -40,17 +40,16 @@ ◯ ◯ BmpInfoHeader5 | 0..32 bpp > Compression - C I 2 3 4 5 - ◯ ● ◯ ● ◯ ◯ RGB | none | most common - ◯ ◯ ◯ ◯ ◯ ◯ RLE8 | RLE 8bit | only with 8-bit/pixel - ◯ ◯ ◯ ◯ ◯ ◯ RLE4 | RLE 4bit | only with 4-bit/pixel - ◯ ● ◯ ● ◯ ◯ BITFIELDS | RGB bit field masks - ◯ ◯ ◯ ◯ ◯ ◯ JPEG | ? - ◯ ◯ ◯ ◯ ◯ ◯ PNG | ? - ◯ ● ◯ ● ◯ ◯ ALPHABITFIELDS | RGBA bit field masks - ◯ ◯ ◯ ◯ ◯ ◯ CMYK | none - ◯ ◯ ◯ ◯ ◯ ◯ CMYKRLE8 | RLE-8 | only windows metafile - ◯ ◯ ◯ ◯ ◯ ◯ CMYKRLE4 | RLE-4 | only windows metafile + ● RGB | none | most common + ◯ RLE8 | RLE 8bit | only with 8-bit/pixel + ◯ RLE4 | RLE 4bit | only with 4-bit/pixel + ● BITFIELDS | RGB/A bit field masks + ◯ JPEG | ? + ◯ PNG | ? + ● ALPHABITFIELDS | RGBA bit field masks + ◯ CMYK | none + ◯ CMYKRLE8 | RLE-8 | only windows metafile + ◯ CMYKRLE4 | RLE-4 | only windows metafile > Color Table usually the order is bgr0. @@ -63,11 +62,13 @@ ◯ ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table - ◯ ● ◯ ◯ ◯ ◯ 8bpp : 256 colors | table + ◯ ◯ ◯ ◯ ◯ ◯ 8bpp : 256 colors | table ◯ ● ◯ ● ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. ◯ ● ◯ ● ◯ ◯ 24bpp: 16777216 colors | b,g,r. ◯ ● ◯ ● ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. +> pixel storage + The size of each row is rounded up to a multiple of 4 bytes. */ // to store bitmasks and shifts @@ -100,339 +101,277 @@ static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields); #define LEXLIB_RGBA_BITFIELDS 0x0B #define LEXLIB_RGB_PALETTE 0x0C +#define COLORBITMASK_BGRA_8888 ((ColorBitMask){8, 8, 8, 8, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, 16, 8, 0, 24}) +#define COLORBITMASK_BGRA_555 ((ColorBitMask){5, 6, 5, 8, 0x0000FC00, 0x000003E0, 0x0000001F, 0x00000000, 16, 8, 0, 00}) +#define COLORBITMASK_BGRA_565 ((ColorBitMask){5, 6, 5, 8, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000, 16, 8, 0, 00}) + // TODO probably needs big endian to little transforms on big endian machines LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; + + // load file as bytes LexlibBytes bmp = lexlibFileBytes(filename); if(!bmp.data){ image.depth = bmp.count; return image; } - BmpFileHeader* fileheader = (BmpFileHeader*)bmp.data; + BmpFileHeader* fileHeader = (BmpFileHeader*)(bmp.data); + BmpCoreHeader* coreHeader = (BmpCoreHeader*)(bmp.data + sizeof(BmpFileHeader)); - /* check if file is bmp */ - if(fileheader->type != 0x4D42){ + // check if file is bmp + if(fileHeader->type != 0x4D42){ image.depth = LEXLIB_INVALID_FILE_TYPE; free(bmp.data); return image; } - /* info header */ - if(fileheader->infoHeaderSize == 40){ - BmpInfoHeader* infoheader = (BmpInfoHeader*)(bmp.data+14); + // check if header is valid + switch(coreHeader->size){ + // supported + case sizeof(BmpInfoHeader): + case sizeof(BmpInfoHeader3): + break; + // unsuported + case sizeof(BmpCoreHeader): + case sizeof(BmpInfoHeader4): + case sizeof(BmpInfoHeader5): + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; + default: + free(bmp.data); + image.depth = LEXLIB_INVALID_FILE_DATA; + return image; + } + + ColorBitMask bitMask = {0}; + LexlibColorFlt colorMax = {0}; + uint8_t profile = 0; + uint8_t bmpPixelSize = 0; + uint8_t bmpRowStride = 0; + uint8_t bmpRowPadding = 0; + + // InfoHeader + if(coreHeader->size == sizeof(BmpInfoHeader)){ + BmpInfoHeader infoHeader; + memcpy(&infoHeader, coreHeader, sizeof(BmpInfoHeader)); - ColorBitMask colorMask = {0}; - LexlibColorFlt colorMax = {0}; - uint8_t profile = 0; - uint8_t bmpPixelSize = 0; + image.width = infoHeader.width; + image.height = infoHeader.height; + bmpRowStride = ((((infoHeader.width * infoHeader.bitdepth) + 31) & ~31) >> 3); + bmpRowPadding = (bmpRowStride) - (infoHeader.width * (infoHeader.bitdepth / 8)); // check for compression - switch(infoheader->compression){ + switch(infoHeader.compression){ case BMP_RGB: break; case BMP_BITFIELDS: - colorMask = colorBitMaskGet((uint32_t*)(bmp.data+54), LEXLIB_RGB_BITFIELDS); - profile = LEXLIB_RGB_BITFIELDS; + bitMask = colorBitMaskGet((uint32_t*)(bmp.data + sizeof(BmpFileHeader) + sizeof(BmpInfoHeader)), LEXLIB_RGB_BITFIELDS); // calculate max color value - colorMax.r = lexlibPowu(2, colorMask.r) - 1; - colorMax.g = lexlibPowu(2, colorMask.g) - 1; - colorMax.b = lexlibPowu(2, colorMask.b) - 1; + colorMax.r = lexlibPowu(2, bitMask.r) - 1; + colorMax.g = lexlibPowu(2, bitMask.g) - 1; + colorMax.b = lexlibPowu(2, bitMask.b) - 1; + profile = LEXLIB_RGB_BITFIELDS; break; case BMP_ALPHABITFIELDS: - colorMask = colorBitMaskGet((uint32_t*)(bmp.data+54), LEXLIB_RGBA_BITFIELDS); - profile = LEXLIB_RGB_BITFIELDS; + bitMask = colorBitMaskGet((uint32_t*)(bmp.data + sizeof(BmpFileHeader) + sizeof(BmpInfoHeader)), LEXLIB_RGBA_BITFIELDS); // calculate max color value - colorMax.r = lexlibPowu(2, colorMask.r) - 1; - colorMax.g = lexlibPowu(2, colorMask.g) - 1; - colorMax.b = lexlibPowu(2, colorMask.b) - 1; - colorMax.a = lexlibPowu(2, colorMask.a); + colorMax.r = lexlibPowu(2, bitMask.r) - 1; + colorMax.g = lexlibPowu(2, bitMask.g) - 1; + colorMax.b = lexlibPowu(2, bitMask.b) - 1; + colorMax.a = lexlibPowu(2, bitMask.a) - 1; + profile = LEXLIB_RGBA_BITFIELDS; break; default: // unsuported free(bmp.data); - lexlibImageDelete(&image); image.depth = LEXLIB_UNSUPORTED; return image; - break; } - // check for color profile - switch(infoheader->bitdepth){ - case 8: - bmpPixelSize = 1; + // check for profile + switch(infoHeader.bitdepth){ + case 16: image.depth = 8; image.profile = LEXLIB_RGB; - if(infoheader->colorpalette){ - profile = LEXLIB_RGB_PALETTE; - if(infoheader->compression){ - free(bmp.data); - image.depth = LEXLIB_UNSUPORTED; - return image; - } - break; - } - if(infoheader->compression == BMP_BITFIELDS){ - image.profile = (colorMask.a) ? LEXLIB_RGBA : LEXLIB_RGB; - break; - } - if(infoheader->compression == BMP_ALPHABITFIELDS){ - image.profile = LEXLIB_RGBA; - break; - } - free(bmp.data); - image.depth = LEXLIB_UNSUPORTED; - return image; - break; - case 16: bmpPixelSize = 2; - if(infoheader->compression == BMP_RGB){ - profile = LEXLIB_RGB555; - image.depth = 8; - image.profile = LEXLIB_RGB; - colorMax.r = 31.0f; - colorMax.g = 31.0f; - colorMax.b = 31.0f; - break; - } - if(infoheader->compression == BMP_BITFIELDS){ - image.depth = 8; - image.profile = (colorMask.a) ? LEXLIB_RGBA : LEXLIB_RGB; - break; + if(!infoHeader.compression){ + profile = LEXLIB_RGB_BITFIELDS; + bitMask = COLORBITMASK_BGRA_555; } - if(infoheader->compression == BMP_ALPHABITFIELDS){ - image.depth = 8; - image.profile = LEXLIB_RGBA; - break; - } - free(bmp.data); - image.depth = LEXLIB_INVALID_FILE_DATA; - return image; + break; case 24: image.depth = 8; image.profile = LEXLIB_RGB; - if(!profile) - profile = LEXLIB_RGB; bmpPixelSize = 3; + if(!infoHeader.compression){ + profile = LEXLIB_RGB; + } break; case 32: image.depth = 8; image.profile = LEXLIB_RGBA; - if(!profile) - profile = LEXLIB_RGBA; bmpPixelSize = 4; + if(!infoHeader.compression){ + profile = LEXLIB_RGBA; + break; + } break; - default: + default: // unsuported free(bmp.data); image.depth = LEXLIB_UNSUPORTED; return image; } - - // create image and set offsets - image = lexlibImageNew(infoheader->width, infoheader->height, image.profile, image.depth); - if(!image.data){ - free(bmp.data); - return image; - } - uint64_t pixelCount = image.width * image.height; - size_t bmpoffset = fileheader->start; - size_t imgoffset = 0; - - // load pixel data from bmp to lexlib image - if(profile == LEXLIB_RGB_BITFIELDS) - for(uint64_t i = 0; i < pixelCount; i++){ - uint16_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); - - image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> colorMask.rs) / colorMax.r) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> colorMask.gs) / colorMax.g) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> colorMask.bs) / colorMax.b) * 255.0f); - image.data[imgoffset+3] = (image.profile == LEXLIB_RGBA) ? rintf(( ((bmppixel & colorMask.ab) >> colorMask.as) / colorMax.a) * 256.0f) - 1 : 0xFF; - - bmpoffset += bmpPixelSize; - imgoffset += image.channels; - } - - if(profile == LEXLIB_RGB555) - for(uint64_t i = 0; i < pixelCount; i++){ - uint16_t bmppixel = *((uint16_t*)(bmp.data+bmpoffset)); - - image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> 10) / 31.0f) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> 5) / 31.0f) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> 0) / 31.0f) * 255.0f); - if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } - - bmpoffset += 2; - imgoffset += image.channels; - } - - if(profile == LEXLIB_RGB) - for(uint64_t i = 0; i < pixelCount; i++){ - image.data[imgoffset+0] = bmp.data[bmpoffset+2]; - image.data[imgoffset+1] = bmp.data[bmpoffset+1]; - image.data[imgoffset+2] = bmp.data[bmpoffset+0]; - if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } - - bmpoffset+=3; - imgoffset+=image.channels; - } - - if(profile == LEXLIB_RGBA) - for(uint64_t i = 0; i < pixelCount; i++){ - image.data[imgoffset+0] = bmp.data[bmpoffset+3]; - image.data[imgoffset+1] = bmp.data[bmpoffset+2]; - image.data[imgoffset+2] = bmp.data[bmpoffset+1]; - image.data[imgoffset+3] = bmp.data[bmpoffset+0]; - - bmpoffset+=4; - imgoffset+=image.channels; - } - - if(profile == LEXLIB_RGB_PALETTE){ - LexlibColor8* palette = (LexlibColor8*)(bmp.data+54); - for(uint64_t i = 0; i < pixelCount; i++){ - - image.data[imgoffset+0] = palette[bmp.data[bmpoffset]].b; - image.data[imgoffset+1] = palette[bmp.data[bmpoffset]].g; - image.data[imgoffset+2] = palette[bmp.data[bmpoffset]].r; - if(image.profile == LEXLIB_RGBA) image.data[imgoffset+4] = palette[bmp.data[bmpoffset]].a; - - bmpoffset += bmpPixelSize; - imgoffset += image.channels; - }} - - free(bmp.data); - return image; - }/* ..info header.. */ + } - /* info header v3 */ - if(fileheader->infoHeaderSize == 56){ - BmpInfoHeader3* infoheader3 = (BmpInfoHeader3*)(bmp.data+14); + // InfoHeader v3 + if(coreHeader->size == sizeof(BmpInfoHeader3)){ + BmpInfoHeader3* infoHeader3 = (BmpInfoHeader3*)coreHeader; - ColorBitMask colorMask = {0}; - LexlibColorFlt colorMax = {0}; - uint8_t profile = 0; - uint8_t bmpPixelSize = 0; + image.width = infoHeader3->width; + image.height = infoHeader3->height; + bmpRowStride = ((((infoHeader3->width * infoHeader3->bitdepth) + 31) & ~31) >> 3); + bmpRowPadding = (bmpRowStride) - (infoHeader3->width * (infoHeader3->bitdepth / 8)); // check for compression - switch(infoheader3->compression){ + switch(infoHeader3->compression){ case BMP_RGB: break; case BMP_BITFIELDS: case BMP_ALPHABITFIELDS: - colorMask = colorBitMaskGet((uint32_t*)(bmp.data+54), LEXLIB_RGBA_BITFIELDS); - profile = LEXLIB_RGB_BITFIELDS; + bitMask = colorBitMaskGet(&infoHeader3->redbitmask, LEXLIB_RGBA_BITFIELDS); // calculate max color value - colorMax.r = lexlibPowu(2, colorMask.r) - 1; - colorMax.g = lexlibPowu(2, colorMask.g) - 1; - colorMax.b = lexlibPowu(2, colorMask.b) - 1; - colorMax.a = lexlibPowu(2, colorMask.a); + colorMax.r = lexlibPowu(2, bitMask.r) - 1; + colorMax.g = lexlibPowu(2, bitMask.g) - 1; + colorMax.b = lexlibPowu(2, bitMask.b) - 1; + if(bitMask.a){ + colorMax.a = lexlibPowu(2, bitMask.a) - 1; + profile = LEXLIB_RGBA_BITFIELDS; + } else { + profile = LEXLIB_RGB_BITFIELDS; + } break; default: // unsuported free(bmp.data); - lexlibImageDelete(&image); image.depth = LEXLIB_UNSUPORTED; return image; - break; } - // set the color profile - if(infoheader3->bitdepth == 8){ - free(bmp.data); - image.depth = LEXLIB_UNSUPORTED; - return image; - } - if(infoheader3->bitdepth == 16){ - image.depth = 8; - image.profile = (colorMask.a) ? LEXLIB_RGBA : LEXLIB_RGB; - if(!profile) - profile = LEXLIB_RGB555; - bmpPixelSize = 2; - } - if(infoheader3->bitdepth == 24){ - image.depth = 8; - image.profile = LEXLIB_RGB; - if(!profile) - profile = LEXLIB_RGB; - bmpPixelSize = 3; - } - if(infoheader3->bitdepth == 32){ - image.depth = 8; - image.profile = LEXLIB_RGBA; - if(!profile) - profile = LEXLIB_RGBA; - bmpPixelSize = 4; - } - - // create image and set offsets - image = lexlibImageNew(infoheader3->width, infoheader3->height, image.profile, image.depth); - if(!image.data){ - free(bmp.data); - return image; + // check for profile + switch(infoHeader3->bitdepth){ + case 16: + image.depth = 8; + image.profile = LEXLIB_RGB; + bmpPixelSize = 2; + if(!infoHeader3->compression){ + profile = LEXLIB_RGB_BITFIELDS; + bitMask = COLORBITMASK_BGRA_555; + } + break; + case 24: + image.depth = 8; + image.profile = LEXLIB_RGB; + bmpPixelSize = 3; + if(!infoHeader3->compression) + profile = LEXLIB_RGB; + break; + case 32: + image.depth = 8; + image.profile = LEXLIB_RGBA; + bmpPixelSize = 4; + if(!infoHeader3->compression) + profile = LEXLIB_RGBA; + break; + default: // unsuported + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; } - uint64_t pixelCount = image.width * image.height; - size_t bmpoffset = fileheader->start; - size_t imgoffset = 0; - - // load pixel data from bmp to lexlib image - if(profile == LEXLIB_RGB_BITFIELDS) - for(uint64_t i = 0; i < pixelCount; i++){ - uint32_t bmppixel = *((uint32_t*)(bmp.data+bmpoffset)); - - image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> colorMask.rs) / colorMax.r) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> colorMask.gs) / colorMax.g) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> colorMask.bs) / colorMax.b) * 255.0f); - image.data[imgoffset+3] = (image.profile == LEXLIB_RGBA) ? rintf(( ((bmppixel & colorMask.ab) >> colorMask.as) / colorMax.a) * 256.0f) - 1 : 0xFF; + } + + // create image + image = lexlibImageNew(image.width, image.height, image.profile, image.depth); + if(!image.data){ + free(bmp.data); + return image; + } + + // set image pixels depending of the profile + if(profile == LEXLIB_RGB){ // perfect RGB, 8.8.8 + uint8_t* bmpOffset = bmp.data + fileHeader->offset; + for(uint32_t y = 0; y < image.height; y++){ + for(uint32_t x = 0; x < image.width; x++){ + LexlibColor imgPixel = { + .r = bmpOffset[2], + .g = bmpOffset[1], + .b = bmpOffset[0], + .a = 0xFF + }; + lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); - bmpoffset += bmpPixelSize; - imgoffset += image.channels; + bmpOffset += bmpPixelSize; } - - if(profile == LEXLIB_RGB555) - for(uint64_t i = 0; i < pixelCount; i++){ - uint16_t bmppixel = *((uint16_t*)(bmp.data+bmpoffset)); - - image.data[imgoffset+0] = rintf(( ((bmppixel & colorMask.rb) >> 10) / 31.0f) * 255.0f); - image.data[imgoffset+1] = rintf(( ((bmppixel & colorMask.gb) >> 5) / 31.0f) * 255.0f); - image.data[imgoffset+2] = rintf(( ((bmppixel & colorMask.bb) >> 0) / 31.0f) * 255.0f); - if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + bmpOffset += bmpRowPadding; + }} + + if(profile == LEXLIB_RGBA){ // perfect RGBA, 8.8.8.8 + uint8_t* bmpOffset = (uint8_t*)(bmp.data + fileHeader->offset); + for(uint32_t y = 0; y < image.height; y++){ + for(uint32_t x = 0; x < image.width; x++){ + LexlibColor imgPixel = { + .r = bmpOffset[2], + .g = bmpOffset[1], + .b = bmpOffset[0], + .a = bmpOffset[4] + }; + lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); - bmpoffset += 2; - imgoffset += image.channels; + bmpOffset += bmpPixelSize; } - - if(profile == LEXLIB_RGB) - for(uint64_t i = 0; i < pixelCount; i++){ - image.data[imgoffset+0] = bmp.data[bmpoffset+2]; - image.data[imgoffset+1] = bmp.data[bmpoffset+1]; - image.data[imgoffset+2] = bmp.data[bmpoffset+0]; - if(image.profile == LEXLIB_RGBA){ image.data[imgoffset+3] = 0xFF; } + bmpOffset += bmpRowPadding; + }} + + if(profile == LEXLIB_RGB_BITFIELDS){ // bitfields without alpha + uint32_t* bmpOffset = (uint32_t*)(bmp.data + fileHeader->offset); + for(uint32_t y = 0; y < image.height; y++){ + for(uint32_t x = 0; x < image.width; x++){ + uint32_t bmpPixel = *bmpOffset; - bmpoffset+=3; - imgoffset+=image.channels; - } - - if(profile == LEXLIB_RGBA) - for(uint64_t i = 0; i < pixelCount; i++){ - image.data[imgoffset+0] = bmp.data[bmpoffset+3]; - image.data[imgoffset+1] = bmp.data[bmpoffset+2]; - image.data[imgoffset+2] = bmp.data[bmpoffset+1]; - image.data[imgoffset+3] = bmp.data[bmpoffset+0]; + LexlibColor imgPixel = { + .r = rintf((((bmpPixel & bitMask.rb) >> bitMask.rs) / colorMax.r) * 255.0f), + .g = rintf((((bmpPixel & bitMask.gb) >> bitMask.gs) / colorMax.g) * 255.0f), + .b = rintf((((bmpPixel & bitMask.bb) >> bitMask.bs) / colorMax.b) * 255.0f), + .a = 0xFF + }; + lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); - bmpoffset+=4; - imgoffset+=image.channels; + bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpPixelSize); } - - free(bmp.data); - return image; - }/* ..info header v3.. */ + bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); + }} - LEXLIB_UNREACHABLE - if(bmp.data) - free(bmp.data); + if(profile == LEXLIB_RGBA_BITFIELDS){ // bitfields with alpha + uint32_t* bmpOffset = (uint32_t*)(bmp.data + fileHeader->offset); + for(uint32_t y = 0; y < image.height; y++){ + for(uint32_t x = 0; x < image.width; x++){ + uint32_t bmpPixel = *bmpOffset; + LexlibColor imgPixel = { + .r = rintf((((bmpPixel & bitMask.rb) >> bitMask.rs) / colorMax.r) * 255.0f), + .g = rintf((((bmpPixel & bitMask.gb) >> bitMask.gs) / colorMax.g) * 255.0f), + .b = rintf((((bmpPixel & bitMask.bb) >> bitMask.bs) / colorMax.b) * 255.0f), + .a = rintf((((bmpPixel & bitMask.ab) >> bitMask.as) / colorMax.a) * 255.0f), + }; + lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); + + bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpPixelSize); + } + bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); + }} + free(bmp.data); return image; } - /* Static Functions */ #define UNSIGNED_BIT_WIDTH (CHAR_BIT * sizeof(unsigned)) LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index){ diff --git a/test/imageBmp.c b/test/imageBmp.c index df15286..8c98b67 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -82,6 +82,56 @@ void testImageBmp(void){ lexlibImageSave(&image, "resources/out/plasma32.png"); testEnd(1, NULL);} + {testStart("bmp macintosh"); + LexlibImage image = lexlibImageLoadBmp("resources/image/macintosh.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/macintosh.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/macintosh.png"); + testEnd(1, NULL);} + + {testStart("bmp macintosh_alpha70"); + LexlibImage image = lexlibImageLoadBmp("resources/image/macintosh_alpha70.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/macintosh_alpha70.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/macintosh_alpha70.png"); + testEnd(1, NULL);} + {testStart("bmp puro"); LexlibImage image = lexlibImageLoadBmp("resources/image/puro.bmp"); if(!image.data) -- GitLab From 1db8ce4572d3249ba2af1b416693523f2297aa45 Mon Sep 17 00:00:00 2001 From: alexevier Date: Mon, 15 May 2023 15:25:28 -0400 Subject: [PATCH 37/64] fixed segfault when setting a pixel in image --- src/image.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/image.c b/src/image.c index df7f216..e666add 100644 --- a/src/image.c +++ b/src/image.c @@ -188,8 +188,12 @@ uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, Le return LEXLIB_OK; } if(image->channels == 3){ - LexlibColor pixel = *((LexlibColor*)image->data+offset); - pixel.a = -1; + LexlibColor pixel = { + .r = image->data[offset+0], + .g = image->data[offset+1], + .b = image->data[offset+2], + .a = 0xFF, + }; pixel = lexlibColorPremultiply(lexlibColorBlend(pixel, color, blendmode)); image->data[offset+0] = pixel.r; image->data[offset+1] = pixel.g; -- GitLab From 5a438c492a3cfd205c6af26f4ab97ab88d97ed9b Mon Sep 17 00:00:00 2001 From: alexevier Date: Mon, 15 May 2023 15:51:35 -0400 Subject: [PATCH 38/64] bmp 8bpp support --- src/image/bmp.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++--- test/imageBmp.c | 28 +++++++++++++++++++++-- 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 02aa9e1..558b4ab 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -62,7 +62,7 @@ ◯ ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table - ◯ ◯ ◯ ◯ ◯ ◯ 8bpp : 256 colors | table + ◯ ● ◯ ● ◯ ◯ 8bpp : 256 colors | table ◯ ● ◯ ● ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. ◯ ● ◯ ● ◯ ◯ 24bpp: 16777216 colors | b,g,r. ◯ ● ◯ ● ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. @@ -149,8 +149,9 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ LexlibColorFlt colorMax = {0}; uint8_t profile = 0; uint8_t bmpPixelSize = 0; - uint8_t bmpRowStride = 0; - uint8_t bmpRowPadding = 0; + uint32_t bmpRowStride = 0; + uint32_t bmpRowPadding = 0; + uint32_t bmpHeadOffset = sizeof(BmpFileHeader); // InfoHeader if(coreHeader->size == sizeof(BmpInfoHeader)){ @@ -161,6 +162,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.height = infoHeader.height; bmpRowStride = ((((infoHeader.width * infoHeader.bitdepth) + 31) & ~31) >> 3); bmpRowPadding = (bmpRowStride) - (infoHeader.width * (infoHeader.bitdepth / 8)); + bmpHeadOffset += sizeof(BmpInfoHeader); // check for compression switch(infoHeader.compression){ @@ -173,6 +175,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ colorMax.g = lexlibPowu(2, bitMask.g) - 1; colorMax.b = lexlibPowu(2, bitMask.b) - 1; profile = LEXLIB_RGB_BITFIELDS; + bmpHeadOffset += 12; break; case BMP_ALPHABITFIELDS: bitMask = colorBitMaskGet((uint32_t*)(bmp.data + sizeof(BmpFileHeader) + sizeof(BmpInfoHeader)), LEXLIB_RGBA_BITFIELDS); @@ -182,6 +185,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ colorMax.b = lexlibPowu(2, bitMask.b) - 1; colorMax.a = lexlibPowu(2, bitMask.a) - 1; profile = LEXLIB_RGBA_BITFIELDS; + bmpHeadOffset += 16; break; default: // unsuported free(bmp.data); @@ -191,6 +195,21 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ // check for profile switch(infoHeader.bitdepth){ + case 8: + image.depth = 8; + image.profile = LEXLIB_RGB; + bmpPixelSize = 1; + if(infoHeader.colorPalette){ + profile = LEXLIB_RGB_PALETTE; + bitMask.rb = 0x000000FF; + } + if(infoHeader.compression){ + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; + } + + break; case 16: image.depth = 8; image.profile = LEXLIB_RGB; @@ -232,6 +251,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.height = infoHeader3->height; bmpRowStride = ((((infoHeader3->width * infoHeader3->bitdepth) + 31) & ~31) >> 3); bmpRowPadding = (bmpRowStride) - (infoHeader3->width * (infoHeader3->bitdepth / 8)); + bmpHeadOffset += sizeof(BmpInfoHeader3); // check for compression switch(infoHeader3->compression){ @@ -259,6 +279,20 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ // check for profile switch(infoHeader3->bitdepth){ + case 8: + image.depth = 8; + image.profile = LEXLIB_RGB; + bmpPixelSize = 1; + if(infoHeader3->colorPalette){ + profile = LEXLIB_RGB_PALETTE; + bitMask.rb = 0x000000FF; + } + if(infoHeader3->compression){ + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; + } + break; case 16: image.depth = 8; image.profile = LEXLIB_RGB; @@ -368,6 +402,27 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); }} + // here bitMask.rb is used to determine how much of bmpOffset should be used; + // 0x000000FF for 8 bit, 0x0000FFFF for 16 and so on. + if(profile == LEXLIB_RGB_PALETTE){ // color palette + uint32_t* bmpOffset = (uint32_t*)(bmp.data + fileHeader->offset); + uint32_t* palette = (uint32_t*)(bmp.data + bmpHeadOffset); + for(uint32_t y = 0; y < image.height; y++){ + for(uint32_t x = 0; x < image.width; x++){ + uint32_t bmpPixel = palette[*bmpOffset & bitMask.rb]; + LexlibColor imgPixel = { + .r = bmpPixel >> 16, + .g = bmpPixel >> 8, + .b = bmpPixel >> 0, + .a = 0xFF, + }; + lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); + + bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpPixelSize); + } + bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); + }} + free(bmp.data); return image; } diff --git a/test/imageBmp.c b/test/imageBmp.c index 8c98b67..b26fa30 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -132,6 +132,31 @@ void testImageBmp(void){ lexlibImageSave(&image, "resources/out/macintosh_alpha70.png"); testEnd(1, NULL);} + {testStart("bmp macintosh_8bpp"); + LexlibImage image = lexlibImageLoadBmp("resources/image/macintosh_8bpp.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/macintosh_8bpp.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/macintosh_8bpp.png"); + testEnd(1, NULL);} + {testStart("bmp puro"); LexlibImage image = lexlibImageLoadBmp("resources/image/puro.bmp"); if(!image.data) @@ -155,6 +180,5 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/puro.png"); - testEnd(1, NULL);} - + testEnd(1, NULL);} } \ No newline at end of file -- GitLab From 9912d4c054e172bad03e6b535acfda03c0bb77d8 Mon Sep 17 00:00:00 2001 From: alexevier Date: Mon, 15 May 2023 17:46:44 -0400 Subject: [PATCH 39/64] bmp 1bpp coreheader support --- resources/image/floppy.bmp | Bin 0 -> 126 bytes src/image/bmp.c | 63 ++++++++++++++++++++++++++++++++++--- test/imageBmp.c | 35 ++++++++++++++++++++- 3 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 resources/image/floppy.bmp diff --git a/resources/image/floppy.bmp b/resources/image/floppy.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1ea7b9cab0c4ed5533d65ac07b0fa5445da696b6 GIT binary patch literal 126 zcmZ?rtz&=yI|c>@9tH*m0R{mEMg~TZ;Q#;s85kTG7#M`PA()AQfdPc!k_-$C_J1JQ bfCoYwLTQjVNF5Bz|6pK{pMivbFfafBFX9sC literal 0 HcmV?d00001 diff --git a/src/image/bmp.c b/src/image/bmp.c index 558b4ab..34316bf 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -59,7 +59,7 @@ > pixel formats left-most pixel in the most-significant bit of the first byte. C I 2 3 4 5 - ◯ ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table + ● ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table ◯ ● ◯ ● ◯ ◯ 8bpp : 256 colors | table @@ -100,6 +100,7 @@ static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields); #define LEXLIB_RGB_BITFIELDS 0x0A #define LEXLIB_RGBA_BITFIELDS 0x0B #define LEXLIB_RGB_PALETTE 0x0C +#define LEXLIB_1BPP 0x0D #define COLORBITMASK_BGRA_8888 ((ColorBitMask){8, 8, 8, 8, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, 16, 8, 0, 24}) #define COLORBITMASK_BGRA_555 ((ColorBitMask){5, 6, 5, 8, 0x0000FC00, 0x000003E0, 0x0000001F, 0x00000000, 16, 8, 0, 00}) @@ -129,11 +130,11 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ // check if header is valid switch(coreHeader->size){ // supported + case sizeof(BmpCoreHeader): case sizeof(BmpInfoHeader): case sizeof(BmpInfoHeader3): break; // unsuported - case sizeof(BmpCoreHeader): case sizeof(BmpInfoHeader4): case sizeof(BmpInfoHeader5): free(bmp.data); @@ -153,6 +154,29 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ uint32_t bmpRowPadding = 0; uint32_t bmpHeadOffset = sizeof(BmpFileHeader); + // CoreHeader + if(coreHeader->size == sizeof(BmpCoreHeader)){ + image.width = coreHeader->width; + image.height = coreHeader->height; + bmpRowStride = ((((coreHeader->width * coreHeader->bitdepth) + 31) & ~31) >> 3); + bmpRowPadding = (bmpRowStride) - ceil(coreHeader->width * (coreHeader->bitdepth / 8.0f)); + bmpHeadOffset += sizeof(BmpCoreHeader); + + // check for profile + switch(coreHeader->bitdepth){ + case 1: + image.depth = 8; + image.profile = LEXLIB_RGB; + bmpPixelSize = 1; + profile = LEXLIB_1BPP; + break; + default: // unsuported + free(bmp.data); + image.depth = LEXLIB_UNSUPORTED; + return image; + } + } + // InfoHeader if(coreHeader->size == sizeof(BmpInfoHeader)){ BmpInfoHeader infoHeader; @@ -161,7 +185,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.width = infoHeader.width; image.height = infoHeader.height; bmpRowStride = ((((infoHeader.width * infoHeader.bitdepth) + 31) & ~31) >> 3); - bmpRowPadding = (bmpRowStride) - (infoHeader.width * (infoHeader.bitdepth / 8)); + bmpRowPadding = (bmpRowStride) - ceil(infoHeader.width * (infoHeader.bitdepth / 8.0f)); bmpHeadOffset += sizeof(BmpInfoHeader); // check for compression @@ -250,7 +274,7 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ image.width = infoHeader3->width; image.height = infoHeader3->height; bmpRowStride = ((((infoHeader3->width * infoHeader3->bitdepth) + 31) & ~31) >> 3); - bmpRowPadding = (bmpRowStride) - (infoHeader3->width * (infoHeader3->bitdepth / 8)); + bmpRowPadding = (bmpRowStride) - ceil(infoHeader3->width * (infoHeader3->bitdepth / 8.0f)); bmpHeadOffset += sizeof(BmpInfoHeader3); // check for compression @@ -423,6 +447,37 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); }} + if(profile == LEXLIB_1BPP){ + uint8_t* bmpOffset = bmp.data + fileHeader->offset; + uint8_t* palette = bmp.data + bmpHeadOffset; + // bmpRowPadding = 1; + for(uint32_t y = 0; y < image.height; y++){ + for(uint32_t x = 0; x < image.width;){ + LexlibColor imgPixel; + int8_t bit = 8; + + do { + bit--; + if(bitGet(*bmpOffset, bit)){ + imgPixel.r = palette[5]; + imgPixel.g = palette[4]; + imgPixel.b = palette[3]; + imgPixel.a = 0xFF; + } else { + imgPixel.r = palette[2]; + imgPixel.g = palette[1]; + imgPixel.b = palette[0]; + imgPixel.a = 0xFF; + } + lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); + x++; + } while(bit != 0); + + bmpOffset++; + } + bmpOffset += bmpRowPadding; + }} + free(bmp.data); return image; } diff --git a/test/imageBmp.c b/test/imageBmp.c index b26fa30..ea8ab57 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -30,6 +30,7 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/plasma16.png"); + lexlibImageDelete(&image); testEnd(1, NULL);} {testStart("bmp plasma24"); @@ -55,6 +56,7 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/plasma24.png"); + lexlibImageDelete(&image); testEnd(1, NULL);} {testStart("bmp plasma32"); @@ -80,6 +82,7 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/plasma32.png"); + lexlibImageDelete(&image); testEnd(1, NULL);} {testStart("bmp macintosh"); @@ -105,6 +108,7 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/macintosh.png"); + lexlibImageDelete(&image); testEnd(1, NULL);} {testStart("bmp macintosh_alpha70"); @@ -130,6 +134,7 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/macintosh_alpha70.png"); + lexlibImageDelete(&image); testEnd(1, NULL);} {testStart("bmp macintosh_8bpp"); @@ -155,6 +160,7 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/macintosh_8bpp.png"); + lexlibImageDelete(&image); testEnd(1, NULL);} {testStart("bmp puro"); @@ -180,5 +186,32 @@ void testImageBmp(void){ lexlibImageFlip(&image, LEXLIB_FLIP_Y); lexlibImageSave(&image, "resources/out/puro.png"); - testEnd(1, NULL);} + lexlibImageDelete(&image); + testEnd(1, NULL);} + + {testStart("bmp floppy"); + LexlibImage image = lexlibImageLoadBmp("resources/image/floppy.bmp"); + if(!image.data) + switch(image.depth){ + case LEXLIB_CANT_OPEN: + testEnd(0, "can't open \"resources/image/floppy.bmp\""); + return; + case LEXLIB_INVALID_FILE_TYPE: + testEnd(0, "file is not a bmp"); + return; + case LEXLIB_OUT_OF_MEMORY: + testEnd(0, "OOM"); + return; + case LEXLIB_UNSUPORTED: + testEnd(0, "unsuported"); + return; + default: + testEnd(0, "other error"); + return; + } + + lexlibImageFlip(&image, LEXLIB_FLIP_Y); + lexlibImageSave(&image, "resources/out/floppy.png"); + lexlibImageDelete(&image); + testEnd(1, NULL);} } \ No newline at end of file -- GitLab From 9b83b17c9043f8e731d5c3aebde881c812722f00 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 01:38:58 -0400 Subject: [PATCH 40/64] imageGetPixel() --- include/lexlib/image.h | 16 ++++++++-- src/image.c | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index e7cf228..1e1d3ed 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -10,6 +10,7 @@ /* NOTE s images with more bitdepth than 8 are bigendian. + its being considered if image data should be in the native machine endianess. > Image file formats L S M @@ -52,16 +53,19 @@ typedef struct LexlibImage LexlibImage; #define LEXLIB_BW 0x02 /* Black White */ #define LEXLIB_RGB 0x03 /* Red Green Blue */ #define LEXLIB_RGBA 0x04 /* Red Green Blue Alpha */ - #define LEXLIB_RGB24 0x03 /* R.G.B.A.X: 8.8.8.0.0 */ #define LEXLIB_RGB555 0x05 /* R.G.B.A.X: 5.5.5.0.1 */ #define LEXLIB_RGB565 0x06 /* R.G.B.A.X: 5.6.5.0.0 */ - // flags #define LEXLIB_FLIP_X 0x01 #define LEXLIB_FLIP_Y 0x02 +// file formats +#define LEXLIB_BMP_CORE 0x0C +#define LEXLIB_BMP_INFO 0x28 +#define LEXLIB_BMP_INFO_V3 0x38 + // "constants" #define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0, 0, 0}) @@ -91,6 +95,11 @@ LEXLIB_EXTERN LexlibColor lexlibImagePixel(const LexlibImage* image, uint32_t x, // returns LEXLIB_OK on success, LEXLIB_INVALID_OPERATION on error. LEXLIB_EXTERN uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode); +// gets a pixel from a LexlibImage +// according transformations to the returned color are performed depending of the image profile. +// alpha is at max if the image does not have a alpha channel. +LEXLIB_EXTERN LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y); + // loads a image into a LexlibImage. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_INVALID_FILENAME, ... *errors sujected to the actual loader*) LEXLIB_EXTERN LexlibImage lexlibImageLoad(const char* filename); @@ -104,6 +113,9 @@ LEXLIB_EXTERN uint8_t lexlibImageSave(const LexlibImage* image, const char* file // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_OPERATION, LEXLIB_OUT_OF_MEMORY, LEXLIB_ERROR). LEXLIB_EXTERN LexlibImage lexlibImageLoadBmp(const char* filename); +// saves a bmp to a file +LEXLIB_EXTERN uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uint8_t profile, uint8_t header); + // loads a png into LexlibImage // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_OPERATION, LEXLIB_OUT_OF_MEMORY, LEXLIB_ERROR). LEXLIB_EXTERN LexlibImage lexlibImageLoadPng(const char* filename); diff --git a/src/image.c b/src/image.c index e666add..34f4a13 100644 --- a/src/image.c +++ b/src/image.c @@ -303,6 +303,75 @@ uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, Le return LEXLIB_ERROR; } +LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y){ + uint8_t pixelSize = image->bpp / 8; + size_t rowsize = image->width * pixelSize; + size_t offset = (y * rowsize) + (x * pixelSize); + + if(image->depth == 8){ + if(image->channels == 1){ + LexlibColor pixel; + pixel.r = image->data[offset]; + pixel.g = pixel.r; + pixel.b = pixel.r; + pixel.a = 0xFF; + return pixel; + } + if(image->channels == 3){ + LexlibColor pixel; + pixel.r = image->data[offset]; + pixel.g = image->data[offset+1]; + pixel.b = image->data[offset+2]; + pixel.a = 0xFF; + return pixel; + } + if(image->channels == 4){ + return *(LexlibColor*)(image->data+offset); + } + } + + if(image->depth == 16){ + LexlibColor16 pixel16 = {0}; + uint8_t* pixel16raw = (uint8_t*)&pixel16; + if(image->channels == 1){ + pixel16raw[0] = image->data[offset+1]; + pixel16raw[1] = image->data[offset+0]; + + pixel16.g = pixel16.r; + pixel16.b = pixel16.r; + pixel16.a = 0xFFFF; + + return lexlibColor16To8(pixel16); + } + if(image->channels == 4){ + pixel16raw[0] = image->data[offset+1]; + pixel16raw[1] = image->data[offset+0]; + pixel16raw[2] = image->data[offset+3]; + pixel16raw[3] = image->data[offset+2]; + pixel16raw[4] = image->data[offset+5]; + pixel16raw[5] = image->data[offset+4]; + + pixel16.a = 0xFFFF; + + return lexlibColor16To8(pixel16); + } + if(image->channels == 4){ + pixel16raw[0] = image->data[offset+1]; + pixel16raw[1] = image->data[offset+0]; + pixel16raw[2] = image->data[offset+3]; + pixel16raw[3] = image->data[offset+2]; + pixel16raw[4] = image->data[offset+5]; + pixel16raw[5] = image->data[offset+4]; + pixel16raw[6] = image->data[offset+7]; + pixel16raw[7] = image->data[offset+6]; + + return lexlibColor16To8(pixel16); + } + } + + return LEXLIB_COLOR_MAGENTA; +} + LexlibImage lexlibImageLoad(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; -- GitLab From 9267d72913cc8bdf36635ce8631013ad877b389c Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 01:39:41 -0400 Subject: [PATCH 41/64] initial support for saving bmp files --- src/image/bmp.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++- test/imageBmp.c | 24 +++---- 2 files changed, 183 insertions(+), 17 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 34316bf..45056e7 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -33,7 +33,7 @@ L S ◯ ◯ BmpCoreHeader | 1..24 bpp ◇ ◇ ?? | ?..?? bpp | not sure about which one it is - ● ◯ BmpInfoHeader | 1..32 bpp + ● ● BmpInfoHeader | 1..32 bpp ◇ ◇ BmpInfoHeader2 | ?..?? bpp | not documented | no struct exist ● ◯ BmpInfoHeader3 | ?..?? bpp | not officially documented ◯ ◯ BmpInfoHeader4 | 0..32 bpp @@ -93,6 +93,14 @@ typedef struct ColorRGB { uint8_t b; } ColorRGB; +typedef union MasterHeader { + BmpCoreHeader core; + BmpInfoHeader info; + BmpInfoHeader3 info3; + BmpInfoHeader4 info4; + BmpInfoHeader5 info5; +} MasterHeader; + /* Static Functions */ LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index); static ColorBitMask colorBitMaskGet(const uint32_t* data, uint8_t bitfields); @@ -482,6 +490,172 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ return image; } +uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uint8_t profile, uint8_t bmpheader){ + BmpFileHeader fileHeader = {0}; + MasterHeader header = {0}; + + uint8_t bitMaskCnt = 0; + uint32_t bitMask[4] = {0}; + uint32_t pixelSize = 0; + uint32_t rowStride = 0; + uint32_t rowPadding = 0; + + // initial file header data + fileHeader.type = 0x4D42; + fileHeader.size = sizeof(BmpFileHeader); + fileHeader.reserved1 = 0; + fileHeader.reserved2 = 0; + + // arguments check + if(!profile) + profile = image->profile; + if(!bmpheader) + bmpheader = LEXLIB_BMP_INFO; + + // check if header supported + switch(bmpheader){ + // supported + case LEXLIB_BMP_INFO: + break; + default: + return LEXLIB_UNSUPORTED; + } + + if(bmpheader == LEXLIB_BMP_INFO){ + header.info.size = sizeof(BmpInfoHeader); + header.info.width = image->width; + header.info.height = image->height; + header.info.planes = 1; + header.info.xresm = 2835; + header.info.yresm = 2835; + header.info.colorPalette = 0; + header.info.colorImportant = 0; + + // set profile + switch(profile){ + case LEXLIB_RGB555: + header.info.bitdepth = 16; + header.info.compression = BMP_RGB; + pixelSize = 2; + break; + case LEXLIB_RGB565: + header.info.bitdepth = 16; + header.info.compression = BMP_BITFIELDS; + pixelSize = 2; + bitMaskCnt = 3; + bitMask[0] = 0x0000F800; + bitMask[1] = 0x000007E0; + bitMask[2] = 0x0000001F; + break; + case LEXLIB_RGB: + header.info.bitdepth = 24; + header.info.compression = BMP_RGB; + pixelSize = 3; + break; + case LEXLIB_RGBA: + header.info.bitdepth = 32; + header.info.compression = BMP_RGB; + pixelSize = 4; + break; + default: + return LEXLIB_UNSUPORTED; + } + + // calculate stride and image size + rowStride = ((((header.info.width * header.info.bitdepth) + 31) & ~31) >> 3); + rowPadding = (rowStride) - ceil(header.info.width * (header.info.bitdepth / 8.0f)); + header.info.imagesize = image->datasize + (header.info.height * rowPadding); + fileHeader.size += header.info.imagesize; + } + + fileHeader.size += header.core.size; + fileHeader.size += bitMaskCnt * sizeof(uint32_t); + fileHeader.offset = sizeof(BmpFileHeader) + header.core.size + (bitMaskCnt * sizeof(uint32_t)); + + printf("pad %u\n", rowPadding); + printf("head: %u\n", fileHeader.size); + printf("offs: %u\n", fileHeader.offset); + printf("siz: %u\n", header.info.imagesize); + + // create file + FILE* file = fopen(filename, "wb"); + if(!file) + return LEXLIB_CANT_WRITE; + + // set data accordingly // + fwrite(&fileHeader, sizeof(BmpFileHeader), 1, file); + fwrite(&header, header.core.size, 1, file); + fwrite(bitMask, sizeof(uint32_t), bitMaskCnt, file); + + if(profile == LEXLIB_RGB){ // RGB + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); + LexlibColor pixel = { + .r = imgPixel.b, + .g = imgPixel.g, + .b = imgPixel.r, + .a = imgPixel.a + }; + fwrite(&pixel, pixelSize, 1, file); + } + for(uint8_t pad = 0; pad < rowPadding; pad++) + fputc(0, file); + }} + + if(profile == LEXLIB_RGBA){ // RGB + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor imgPixel = lexlibImagePixelGet(image, x, y); + LexlibColor pixel = { + .r = imgPixel.b, + .g = imgPixel.g, + .b = imgPixel.r, + .a = imgPixel.a + }; + fwrite(&pixel, pixelSize, 1, file); + } + for(uint8_t pad = 0; pad < rowPadding; pad++) + fputc(0, file); + }} + + if(profile == LEXLIB_RGB555){ // RGB555 + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); + uint16_t pixel = 0x0000; + + pixel |= (uint16_t)rintf((imgPixel.r / 255.0f) * 31.0f) << 10; + pixel |= (uint16_t)rintf((imgPixel.g / 255.0f) * 31.0f) << 5; + pixel |= (uint16_t)rintf((imgPixel.b / 255.0f) * 31.0f); + + fwrite(&pixel, pixelSize, 1, file); + } + for(uint8_t pad = 0; pad < rowPadding; pad++) + fputc(0, file); + }} + + if(profile == LEXLIB_RGB565){ // RGB565 + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); + uint16_t pixel = 0x0000; + + pixel |= (uint16_t)rintf((imgPixel.r / 255.0f) * 31.0f) << 11; + pixel |= (uint16_t)rintf((imgPixel.g / 255.0f) * 63.0f) << 5; + pixel |= (uint16_t)rintf((imgPixel.b / 255.0f) * 31.0f); + + fwrite(&pixel, pixelSize, 1, file); + } + for(uint8_t pad = 0; pad < rowPadding; pad++) + fputc(0, file); + }} + + fflush(file); + fclose(file); + return LEXLIB_OK; +} + /* Static Functions */ #define UNSIGNED_BIT_WIDTH (CHAR_BIT * sizeof(unsigned)) LEXLIB_INLINE unsigned bitGet(unsigned x, unsigned index){ diff --git a/test/imageBmp.c b/test/imageBmp.c index ea8ab57..cf01290 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -28,8 +28,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/plasma16.png"); + lexlibImageSaveBmpEx(&image, "resources/out/plasma16.bmp", LEXLIB_RGB565, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -54,8 +53,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/plasma24.png"); + lexlibImageSaveBmpEx(&image, "resources/out/plasma24.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -80,8 +78,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/plasma32.png"); + lexlibImageSaveBmpEx(&image, "resources/out/plasma32.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -106,8 +103,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/macintosh.png"); + lexlibImageSaveBmpEx(&image, "resources/out/macintosh.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -132,8 +128,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/macintosh_alpha70.png"); + lexlibImageSaveBmpEx(&image, "resources/out/macintosh_alpha70.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -158,8 +153,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/macintosh_8bpp.png"); + lexlibImageSaveBmpEx(&image, "resources/out/macintosh_8bpp.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -184,8 +178,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/puro.png"); + lexlibImageSaveBmpEx(&image, "resources/out/puro.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -210,8 +203,7 @@ void testImageBmp(void){ return; } - lexlibImageFlip(&image, LEXLIB_FLIP_Y); - lexlibImageSave(&image, "resources/out/floppy.png"); + lexlibImageSaveBmpEx(&image, "resources/out/floppy.bmp", 0, 0); lexlibImageDelete(&image); testEnd(1, NULL);} } \ No newline at end of file -- GitLab From 4317a8aa903e9b2c59e12f4ef65f8b1622d388b0 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 15:14:48 -0400 Subject: [PATCH 42/64] CMAKE_BUILD_TYPE check for debug in CMakeLists --- CHANGELOG.md | 3 +++ CMakeLists.txt | 7 ++++++- linux.sh | 24 ++++++++++++++++++------ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e3fc1..292b04b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ + lexlibImagePixelSet(). + lexlibFileBytes(): + lexlibPowu(); ++ lexlibImageLoadBmp(); ++ lexlibImagePixelGet(); ++ lexlibImageSaveBmpEx(); ## 1.4.0 + function lexlibStrCopy(). diff --git a/CMakeLists.txt b/CMakeLists.txt index ce0dec1..8200e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,12 @@ target_sources(${PROJECT_NAME} PRIVATE # options/properties set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) target_include_directories(${PROJECT_NAME} PRIVATE include) -target_compile_options(${PROJECT_NAME} PRIVATE -O3 -Wall -Wextra -DLEXLIB_VERSION="${PROJECT_VERSION}") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${PROJECT_NAME} PRIVATE -g -Wall -Wextra -fsanitize=address -DLEXLIB_VERSION="${PROJECT_VERSION}") + target_link_libraries(${PROJECT_NAME} PUBLIC m -fsanitize=address) +else() + target_compile_options(${PROJECT_NAME} PRIVATE -O3 -Wall -Wextra -DLEXLIB_VERSION="${PROJECT_VERSION}") +endif() set_source_files_properties(src/path.c PROPERTIES COMPILE_FLAGS -Wdeprecated-declarations) # explicitly define public headers diff --git a/linux.sh b/linux.sh index 24a36f0..23ec2e3 100755 --- a/linux.sh +++ b/linux.sh @@ -14,30 +14,42 @@ function message () { if [[ $1 == cmake ]]; then if [[ $2 == all ]]; then - ./linux.sh cmake linux - ./linux.sh cmake mingw32 - ./linux.sh cmake mingw64 + ./linux.sh cmake linux $3 + ./linux.sh cmake mingw32 $3 + ./linux.sh cmake mingw64 $3 exit 0 fi if [[ $2 == linux ]]; then message "cmake linux" mkdir -p build/linux cd build/linux - cmake ../.. + if [[ $3 == debug ]]; then + cmake ../.. -DCMAKE_BUILD_TYPE=Debug + else + cmake ../.. + fi exit 0 fi if [[ $2 == mingw32 ]]; then message "cmake mingw32" mkdir -p build/mingw32 cd build/mingw32 - cmake ../.. -DCMAKE_TOOLCHAIN_FILE=../../resources/tc_mingw32.cmake + if [[ $3 == debug ]]; then + cmake ../.. -DCMAKE_TOOLCHAIN_FILE=../../resources/tc_mingw32.cmake -DCMAKE_BUILD_TYPE=Debug + else + cmake ../.. -DCMAKE_TOOLCHAIN_FILE=../../resources/tc_mingw32.cmake + fi exit 0 fi if [[ $2 == mingw64 ]]; then message "cmake mingw64" mkdir -p build/mingw64 cd build/mingw64 - cmake ../.. -DCMAKE_TOOLCHAIN_FILE=../../resources/tc_mingw64.cmake + if [[ $3 == debug ]]; then + cmake ../.. -DCMAKE_TOOLCHAIN_FILE=../../resources/tc_mingw64.cmake -DCMAKE_BUILD_TYPE=Debug + else + cmake ../.. -DCMAKE_TOOLCHAIN_FILE=../../resources/tc_mingw64.cmake + fi exit 0 fi if [[ $2 == install ]]; then -- GitLab From 3adab047ea153f5b4efa9af428a14b934da001ff Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 15:29:30 -0400 Subject: [PATCH 43/64] fixed strPath buffer overflows --- src/str.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/str.c b/src/str.c index 302a0e9..fe61616 100644 --- a/src/str.c +++ b/src/str.c @@ -66,7 +66,7 @@ uint8_t lexlibStrPathPush(char** str, const char* add){ if(strLen == 0 || addLen == 0) return LEXLIB_INVALID_VALUE; - char* path = realloc(*str, (len+1) * sizeof(char)); + char* path = realloc(*str, (len+2) * sizeof(char)); char* offPath = path+strLen; if(!path) @@ -167,10 +167,5 @@ uint8_t lexlibStrPathAsFile(char** str){ else return LEXLIB_OK; - char* new = realloc(*str, (len) * sizeof(char)); - if(!new) - return LEXLIB_OUT_OF_MEMORY; - - *str = new; return LEXLIB_OK; } \ No newline at end of file -- GitLab From b8034ac304b0f02dda96716b8c167b2e7d084a78 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 15:51:56 -0400 Subject: [PATCH 44/64] fixed buffer over in imageFlip (flip x) --- src/image.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/image.c b/src/image.c index 34f4a13..bf9fd38 100644 --- a/src/image.c +++ b/src/image.c @@ -93,7 +93,7 @@ uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags){ return imgStat; // common - size_t rowSize = image->width * image->channels * (image->depth / 8); + size_t rowSize = image->width * (image->bpp / 8); // flip y if(flags & LEXLIB_FLIP_Y){ @@ -150,20 +150,23 @@ uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags){ // flip x if(flags & LEXLIB_FLIP_X){ // allocate a buffer - uint8_t* tmp = calloc(rowSize, sizeof(uint8_t)); + uint8_t* tmp = malloc(rowSize * sizeof(uint8_t)); if(!tmp) return LEXLIB_OUT_OF_MEMORY; size_t offset = 0; - uint8_t pixelSize = image->channels * (image->depth / 8); - + uint8_t pixelSize = image->bpp / 8; + printf("ps %u\n", pixelSize); + printf("rs %u\n", rowSize); // copy pixel per pixel. for(size_t y = 0; y < image->height; y++){ memcpy(tmp, image->data+offset, rowSize); - size_t offsetX = rowSize; - for(size_t x = 0; x < rowSize; x+=pixelSize){ - memcpy(image->data+(offsetX+offset), tmp+x, pixelSize); - offsetX-=pixelSize; + size_t xOffset = rowSize - pixelSize; + size_t tmpOffset = 0; + for(size_t x = 0; x < image->width; x++){ + memcpy(image->data+(xOffset + offset), tmp+tmpOffset, pixelSize); + xOffset -= pixelSize; + tmpOffset += pixelSize; } offset += rowSize; } -- GitLab From 6f1399ffa8335be98ed64e300484806b51aa4f73 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 15:52:59 -0400 Subject: [PATCH 45/64] removed printfs from image.c --- src/image.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/image.c b/src/image.c index bf9fd38..f044202 100644 --- a/src/image.c +++ b/src/image.c @@ -156,8 +156,6 @@ uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags){ size_t offset = 0; uint8_t pixelSize = image->bpp / 8; - printf("ps %u\n", pixelSize); - printf("rs %u\n", rowSize); // copy pixel per pixel. for(size_t y = 0; y < image->height; y++){ memcpy(tmp, image->data+offset, rowSize); -- GitLab From aebf47df73d90dc3b207184e209093f8d60cc36a Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 16:33:14 -0400 Subject: [PATCH 46/64] fixed bmp loader over_reads --- src/image/bmp.c | 81 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index 45056e7..ef7d43a 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -398,10 +398,25 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ }} if(profile == LEXLIB_RGB_BITFIELDS){ // bitfields without alpha - uint32_t* bmpOffset = (uint32_t*)(bmp.data + fileHeader->offset); + uint8_t* bmpOffset = bmp.data + fileHeader->offset; for(uint32_t y = 0; y < image.height; y++){ for(uint32_t x = 0; x < image.width; x++){ - uint32_t bmpPixel = *bmpOffset; + // get the pixel + uint32_t bmpPixel = 0x00000000; + switch(bmpPixelSize){ + case 4: + bmpPixel |= bmpOffset[3] << 24; + LEXLIB_FALLTHROUGH; + case 3: + bmpPixel |= bmpOffset[2] << 16; + LEXLIB_FALLTHROUGH; + case 2: + bmpPixel |= bmpOffset[1] << 8; + LEXLIB_FALLTHROUGH; + case 1: + bmpPixel |= bmpOffset[0]; + break; + } LexlibColor imgPixel = { .r = rintf((((bmpPixel & bitMask.rb) >> bitMask.rs) / colorMax.r) * 255.0f), @@ -411,16 +426,32 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ }; lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); - bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpPixelSize); + bmpOffset += bmpPixelSize; } - bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); + bmpOffset += bmpRowPadding; }} if(profile == LEXLIB_RGBA_BITFIELDS){ // bitfields with alpha - uint32_t* bmpOffset = (uint32_t*)(bmp.data + fileHeader->offset); + uint8_t* bmpOffset = (uint8_t*)(bmp.data + fileHeader->offset); for(uint32_t y = 0; y < image.height; y++){ for(uint32_t x = 0; x < image.width; x++){ - uint32_t bmpPixel = *bmpOffset; + // get the pixel + uint32_t bmpPixel = 0x00000000; + switch(bmpPixelSize){ + case 4: + bmpPixel |= bmpOffset[3] << 24; + LEXLIB_FALLTHROUGH; + case 3: + bmpPixel |= bmpOffset[2] << 16; + LEXLIB_FALLTHROUGH; + case 2: + bmpPixel |= bmpOffset[1] << 8; + LEXLIB_FALLTHROUGH; + case 1: + bmpPixel |= bmpOffset[0]; + break; + } + LexlibColor imgPixel = { .r = rintf((((bmpPixel & bitMask.rb) >> bitMask.rs) / colorMax.r) * 255.0f), .g = rintf((((bmpPixel & bitMask.gb) >> bitMask.gs) / colorMax.g) * 255.0f), @@ -429,19 +460,34 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ }; lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); - bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpPixelSize); + bmpOffset += bmpPixelSize; } - bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); + bmpOffset += bmpRowPadding; }} - // here bitMask.rb is used to determine how much of bmpOffset should be used; - // 0x000000FF for 8 bit, 0x0000FFFF for 16 and so on. if(profile == LEXLIB_RGB_PALETTE){ // color palette - uint32_t* bmpOffset = (uint32_t*)(bmp.data + fileHeader->offset); + uint8_t* bmpOffset = (uint8_t*)(bmp.data + fileHeader->offset); uint32_t* palette = (uint32_t*)(bmp.data + bmpHeadOffset); for(uint32_t y = 0; y < image.height; y++){ for(uint32_t x = 0; x < image.width; x++){ - uint32_t bmpPixel = palette[*bmpOffset & bitMask.rb]; + // get the pixel + uint32_t bmpPixel = 0x00000000; + switch(bmpPixelSize){ + case 4: + bmpPixel |= bmpOffset[3] << 24; + LEXLIB_FALLTHROUGH; + case 3: + bmpPixel |= bmpOffset[2] << 16; + LEXLIB_FALLTHROUGH; + case 2: + bmpPixel |= bmpOffset[1] << 8; + LEXLIB_FALLTHROUGH; + case 1: + bmpPixel |= bmpOffset[0]; + break; + } + bmpPixel = palette[bmpPixel]; + LexlibColor imgPixel = { .r = bmpPixel >> 16, .g = bmpPixel >> 8, @@ -450,15 +496,14 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ }; lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); - bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpPixelSize); + bmpOffset += bmpPixelSize; } - bmpOffset = (uint32_t*)(((uint8_t*)bmpOffset) + bmpRowPadding); + bmpOffset += bmpRowPadding; }} - if(profile == LEXLIB_1BPP){ + if(profile == LEXLIB_1BPP){ // one bit per pixel uint8_t* bmpOffset = bmp.data + fileHeader->offset; uint8_t* palette = bmp.data + bmpHeadOffset; - // bmpRowPadding = 1; for(uint32_t y = 0; y < image.height; y++){ for(uint32_t x = 0; x < image.width;){ LexlibColor imgPixel; @@ -477,7 +522,11 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ imgPixel.b = palette[0]; imgPixel.a = 0xFF; } + /* in case the pixel is out of bounds pixelSet will handle it. + it would happend with images that aren't byte aligned, a width of 15 uses + 1111111111111110 which is not completly aligned and the last bit will be read and set. */ lexlibImagePixelSet(&image, x, y, imgPixel, LEXLIB_NONE); + x++; } while(bit != 0); -- GitLab From 64215970b9636603707566d11babce968d52e94e Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 17:07:28 -0400 Subject: [PATCH 47/64] fixed linkedlist memleak and testStrPath memleak --- src/linkedList.c | 31 +++++++++++-------------------- test/strPath.c | 1 + 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/linkedList.c b/src/linkedList.c index ae118a6..53ddd6e 100644 --- a/src/linkedList.c +++ b/src/linkedList.c @@ -33,40 +33,24 @@ void lexlibLinkedListDelete(struct LexlibLinkedList* list){ } void lexlibLinkedListFree(struct LexlibLinkedList* list){ - typeof(list->head) curr = list->head; - typeof(list->head)* all = calloc(list->count, sizeof(list->head)); + LexlibLinkedNode* curr = list->head; + LexlibLinkedNode* old = list->head; for(size_t i = 0; ;i++){ if(!curr) break; if(curr->data) free(curr->data); - all[i] = curr; + old = curr; curr = curr->next; + free(old); } - for(size_t i = 0; i < list->count; i++){ - free(all[i]); - } - - free(all); list->head = NULL; list->count = 0; } uint8_t lexlibLinkedListAdd(struct LexlibLinkedList* list, void* data){ - if(!list->head){ - list->head = malloc(sizeof(list->head)); - } else { - typeof(list->head) newMem = realloc(list->head, (list->count+1) * sizeof(list->head)); - if(!newMem) - return 1; - list->head = newMem; - } - - if(!list->head) - return 1; - struct LexlibLinkedNode* node = malloc(sizeof(struct LexlibLinkedNode)); if(!node) return 1; @@ -74,6 +58,13 @@ uint8_t lexlibLinkedListAdd(struct LexlibLinkedList* list, void* data){ node->data = data; node->next = NULL; + if(list->head){ + typeof(list->head) newMem = realloc(list->head, (list->count+1) * sizeof(LexlibLinkedNode*)); + if(!newMem) + return 1; + list->head = newMem; + } + if(list->count == 0){ list->head = node; } else { diff --git a/test/strPath.c b/test/strPath.c index 3171ce5..48473a4 100644 --- a/test/strPath.c +++ b/test/strPath.c @@ -25,5 +25,6 @@ void testStrPath(void){ if(strcmp(path, patht)) err = 0; + free(path); testEnd(err, NULL); } \ No newline at end of file -- GitLab From 33cc298f154fc7f07c9b292a3ffe4806430ba433 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 17:39:21 -0400 Subject: [PATCH 48/64] support for saving bmp header v3 --- CHANGELOG.md | 3 +++ src/image/bmp.c | 64 +++++++++++++++++++++++++++++++++++++++++++++---- test/imageBmp.c | 2 +- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 292b04b..02921d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ + lexlibImageLoadBmp(); + lexlibImagePixelGet(); + lexlibImageSaveBmpEx(); ++ fixed strPath buffer overflows. ++ fixed imageFlip X buffer overflow. ++ fixed linkedList memleaks. ## 1.4.0 + function lexlibStrCopy(). diff --git a/src/image/bmp.c b/src/image/bmp.c index ef7d43a..bd1dd9a 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -565,6 +565,7 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin switch(bmpheader){ // supported case LEXLIB_BMP_INFO: + case LEXLIB_BMP_INFO_V3: break; default: return LEXLIB_UNSUPORTED; @@ -609,14 +610,67 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin default: return LEXLIB_UNSUPORTED; } + } + + if(bmpheader == LEXLIB_BMP_INFO_V3){ + header.info3.size = sizeof(BmpInfoHeader3); + header.info3.width = image->width; + header.info3.height = image->height; + header.info3.planes = 1; + header.info3.xresm = 2835; + header.info3.yresm = 2835; + header.info3.colorPalette = 0; + header.info3.colorImportant = 0; - // calculate stride and image size - rowStride = ((((header.info.width * header.info.bitdepth) + 31) & ~31) >> 3); - rowPadding = (rowStride) - ceil(header.info.width * (header.info.bitdepth / 8.0f)); - header.info.imagesize = image->datasize + (header.info.height * rowPadding); - fileHeader.size += header.info.imagesize; + // set profile + switch(profile){ + case LEXLIB_RGB555: + header.info3.bitdepth = 16; + header.info3.compression = BMP_BITFIELDS; + pixelSize = 2; + header.info3.redbitmask = 0x00007C00; + header.info3.greenbitmask = 0x000003E0; + header.info3.bluebitmask = 0x0000001F; + header.info3.alphabitmask = 0x00000000; + break; + case LEXLIB_RGB565: + header.info3.bitdepth = 16; + header.info3.compression = BMP_BITFIELDS; + pixelSize = 2; + header.info3.redbitmask = 0x0000F800; + header.info3.greenbitmask = 0x000007E0; + header.info3.bluebitmask = 0x0000001F; + header.info3.alphabitmask = 0x00000000; + break; + case LEXLIB_RGB: + header.info3.bitdepth = 24; + header.info3.compression = BMP_BITFIELDS; + pixelSize = 3; + header.info3.redbitmask = 0x00FF0000; + header.info3.greenbitmask = 0x0000FF00; + header.info3.bluebitmask = 0x000000FF; + header.info3.alphabitmask = 0x00000000; + break; + case LEXLIB_RGBA: + header.info3.bitdepth = 32; + header.info3.compression = BMP_BITFIELDS; + pixelSize = 4; + header.info3.redbitmask = 0x00FF0000; + header.info3.greenbitmask = 0x0000FF00; + header.info3.bluebitmask = 0x000000FF; + header.info3.alphabitmask = 0xFF000000; + break; + default: + return LEXLIB_UNSUPORTED; + } } + // calculate stride and image size + rowStride = ((((header.info.width * header.info.bitdepth) + 31) & ~31) >> 3); + rowPadding = (rowStride) - ceil(header.info.width * (header.info.bitdepth / 8.0f)); + header.info.imagesize = image->datasize + (header.info.height * rowPadding); + fileHeader.size += header.info.imagesize; + fileHeader.size += header.core.size; fileHeader.size += bitMaskCnt * sizeof(uint32_t); fileHeader.offset = sizeof(BmpFileHeader) + header.core.size + (bitMaskCnt * sizeof(uint32_t)); diff --git a/test/imageBmp.c b/test/imageBmp.c index cf01290..a8bcaff 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -128,7 +128,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/macintosh_alpha70.bmp", 0, 0); + lexlibImageSaveBmpEx(&image, "resources/out/macintosh_alpha70.bmp", 0, LEXLIB_BMP_INFO_V3); lexlibImageDelete(&image); testEnd(1, NULL);} -- GitLab From 827f2ce8788b85121d8b100875960f4923f79e9d Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 18:06:39 -0400 Subject: [PATCH 49/64] imageSaveBmp() --- include/lexlib/image.h | 10 +++++++++- src/image/bmp.c | 39 ++++++++++++++++++++++++++++++--------- test/imageBmp.c | 2 +- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 1e1d3ed..e556d13 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -113,7 +113,15 @@ LEXLIB_EXTERN uint8_t lexlibImageSave(const LexlibImage* image, const char* file // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_OPERATION, LEXLIB_OUT_OF_MEMORY, LEXLIB_ERROR). LEXLIB_EXTERN LexlibImage lexlibImageLoadBmp(const char* filename); -// saves a bmp to a file +// saves a image to a bmp file +// sets the optimal bmp header and profile for the image. +// returns LEXLIB_OK on success, on error LEXLIB_CANT_WRITE. +LEXLIB_EXTERN uint8_t lexlibImageSaveBmp(const LexlibImage* image, const char* filename); + +// saves a image to a bmp file with extra options +// for defaults profile and header can be left 0. +// if the image/profile contains alpha its recommended to use LEXLIB_BMP_INFO_V3 for header. +// returns LEXLIB_OK on success, on error LEXLIB_UNSUPORTED or LEXLIB_CANT_WRITE. LEXLIB_EXTERN uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uint8_t profile, uint8_t header); // loads a png into LexlibImage diff --git a/src/image/bmp.c b/src/image/bmp.c index bd1dd9a..b74b8c6 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -35,7 +35,7 @@ ◇ ◇ ?? | ?..?? bpp | not sure about which one it is ● ● BmpInfoHeader | 1..32 bpp ◇ ◇ BmpInfoHeader2 | ?..?? bpp | not documented | no struct exist - ● ◯ BmpInfoHeader3 | ?..?? bpp | not officially documented + ● ● BmpInfoHeader3 | ?..?? bpp | not officially documented ◯ ◯ BmpInfoHeader4 | 0..32 bpp ◯ ◯ BmpInfoHeader5 | 0..32 bpp @@ -58,14 +58,14 @@ > pixel formats left-most pixel in the most-significant bit of the first byte. - C I 2 3 4 5 - ● ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table - ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table - ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table - ◯ ● ◯ ● ◯ ◯ 8bpp : 256 colors | table - ◯ ● ◯ ● ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. - ◯ ● ◯ ● ◯ ◯ 24bpp: 16777216 colors | b,g,r. - ◯ ● ◯ ● ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. + S C I 2 3 4 5 + ◯ ● ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table + ◯ ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table + ◯ ◯ ◯ ◯ ◯ ◯ ◯ 4bpp : 16 colors | table + ◯ ◯ ● ◯ ● ◯ ◯ 8bpp : 256 colors | table + ● ◯ ● ◯ ● ◯ ◯ 16bpp: 65536 colors | 555, 565, bitmask. + ● ◯ ● ◯ ● ◯ ◯ 24bpp: 16777216 colors | b,g,r. + ● ◯ ● ◯ ● ◯ ◯ 32bpp: 4294967296 colors | a,b,g,r. > pixel storage The size of each row is rounded up to a multiple of 4 bytes. @@ -539,6 +539,27 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ return image; } +uint8_t lexlibImageSaveBmp(const LexlibImage* image, const char* filename){ + uint8_t profile = image->profile; + uint8_t bmpHeader; + + // set the optimal header + switch(profile){ + case LEXLIB_GRAY: + case LEXLIB_RGB: + bmpHeader = LEXLIB_BMP_INFO; + break; + case LEXLIB_RGBA: + bmpHeader = LEXLIB_BMP_INFO_V3; + break; + default: + bmpHeader = 0; + break; + } + + return lexlibImageSaveBmpEx(image, filename, profile, bmpHeader); +} + uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uint8_t profile, uint8_t bmpheader){ BmpFileHeader fileHeader = {0}; MasterHeader header = {0}; diff --git a/test/imageBmp.c b/test/imageBmp.c index a8bcaff..8a1cf01 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -128,7 +128,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/macintosh_alpha70.bmp", 0, LEXLIB_BMP_INFO_V3); + lexlibImageSaveBmp(&image, "resources/out/macintosh_alpha70.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} -- GitLab From 0a8adc6a552f9c9772e8be8997c8edfe9cefc354 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 18:11:42 -0400 Subject: [PATCH 50/64] added bmp to imageLoad() and imageSave() --- src/image.c | 4 ++++ src/image/bmp.c | 5 ----- test/imageBmp.c | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/image.c b/src/image.c index f044202..9803630 100644 --- a/src/image.c +++ b/src/image.c @@ -380,6 +380,8 @@ LexlibImage lexlibImageLoad(const char* filename){ if(offset){ if(strcmp(offset, ".png") == 0) return lexlibImageLoadPng(filename); + if(strcmp(offset, ".bmp") == 0) + return lexlibImageLoadBmp(filename); } image.data = NULL; @@ -393,6 +395,8 @@ uint8_t lexlibImageSave(const LexlibImage* image, const char* filename){ if(offset){ if(strcmp(offset, ".png") == 0) return lexlibImageSavePng(image, filename); + if(strcmp(offset, ".bmp") == 0) + return lexlibImageSaveBmp(image, filename); } return LEXLIB_INVALID_FILENAME; diff --git a/src/image/bmp.c b/src/image/bmp.c index b74b8c6..da6022d 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -696,11 +696,6 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin fileHeader.size += bitMaskCnt * sizeof(uint32_t); fileHeader.offset = sizeof(BmpFileHeader) + header.core.size + (bitMaskCnt * sizeof(uint32_t)); - printf("pad %u\n", rowPadding); - printf("head: %u\n", fileHeader.size); - printf("offs: %u\n", fileHeader.offset); - printf("siz: %u\n", header.info.imagesize); - // create file FILE* file = fopen(filename, "wb"); if(!file) diff --git a/test/imageBmp.c b/test/imageBmp.c index 8a1cf01..18267a2 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -83,7 +83,7 @@ void testImageBmp(void){ testEnd(1, NULL);} {testStart("bmp macintosh"); - LexlibImage image = lexlibImageLoadBmp("resources/image/macintosh.bmp"); + LexlibImage image = lexlibImageLoad("resources/image/macintosh.bmp"); if(!image.data) switch(image.depth){ case LEXLIB_CANT_OPEN: @@ -128,7 +128,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmp(&image, "resources/out/macintosh_alpha70.bmp"); + lexlibImageSave(&image, "resources/out/macintosh_alpha70.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} -- GitLab From 5ae70a8a7f71b5b2b491497e5a2b6ed3ae825f41 Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 18:13:29 -0400 Subject: [PATCH 51/64] removed unused lexlibImagePixel() function declaration --- include/lexlib/image.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index e556d13..938f211 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -85,9 +85,6 @@ LEXLIB_EXTERN uint8_t lexlibImageValidate(const LexlibImage* image); // return LEXLIB_OK on success, on error might return LEXLIB_INVALID_VALUE or LEXLIB_OUT_OF_MEMORY. LEXLIB_EXTERN uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags); -// gets a pixel from a image at a coordinate -LEXLIB_EXTERN LexlibColor lexlibImagePixel(const LexlibImage* image, uint32_t x, uint32_t y); - // sets a pixel of a LexlibImage // according transformations to the passed color are performed depending of the image profile. // alpha is premultiplied if the image does not have a alpha channel. -- GitLab From 843017061b1c16207c0f702fe1d3de7d8c8783bb Mon Sep 17 00:00:00 2001 From: alexevier Date: Tue, 16 May 2023 23:31:57 -0400 Subject: [PATCH 52/64] imagePixelGet() bounds check --- include/lexlib/image.h | 3 ++- src/image.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 938f211..5fcdffa 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -90,11 +90,12 @@ LEXLIB_EXTERN uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags); // alpha is premultiplied if the image does not have a alpha channel. // if image is gray the color gets averaged. // returns LEXLIB_OK on success, LEXLIB_INVALID_OPERATION on error. -LEXLIB_EXTERN uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode); +LEXLIB_EXTERN uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode); // gets a pixel from a LexlibImage // according transformations to the returned color are performed depending of the image profile. // alpha is at max if the image does not have a alpha channel. +// in case of error LEXLIB_COLOR_MAGENTA is returned. LEXLIB_EXTERN LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y); // loads a image into a LexlibImage. diff --git a/src/image.c b/src/image.c index 9803630..7999aed 100644 --- a/src/image.c +++ b/src/image.c @@ -175,7 +175,7 @@ uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags){ return LEXLIB_OK; } -uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode){ +uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode){ if(x >= image->width || y >= image->height) return LEXLIB_INVALID_OPERATION; @@ -305,6 +305,9 @@ uint8_t lexlibImagePixelSet(const LexlibImage* image, uint32_t x, uint32_t y, Le } LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y){ + if(x >= image->width || y >= image->height) + return LEXLIB_COLOR_MAGENTA; + uint8_t pixelSize = image->bpp / 8; size_t rowsize = image->width * pixelSize; size_t offset = (y * rowsize) + (x * pixelSize); -- GitLab From d75502f803325bb73c1579c24ffc46ad4326d9e1 Mon Sep 17 00:00:00 2001 From: alexevier Date: Wed, 17 May 2023 03:29:52 -0400 Subject: [PATCH 53/64] documentation updates --- CHANGELOG.md | 9 +- documentation/color.html | 1 + documentation/file.html | 45 +++++++++- documentation/frontpage.html | 14 +++ documentation/image.html | 164 ++++++++++++++++++++++++++++++++++- documentation/lexlib.html | 7 ++ include/lexlib/image.h | 4 +- 7 files changed, 231 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02921d4..c223f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ ## 1.5.0 (under development) -+ lexlibColor16Blend(). -+ lexlibImagePixelSet(). -+ lexlibFileBytes(): ++ lexlibFileBytes(); + lexlibPowu(); -+ lexlibImageLoadBmp(); ++ lexlibColor16Blend(); ++ lexlibImagePixelSet(); + lexlibImagePixelGet(); ++ lexlibImageLoadBmp(); ++ lexlibImageSaveBmp(); + lexlibImageSaveBmpEx(); + fixed strPath buffer overflows. + fixed imageFlip X buffer overflow. diff --git a/documentation/color.html b/documentation/color.html index 42cdae4..197eefe 100644 --- a/documentation/color.html +++ b/documentation/color.html @@ -145,6 +145,7 @@

Functions

    LexlibColor lexlibColorBlend(const LexlibColor dst, const LexlibColor src, uint8_t mode);
    + LexlibColor16 lexlibColor16Blend(const LexlibColor16 dst, const LexlibColor16 src, uint8_t mode);
    LexlibColorFlt lexlibColorFltBlend(const LexlibColorFlt dst, const LexlibColorFlt src, uint8_t mode);

diff --git a/documentation/file.html b/documentation/file.html index 7ddfc1d..6837a1e 100644 --- a/documentation/file.html +++ b/documentation/file.html @@ -12,14 +12,28 @@ Frontpage GitLab
- lexlibFileToString(); - lexlibStringToFile(); + Structs + lexlibFileToString() + lexlibStringToFile() + lexlibFileBytes()

lexlib file handeling.
- these functions are defined in <lexlib/file.h> + header: <lexlib/file.h> +

+

+


+

Structs

+

+

    + struct LexlibBytes {
      + uint8_t* data;
      + size_t count; +
    }; +
+


@@ -83,6 +97,7 @@

Example

+ // this example is outdated
const char text[] = "the dog is a good boi";
switch(lexlibStringToFile(text, "path/to/goodbois/file.txt")){
    case 0: printf("file written successfully.\n");
@@ -100,6 +115,30 @@

+

+


+

lexlibFileBytes()

+

+ reads a file as bytes and returns them in a LexlibBytes struct. +

+

Function
+

    LexlibBytes lexlibFileBytes(const char* filename);
+

+

Parameter
+

    const char* filename : name/path of the file to be loaded.
+

+

Return
+

    LexlibBytes : with the data.
+
+
On error bytes.data will be NULL and the error is placed in bytes.count. +
LEXLIB_CANT_OPEN : can't open the file, might not exist or not have permissions. +
LEXLIB_OUT_OF_MEMORY : can't allocate the memory for the file data. +
LEXLIB_ERROR : error parsing the file. +
LEXLIB_PARTIAL_READ : couldn't read the entire file. +
+ +

+

diff --git a/documentation/frontpage.html b/documentation/frontpage.html index 45ef7a9..9f8e866 100644 --- a/documentation/frontpage.html +++ b/documentation/frontpage.html @@ -61,6 +61,20 @@

> Full Changelog

+

1.5.0

+
    +
  • lexlibFileBytes(); +
  • lexlibPowu(); +
  • lexlibColor16Blend(); +
  • lexlibImagePixelSet(); +
  • lexlibImagePixelGet(); +
  • lexlibImageLoadBmp(); +
  • lexlibImageSaveBmp(); +
  • lexlibImageSaveBmpEx(); +
  • fixed strPath buffer overflows. +
  • fixed imageFlip X buffer overflow. +
  • fixed linkedList memleaks. +

1.4.0

  • function lexlibStrCopy(). diff --git a/documentation/image.html b/documentation/image.html index aa6a509..afc6540 100644 --- a/documentation/image.html +++ b/documentation/image.html @@ -18,8 +18,13 @@ lexlibImageDelete() lexlibImageValidate() lexlibImageFlip() + lexlibImagePixelSet() + lexlibImagePixelGet() lexlibImageLoad() lexlibImageSave() + lexlibImageLoadBmp() + lexlibImageSaveBmp() + lexlibImageSaveBmpEx() lexlibImageLoadPng() lexlibImageSavePng()
    @@ -30,14 +35,26 @@ header: <lexlib/image.h>

    - supported formats: png. -

    + supported formats: +
      +
    • bmp : included in lexlib; supports until header v3. +
    • png : libpng and zlib required. +

    - be noted that is required to link libpng for png support, and zlib too. + be noted that is required to link libpng and zlib for png support.

    the raw data in a LexlibImage (image.data) is just pixel data with each pixel being channels * (depth / 8) layed one next to the other, can be used with opengl but you might want to flip the image in the Y axis since pngs have their rows inversed from how opengl takes them.

    +

    + images with a bpp more than 8 are bigendian. (this may change)
    + it's being considered if image data should be in the native machine endianess.
    + it recommended to use the pixelGet and pixelSet functions instead of changing the .data directly +

    +

    + currently the only color profiles supported are RGB w/o alpha and Gray_Scale without alpha. + any loading or saving with a profile the image doesn't work directly with results in a conversion. +


    Constants

    @@ -46,9 +63,16 @@
    LEXLIB_GRAY 0x01
    LEXLIB_RGB  0x03
    LEXLIB_RGBA 0x04 +
    LEXLIB_RGB24  0x03 +
    LEXLIB_RGB555 0x05 +
    LEXLIB_RGB565 0x06
    // flags
    LEXLIB_FLIP_X 0x01
    LEXLIB_FLIP_Y 0x02 +
    // file formats +
    LEXLIB_BMP_CORE 0x0C +
    LEXLIB_BMP_INFO 0x28 (default bmp format) +
    LEXLIB_BMP_INFO_V3 0x38

@@ -58,10 +82,13 @@ struct LexlibImage {

    uint8_t* data;
    + size_t datasize;
    uint32_t width;
    uint32_t height;
    uint8_t channels;
    - uint8_t depth;
    + uint8_t depth; /* bits per color */
    + uint8_t bpp; /* bits per pixel */
    + uint8_t profile;
}; @@ -164,6 +191,62 @@

+

+


+

lexlibImagePixelSet()

+

+ sets a pixel in a image at a coordinate. +

+

+ according transformations to the passed color are performed depending of the image profile.
+ alpha is premultiplied if the image does not have a alpha channel. +

+

Function +

uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode);
+

+

Arguments +

LexlibImage* image : the image to be modified. +
uint32_t x : x coordinate of the pixel. +
uint32_t y : y coordinate of the pixel. +
LexlibColor color : the color to be set. +
uint8_t blendmode : blendmode to be used; check Blend Modes. +
+

+

Returns +

LEXLIB_OK (0): everthing went right. +
LEXLIB_INVALID_VALUE: image containts a invalid value. +
LEXLIB_INVALID_OPERATION: pixel out of bounds. +
+

+

+

+


+

lexlibImagePixelGet()

+

+ gets a pixel from a image at a coordinate. +

+

+ according transformations to the returned color are performed depending of the image profile.
+ alpha is at max if the image does not have a alpha channel. +

+

+ in case of error LEXLIB_COLOR_MAGENTA is returned. +

+

Function +

LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y);
+

+

Arguments +

LexlibImage* image : the image to be modified. +
uint32_t x : x coordinate of the pixel. +
uint32_t y : y coordinate of the pixel. +
+

+

Return +

LexlibColor : requested pixel color. +
LEXLIB_COLOR_MAGENTA : may mean that a error ocurred, very likely out of bounds. +
+

+


lexlibImageLoad()

@@ -207,6 +290,79 @@

+

+


+

lexlibImageLoadBmp()

+

+ loads a bmp file into a LexlibImage.
+

+

Function +

LexlibImage lexlibImageLoadBmp(const char* filename);
+

+

Argument +

const char* filename : path to the file to be loaded.
+

+

Return +

LexlibImage : the image. +
Note: on error image.data will be NULL and the error code will be placed in image.depth. +
Error: LEXLIB_CANT_OPEN : file may not exist or insufficient permissions. +
Error: LEXLIB_UNSUPORTED : bmp has a unsupported feature/format/header. +
Error: LEXLIB_INVALID_FILE_TYPE : file is not a valid bmp, header may be corrupted. +
Error: LEXLIB_INVALID_FILE_DATA : file is a bmp but header was not recognised, may be corrupted. +
Error: LEXLIB_OUT_OF_MEMORY : failed to allocate memory for the image or temporal buffer. +
+

+

+

+


+

lexlibImageSaveBmp()

+

+ saves a LexlibImage as a bmp file. +

+

+ sets the optimal bmp header and profile for the image.
+ performs lexlibImageSaveBmpEx(); +

+

Function +

uint8_t lexlibImageSaveBmp(const LexlibImage* image, const char* filename);
+

+

Arguments +

const LexlibImage* image : the LexlibImage to be saved. +
const char* filename : filename to be saved as. +
+

+

Returns +

LEXLIB_OK (0): image was succesfully saved. +
LEXLIB_CANT_WRITE : can't write the file. +
+

+

+

+


+

lexlibImageSaveBmpEx()

+

+ saves a LexlibImage as a bmp file with extra options. +

+

+ for defaults profile and header can be left 0.
+ if the image/profile contains alpha its recommended to use LEXLIB_BMP_INFO_V3 for header. +

+

Function +

uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uint8_t profile, uint8_t header);
+

+

Arguments +

const LexlibImage* image : the LexlibImage to be saved. +
const char* filename : filename to be saved as. +
uint8_t profile : color profile. (RGB, RGBA, RGB24, RGB555, RGB565) +
uint8_t header: bmp header to be used. (BMP_INFO, BMP_INFO_V3) +
+

+

Returns +

LEXLIB_OK (0): image was succesfully saved. +
LEXLIB_CANT_WRITE : can't write the file. +
+

+


lexlibImageLoadPng()

diff --git a/documentation/lexlib.html b/documentation/lexlib.html index fc329b7..45630bc 100644 --- a/documentation/lexlib.html +++ b/documentation/lexlib.html @@ -110,13 +110,19 @@

file.h

lexlibFileToString()
lexlibStringToFile() +
lexlibFileBytes()

image.h

lexlibImageNew()
lexlibImageDelete()
lexlibImageValidate()
lexlibImageFlip() +
lexlibImagePixelSet() +
lexlibImagePixelGet()
lexlibImageLoad()
lexlibImageSave() +
lexlibImageLoadBmp() +
lexlibImageSaveBmp() +
lexlibImageSaveBmpEx()
lexlibImageLoadPng()
lexlibImageSavePng()

lexlib.h

@@ -134,6 +140,7 @@
lexlibRadToDeg()
lexlibDegToRadf()
lexlibRadToDegf() +
lexlibPowu()
lexlibVec#New()
lexlibVec#NewZero()
lexlibVec#Clone() diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 5fcdffa..8ac833d 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -14,7 +14,7 @@ > Image file formats L S M - ● ◯ ◆ bmp + ● ● ◆ bmp ◯ ◯ ◯ gif ◯ ◯ ◯ jpg ● ● ◯ png | libpng and zlib required @@ -108,7 +108,7 @@ LEXLIB_EXTERN LexlibImage lexlibImageLoad(const char* filename); LEXLIB_EXTERN uint8_t lexlibImageSave(const LexlibImage* image, const char* filename); // loads a bmp into LexlibImage -// on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_OPERATION, LEXLIB_OUT_OF_MEMORY, LEXLIB_ERROR). +// on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_CANT_OPEN, LEXLIB_INVALID_FILE_TYPE, LEXLIB_INVALID_FILE_DATA, LEXLIB_UNSUPORTED, LEXLIB_OUT_OF_MEMORY). LEXLIB_EXTERN LexlibImage lexlibImageLoadBmp(const char* filename); // saves a image to a bmp file -- GitLab From b13279e95ac253c3331aa7cca07072fa5285df3f Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 18 May 2023 20:31:42 -0400 Subject: [PATCH 54/64] imagePixel16Get --- include/lexlib/image.h | 2 + src/image.c | 84 ++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 8ac833d..7a4b96f 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -97,6 +97,8 @@ LEXLIB_EXTERN uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32 // alpha is at max if the image does not have a alpha channel. // in case of error LEXLIB_COLOR_MAGENTA is returned. LEXLIB_EXTERN LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y); +LEXLIB_EXTERN LexlibColor16 lexlibImagePixel16Get(const LexlibImage* image, uint32_t x, uint32_t y); + // loads a image into a LexlibImage. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_INVALID_FILENAME, ... *errors sujected to the actual loader*) diff --git a/src/image.c b/src/image.c index 7999aed..662b5c3 100644 --- a/src/image.c +++ b/src/image.c @@ -308,6 +308,9 @@ LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y if(x >= image->width || y >= image->height) return LEXLIB_COLOR_MAGENTA; + if(image->depth == 16) + return lexlibColor16To8(lexlibImagePixel16Get(image, x, y)); + uint8_t pixelSize = image->bpp / 8; size_t rowsize = image->width * pixelSize; size_t offset = (y * rowsize) + (x * pixelSize); @@ -334,48 +337,51 @@ LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y } } - if(image->depth == 16){ - LexlibColor16 pixel16 = {0}; - uint8_t* pixel16raw = (uint8_t*)&pixel16; - if(image->channels == 1){ - pixel16raw[0] = image->data[offset+1]; - pixel16raw[1] = image->data[offset+0]; - - pixel16.g = pixel16.r; - pixel16.b = pixel16.r; - pixel16.a = 0xFFFF; - - return lexlibColor16To8(pixel16); - } - if(image->channels == 4){ - pixel16raw[0] = image->data[offset+1]; - pixel16raw[1] = image->data[offset+0]; - pixel16raw[2] = image->data[offset+3]; - pixel16raw[3] = image->data[offset+2]; - pixel16raw[4] = image->data[offset+5]; - pixel16raw[5] = image->data[offset+4]; - - pixel16.a = 0xFFFF; - - return lexlibColor16To8(pixel16); - } - if(image->channels == 4){ - pixel16raw[0] = image->data[offset+1]; - pixel16raw[1] = image->data[offset+0]; - pixel16raw[2] = image->data[offset+3]; - pixel16raw[3] = image->data[offset+2]; - pixel16raw[4] = image->data[offset+5]; - pixel16raw[5] = image->data[offset+4]; - pixel16raw[6] = image->data[offset+7]; - pixel16raw[7] = image->data[offset+6]; - - return lexlibColor16To8(pixel16); - } - } - return LEXLIB_COLOR_MAGENTA; } +LexlibColor16 lexlibImagePixel16Get(const LexlibImage* image, uint32_t x, uint32_t y){ + if(x >= image->width || y >= image->height) + return lexlibColorTo16(LEXLIB_COLOR_MAGENTA); + + if(image->depth != 16) + return lexlibColorTo16(lexlibImagePixelGet(image, x, y)); + + uint8_t pixelSize = image->bpp / 8; + size_t rowsize = image->width * pixelSize; + size_t offset = (y * rowsize) + (x * pixelSize); + uint16_t* imgData16 = (uint16_t*)(image->data + offset); + + if(image->channels == 1){ + return (LexlibColor16){ + .r = *imgData16, + .g = *imgData16, + .b = *imgData16, + .a = 0xFFFF + }; + } + + if(image->channels == 3){ + return (LexlibColor16){ + .r = imgData16[0], + .g = imgData16[1], + .b = imgData16[2], + .a = 0xFFFF + }; + } + + if(image->channels == 4){ + return (LexlibColor16){ + .r = imgData16[0], + .g = imgData16[1], + .b = imgData16[2], + .a = imgData16[3] + }; + } + + return lexlibColorTo16(LEXLIB_COLOR_MAGENTA); +} + LexlibImage lexlibImageLoad(const char* filename){ LexlibImage image = LEXLIB_IMAGE_ZERO; -- GitLab From e5e4e9ad725996c7f7ed361c47a29bfb04f773a3 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 18 May 2023 20:37:35 -0400 Subject: [PATCH 55/64] new image.flags element --- include/lexlib/image.h | 3 ++- src/image.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 7a4b96f..86d9a4f 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -42,6 +42,7 @@ struct LexlibImage { uint8_t depth; /* bits per color */ uint8_t bpp; /* bits per pixel */ uint8_t profile; + uint8_t flags; }; #ifndef __cplusplus @@ -67,7 +68,7 @@ typedef struct LexlibImage LexlibImage; #define LEXLIB_BMP_INFO_V3 0x38 // "constants" -#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0, 0, 0}) +#define LEXLIB_IMAGE_ZERO ((LexlibImage){NULL, 0, 0, 0, 0, 0, 0, 0, 0x00}) // creates a new LexlibImage and allocates its memory. // on error image.data will be NULL and the error code will be placed in image.depth. (LEXLIB_OUT_OF_MEMORY) diff --git a/src/image.c b/src/image.c index 662b5c3..b36822b 100644 --- a/src/image.c +++ b/src/image.c @@ -17,7 +17,8 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin .channels = 0, .depth = depth, .bpp = 0, - .profile = profile + .profile = profile, + .flags = 0, }; // corrections @@ -42,6 +43,7 @@ LexlibImage lexlibImageNew(uint32_t width, uint32_t height, uint8_t profile, uin break; case LEXLIB_RGBA: image.channels = 4; + image.flags |= 0x80; break; default: image.profile = LEXLIB_RGB; -- GitLab From 1d69e2401222eab411892c6614bde076bc6cebec Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 18 May 2023 21:18:04 -0400 Subject: [PATCH 56/64] imagePixelSet() and imageProfileChange() --- include/lexlib/image.h | 6 + src/image.c | 369 ++++++++++++++++++++++++++++++----------- 2 files changed, 275 insertions(+), 100 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 86d9a4f..06e0dff 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -86,12 +86,18 @@ LEXLIB_EXTERN uint8_t lexlibImageValidate(const LexlibImage* image); // return LEXLIB_OK on success, on error might return LEXLIB_INVALID_VALUE or LEXLIB_OUT_OF_MEMORY. LEXLIB_EXTERN uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags); +// changes the color profile of a LexlibImage +// performs color convertions. +// returns LEXLIB_OK, on error LEXLIB_INVALID_VALUE or LEXLIB_OUT_OF_MEMORY. +LEXLIB_EXTERN uint8_t lexlibImageProfileChange(LexlibImage* image, uint8_t profile); + // sets a pixel of a LexlibImage // according transformations to the passed color are performed depending of the image profile. // alpha is premultiplied if the image does not have a alpha channel. // if image is gray the color gets averaged. // returns LEXLIB_OK on success, LEXLIB_INVALID_OPERATION on error. LEXLIB_EXTERN uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode); +LEXLIB_EXTERN uint8_t lexlibImagePixel16Set(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor16 color, uint8_t blendmode); // gets a pixel from a LexlibImage // according transformations to the returned color are performed depending of the image profile. diff --git a/src/image.c b/src/image.c index b36822b..999b1b3 100644 --- a/src/image.c +++ b/src/image.c @@ -177,130 +177,299 @@ uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags){ return LEXLIB_OK; } -uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode){ - if(x >= image->width || y >= image->height) - return LEXLIB_INVALID_OPERATION; - - size_t pixelsize = image->channels * (image->depth / 8); - size_t rowsize = image->width * pixelsize; - size_t offset = (y * rowsize) + (x * pixelsize); +uint8_t lexlibImageProfileChange(LexlibImage* image, uint8_t profile){ + // nothing to do if image has the requested profile + if(image->profile == profile) + return LEXLIB_OK; - if(image->depth == 8){ - if(image->channels == 1){ - image->data[offset] = lexlibColorGray(color); + if(image->depth == 8){/* 8bpc */ + if(profile == LEXLIB_GRAY){ + size_t dataOffset = 0; + + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor pixel = lexlibImagePixelGet(image, x, y); + image->data[dataOffset++] = lexlibColorGray(pixel); + } + } + + image->datasize = image->width * image->height; + image->channels = 1; + image->bpp = 8; + image->profile = LEXLIB_GRAY; + image->flags &= ~0x80; + + uint8_t* newData = realloc(image->data, image->datasize); + if(!newData) + return LEXLIB_OUT_OF_MEMORY; + image->data = newData; + return LEXLIB_OK; } - if(image->channels == 3){ - LexlibColor pixel = { - .r = image->data[offset+0], - .g = image->data[offset+1], - .b = image->data[offset+2], - .a = 0xFF, - }; - pixel = lexlibColorPremultiply(lexlibColorBlend(pixel, color, blendmode)); - image->data[offset+0] = pixel.r; - image->data[offset+1] = pixel.g; - image->data[offset+2] = pixel.b; + + if(profile == LEXLIB_RGB){ + size_t datasize = image->width * image->height * 3; + size_t dataOffset = 0; + uint8_t* data = malloc(datasize); + if(!data) + return LEXLIB_OUT_OF_MEMORY; + + if(image->flags & 0x80){// image has alpha and should be premultiplied + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor pixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); + + data[dataOffset++] = pixel.r; + data[dataOffset++] = pixel.g; + data[dataOffset++] = pixel.b; + } + } + + image->flags &= ~0x80; + } else {// image doesn't has alpha + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor pixel = lexlibImagePixelGet(image, x, y); + + data[dataOffset++] = pixel.r; + data[dataOffset++] = pixel.g; + data[dataOffset++] = pixel.b; + } + } + } + + free(image->data); + + image->data = data; + image->datasize = datasize; + image->channels = 3; + image->bpp = 24; + image->profile = LEXLIB_RGB; + return LEXLIB_OK; } - if(image->channels == 4){ - *(LexlibColor*)(image->data+offset) = lexlibColorBlend(*(LexlibColor*)(image->data+offset), color, blendmode); + + if(profile == LEXLIB_RGBA){ + size_t datasize = image->width * image->height * 4; + size_t dataOffset = 0; + uint8_t* data = malloc(datasize); + if(!data) + return LEXLIB_OUT_OF_MEMORY; + + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor pixel = lexlibImagePixelGet(image, x, y); + + data[dataOffset++] = pixel.r; + data[dataOffset++] = pixel.g; + data[dataOffset++] = pixel.b; + data[dataOffset++] = pixel.a; + } + } + + free(image->data); + + image->data = data; + image->datasize = datasize; + image->channels = 4; + image->bpp = 32; + image->profile = LEXLIB_RGBA; + image->flags |= 0x80; + return LEXLIB_OK; } - return LEXLIB_INVALID_VALUE; - } + }/* ..8bpc.. */ - if(image->depth == 16){ -#if LEXLIB_BIG_ENDIAN - if(image->channels == 1){ - uint16_t col = lexlibColor16Gray(lexlibColorTo16(color)); - image->data[offset+0] = ((uint8_t*)&col)[0]; - image->data[offset+1] = ((uint8_t*)&col)[1]; - return LEXLIB_OK; - } - if(image->channels == 3){ - LexlibColor16 color16 = lexlibColorTo16(color); - LexlibColor16 pixel16 = *((LexlibColor16*)image->data+offset); - pixel16.a = -1; - pixel16 = lexlibColor16Premultiply(lexlibColor16Blend(pixel16, color16, blendmode)); - image->data[offset+0] = ((uint8_t*)&pixel16)[0]; - image->data[offset+1] = ((uint8_t*)&pixel16)[1]; - image->data[offset+2] = ((uint8_t*)&pixel16)[2]; - image->data[offset+3] = ((uint8_t*)&pixel16)[3]; - image->data[offset+4] = ((uint8_t*)&pixel16)[4]; - image->data[offset+5] = ((uint8_t*)&pixel16)[5]; - return LEXLIB_OK; - } - - if(image->channels == 4){ - *(LexlibColor16*)(image->data+offset) = lexlibColor16Blend(*(LexlibColor16*)(image->data+offset), lexlibColorTo16(color), blendmode); - return LEXLIB_OK; - } -#else /* little endian */ - if(image->channels == 1){ - uint16_t col = lexlibColor16Gray(lexlibColorTo16(color)); - image->data[offset+0] = ((uint8_t*)&col)[1]; - image->data[offset+1] = ((uint8_t*)&col)[0]; + if(image->depth == 16){/* 16bpc */ + if(profile == LEXLIB_GRAY){ + size_t dataOffset = 0; + uint16_t* data16 = (uint16_t*)image->data; + + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor16 pixel = lexlibImagePixel16Get(image, x, y); + data16[dataOffset++] = lexlibColor16Gray(pixel); + } + } + + image->datasize = (image->width * image->height) * 2; + image->channels = 1; + image->bpp = 16; + image->profile = LEXLIB_GRAY; + image->flags &= ~0x80; + + uint8_t* newData = realloc(image->data, image->datasize); + if(!newData) + return LEXLIB_OUT_OF_MEMORY; + image->data = newData; + return LEXLIB_OK; } - if(image->channels == 3){ - LexlibColor16 color16 = lexlibColorTo16(color); - LexlibColor16 pixel16; - uint8_t* color16raw = (uint8_t*)&color16; - uint8_t* pixel16raw = (uint8_t*)&pixel16; + if(profile == LEXLIB_RGB){ + size_t datasize = (image->width * image->height * 3) * 2; + size_t dataOffset = 0; + uint16_t* data16 = malloc(datasize); + if(!data16) + return LEXLIB_OUT_OF_MEMORY; - pixel16raw[0] = image->data[offset+1]; - pixel16raw[1] = image->data[offset+0]; - pixel16raw[2] = image->data[offset+3]; - pixel16raw[3] = image->data[offset+2]; - pixel16raw[4] = image->data[offset+5]; - pixel16raw[5] = image->data[offset+4]; - pixel16.a = -1; + if(image->flags & 0x80){// image has alpha and should be premultiplied + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor16 pixel = lexlibColor16Premultiply(lexlibImagePixel16Get(image, x, y)); + + data16[dataOffset++] = pixel.r; + data16[dataOffset++] = pixel.g; + data16[dataOffset++] = pixel.b; + } + } + + image->flags &= ~0x80; + } else {// image doesn't has alpha + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor16 pixel = lexlibImagePixel16Get(image, x, y); + + data16[dataOffset++] = pixel.r; + data16[dataOffset++] = pixel.g; + data16[dataOffset++] = pixel.b; + } + } + } - color16 = lexlibColor16Blend(pixel16, color16, blendmode); - color16 = lexlibColor16Premultiply(color16); + free(image->data); - image->data[offset+0] = color16raw[1]; - image->data[offset+1] = color16raw[0]; - image->data[offset+2] = color16raw[3]; - image->data[offset+3] = color16raw[2]; - image->data[offset+4] = color16raw[5]; - image->data[offset+5] = color16raw[4]; + image->data = (uint8_t*)data16; + image->datasize = datasize; + image->channels = 3; + image->bpp = 24; + image->profile = LEXLIB_RGB; return LEXLIB_OK; } - if(image->channels == 4){ - LexlibColor16 color16 = lexlibColorTo16(color); - LexlibColor16 pixel16; - uint8_t* color16raw = (uint8_t*)&color16; - uint8_t* pixel16raw = (uint8_t*)&pixel16; + if(profile == LEXLIB_RGBA){ + size_t dataSize = (image->width * image->height * 4) * 2; + size_t dataOffset = 0; + uint16_t* data16 = malloc(dataSize); + if(!data16) + return LEXLIB_OUT_OF_MEMORY; - pixel16raw[0] = image->data[offset+1]; - pixel16raw[1] = image->data[offset+0]; - pixel16raw[2] = image->data[offset+3]; - pixel16raw[3] = image->data[offset+2]; - pixel16raw[4] = image->data[offset+5]; - pixel16raw[5] = image->data[offset+4]; - pixel16raw[6] = image->data[offset+7]; - pixel16raw[7] = image->data[offset+6]; + for(uint32_t y = 0; y < image->height; y++){ + for(uint32_t x = 0; x < image->width; x++){ + LexlibColor16 pixel = lexlibImagePixel16Get(image, x, y); + + data16[dataOffset++] = pixel.r; + data16[dataOffset++] = pixel.g; + data16[dataOffset++] = pixel.b; + data16[dataOffset++] = pixel.a; + } + } - color16 = lexlibColor16Blend(pixel16, color16, blendmode); + free(image->data); - image->data[offset+0] = color16raw[1]; - image->data[offset+1] = color16raw[0]; - image->data[offset+2] = color16raw[3]; - image->data[offset+3] = color16raw[2]; - image->data[offset+4] = color16raw[5]; - image->data[offset+5] = color16raw[4]; - image->data[offset+6] = color16raw[7]; - image->data[offset+7] = color16raw[6]; + image->data = (uint8_t*)data16; + image->datasize = dataSize; + image->channels = 4; + image->bpp = 64; + image->profile = LEXLIB_RGBA; + image->flags |= 0x80; return LEXLIB_OK; } -#endif - return LEXLIB_INVALID_VALUE; + }/* ..16bpc.. */ + + return LEXLIB_INVALID_VALUE; +} + +uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode){ + if(x >= image->width || y >= image->height) + return LEXLIB_INVALID_OPERATION; + + if(image->depth == 16) + return lexlibImagePixel16Set(image, x, y, lexlibColorTo16(color), blendmode); + + uint8_t pixelsize = image->channels * (image->depth / 8); + size_t rowsize = image->width * pixelsize; + size_t offset = (y * rowsize) + (x * pixelsize); + uint8_t* imgData = (image->data + offset); + + if(image->channels == 1){ + *imgData = lexlibColorGray(color); + return LEXLIB_OK; + } + + if(image->channels == 3){ + LexlibColor pixel = { + .r = imgData[0], + .g = imgData[1], + .b = imgData[2], + .a = 0xFF, + }; + + color = lexlibColorBlend(pixel, color, blendmode); + color = lexlibColorPremultiply(color); + + imgData[0] = color.r; + imgData[1] = color.g; + imgData[2] = color.b; + + return LEXLIB_OK; + } + + if(image->channels == 4){ + LexlibColor* pixel = (LexlibColor*)imgData; + + *pixel = lexlibColorBlend(*pixel, color, blendmode); + + return LEXLIB_OK; + } + + return LEXLIB_INVALID_VALUE; +} + +uint8_t lexlibImagePixel16Set(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor16 color, uint8_t blendmode){ + if(x >= image->width || y >= image->height) + return LEXLIB_INVALID_OPERATION; + + if(image->depth != 16) + return lexlibImagePixelSet(image, x, y, lexlibColor16To8(color), blendmode); + + uint8_t pixelSize = image->bpp / 8; + size_t rowsize = image->width * pixelSize; + size_t offset = (y * rowsize) + (x * pixelSize); + uint16_t* imgData16 = (uint16_t*)(image->data + offset); + + if(image->channels == 1){ + *imgData16 = lexlibColor16Gray(color); + + return LEXLIB_OK; + } + + if(image->channels == 3){ + LexlibColor16 pixel = { + .r = imgData16[0], + .g = imgData16[1], + .b = imgData16[2], + .a = 0xFFFF + }; + + color = lexlibColor16Blend(pixel, color, blendmode); + color = lexlibColor16Premultiply(color); + + imgData16[0] = color.r; + imgData16[1] = color.g; + imgData16[2] = color.b; + + return LEXLIB_OK; + } + + if(image->channels == 4){ + LexlibColor16* pixel = (LexlibColor16*)imgData16; + + *pixel = lexlibColor16Blend(*pixel, color, blendmode); + + return LEXLIB_OK; } return LEXLIB_ERROR; -- GitLab From 4849e4360660d8a5dc57da7c6f2408482a68cdbc Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 18 May 2023 21:18:52 -0400 Subject: [PATCH 57/64] bmp save image with LEXLIB_GRAY profile --- src/image/bmp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/image/bmp.c b/src/image/bmp.c index da6022d..5f4fee8 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -618,6 +618,12 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin bitMask[1] = 0x000007E0; bitMask[2] = 0x0000001F; break; + case LEXLIB_GRAY: + header.info.bitdepth = 24; + header.info.compression = BMP_RGB; + pixelSize = 3; + profile = LEXLIB_RGB; + break; case LEXLIB_RGB: header.info.bitdepth = 24; header.info.compression = BMP_RGB; -- GitLab From f02c624a294db20a807872c78f7a46db02d1ff79 Mon Sep 17 00:00:00 2001 From: alexevier Date: Thu, 18 May 2023 22:30:38 -0400 Subject: [PATCH 58/64] updated png reading --- src/image/png.c | 72 ++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/image/png.c b/src/image/png.c index 9d13c8e..8f72099 100644 --- a/src/image/png.c +++ b/src/image/png.c @@ -53,19 +53,41 @@ LexlibImage lexlibImageLoadPng(const char* filename){ return image; } + int readTransforms = 0; +#if LEXLIB_LITTLE_ENDIAN + readTransforms |= PNG_TRANSFORM_PACKSWAP; +#endif + // initial read png_init_io(pngPtr, file); png_set_sig_bytes(pngPtr, 8); - png_read_png(pngPtr, infoPtr, 0, NULL); + png_read_png(pngPtr, infoPtr, readTransforms, NULL); // read info image.width = png_get_image_width(pngPtr, infoPtr); image.height = png_get_image_height(pngPtr, infoPtr); image.depth = png_get_bit_depth(pngPtr, infoPtr); - image.channels = png_get_channels(pngPtr, infoPtr);; - image = lexlibImageNew(image.width, image.height, image.channels, image.depth); + image.channels = png_get_channels(pngPtr, infoPtr); - // check for errors + switch(png_get_color_type(pngPtr, infoPtr)){ + case PNG_COLOR_TYPE_GRAY: + image.profile = LEXLIB_GRAY; + break; + case PNG_COLOR_TYPE_RGB: + image.profile = LEXLIB_RGB; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + image.profile = LEXLIB_RGBA; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + default: + png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo); + image.depth = LEXLIB_UNSUPORTED; + return image; + } + + // create image + image = lexlibImageNew(image.width, image.height, image.profile, image.depth); if(image.data == NULL){ switch(image.depth){ case LEXLIB_OUT_OF_MEMORY: @@ -95,25 +117,6 @@ LexlibImage lexlibImageLoadPng(const char* filename){ } uint8_t lexlibImageSavePng(const LexlibImage* image, const char* filename){ - // image checks - if(image->data == NULL) - return LEXLIB_INVALID_VALUE; - if(image->width == 0) - return LEXLIB_INVALID_VALUE; - if(image->height == 0) - return LEXLIB_INVALID_VALUE; - if(image->channels == 0) - return LEXLIB_INVALID_VALUE; - if(image->depth == 0) - return LEXLIB_INVALID_VALUE; - if((image->depth % 8) != 0) - return LEXLIB_INVALID_VALUE; - - // create file - FILE* file = fopen(filename, "wb"); - if(!file) - return LEXLIB_CANT_WRITE; - // allocate libpng structs png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!pngPtr) @@ -126,29 +129,42 @@ uint8_t lexlibImageSavePng(const LexlibImage* image, const char* filename){ // get color type int colorType = 0; - switch(image->channels){ - case 1: + switch(image->profile){ + case LEXLIB_GRAY: colorType = PNG_COLOR_TYPE_GRAY; break; - case 3: + case LEXLIB_RGB: colorType = PNG_COLOR_TYPE_RGB; break; - case 4: + case LEXLIB_RGBA: colorType = PNG_COLOR_TYPE_RGB_ALPHA; break; default: - colorType = PNG_COLOR_TYPE_GRAY; + png_destroy_write_struct(&pngPtr, &infoPtr); + return LEXLIB_UNSUPORTED; } // set png info png_set_IHDR(pngPtr, infoPtr, image->width, image->height, image->depth, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_compression_level(pngPtr, 9); + // create file + FILE* file = fopen(filename, "wb"); + if(!file){ + png_destroy_write_struct(&pngPtr, &infoPtr); + return LEXLIB_CANT_WRITE; + } + // write png png_init_io(pngPtr, file); png_write_info(pngPtr, infoPtr); png_write_flush(pngPtr); +#if LEXLIB_LITTLE_ENDIAN + if(image->depth > 8) + png_set_swap(pngPtr); +#endif + // write data size_t rowSize = image->width * image->channels * (image->depth / 8); uint32_t offset = 0; -- GitLab From 48a4c035d6b3bbf17472fab34c81be4f08596f8a Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 19 May 2023 01:39:09 -0400 Subject: [PATCH 59/64] bmp loading accounts for negative height and fixed saving --- include/lexlib/image.h | 5 ++- src/image/bmp.c | 73 +++++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/include/lexlib/image.h b/include/lexlib/image.h index 06e0dff..0c8aed8 100644 --- a/include/lexlib/image.h +++ b/include/lexlib/image.h @@ -9,8 +9,7 @@ #include"color.h" /* NOTE s - images with more bitdepth than 8 are bigendian. - its being considered if image data should be in the native machine endianess. + images are on the native machine endianness. > Image file formats L S M @@ -18,12 +17,12 @@ ◯ ◯ ◯ gif ◯ ◯ ◯ jpg ● ● ◯ png | libpng and zlib required - ◯ ◯ ◯ webp > Color profiles lexlib images work directly only with certain color profiles in the R.G.B.A order. any loading or saving with a profile the image doesn't work directly results in a conversion. ● GRAY + ◯ GRAY ALPHA ◯ BW (black white) ● RGB ● RGBA diff --git a/src/image/bmp.c b/src/image/bmp.c index 5f4fee8..f3964e0 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -58,6 +58,7 @@ > pixel formats left-most pixel in the most-significant bit of the first byte. + pixels are stored "bottom-up" unless the height value is negative. S C I 2 3 4 5 ◯ ● ◯ ◯ ◯ ◯ ◯ 1bpp : 2 colors | table ◯ ◯ ◯ ◯ ◯ ◯ ◯ 2bpp : 4 colors | table @@ -161,9 +162,22 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ uint32_t bmpRowStride = 0; uint32_t bmpRowPadding = 0; uint32_t bmpHeadOffset = sizeof(BmpFileHeader); + uint8_t bmpBottom = 1; // CoreHeader if(coreHeader->size == sizeof(BmpCoreHeader)){ + // integrity checks + if( + coreHeader->width <= 0 || + coreHeader->height == 0 || + coreHeader->bitdepth == 0 + ){ + free(bmp.data); + image.depth = LEXLIB_INVALID_FILE_DATA; + return image; + } + + // set initial data image.width = coreHeader->width; image.height = coreHeader->height; bmpRowStride = ((((coreHeader->width * coreHeader->bitdepth) + 31) & ~31) >> 3); @@ -190,11 +204,24 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ BmpInfoHeader infoHeader; memcpy(&infoHeader, coreHeader, sizeof(BmpInfoHeader)); + // integrity checks + if( + infoHeader.width <= 0 || + infoHeader.height == 0 || + infoHeader.bitdepth == 0 + ){ + free(bmp.data); + image.depth = LEXLIB_INVALID_FILE_DATA; + return image; + } + + // set initial data image.width = infoHeader.width; - image.height = infoHeader.height; + image.height = abs(infoHeader.height); bmpRowStride = ((((infoHeader.width * infoHeader.bitdepth) + 31) & ~31) >> 3); bmpRowPadding = (bmpRowStride) - ceil(infoHeader.width * (infoHeader.bitdepth / 8.0f)); bmpHeadOffset += sizeof(BmpInfoHeader); + bmpBottom = (infoHeader.height > 0) ? 1 : 0; // check for compression switch(infoHeader.compression){ @@ -279,11 +306,24 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ if(coreHeader->size == sizeof(BmpInfoHeader3)){ BmpInfoHeader3* infoHeader3 = (BmpInfoHeader3*)coreHeader; + // integrity checks + if( + infoHeader3->width <= 0 || + infoHeader3->height == 0 || + infoHeader3->bitdepth == 0 + ){ + free(bmp.data); + image.depth = LEXLIB_INVALID_FILE_DATA; + return image; + } + + // set initial data image.width = infoHeader3->width; - image.height = infoHeader3->height; + image.height = abs(infoHeader3->height); bmpRowStride = ((((infoHeader3->width * infoHeader3->bitdepth) + 31) & ~31) >> 3); bmpRowPadding = (bmpRowStride) - ceil(infoHeader3->width * (infoHeader3->bitdepth / 8.0f)); bmpHeadOffset += sizeof(BmpInfoHeader3); + bmpBottom = (infoHeader3->height > 0) ? 1 : 0; // check for compression switch(infoHeader3->compression){ @@ -536,6 +576,8 @@ LexlibImage lexlibImageLoadBmp(const char* filename){ }} free(bmp.data); + if(bmpBottom) + lexlibImageFlip(&image, LEXLIB_FLIP_Y); return image; } @@ -712,8 +754,10 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin fwrite(&header, header.core.size, 1, file); fwrite(bitMask, sizeof(uint32_t), bitMaskCnt, file); - if(profile == LEXLIB_RGB){ // RGB - for(uint32_t y = 0; y < image->height; y++){ + if(profile == LEXLIB_RGB){// RGB + uint32_t y = image->height; + do { + y--; for(uint32_t x = 0; x < image->width; x++){ LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); LexlibColor pixel = { @@ -726,10 +770,12 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin } for(uint8_t pad = 0; pad < rowPadding; pad++) fputc(0, file); - }} + } while(y != 0);} - if(profile == LEXLIB_RGBA){ // RGB - for(uint32_t y = 0; y < image->height; y++){ + if(profile == LEXLIB_RGBA){// RGBA + uint32_t y = image->height; + do { + y--; for(uint32_t x = 0; x < image->width; x++){ LexlibColor imgPixel = lexlibImagePixelGet(image, x, y); LexlibColor pixel = { @@ -742,10 +788,11 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin } for(uint8_t pad = 0; pad < rowPadding; pad++) fputc(0, file); - }} + } while(y != 0);} if(profile == LEXLIB_RGB555){ // RGB555 - for(uint32_t y = 0; y < image->height; y++){ + uint32_t y = image->height; + do { for(uint32_t x = 0; x < image->width; x++){ LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); uint16_t pixel = 0x0000; @@ -758,10 +805,12 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin } for(uint8_t pad = 0; pad < rowPadding; pad++) fputc(0, file); - }} + } while(y != 0);} if(profile == LEXLIB_RGB565){ // RGB565 - for(uint32_t y = 0; y < image->height; y++){ + uint32_t y = image->height; + do { + y--; for(uint32_t x = 0; x < image->width; x++){ LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); uint16_t pixel = 0x0000; @@ -774,7 +823,7 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin } for(uint8_t pad = 0; pad < rowPadding; pad++) fputc(0, file); - }} + } while(y != 0);} fflush(file); fclose(file); -- GitLab From 2a0279767fd0552d2488e2575ad630c2f9e7e665 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 19 May 2023 01:40:05 -0400 Subject: [PATCH 60/64] png loading sat the correct constant for endianness --- src/image/png.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/image/png.c b/src/image/png.c index 8f72099..36d9f9e 100644 --- a/src/image/png.c +++ b/src/image/png.c @@ -55,7 +55,7 @@ LexlibImage lexlibImageLoadPng(const char* filename){ int readTransforms = 0; #if LEXLIB_LITTLE_ENDIAN - readTransforms |= PNG_TRANSFORM_PACKSWAP; + readTransforms |= PNG_TRANSFORM_SWAP_ENDIAN; #endif // initial read -- GitLab From 7db14366f5692cae2151ef4d98c16132ed7b8803 Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 19 May 2023 01:58:29 -0400 Subject: [PATCH 61/64] update image documentation --- CHANGELOG.md | 4 + README.md | 2 +- documentation/assets/image_origin.svg | 790 ++++++++++++++++++++++++++ documentation/frontpage.html | 6 +- documentation/image.html | 40 +- documentation/lexlib.html | 1 + src/image.c | 1 + 7 files changed, 837 insertions(+), 7 deletions(-) create mode 100644 documentation/assets/image_origin.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index c223f9e..41290a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,13 @@ + lexlibColor16Blend(); + lexlibImagePixelSet(); + lexlibImagePixelGet(); ++ lexlibImagePixel16Set(); ++ lexlibImagePixel16Get(); ++ lexlibImageProfileChange(); + lexlibImageLoadBmp(); + lexlibImageSaveBmp(); + lexlibImageSaveBmpEx(); ++ LexlibImage.data is officially native to the machine endianness. + fixed strPath buffer overflows. + fixed imageFlip X buffer overflow. + fixed linkedList memleaks. diff --git a/README.md b/README.md index e956801..f6d9339 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ lexlib also works on C++ without hassle. + os abstractions for linux and windows. + image manipulation. -+ colors manupulation. ++ colors manipulation. + raw strings manipulation. + simple file reading/writing implemented with the stdlib. + small math things. diff --git a/documentation/assets/image_origin.svg b/documentation/assets/image_origin.svg new file mode 100644 index 0000000..90700db --- /dev/null +++ b/documentation/assets/image_origin.svg @@ -0,0 +1,790 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + + + + + + + + + alexevier + + + + + + + + + + + diff --git a/documentation/frontpage.html b/documentation/frontpage.html index 9f8e866..f89b79e 100644 --- a/documentation/frontpage.html +++ b/documentation/frontpage.html @@ -48,7 +48,7 @@
  • os abstractions for linux and windows.
  • image manipulation. -
  • colors manupulation. +
  • colors manipulation.
  • raw strings manipulation.
  • simple file reading/writing implemented with the stdlib.
  • small math things. @@ -68,9 +68,13 @@
  • lexlibColor16Blend();
  • lexlibImagePixelSet();
  • lexlibImagePixelGet(); +
  • lexlibImagePixel16Set(); +
  • lexlibImagePixel16Get(); +
  • lexlibImageProfileChange();
  • lexlibImageLoadBmp();
  • lexlibImageSaveBmp();
  • lexlibImageSaveBmpEx(); +
  • LexlibImage.data is officially native to the machine endianness.
  • fixed strPath buffer overflows.
  • fixed imageFlip X buffer overflow.
  • fixed linkedList memleaks. diff --git a/documentation/image.html b/documentation/image.html index afc6540..6595c47 100644 --- a/documentation/image.html +++ b/documentation/image.html @@ -18,6 +18,7 @@ lexlibImageDelete() lexlibImageValidate() lexlibImageFlip() + lexlibImageProfileChange() lexlibImagePixelSet() lexlibImagePixelGet() lexlibImageLoad() @@ -44,12 +45,16 @@ be noted that is required to link libpng and zlib for png support.

    - the raw data in a LexlibImage (image.data) is just pixel data with each pixel being channels * (depth / 8) layed one next to the other, can be used with opengl but you might want to flip the image in the Y axis since pngs have their rows inversed from how opengl takes them. + the raw data in a LexlibImage (image.data) is just pixel data with each pixel being (bpp / 8) layed one next to the other.
    + the origin is at the top left corner.
    + *image_origin.svg*

    - images with a bpp more than 8 are bigendian. (this may change)
    - it's being considered if image data should be in the native machine endianess.
    - it recommended to use the pixelGet and pixelSet functions instead of changing the .data directly + can be used with opengl but you might want to flip the image in the Y axis since opengl has its origin at the bottom left. +

    +

    + image data are on the native machine endianness.
    + still it's recommended to use the pixelGet and pixelSet functions instead of changing the .data directly.

    currently the only color profiles supported are RGB w/o alpha and Gray_Scale without alpha. @@ -89,6 +94,7 @@ uint8_t depth; /* bits per color */
    uint8_t bpp; /* bits per pixel */
    uint8_t profile;
    + uint8_t flags;

}; @@ -191,6 +197,28 @@

+

+


+

lexlibImageProfileChange()

+

+ changes the color profile of a image.
+ performs color convertions. +

+

Function +

uint8_t lexlibImageProfileChange(LexlibImage* image, uint8_t profile);
+

+

Arguments +

LexlibImage* image : the image to be changed. +
uint8_t profile : the new profile, LEXLIB_GRAY, LEXLIB_RGB, LEXLIB_RGBA. +
+

+

Returns +

LEXLIB_OK (0): everthing went right. +
LEXLIB_INVALID_VALUE: image containts a invalid value or the new profile is invalid. +
LEXLIB_OUT_OF_MEMORY: failed to allocate temporal buffers, image might have been corrupted. +
+

+


lexlibImagePixelSet()

@@ -201,8 +229,9 @@ according transformations to the passed color are performed depending of the image profile.
alpha is premultiplied if the image does not have a alpha channel.

-

Function +

Functions

uint8_t lexlibImagePixelSet(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor color, uint8_t blendmode);
+
uint8_t lexlibImagePixel16Set(LexlibImage* image, uint32_t x, uint32_t y, LexlibColor16 color, uint8_t blendmode);

Arguments

LexlibImage* image : the image to be modified. @@ -234,6 +263,7 @@

Function

LexlibColor lexlibImagePixelGet(const LexlibImage* image, uint32_t x, uint32_t y);
+
LexlibColor16 lexlibImagePixel16Get(const LexlibImage* image, uint32_t x, uint32_t y);

Arguments

LexlibImage* image : the image to be modified. diff --git a/documentation/lexlib.html b/documentation/lexlib.html index 45630bc..146eddf 100644 --- a/documentation/lexlib.html +++ b/documentation/lexlib.html @@ -116,6 +116,7 @@
lexlibImageDelete()
lexlibImageValidate()
lexlibImageFlip() +
lexlibImageProfileChange()
lexlibImagePixelSet()
lexlibImagePixelGet()
lexlibImageLoad() diff --git a/src/image.c b/src/image.c index 999b1b3..49b9287 100644 --- a/src/image.c +++ b/src/image.c @@ -177,6 +177,7 @@ uint8_t lexlibImageFlip(LexlibImage* image, uint8_t flags){ return LEXLIB_OK; } +// TODO this can be optimized to use less memory. uint8_t lexlibImageProfileChange(LexlibImage* image, uint8_t profile){ // nothing to do if image has the requested profile if(image->profile == profile) -- GitLab From 963efe4dbad3bbb7397e9cd503868174d34fb03c Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 19 May 2023 02:09:51 -0400 Subject: [PATCH 62/64] added missing y--; updated bmp tests --- src/image/bmp.c | 1 + test/imageBmp.c | 54 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/image/bmp.c b/src/image/bmp.c index f3964e0..ad423a4 100644 --- a/src/image/bmp.c +++ b/src/image/bmp.c @@ -793,6 +793,7 @@ uint8_t lexlibImageSaveBmpEx(const LexlibImage* image, const char* filename, uin if(profile == LEXLIB_RGB555){ // RGB555 uint32_t y = image->height; do { + y--; for(uint32_t x = 0; x < image->width; x++){ LexlibColor imgPixel = lexlibColorPremultiply(lexlibImagePixelGet(image, x, y)); uint16_t pixel = 0x0000; diff --git a/test/imageBmp.c b/test/imageBmp.c index 18267a2..2090da8 100644 --- a/test/imageBmp.c +++ b/test/imageBmp.c @@ -28,7 +28,8 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/plasma16.bmp", LEXLIB_RGB565, 0); + lexlibImageSaveBmpEx(&image, "resources/out/plasma16.bmp", LEXLIB_RGB565, LEXLIB_BMP_INFO); + lexlibImageSaveBmpEx(&image, "resources/out/plasma16_555.bmp", LEXLIB_RGB555, LEXLIB_BMP_INFO); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -53,7 +54,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/plasma24.bmp", 0, 0); + lexlibImageSaveBmp(&image, "resources/out/plasma24.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -78,7 +79,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/plasma32.bmp", 0, 0); + lexlibImageSaveBmpEx(&image, "resources/out/plasma32.bmp", image.profile, LEXLIB_BMP_INFO); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -103,7 +104,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/macintosh.bmp", 0, 0); + lexlibImageSaveBmp(&image, "resources/out/macintosh.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -128,7 +129,7 @@ void testImageBmp(void){ return; } - lexlibImageSave(&image, "resources/out/macintosh_alpha70.bmp"); + lexlibImageSaveBmp(&image, "resources/out/macintosh_alpha70.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -153,7 +154,7 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/macintosh_8bpp.bmp", 0, 0); + lexlibImageSaveBmp(&image, "resources/out/macintosh_8bpp.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -178,7 +179,12 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/puro.bmp", 0, 0); + lexlibImageProfileChange(&image, LEXLIB_RGB); + lexlibImageProfileChange(&image, LEXLIB_GRAY); + lexlibImageProfileChange(&image, LEXLIB_RGBA); + lexlibImageProfileChange(&image, LEXLIB_GRAY); + lexlibImageProfileChange(&image, LEXLIB_RGB); + lexlibImageSaveBmp(&image, "resources/out/puro.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} @@ -203,7 +209,39 @@ void testImageBmp(void){ return; } - lexlibImageSaveBmpEx(&image, "resources/out/floppy.bmp", 0, 0); + lexlibImageSaveBmp(&image, "resources/out/floppy.bmp"); + lexlibImageDelete(&image); + testEnd(1, NULL);} + + {testStart("draw bmp"); + LexlibImage image = lexlibImageNew(8, 8, LEXLIB_RGB, 8); + + // YES!1!1! + for(int y = 0; y < 8; y++) + for(int x = 0; x < 8; x++) + lexlibImagePixelSet(&image, x, y, (LexlibColor){0xEE, 0xB5, 0x70, 0xFF}, LEXLIB_NONE); + for(int i = 0; i < 8; i++) lexlibImagePixelSet(&image, i, 0, (LexlibColor){0xEC, 0xFF, 0xBC, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 0, 1, (LexlibColor){0xEC, 0xFF, 0xBC, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 6, 1, (LexlibColor){0xEC, 0xFF, 0xBC, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 7, 1, (LexlibColor){0xEC, 0xFF, 0xBC, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 6, 2, (LexlibColor){0xEC, 0xFF, 0xBC, 0xFF}, LEXLIB_NONE); + for(int i = 0; i < 5; i++) lexlibImagePixelSet(&image, i+1, 1, (LexlibColor){0xEE, 0xB5, 0x70, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 2, 2, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 4, 2, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + for(int i = 0; i < 3; i++) lexlibImagePixelSet(&image, i+2, 3, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + for(int i = 0; i < 3; i++) lexlibImagePixelSet(&image, i+2, 4, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 2, 4, (LexlibColor){0xE3, 0xE3, 0xF2, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 4, 4, (LexlibColor){0xE3, 0xE3, 0xF2, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 2, 5, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 3, 5, (LexlibColor){0xE3, 0xE3, 0xF2, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 4, 5, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + for(int i = 0; i < 3; i++) lexlibImagePixelSet(&image, i+2, 6, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + for(int i = 0; i < 4; i++) lexlibImagePixelSet(&image, i+2, 7, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 6, 6, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 6, 5, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + lexlibImagePixelSet(&image, 7, 4, (LexlibColor){0x47, 0x47, 0x51, 0xFF}, LEXLIB_NONE); + + lexlibImageSaveBmp(&image, "resources/out/draw.bmp"); lexlibImageDelete(&image); testEnd(1, NULL);} } \ No newline at end of file -- GitLab From c4aa5ab792fcb5d406ae2166f2cd9a2fb033ee3c Mon Sep 17 00:00:00 2001 From: alexevier Date: Fri, 19 May 2023 02:40:36 -0400 Subject: [PATCH 63/64] update readme --- CHANGELOG.md | 2 +- README.md | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41290a4..0c20640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.5.0 (under development) +## 1.5.0 + lexlibFileBytes(); + lexlibPowu(); + lexlibColor16Blend(); diff --git a/README.md b/README.md index f6d9339..9bb0762 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,23 @@ Licensed under either the MIT License or the MIT License or the Date: Fri, 19 May 2023 02:48:40 -0400 Subject: [PATCH 64/64] package update --- linux.sh | 9 ++++++--- resources/archPackage/PKGBUILD | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/linux.sh b/linux.sh index 23ec2e3..0674ac6 100755 --- a/linux.sh +++ b/linux.sh @@ -242,11 +242,14 @@ if [[ $1 == package ]]; then mkdir -p build/tmp/lexlib_linux_$version/lib mkdir -p build/tmp/lexlib_mingw32_$version/lib mkdir -p build/tmp/lexlib_mingw64_$version/lib + mkdir -p build/tmp/lexlib_linux_$version/include + mkdir -p build/tmp/lexlib_mingw32_$version/include + mkdir -p build/tmp/lexlib_mingw64_$version/include mkdir -p build/tmp/lexlib_documentation_$version # copy stuff - cp -r include build/tmp/lexlib_linux_$version/ - cp -r include build/tmp/lexlib_mingw32_$version/ - cp -r include build/tmp/lexlib_mingw64_$version/ + cp -r include/lexlib build/tmp/lexlib_linux_$version/include/ + cp -r include/lexlib build/tmp/lexlib_mingw32_$version/include/ + cp -r include/lexlib build/tmp/lexlib_mingw64_$version/include/ cp build/linux/liblexlib.a build/tmp/lexlib_linux_$version/lib/ cp build/mingw32/liblexlib.a build/tmp/lexlib_mingw32_$version/lib/ cp build/mingw64/liblexlib.a build/tmp/lexlib_mingw64_$version/lib/ diff --git a/resources/archPackage/PKGBUILD b/resources/archPackage/PKGBUILD index 9ac9940..d9d3f44 100644 --- a/resources/archPackage/PKGBUILD +++ b/resources/archPackage/PKGBUILD @@ -1,14 +1,14 @@ # Maintainer: alexevier pkgname=lexlib -pkgver=1.4.0 +pkgver=1.5.0 pkgrel=1 pkgdesc="miscellaneous stuff for C development" arch=(x86_64) url="https://gitlab.com/alexevier/lexlib-c" license=('MIT', 'Apache 2.0') depends=() -source=("https://gitlab.com/alexevier/binaries/-/raw/main/lexlib/lexlib_linux_1.4.0.tar.gz") -sha256sums=('96136f98dd8dbc3716dc74b28539474299ba3913736016dc55333795472bd161') +source=("https://gitlab.com/alexevier/binaries/-/raw/main/lexlib/lexlib_linux_1.5.0.tar.gz") +sha256sums=('684b94ef5bd37c895fce5a913350f43523f1ca3c5fc5d7c6c9defe8e9748b39b') package() { # lib -- GitLab