mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 16:50:54 +01:00
414 lines
12 KiB
C
414 lines
12 KiB
C
/*
|
|
|
|
ZPL - Image module
|
|
|
|
Usage:
|
|
#define ZPLI_IMPLEMENTATION exactly in ONE source file right BEFORE including the library, like:
|
|
|
|
#define ZPLI_IMPLEMENTATION
|
|
#include"zpl_image.h"
|
|
|
|
Dependencies:
|
|
zpl.h
|
|
stb_image.h
|
|
|
|
Make sure you properly include them!
|
|
|
|
Optional switches:
|
|
ZPLI_NO_GIF
|
|
ZPLI_NO_IMAGE_OPS
|
|
|
|
Credits:
|
|
Read AUTHORS.md
|
|
|
|
GitHub:
|
|
https://github.com/zpl-c/zpl
|
|
|
|
Version History:
|
|
2.0.0 - New syntax
|
|
|
|
1.0.2 - Switch fixes
|
|
1.0.1 - Got rid of unused switches and fixes
|
|
1.0.0 - Initial version
|
|
|
|
|
|
|
|
This Software is dual licensed under the following licenses:
|
|
|
|
Unlicense
|
|
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 <http://unlicense.org/>
|
|
|
|
Apache 2.0
|
|
Copyright 2017-2018 Dominik Madarász <zaklaus@outlook.com>
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
#ifndef ZPL_INCLUDE_ZPL_IMAGE_H
|
|
#define ZPL_INCLUDE_ZPL_IMAGE_H
|
|
|
|
#if defined(__cplusplus)
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef union zpli_rgb_colour {
|
|
u32 colour;
|
|
struct { u8 r, g, b;};
|
|
} zpli_rgb_colour;
|
|
|
|
typedef struct zpli_hsv_colour {
|
|
u32 colour;
|
|
struct { u8 h, s, v; };
|
|
} zpli_hsv_colour;
|
|
|
|
ZPL_DEF zpli_rgb_colour zpli_rgb_lerp(zpli_rgb_colour a, zpli_rgb_colour b, f32 t);
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// GIF Loader
|
|
//
|
|
// Uses stb_image.h for loading gif frames.
|
|
//
|
|
|
|
#ifndef ZPLI_NO_GIF
|
|
|
|
typedef struct zpli_gif_result {
|
|
i32 delay;
|
|
u8 *data;
|
|
struct zpli_gif_result *next;
|
|
} zpli_gif_result;
|
|
|
|
ZPL_DEF zpli_gif_result *zpli_gif_load(char const *filename, i32 *x, i32 *y, i32 *frames);
|
|
ZPL_DEF void zpli_gif_free(zpli_gif_result *gif, b32 aligned);
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Image Operations
|
|
//
|
|
|
|
#ifndef ZPLI_NO_IMAGE_OPS
|
|
// NOTE(ZaKlaus): This is not sRGB aware!
|
|
ZPL_DEF void zpli_rgb_resize(u32 *source, i32 source_w, i32 source_h,
|
|
u32 *dest, i32 dest_w, i32 dest_h,
|
|
i32 blur_iter, u32 *blur_mem);
|
|
|
|
ZPL_DEF void zpli_rgb_filter(u32 *source, i32 source_w, i32 source_h,
|
|
u32 *dest,
|
|
f64 *filter, i32 filter_w, i32 filter_h,
|
|
f64 factor, f64 bias);
|
|
|
|
// TODO(ZaKlaus): Implement this
|
|
/*
|
|
ZPL_DEF void zpli_init_srgb_table(u8 **table);
|
|
ZPL_DEF zpli_rgb_colour zpli_lin_to_srgb (u8 *table, f64 vals[3]);
|
|
*/
|
|
|
|
ZPL_DEF zpli_hsv_colour zpli_rgb_to_hsv (zpli_rgb_colour colour);
|
|
ZPL_DEF zpli_rgb_colour zpli_hsv_to_rgb (zpli_hsv_colour colour);
|
|
#endif
|
|
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
|
|
#if defined(ZPLI_IMPLEMENTATION) && !defined(ZPLI_IMPLEMENTATION_DONE)
|
|
#define ZPLI_IMPLEMENTATION_DONE
|
|
|
|
#if defined(__cplusplus)
|
|
extern "C" {
|
|
#endif
|
|
|
|
zpli_rgb_colour zpli_rgb_lerp(zpli_rgb_colour a, zpli_rgb_colour b, f32 t) {
|
|
#define LERP(c1, c2, c3) c1*(1.0-c3) + c2*c3
|
|
zpli_rgb_colour result = {0};
|
|
|
|
result.r = LERP(a.r, b.r, t);
|
|
result.g = LERP(a.g, b.g, t);
|
|
result.b = LERP(a.b, b.b, t);
|
|
|
|
return result;
|
|
#undef LERP
|
|
}
|
|
|
|
// NOTE(ZaKlaus): Gif
|
|
|
|
#ifndef ZPL_NO_GIF
|
|
zpli_gif_result *zpli_gif_load(char const *filename, i32 *x, i32 *y, i32 *frames) {
|
|
FILE *file;
|
|
stbi__context s;
|
|
zpli_gif_result *result;
|
|
|
|
if (!(file = stbi__fopen(filename, "rb"))) {
|
|
stbi__errpuc("can't open file", "Unable to open file"); return 0;
|
|
}
|
|
|
|
stbi__start_file(&s, file);
|
|
|
|
if (stbi__gif_test(&s)) {
|
|
i32 c;
|
|
stbi__gif g;
|
|
zpli_gif_result *head = stbi__malloc(zpl_size_of(zpli_gif_result_t));
|
|
zpli_gif_result *prev = 0, *gr = head;
|
|
|
|
zpl_zero_item(&g);
|
|
zpl_zero_item(head);
|
|
|
|
*frames = 0;
|
|
|
|
while(gr->data = stbi__gif_load_next(&s, &g, &c, 4)) {
|
|
if (gr->data == cast(u8 *)&s) {
|
|
gr->data = 0;
|
|
break;
|
|
}
|
|
|
|
if (prev) prev->next = gr;
|
|
gr->delay = g.delay;
|
|
prev = gr;
|
|
gr = cast(zpli_gif_result *)stbi__malloc(zpl_size_of(zpli_gif_result_t));
|
|
zpl_zero_item(gr);
|
|
++(*frames);
|
|
}
|
|
|
|
STBI_FREE(g.out);
|
|
|
|
if (gr != head) {
|
|
//STBI_FREE(gr);
|
|
}
|
|
|
|
if (*frames > 0) {
|
|
*x = g.w;
|
|
*y = g.h;
|
|
}
|
|
|
|
result = head;
|
|
}
|
|
else {
|
|
// TODO(ZaKlaus): Add support.
|
|
result = 0; //stbi__load_main(&s, x, y, frames, &bpp, 4, &ri, 8);
|
|
*frames = !!result;
|
|
}
|
|
|
|
fclose(file);
|
|
return result;
|
|
}
|
|
|
|
void zpli_gif_free(zpli_gif_result *gif, b32 aligned) {
|
|
zpli_gif_result *elem, *prev = 0;
|
|
for (elem = gif; elem; elem = elem->next) {
|
|
if (aligned) {
|
|
zpl_mfree(elem->data);
|
|
}
|
|
else {
|
|
STBI_FREE(elem->data);
|
|
}
|
|
STBI_FREE(prev);
|
|
prev = elem;
|
|
}
|
|
|
|
STBI_FREE(prev);
|
|
}
|
|
#endif
|
|
|
|
zpli_hsv_colour zpli_rgb_to_hsv(zpli_rgb_colour colour) {
|
|
zpli_hsv_colour result = {0};
|
|
u8 rgb_min, rgb_max;
|
|
|
|
rgb_min = colour.r < colour.g ? (colour.r < colour.b ? colour.r : colour.b) : (colour.g < colour.b ? colour.g : colour.b);
|
|
rgb_max = colour.r > colour.g ? (colour.r > colour.b ? colour.r : colour.b) : (colour.g > colour.b ? colour.g : colour.b);
|
|
|
|
result.v = rgb_max;
|
|
if (result.v == 0) {
|
|
result.h = result.s = 0;
|
|
return result;
|
|
}
|
|
|
|
result.s = 255 * cast(i64)(rgb_max - rgb_min) / result.v;
|
|
if (result.s == 0) {
|
|
result.h = 0;
|
|
return result;
|
|
}
|
|
|
|
/**/ if (rgb_max == colour.r) {
|
|
result.h = 0 + 43 * (colour.g - colour.b) / (rgb_max - rgb_min);
|
|
}
|
|
else if (rgb_max == colour.g) {
|
|
result.h = 85 + 43 * (colour.b - colour.r) / (rgb_max - rgb_min);
|
|
}
|
|
else {
|
|
result.h = 171 + 43 * (colour.r - colour.g) / (rgb_max - rgb_min);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
zpli_rgb_colour zpli_hsv_to_rgb(zpli_hsv_colour colour) {
|
|
zpli_rgb_colour result = {0};
|
|
u8 region, rem, p, q, t;
|
|
|
|
if (colour.s == 0) {
|
|
result.r = result.g = result.b = colour.v;
|
|
return result;
|
|
}
|
|
|
|
region = colour.h / 43;
|
|
rem = (colour.h - (region * 43)) * 6;
|
|
|
|
p = (colour.v * (255 - colour.s)) >> 8;
|
|
q = (colour.v * (255 - ((colour.s * rem) >> 8))) >> 8;
|
|
t = (colour.v * (255 - ((colour.s * (255 - rem)) >> 8))) >> 8;
|
|
|
|
switch (region)
|
|
{
|
|
case 0: result.r = colour.v; result.g = t; result.b = p; break;
|
|
case 1: result.r = q; result.g = colour.v; result.b = p; break;
|
|
case 2: result.r = p; result.g = colour.v; result.b = t; break;
|
|
case 3: result.r = p; result.g = q; result.b = colour.v; break;
|
|
case 4: result.r = t; result.g = p; result.b = colour.v; break;
|
|
default:result.r = colour.v; result.g = p; result.b = q; break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifndef ZPL_NO_IMAGE_OPS
|
|
void zpli_rgb_resize(u32 *source, i32 source_w, i32 source_h,
|
|
u32 *dest, i32 dest_w, i32 dest_h,
|
|
i32 blur_iter, u32 *blur_mem) {
|
|
|
|
zpli_rgb_colour *src = cast(zpli_rgb_colour *)&(source);
|
|
zpli_rgb_colour *dst = cast(zpli_rgb_colour *)&(dest);
|
|
|
|
b32 x_down = dest_w < source_w;
|
|
b32 y_down = dest_h < source_h;
|
|
|
|
i32 step_x;
|
|
i32 step_y;
|
|
|
|
if(x_down) {
|
|
step_x = cast(i32)(source_w / cast(f32)dest_w);
|
|
}
|
|
else {
|
|
step_x = cast(i32)(dest_w / cast(f32)source_w);
|
|
}
|
|
|
|
if(y_down) {
|
|
step_y = cast(i32)(source_h / cast(f32)dest_h);
|
|
}
|
|
else {
|
|
step_y = cast(i32)(dest_h / cast(f32)source_h);
|
|
}
|
|
|
|
|
|
|
|
for (i32 y = 0; y < dest_h; ++y) {
|
|
for(i32 x = 0; x < dest_w; ++x) {
|
|
zpli_rgb_colour colour = {0};
|
|
|
|
i32 o_x = x/step_x;
|
|
if (x_down) o_x = x*step_x;
|
|
i32 o_y = y/step_y;
|
|
if (y_down) o_y = y*step_y;
|
|
|
|
colour = src[o_y*source_w + o_x];
|
|
|
|
dst[y*dest_w + x] = colour;
|
|
}
|
|
}
|
|
|
|
if (blur_iter > 0) {
|
|
ZPL_ASSERT_NOT_NULL(blur_mem);
|
|
zpl_local_persist f64 filter[5][5] = {
|
|
0, 0, 1, 0, 0,
|
|
0, 1, 1, 1, 0,
|
|
1, 1, 1, 1, 1,
|
|
0, 1, 1, 1, 0,
|
|
0, 0, 1, 0, 0,
|
|
};
|
|
|
|
f64 factor = 1.0 / 13.0;
|
|
f64 bias = 0.0;
|
|
|
|
zpl_memcopy(blur_mem, dest, dest_w*dest_h*4);
|
|
|
|
zpli_rgb_filter(blur_mem, dest_w, dest_h,
|
|
cast(u32 *)dest,
|
|
&filter[0][0], 5, 5,
|
|
factor, bias);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
void zpli_rgb_filter(u32 *source, i32 source_w, i32 source_h,
|
|
u32 *dest,
|
|
f64 *filter, i32 filter_w, i32 filter_h,
|
|
f64 factor, f64 bias) {
|
|
|
|
zpli_rgb_colour *src = cast(zpli_rgb_colour *)(source);
|
|
zpli_rgb_colour *dst = cast(zpli_rgb_colour *)(dest);
|
|
|
|
for (i32 y = 0; y < source_h; ++y) {
|
|
for(i32 x = 0; x < source_w; ++x) {
|
|
i32 r = 0, g = 0, b = 0;
|
|
|
|
for (i32 fy = 0; fy < filter_h; ++fy) {
|
|
for (i32 fx = 0; fx < filter_w; ++fx) {
|
|
i32 img_x = (x - filter_w / 2 + fx + source_w) % source_w;
|
|
i32 img_y = (y - filter_h / 2 + fy + source_h) % source_h;
|
|
|
|
r += src[img_y * source_w + img_x].r * filter[fy*filter_w + fx];
|
|
g += src[img_y * source_w + img_x].g * filter[fy*filter_w + fx];
|
|
b += src[img_y * source_w + img_x].b * filter[fy*filter_w + fx];
|
|
}
|
|
}
|
|
|
|
dst[y * source_w + x].r = zpl_min(zpl_max(cast(i32)(factor * r + bias), 0), 255);
|
|
dst[y * source_w + x].g = zpl_min(zpl_max(cast(i32)(factor * g + bias), 0), 255);
|
|
dst[y * source_w + x].b = zpl_min(zpl_max(cast(i32)(factor * b + bias), 0), 255);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif // ZPL_INCLUDE_ZPL_IMAGE_H
|