301 lines
9.8 KiB
C
301 lines
9.8 KiB
C
#include "plutovg-private.h"
|
|
#include "plutovg-utils.h"
|
|
|
|
#define STB_IMAGE_WRITE_STATIC
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#include "plutovg-stb-image-write.h"
|
|
|
|
#define STB_IMAGE_STATIC
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include "plutovg-stb-image.h"
|
|
|
|
static plutovg_surface_t* plutovg_surface_create_uninitialized(int width, int height)
|
|
{
|
|
if(width > STBI_MAX_DIMENSIONS || height > STBI_MAX_DIMENSIONS)
|
|
return NULL;
|
|
const size_t size = width * height * 4;
|
|
plutovg_surface_t* surface = malloc(size + sizeof(plutovg_surface_t));
|
|
if(surface == NULL)
|
|
return NULL;
|
|
surface->ref_count = 1;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
surface->stride = width * 4;
|
|
surface->data = (uint8_t*)(surface + 1);
|
|
return surface;
|
|
}
|
|
|
|
plutovg_surface_t* plutovg_surface_create(int width, int height)
|
|
{
|
|
plutovg_surface_t* surface = plutovg_surface_create_uninitialized(width, height);
|
|
if(surface)
|
|
memset(surface->data, 0, surface->height * surface->stride);
|
|
return surface;
|
|
}
|
|
|
|
plutovg_surface_t* plutovg_surface_create_for_data(unsigned char* data, int width, int height, int stride)
|
|
{
|
|
plutovg_surface_t* surface = malloc(sizeof(plutovg_surface_t));
|
|
surface->ref_count = 1;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
surface->stride = stride;
|
|
surface->data = data;
|
|
return surface;
|
|
}
|
|
|
|
static plutovg_surface_t* plutovg_surface_load_from_image(stbi_uc* image, int width, int height)
|
|
{
|
|
plutovg_surface_t* surface = plutovg_surface_create_uninitialized(width, height);
|
|
if(surface)
|
|
plutovg_convert_rgba_to_argb(surface->data, image, surface->width, surface->height, surface->stride);
|
|
stbi_image_free(image);
|
|
return surface;
|
|
}
|
|
|
|
plutovg_surface_t* plutovg_surface_load_from_image_file(const char* filename)
|
|
{
|
|
int width, height, channels;
|
|
stbi_uc* image = stbi_load(filename, &width, &height, &channels, STBI_rgb_alpha);
|
|
if(image == NULL)
|
|
return NULL;
|
|
return plutovg_surface_load_from_image(image, width, height);
|
|
}
|
|
|
|
plutovg_surface_t* plutovg_surface_load_from_image_data(const void* data, int length)
|
|
{
|
|
int width, height, channels;
|
|
stbi_uc* image = stbi_load_from_memory(data, length, &width, &height, &channels, STBI_rgb_alpha);
|
|
if(image == NULL)
|
|
return NULL;
|
|
return plutovg_surface_load_from_image(image, width, height);
|
|
}
|
|
|
|
static const uint8_t base64_table[128] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F,
|
|
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
|
|
0x3C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
|
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
|
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
|
0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
|
|
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
|
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
|
|
0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
plutovg_surface_t* plutovg_surface_load_from_image_base64(const char* data, int length)
|
|
{
|
|
plutovg_surface_t* surface = NULL;
|
|
uint8_t* output_data = NULL;
|
|
size_t output_length = 0;
|
|
|
|
size_t equals_sign_count = 0;
|
|
size_t sidx = 0;
|
|
size_t didx = 0;
|
|
|
|
if(length == -1)
|
|
length = strlen(data);
|
|
output_data = malloc(length);
|
|
if(output_data == NULL)
|
|
return NULL;
|
|
for(int i = 0; i < length; ++i) {
|
|
uint8_t cc = data[i];
|
|
if(cc == '=') {
|
|
++equals_sign_count;
|
|
} else if(cc == '+' || cc == '/' || PLUTOVG_IS_ALNUM(cc)) {
|
|
if(equals_sign_count > 0)
|
|
goto cleanup;
|
|
output_data[output_length++] = base64_table[cc];
|
|
} else if(!PLUTOVG_IS_WS(cc)) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if(output_length == 0 || equals_sign_count > 2 || (output_length % 4) == 1)
|
|
goto cleanup;
|
|
output_length -= (output_length + 3) / 4;
|
|
if(output_length == 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if(output_length > 1) {
|
|
while(didx < output_length - 2) {
|
|
output_data[didx + 0] = (((output_data[sidx + 0] << 2) & 255) | ((output_data[sidx + 1] >> 4) & 003));
|
|
output_data[didx + 1] = (((output_data[sidx + 1] << 4) & 255) | ((output_data[sidx + 2] >> 2) & 017));
|
|
output_data[didx + 2] = (((output_data[sidx + 2] << 6) & 255) | ((output_data[sidx + 3] >> 0) & 077));
|
|
sidx += 4;
|
|
didx += 3;
|
|
}
|
|
}
|
|
|
|
if(didx < output_length)
|
|
output_data[didx] = (((output_data[sidx + 0] << 2) & 255) | ((output_data[sidx + 1] >> 4) & 003));
|
|
if(++didx < output_length) {
|
|
output_data[didx] = (((output_data[sidx + 1] << 4) & 255) | ((output_data[sidx + 2] >> 2) & 017));
|
|
}
|
|
|
|
surface = plutovg_surface_load_from_image_data(output_data, output_length);
|
|
cleanup:
|
|
free(output_data);
|
|
return surface;
|
|
}
|
|
|
|
plutovg_surface_t* plutovg_surface_reference(plutovg_surface_t* surface)
|
|
{
|
|
if(surface == NULL)
|
|
return NULL;
|
|
++surface->ref_count;
|
|
return surface;
|
|
}
|
|
|
|
void plutovg_surface_destroy(plutovg_surface_t* surface)
|
|
{
|
|
if(surface == NULL)
|
|
return;
|
|
if(--surface->ref_count == 0) {
|
|
free(surface);
|
|
}
|
|
}
|
|
|
|
int plutovg_surface_get_reference_count(const plutovg_surface_t* surface)
|
|
{
|
|
if(surface)
|
|
return surface->ref_count;
|
|
return 0;
|
|
}
|
|
|
|
unsigned char* plutovg_surface_get_data(const plutovg_surface_t* surface)
|
|
{
|
|
return surface->data;
|
|
}
|
|
|
|
int plutovg_surface_get_width(const plutovg_surface_t* surface)
|
|
{
|
|
return surface->width;
|
|
}
|
|
|
|
int plutovg_surface_get_height(const plutovg_surface_t* surface)
|
|
{
|
|
return surface->height;
|
|
}
|
|
|
|
int plutovg_surface_get_stride(const plutovg_surface_t* surface)
|
|
{
|
|
return surface->stride;
|
|
}
|
|
|
|
void plutovg_surface_clear(plutovg_surface_t* surface, const plutovg_color_t* color)
|
|
{
|
|
uint32_t pixel = plutovg_premultiply_argb(plutovg_color_to_argb32(color));
|
|
for(int y = 0; y < surface->height; y++) {
|
|
uint32_t* pixels = (uint32_t*)(surface->data + surface->stride * y);
|
|
plutovg_memfill32(pixels, surface->width, pixel);
|
|
}
|
|
}
|
|
|
|
static void plutovg_surface_write_begin(const plutovg_surface_t* surface)
|
|
{
|
|
plutovg_convert_argb_to_rgba(surface->data, surface->data, surface->width, surface->height, surface->stride);
|
|
}
|
|
|
|
static void plutovg_surface_write_end(const plutovg_surface_t* surface)
|
|
{
|
|
plutovg_convert_rgba_to_argb(surface->data, surface->data, surface->width, surface->height, surface->stride);
|
|
}
|
|
|
|
bool plutovg_surface_write_to_png(const plutovg_surface_t* surface, const char* filename)
|
|
{
|
|
plutovg_surface_write_begin(surface);
|
|
int success = stbi_write_png(filename, surface->width, surface->height, 4, surface->data, surface->stride);
|
|
plutovg_surface_write_end(surface);
|
|
return success;
|
|
}
|
|
|
|
bool plutovg_surface_write_to_jpg(const plutovg_surface_t* surface, const char* filename, int quality)
|
|
{
|
|
plutovg_surface_write_begin(surface);
|
|
int success = stbi_write_jpg(filename, surface->width, surface->height, 4, surface->data, quality);
|
|
plutovg_surface_write_end(surface);
|
|
return success;
|
|
}
|
|
|
|
bool plutovg_surface_write_to_png_stream(const plutovg_surface_t* surface, plutovg_write_func_t write_func, void* closure)
|
|
{
|
|
plutovg_surface_write_begin(surface);
|
|
int success = stbi_write_png_to_func(write_func, closure, surface->width, surface->height, 4, surface->data, surface->stride);
|
|
plutovg_surface_write_end(surface);
|
|
return success;
|
|
}
|
|
|
|
bool plutovg_surface_write_to_jpg_stream(const plutovg_surface_t* surface, plutovg_write_func_t write_func, void* closure, int quality)
|
|
{
|
|
plutovg_surface_write_begin(surface);
|
|
int success = stbi_write_jpg_to_func(write_func, closure, surface->width, surface->height, 4, surface->data, quality);
|
|
plutovg_surface_write_end(surface);
|
|
return success;
|
|
}
|
|
|
|
void plutovg_convert_argb_to_rgba(unsigned char* dst, const unsigned char* src, int width, int height, int stride)
|
|
{
|
|
for(int y = 0; y < height; y++) {
|
|
const uint32_t* src_row = (const uint32_t*)(src + stride * y);
|
|
unsigned char* dst_row = dst + stride * y;
|
|
for(int x = 0; x < width; x++) {
|
|
uint32_t pixel = src_row[x];
|
|
uint32_t a = (pixel >> 24) & 0xFF;
|
|
if(a == 0) {
|
|
*dst_row++ = 0;
|
|
*dst_row++ = 0;
|
|
*dst_row++ = 0;
|
|
*dst_row++ = 0;
|
|
} else {
|
|
uint32_t r = (pixel >> 16) & 0xFF;
|
|
uint32_t g = (pixel >> 8) & 0xFF;
|
|
uint32_t b = (pixel >> 0) & 0xFF;
|
|
if(a != 255) {
|
|
r = (r * 255) / a;
|
|
g = (g * 255) / a;
|
|
b = (b * 255) / a;
|
|
}
|
|
|
|
*dst_row++ = r;
|
|
*dst_row++ = g;
|
|
*dst_row++ = b;
|
|
*dst_row++ = a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void plutovg_convert_rgba_to_argb(unsigned char* dst, const unsigned char* src, int width, int height, int stride)
|
|
{
|
|
for(int y = 0; y < height; y++) {
|
|
const unsigned char* src_row = src + stride * y;
|
|
uint32_t* dst_row = (uint32_t*)(dst + stride * y);
|
|
for(int x = 0; x < width; x++) {
|
|
uint32_t a = src_row[4 * x + 3];
|
|
if(a == 0) {
|
|
dst_row[x] = 0x00000000;
|
|
} else {
|
|
uint32_t r = src_row[4 * x + 0];
|
|
uint32_t g = src_row[4 * x + 1];
|
|
uint32_t b = src_row[4 * x + 2];
|
|
if(a != 255) {
|
|
r = (r * a) / 255;
|
|
g = (g * a) / 255;
|
|
b = (b * a) / 255;
|
|
}
|
|
|
|
dst_row[x] = (a << 24) | (r << 16) | (g << 8) | b;
|
|
}
|
|
}
|
|
}
|
|
}
|