#include<lexlib/defines.h>
#include<lexlib/image.h>
#include<lexlib/color.h>
#include<lexlib/cfile.h>
#include<lexlib/mem.h>
#include<stdlib.h>
#include<stddef.h>
#include<string.h>
#include<stdio.h>
uint8_t lexlibImageNew(struct LexlibImage *image, uint32_t width, uint32_t height, uint8_t colorProfile, uint8_t bpc){
if(!width || !height)
return LEXLIB_INVALID_VALUE;
switch(bpc){
case 0:
bpc = 8;
case 8:
case 16:
break;
default:
return LEXLIB_INVALID_ARG;
}
image->data = NULL;
image->width = width;
image->height = height;
image->channels = 0;
image->bpc = bpc;
image->bpp = 0;
image->profile = colorProfile;
image->flags = LEXLIB_NONE;
switch((enum LexlibColorProfile)colorProfile){
case LEXLIB_GRAY:
image->profile = LEXLIB_GRAY;
image->channels = 1;
break;
case LEXLIB_GRAYA:
image->profile = LEXLIB_GRAYA;
image->channels = 2;
image->flags |= LEXLIB_ALPHA;
break;
case LEXLIB_RGB555:
case LEXLIB_RGB565:
case LEXLIB_RGB:
image->profile = LEXLIB_RGB;
image->channels = 3;
break;
case LEXLIB_RGBA:
image->profile = LEXLIB_RGBA;
image->channels = 4;
image->flags |= LEXLIB_ALPHA;
break;
default:
return LEXLIB_INVALID_ARG;
}
image->bpp = image->bpc * image->channels;
size_t dataSize = image->width * image->height * image->bpp / 8;
image->data = calloc(dataSize, 1);
if(!image->data)
return LEXLIB_OUT_OF_MEMORY;
return LEXLIB_OK;
}
void lexlibImageDelete(struct LexlibImage *image){
free(image->data);
}
uint8_t lexlibImageCopy(struct LexlibImage *copy, const struct LexlibImage *original){
if(!original->data)
return LEXLIB_INVALID_VALUE;
*copy = *original;
size_t dataSize = copy->width * copy->height * copy->bpp / 8;
copy->data = malloc(dataSize);
if(!copy->data)
return LEXLIB_OUT_OF_MEMORY;
memcpy(copy->data, original->data, dataSize);
return LEXLIB_OK;
}
uint8_t lexlibImageValidate(const struct LexlibImage *image){
if(image->data == NULL ||
image->width == 0 ||
image->height == 0 ||
image->channels == 0 ||
image->bpc == 0 ||
(image->bpc % 8) != 0 ||
image->bpp == 0 ||
(image->bpp % 8) != 0
) return LEXLIB_INVALID_VALUE;
switch(image->profile){
case LEXLIB_GRAY:
case LEXLIB_GRAYA:
case LEXLIB_RGB:
case LEXLIB_RGBA:
break;
default:
return LEXLIB_INVALID_VALUE;
}
if(image->profile == LEXLIB_RGBA || image->profile == LEXLIB_GRAYA){
if(!(image->flags & LEXLIB_ALPHA))
return LEXLIB_INVALID_VALUE;
} else {
if((image->flags & LEXLIB_ALPHA))
return LEXLIB_INVALID_VALUE;
}
return LEXLIB_OK;
}
uint8_t lexlibImageFlip(struct LexlibImage *image, uint8_t flags){
if(!flags)
return LEXLIB_INVALID_VALUE;
size_t rowSize = image->width * (image->bpp / 8);
uint8_t* data = image->data;
if(flags & LEXLIB_FLIP_Y){
uint8_t *tmp = malloc(rowSize);
if(!tmp)
return LEXLIB_OUT_OF_MEMORY;
uint8_t* imgOffset = image->data;
uint8_t* imgOffsetInv = data + (rowSize * (image->height-1));
uint32_t y = image->height / 2;
while(y != 0){
memcpy(tmp, imgOffset, rowSize);
memcpy(imgOffset, imgOffsetInv, rowSize);
memcpy(imgOffsetInv, tmp, rowSize);
imgOffset += rowSize;
imgOffsetInv -= rowSize;
y--;
}
free(tmp);
}
if(flags & LEXLIB_FLIP_X){
uint8_t *tmp = malloc(rowSize);
if(!tmp)
return LEXLIB_OUT_OF_MEMORY;
size_t offset = 0;
uint8_t pixelSize = image->bpp / 8;
for(size_t y = 0; y < image->height; y++){
memcpy(tmp, data+offset, rowSize);
size_t xOffset = rowSize - pixelSize;
size_t tmpOffset = 0;
for(size_t x = 0; x < image->width; x++){
memcpy(data + xOffset + offset, tmp + tmpOffset, pixelSize);
xOffset -= pixelSize;
tmpOffset += pixelSize;
}
offset += rowSize;
}
free(tmp);
}
return LEXLIB_OK;
}
uint8_t lexlibImageProfileChange(struct LexlibImage *image, uint8_t profile){
if(image->profile == profile)
return LEXLIB_OK;
size_t dataSize = image->width * image->height;
size_t imgDataSize = image->width * image->height * image->bpp / 8;
switch(profile){
case LEXLIB_GRAY:
break;
case LEXLIB_GRAYA:
dataSize *= 2;
break;
case LEXLIB_RGB:
dataSize *= 3;
break;
case LEXLIB_RGBA:
dataSize *= 4;
break;
default:
return LEXLIB_INVALID_ARG;
}
if(image->bpc == 8){
uint8_t *data = image->data;
uint8_t *newData = NULL;
if(imgDataSize < dataSize){
newData = malloc(dataSize);
if(!newData)
return LEXLIB_OUT_OF_MEMORY;
data = newData;
}
switch(profile){
case LEXLIB_GRAY:
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor pixel;
lexlibImagePixel(image, x, y, &pixel);
*(data++) = lexlibColorGray(pixel);
}
}
image->channels = 1;
image->bpp = 8;
image->profile = LEXLIB_GRAY;
image->flags &= ~LEXLIB_ALPHA;
break;
case LEXLIB_GRAYA:
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor pixel;
lexlibImagePixel(image, x, y, &pixel);
pixel = lexlibColorGrayAlpha(pixel);
data[0] = pixel.r;
data[1] = pixel.a;
data += 2;
}
}
image->channels = 2;
image->bpp = 16;
image->profile = LEXLIB_GRAYA;
image->flags |= LEXLIB_ALPHA;
break;
case LEXLIB_RGB:
if(image->flags & 0x80){ for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor pixel;
lexlibImagePixel(image, x, y, &pixel);
pixel = lexlibColorPremultiply(pixel);
data[0] = pixel.r;
data[1] = pixel.g;
data[2] = pixel.b;
data += 3;
}
}
} else { for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor pixel;
lexlibImagePixel(image, x, y, &pixel);
data[0] = pixel.r;
data[1] = pixel.g;
data[2] = pixel.b;
data += 3;
}
}
}
image->channels = 3;
image->bpp = 24;
image->profile = LEXLIB_RGB;
image->flags &= ~LEXLIB_ALPHA;
break;
case LEXLIB_RGBA:
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor pixel;
lexlibImagePixel(image, x, y, &pixel);
data[0] = pixel.r;
data[1] = pixel.g;
data[2] = pixel.b;
data[3] = pixel.a;
data += 4;
}
}
image->channels = 4;
image->bpp = 32;
image->profile = LEXLIB_RGBA;
image->flags |= 0x80;
break;
}
if(newData){
free(image->data);
image->data = newData;
} else if(imgDataSize != dataSize){
data = realloc(image->data, dataSize);
if(!data)
return LEXLIB_OUT_OF_MEMORY;
image->data = data;
}
return LEXLIB_OK;
} else if(image->bpc == 16){
uint16_t *data = image->data;
uint16_t *newData = NULL;
dataSize *= 2;
if(imgDataSize < dataSize){
newData = malloc(dataSize);
if(!newData)
return LEXLIB_OUT_OF_MEMORY;
data = newData;
}
switch(profile){
case LEXLIB_GRAY:
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor16 pixel;
lexlibImagePixel16(image, x, y, &pixel);
*(data++) = lexlibColor16Gray(pixel);
}
}
image->channels = 1;
image->bpp = 16;
image->profile = LEXLIB_GRAY;
image->flags &= ~LEXLIB_ALPHA;
break;
case LEXLIB_GRAYA:
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor16 pixel;
lexlibImagePixel16(image, x, y, &pixel);
pixel = lexlibColor16GrayAlpha(pixel);
data[0] = pixel.r;
data[1] = pixel.a;
data += 2;
}
}
image->channels = 2;
image->bpp = 32;
image->profile = LEXLIB_GRAYA;
image->flags |= LEXLIB_ALPHA;
break;
case LEXLIB_RGB:
if(image->flags & LEXLIB_ALPHA){ for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor16 pixel;
lexlibImagePixel16(image, x, y, &pixel);
pixel = lexlibColor16Premultiply(pixel);
data[0] = pixel.r;
data[1] = pixel.g;
data[2] = pixel.b;
data += 3;
}
}
} else { for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor16 pixel;
lexlibImagePixel16(image, x, y, &pixel);
data[0] = pixel.r;
data[1] = pixel.g;
data[2] = pixel.b;
data += 3;
}
}
}
image->channels = 3;
image->bpp = 48;
image->profile = LEXLIB_RGB;
image->flags &= ~LEXLIB_ALPHA;
break;
case LEXLIB_RGBA:
for(uint32_t y = 0; y < image->height; y++){
for(uint32_t x = 0; x < image->width; x++){
struct LexlibColor16 pixel;
lexlibImagePixel16(image, x, y, &pixel);
data[0] = pixel.r;
data[1] = pixel.g;
data[2] = pixel.b;
data[3] = pixel.a;
data += 4;
}
}
image->channels = 4;
image->bpp = 64;
image->profile = LEXLIB_RGBA;
image->flags |= 0x80;
break;
}
if(newData){
free(image->data);
image->data = newData;
} else if(imgDataSize != dataSize){
data = realloc(image->data, dataSize);
if(!data)
return LEXLIB_OUT_OF_MEMORY;
image->data = data;
}
return LEXLIB_OK;
}
return LEXLIB_INVALID_VALUE;
}
uint8_t lexlibImageFillArea(struct LexlibImage *image, uint32_t x, uint32_t y, uint32_t w, uint32_t h, struct LexlibColor color, uint8_t blendmode){
if(x >= image->width || y >= image->height)
return LEXLIB_INVALID_OPERATION;
uint32_t width = x + w;
uint32_t height = y + h;
for(uint32_t yv = y; yv < height; yv++){
for(uint32_t xv = x; xv < width; xv++){
lexlibImagePixelSet(image, xv, yv, color, blendmode);
}
}
return LEXLIB_OK;
}
uint8_t lexlibImagePixel(const struct LexlibImage *image, uint32_t x, uint32_t y, struct LexlibColor* color){
if(x >= image->width || y >= image->height){
*color = LEXLIB_COLOR_MAGENTA;
return LEXLIB_INVALID_OPERATION;
}
if(image->bpc == 16){
struct LexlibColor16 color16;
uint8_t err = lexlibImagePixel16(image, x, y, &color16);
if(err){
*color = LEXLIB_COLOR_MAGENTA;
return err;
}
*color = lexlibColor16To8(color16);
return LEXLIB_OK;
}
uint8_t pixelSize = image->bpp / 8;
size_t rowsize = image->width * pixelSize;
const uint8_t *data = image->data;
data += y * rowsize + x * pixelSize;
if(image->profile == LEXLIB_GRAY){
color->r = data[0];
color->g = data[0];
color->b = data[0];
color->a = LEXLIB_COLOR_MAX;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_GRAYA){
color->r = data[0];
color->g = data[0];
color->b = data[0];
color->a = data[1];
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGB){
color->r = data[0];
color->g = data[1];
color->b = data[2];
color->a = LEXLIB_COLOR_MAX;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGBA){
*color = *(struct LexlibColor*)data;
return LEXLIB_OK;
}
*color = LEXLIB_COLOR_MAGENTA;
return LEXLIB_INVALID_VALUE;
}
uint8_t lexlibImagePixel16(const struct LexlibImage *image, uint32_t x, uint32_t y, struct LexlibColor16* color){
if(x >= image->width || y >= image->height){
*color = lexlibColorTo16(LEXLIB_COLOR_MAGENTA);
return LEXLIB_INVALID_OPERATION;
}
if(image->bpc != 16){
struct LexlibColor color8;
uint8_t err = lexlibImagePixel(image, x, y, &color8);
if(err){
*color = lexlibColorTo16(LEXLIB_COLOR_MAGENTA);
return err;
}
*color = lexlibColorTo16(color8);
return LEXLIB_OK;
}
uint8_t pixelSize = image->bpp / 8;
size_t rowsize = image->width * pixelSize;
const uint16_t *data = (uint16_t*)((uint8_t*)image->data + y * rowsize + x * pixelSize);
if(image->profile == LEXLIB_GRAY){
color->r = data[0];
color->g = data[0];
color->b = data[0];
color->a = LEXLIB_COLOR16_MAX;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_GRAYA){
color->r = data[0];
color->g = data[0];
color->b = data[0];
color->a = data[1];
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGB){
color->r = data[0];
color->g = data[1];
color->b = data[2];
color->a = LEXLIB_COLOR16_MAX;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGBA){
color->r = data[0];
color->g = data[1];
color->b = data[2];
color->a = data[3];
return LEXLIB_OK;
}
*color = lexlibColorTo16(LEXLIB_COLOR_MAGENTA);
return LEXLIB_INVALID_VALUE;
}
uint8_t lexlibImagePixelSet(struct LexlibImage *image, uint32_t x, uint32_t y, struct LexlibColor color, uint8_t blendmode){
if(x >= image->width || y >= image->height)
return LEXLIB_INVALID_OPERATION;
if(image->bpc == 16)
return lexlibImagePixel16Set(image, x, y, lexlibColorTo16(color), blendmode);
uint8_t pixelsize = image->bpp / 8;
size_t rowsize = image->width * pixelsize;
uint8_t *data = (uint8_t*)image->data + y * rowsize + x * pixelsize;
if(image->profile == LEXLIB_GRAY){
*data = lexlibColorGray(color);
return LEXLIB_OK;
}
if(image->profile == LEXLIB_GRAYA){
struct LexlibColor pixel = {
.r = data[0],
.g = data[0],
.b = data[0],
.a = data[1]
};
color = lexlibColorGrayAlpha(color);
pixel = lexlibColorBlend(pixel, color, blendmode);
data[0] = pixel.r;
data[1] = pixel.a;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGB){
struct LexlibColor pixel = {
.r = data[0],
.g = data[1],
.b = data[2],
.a = 0xFF,
};
color = lexlibColorBlend(pixel, color, blendmode);
color = lexlibColorPremultiply(color);
data[0] = color.r;
data[1] = color.g;
data[2] = color.b;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGBA){
struct LexlibColor *pixel = (struct LexlibColor*)data;
*pixel = lexlibColorBlend(*pixel, color, blendmode);
return LEXLIB_OK;
}
return LEXLIB_INVALID_VALUE;
}
uint8_t lexlibImagePixel16Set(struct LexlibImage *image, uint32_t x, uint32_t y, struct LexlibColor16 color, uint8_t blendmode){
if(x >= image->width || y >= image->height)
return LEXLIB_INVALID_OPERATION;
if(image->bpc != 16)
return lexlibImagePixelSet(image, x, y, lexlibColor16To8(color), blendmode);
uint8_t pixelSize = image->bpp / 8;
size_t rowsize = image->width * pixelSize;
uint16_t* data = (uint16_t*)((uint8_t*)image->data + y * rowsize + x * pixelSize);
if(image->profile == LEXLIB_GRAY){
*data = lexlibColor16Gray(color);
return LEXLIB_OK;
}
if(image->profile == LEXLIB_GRAYA){
struct LexlibColor16 pixel = {
.r = data[0],
.g = data[0],
.b = data[0],
.a = data[1]
};
color = lexlibColor16GrayAlpha(color);
pixel = lexlibColor16Blend(pixel, color, blendmode);
data[0] = pixel.r;
data[1] = pixel.a;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGB){
struct LexlibColor16 pixel = {
.r = data[0],
.g = data[1],
.b = data[2],
.a = LEXLIB_COLOR16_MAX
};
color = lexlibColor16Blend(pixel, color, blendmode);
color = lexlibColor16Premultiply(color);
data[0] = color.r;
data[1] = color.g;
data[2] = color.b;
return LEXLIB_OK;
}
if(image->profile == LEXLIB_RGBA){
struct LexlibColor16 *pixel = (struct LexlibColor16*)data;
*pixel = lexlibColor16Blend(*pixel, color, blendmode);
return LEXLIB_OK;
}
return LEXLIB_INVALID_VALUE;
}
uint8_t lexlibImageLoad(struct LexlibImage *image, const char *filename){
FILE *file = fopen(filename, "r");
if(!file)
return LEXLIB_CANT_OPEN;
uint16_t type = lexlibCFileType(file);
fclose(file);
switch(type){
case LEXLIB_FILETYPE_PNG:
return lexlibImageLoadPng(image, filename);
case LEXLIB_FILETYPE_BMP:
return lexlibImageLoadBmp(image, filename);
}
return LEXLIB_INVALID_TYPE;
}
uint8_t lexlibImageSave(const struct LexlibImage *image, const char *filename){
char* offset = strrchr(filename, '.');
if(offset){
if(strcmp(offset, ".png") == 0)
return lexlibImageSavePng(image, filename);
if(strcmp(offset, ".bmp") == 0)
return lexlibImageSaveBmp(image, filename);
}
return LEXLIB_INVALID_NAME;
}
uint8_t lexlibImageRepairStruct(struct LexlibImage *image){
if(!image->width || !image->height)
return LEXLIB_ERROR;
if(!image->data)
return LEXLIB_ERROR;
repairStructGetProfile:
switch(image->profile){
case LEXLIB_GRAY:
image->channels = 1;
image->flags &= ~LEXLIB_ALPHA;
break;
case LEXLIB_GRAYA:
image->channels = 2;
image->flags |= LEXLIB_ALPHA;
break;
case LEXLIB_RGB:
image->channels = 3;
image->flags &= ~LEXLIB_ALPHA;
break;
case LEXLIB_RGBA:
image->channels = 4;
image->flags |= LEXLIB_ALPHA;
break;
default:
image->profile = LEXLIB_NONE;
break;
}
if(image->profile == LEXLIB_NONE){
switch(image->channels){
case 1:
image->profile = LEXLIB_GRAY;
goto repairStructGetProfile;
case 2:
image->profile = LEXLIB_GRAYA;
goto repairStructGetProfile;
case 3:
image->profile = LEXLIB_RGB;
goto repairStructGetProfile;
case 4:
image->profile = LEXLIB_RGBA;
goto repairStructGetProfile;
default:
return LEXLIB_ERROR;
}
}
if(image->bpc == 0)
image->bpc = 8;
if((image->bpc % 8) != 0)
image->bpc = 8;
if(image->bpc > 16)
image->bpc = 16;
image->bpp = image->channels * image->bpc;
return LEXLIB_OK;
}