diff --git a/lib/pandagl/include/pandagl.h b/lib/pandagl/include/pandagl.h index 20a4aefeb..a9c4270d3 100644 --- a/lib/pandagl/include/pandagl.h +++ b/lib/pandagl/include/pandagl.h @@ -20,6 +20,7 @@ #include "pandagl/background.h" #include "pandagl/border.h" #include "pandagl/boxshadow.h" +#include "pandagl/file_reader.h" #include "pandagl/font.h" #include "pandagl/text.h" #include "pandagl/image.h" diff --git a/lib/pandagl/include/pandagl/file_reader.h b/lib/pandagl/include/pandagl/file_reader.h new file mode 100644 index 000000000..21418e75b --- /dev/null +++ b/lib/pandagl/include/pandagl/file_reader.h @@ -0,0 +1,40 @@ + +#ifndef LIB_PANDAGL_INCLUDE_PANDAGL_FILE_READER_H +#define LIB_PANDAGL_INCLUDE_PANDAGL_FILE_READER_H + +#include "common.h" +#include "types.h" + +PD_BEGIN_DECLS + +typedef struct pd_file_reader_t { + void *stream_data; + void (*fn_close)(void*); + void (*fn_rewind)(void*); + size_t (*fn_read)(void *, void *, size_t); + void (*fn_skip)(void *, long); +} pd_file_reader_t; + +PD_INLINE size_t pd_file_reader_read(pd_file_reader_t *reader, void *buffer, + size_t size) +{ + return reader->fn_read(reader->stream_data, buffer, size); +} + +PD_INLINE void pd_file_reader_rewind(pd_file_reader_t *reader) +{ + reader->fn_rewind(reader->stream_data); +} + +PD_INLINE void pd_file_reader_skip(pd_file_reader_t *reader, long bytes) +{ + reader->fn_skip(reader->stream_data, bytes); +} + +PD_PUBLIC pd_file_reader_t *pd_file_reader_create(void); +PD_PUBLIC pd_file_reader_t *pd_file_reader_create_from_file(const char *filename); +PD_PUBLIC void pd_file_reader_destroy(pd_file_reader_t *reader); + +PD_END_DECLS + +#endif diff --git a/lib/pandagl/include/pandagl/image.h b/lib/pandagl/include/pandagl/image.h index fb89a1ff6..c551f8f7e 100644 --- a/lib/pandagl/include/pandagl/image.h +++ b/lib/pandagl/include/pandagl/image.h @@ -18,17 +18,12 @@ PD_BEGIN_DECLS -typedef void (*pd_image_progress_func_t)(void *, float); -typedef size_t (*pd_image_read_func_t)(void *, void *, size_t); -typedef void (*pd_image_skip_func_t)(void *, long); -typedef void (*pd_image_func_t)(void *); - -/** 图像读取器的类型 */ typedef enum pd_image_reader_type_t { - PD_UNKNOWN_READER, - PD_PNG_READER, - PD_JPEG_READER, - PD_BMP_READER + PD_UNKNOWN_READER, + PD_PNG_READER, + PD_JPEG_READER, + PD_BMP_READER, + PD_READER_COUNT } pd_image_reader_type_t; #define PD_UNKNOWN_IMAGE PD_UNKNOWN_READER @@ -37,106 +32,47 @@ typedef enum pd_image_reader_type_t { #define PD_BMP_IMAGE PD_BMP_READER #define pd_image_reader_set_jump(READER) \ - (READER)->env &&setjmp(*((READER)->env)) + (READER)->env &&setjmp(*((READER)->env)) typedef struct pd_image_header_t { - int type; - int bit_depth; - int color_type; - unsigned int width, height; + int type; + int bit_depth; + pd_color_type_t color_type; + unsigned int width, height; } pd_image_header_t; -/** 图像读取器 */ typedef struct pd_image_reader_t { - /** 自定义的输入流数据 */ - void *stream_data; - - /** 是否有错误 */ - unsigned char has_error; - - /** 图像头部信息 */ - pd_image_header_t header; - - /** 在开始读取前调用的函数 */ - pd_image_func_t fn_begin; - - /** 在结束读取时调用的函数 */ - pd_image_func_t fn_end; - - /** 游标重置函数,用于重置当前读取位置到数据流的开头处 */ - pd_image_func_t fn_rewind; - - /** 数据读取函数,用于从数据流中读取数据 */ - pd_image_read_func_t fn_read; - - /** 游标移动函数,用于跳过一段数据 */ - pd_image_skip_func_t fn_skip; - - /** 用于接收图像读取进度的函数 */ - pd_image_progress_func_t fn_prog; - - /** 接收图像读取进度时的附加参数 */ - void *prog_arg; - - /** 图片读取器类型 */ - int type; - - /** 私有数据 */ - void *data; - - /** 私有数据的析构函数 */ - void (*destructor)(void *); - - /** 堆栈环境缓存的指针,用于 setjump() */ - jmp_buf *env; - - /** 默认堆栈环境缓存 */ - jmp_buf env_src; + char error_message[256]; + pd_image_reader_type_t type; + pd_file_reader_t *file_reader; + pd_image_header_t header; + unsigned read_row_index; + unsigned pass; + unsigned passes; + void *reader_data; } pd_image_reader_t; -/** 初始化适用于 PNG 图像的读取器 */ -PD_PUBLIC int pd_png_reader_init(pd_image_reader_t *reader); +PD_PUBLIC pd_image_reader_t *pd_image_reader_create(void); +PD_PUBLIC pd_image_reader_t *pd_image_reader_create_from_file( + const char *filename); -/** 初始化适用于 JPEG 图像的读取器 */ -PD_PUBLIC int pd_jpeg_reader_init(pd_image_reader_t *reader); - -/** 初始化适用于 BMP 图像的读取器 */ -PD_PUBLIC int pd_bmp_reader_init(pd_image_reader_t *reader); - -PD_PUBLIC void pd_image_reader_set_file(pd_image_reader_t *reader, FILE *fp); - -/** 创建图像读取器 */ -PD_PUBLIC int pd_image_reader_init(pd_image_reader_t *reader); - -/** 销毁图像读取器 */ PD_PUBLIC void pd_image_reader_destroy(pd_image_reader_t *reader); - -PD_PUBLIC int pd_png_reader_read_header(pd_image_reader_t *reader); - -PD_PUBLIC int pd_jpeg_reader_read_header(pd_image_reader_t *reader); - -PD_PUBLIC int pd_bmp_reader_read_header(pd_image_reader_t *reader); - -PD_PUBLIC int pd_image_reader_read_header(pd_image_reader_t *reader); - -PD_PUBLIC int pd_png_reader_read_data(pd_image_reader_t *reader, - pd_canvas_t *graph); - -PD_PUBLIC int pd_bmp_reader_read_data(pd_image_reader_t *reader, - pd_canvas_t *graph); - -PD_PUBLIC int pd_image_reader_read_data(pd_image_reader_t *reader, - pd_canvas_t *graph); - -/** 将图像数据写入至png文件 */ -PD_PUBLIC int pd_write_png_file(const char *file_name, const pd_canvas_t *graph); - -/** 载入指定图片文件的图像数据 */ -PD_PUBLIC int pd_read_image_from_file(const char *filepath, pd_canvas_t *out); - -/** 从文件中获取图像尺寸 */ -PD_PUBLIC int pd_read_image_size_from_file(const char *filepath, int *width, - int *height); +PD_PUBLIC jmp_buf *pd_image_reader_jmpbuf(pd_image_reader_t *reader); +PD_PUBLIC pd_error_t pd_image_reader_read_header(pd_image_reader_t *reader); +PD_PUBLIC void pd_image_reader_start(pd_image_reader_t *reader); +PD_PUBLIC pd_error_t pd_image_reader_create_buffer(pd_image_reader_t *reader, + pd_canvas_t *out); +PD_PUBLIC void pd_image_reader_read_row(pd_image_reader_t *reader, + pd_canvas_t *data); +PD_PUBLIC void pd_image_reader_finish(pd_image_reader_t *reader); +PD_PUBLIC pd_error_t pd_image_reader_read_data(pd_image_reader_t *reader, + pd_canvas_t *out); + +PD_PUBLIC pd_error_t pd_read_image_from_file(const char *filepath, + pd_canvas_t *out); + +PD_PUBLIC int pd_write_png_file(const char *file_name, + const pd_canvas_t *graph); PD_END_DECLS diff --git a/lib/pandagl/include/pandagl/types.h b/lib/pandagl/include/pandagl/types.h index 331bc8bfe..e399bbff8 100644 --- a/lib/pandagl/include/pandagl/types.h +++ b/lib/pandagl/include/pandagl/types.h @@ -21,6 +21,7 @@ typedef unsigned char pd_bool_t; #define PD_TRUE 1 typedef enum pd_color_type_t { + PD_COLOR_TYPE_UNSUPPORTED, PD_COLOR_TYPE_INDEX8, /**< 8位索引 */ PD_COLOR_TYPE_GRAY8, /**< 8位灰度 */ PD_COLOR_TYPE_RGB323, /**< RGB323 */ @@ -28,7 +29,7 @@ typedef enum pd_color_type_t { PD_COLOR_TYPE_RGB555, /**< RGB555 */ PD_COLOR_TYPE_RGB565, /**< RGB565 */ PD_COLOR_TYPE_RGB888, /**< RGB888 */ - PD_COLOR_TYPE_ARGB8888 /**< RGB8888 */ + PD_COLOR_TYPE_ARGB8888, /**< RGB8888 */ } pd_color_type_t; #define PD_COLOR_TYPE_RGB PD_COLOR_TYPE_RGB888 @@ -126,4 +127,14 @@ typedef struct pd_border_t { unsigned int bottom_right_radius; } pd_border_t; +typedef enum pd_error_t { + PD_OK = 0, + PD_ERROR_NOT_FOUND, + PD_ERROR_NOT_IMPLEMENTED, + PD_ERROR_IMAGE_HEADER_INVALID, + PD_ERROR_IMAGE_TYPE_INCORRECT, + PD_ERROR_IMAGE_DATA_NOT_SUPPORTED, + PD_ERROR_IMAGE_READING +} pd_error_t; + #endif diff --git a/lib/pandagl/src/file_reader.c b/lib/pandagl/src/file_reader.c new file mode 100644 index 000000000..e63a3c75a --- /dev/null +++ b/lib/pandagl/src/file_reader.c @@ -0,0 +1,52 @@ +#include +#include +#include + +static size_t pd_file_stream_on_read(void *data, void *buffer, size_t size) +{ + return fread(buffer, 1, size, data); +} + +static void pd_file_stream_on_skip(void *data, long offset) +{ + fseek(data, offset, SEEK_CUR); +} + +static void pd_file_stream_on_rewind(void *data) +{ + rewind(data); +} + +static void pd_file_stream_on_close(void *data) +{ + fclose(data); +} + +pd_file_reader_t *pd_file_reader_create(void) +{ + return calloc(1, sizeof(pd_file_reader_t)); +} + +pd_file_reader_t *pd_file_reader_create_from_file(const char *filename) +{ + FILE *fp; + pd_file_reader_t *reader; + + fp = fopen(filename, "rb"); + if (fp == NULL) { + return NULL; + } + reader = pd_file_reader_create(); + reader->stream_data = fp; + reader->fn_skip = pd_file_stream_on_skip; + reader->fn_read = pd_file_stream_on_read; + reader->fn_rewind = pd_file_stream_on_rewind; + reader->fn_close = pd_file_stream_on_close; + return reader; +} + +void pd_file_reader_destroy(pd_file_reader_t *reader) +{ + reader->fn_close(reader->stream_data); + free(reader); +} diff --git a/lib/pandagl/src/image/bmp.c b/lib/pandagl/src/image/bmp.c index c7fc557e4..20e712579 100644 --- a/lib/pandagl/src/image/bmp.c +++ b/lib/pandagl/src/image/bmp.c @@ -14,7 +14,7 @@ #include #include #include -#include "bmp.h" +#include "bmp_private.h" /* clang-format off */ @@ -24,7 +24,7 @@ typedef struct { uint16_t reserved1; uint16_t reserved2; uint32_t offset; /**< Offset to image data, bytes */ -} HEADER; +} pd_bmp_header_t; typedef struct { uint32_t size; /**< Header size in bytes */ @@ -36,123 +36,111 @@ typedef struct { int32_t xresolution, yresolution; /**< Pixels per meter */ uint32_t ncolours; /**< Number of colours */ uint32_t importantcolours; /**< Important colours */ -} INFOHEADER; - -typedef struct { - unsigned char r, g, b, junk; -} COLOURINDEX; - +} pd_bmp_info_header_t; typedef struct pd_bmp_reader_t { - HEADER header; - INFOHEADER info; + pd_bmp_header_t header; + pd_bmp_info_header_t info; + jmp_buf buf; } pd_bmp_reader_t; /* clang-format on */ -static void BMPHeader_Init(HEADER *header, uint16_t buffer[8]) +static void pd_bmp_header_init(pd_bmp_header_t *header, uint16_t buffer[8]) { - header->type = buffer[0]; - header->size = *(uint32_t *)(buffer + 1); - header->reserved1 = buffer[3]; - header->reserved2 = buffer[4]; - header->offset = buffer[5]; + header->type = buffer[0]; + header->size = *(uint32_t *)(buffer + 1); + header->reserved1 = buffer[3]; + header->reserved2 = buffer[4]; + header->offset = buffer[5]; } -int pd_bmp_reader_init(pd_image_reader_t* reader) +void pd_bmp_reader_create(pd_image_reader_t *reader) { - pd_bmp_reader_t * bmp_reader; - - bmp_reader = malloc(sizeof(pd_bmp_reader_t)); - reader->data = bmp_reader; - reader->destructor = free; - reader->type = PD_BMP_READER; - reader->env = &reader->env_src; - if (reader->fn_begin) { - reader->fn_begin(reader->stream_data); - } - return 0; + reader->reader_data = malloc(sizeof(pd_bmp_reader_t)); } -int pd_bmp_reader_read_header(pd_image_reader_t* reader) +void pd_bmp_reader_destroy(pd_image_reader_t *reader) { - size_t n; - uint16_t buffer[8]; - pd_bmp_reader_t * bmp_reader = reader->data; - INFOHEADER *info = &bmp_reader->info; - reader->header.type = PD_UNKNOWN_IMAGE; - if (reader->type != PD_BMP_READER) { - return -EINVAL; - } - n = reader->fn_read(reader->stream_data, buffer, 14); - /* 受到字节对齐影响,直接将读到的写到结构体变量里会让最后一个成员 - * 变量的值不正常,因此需要手动为其成员变量赋值 */ - BMPHeader_Init(&bmp_reader->header, buffer); - if (n < 14 || bmp_reader->header.type != 0x4D42) { - return -2; - } - n = reader->fn_read(reader->stream_data, info, sizeof(INFOHEADER)); - if (n < sizeof(INFOHEADER)) { - return -2; - } - reader->header.width = info->width; - reader->header.height = info->height; - reader->header.type = PD_BMP_IMAGE; - if (info->bits == 24) { - reader->header.color_type = PD_COLOR_TYPE_RGB; - reader->header.bit_depth = 24; - } - return 0; + free(reader->reader_data); } -int pd_bmp_reader_read_data(pd_image_reader_t* reader, pd_canvas_t *graph) +pd_error_t pd_bmp_reader_read_header(pd_image_reader_t *reader) { - long offset; - unsigned char *buffer, *dest; - size_t n, row, bytes_per_row; - pd_bmp_reader_t * bmp_reader = reader->data; - INFOHEADER *info = &bmp_reader->info; - - if (reader->type != PD_BMP_READER) { - return -EINVAL; - } - if (reader->header.type == PD_UNKNOWN_IMAGE) { - if (pd_bmp_reader_read_header(reader) != 0) { - return -2; - } - } - /* 信息头中的偏移位置是相对于起始处,需要减去当前已经偏移的位置 */ - offset = bmp_reader->header.offset - bmp_reader->info.size - 14; - reader->fn_skip(reader->stream_data, offset); - if (0 != pd_canvas_create(graph, info->width, info->height)) { - return -ENOMEM; - } - /* 暂时不实现其它色彩类型处理 */ - if (info->bits != 24) { - return -ENOSYS; - } - bytes_per_row = (info->bits * info->width + 31) / 32 * 4; - if (bytes_per_row < graph->bytes_per_row) { - return -EINVAL; - } - buffer = malloc(bytes_per_row); - if (!buffer) { - return -ENOMEM; - } - /* 从最后一行开始保存 */ - dest = graph->bytes + graph->bytes_per_row * (graph->height - 1); - for (row = 0; row < info->height; ++row) { - n = reader->fn_read(reader->stream_data, buffer, bytes_per_row); - if (n < bytes_per_row) { - break; - } - memcpy(dest, buffer, graph->bytes_per_row); - dest -= graph->bytes_per_row; - if (reader->fn_prog) { - reader->fn_prog(reader->prog_arg, - 100.0f * row / info->height); - } - } - free(buffer); - return 0; + size_t n; + uint16_t buffer[8]; + pd_bmp_reader_t *bmp_reader = reader->reader_data; + pd_bmp_info_header_t *info = &bmp_reader->info; + + if (reader->type != PD_BMP_READER) { + return -EINVAL; + } + n = pd_file_reader_read(reader->file_reader, buffer, 14); + /* 受到字节对齐影响,直接将读到的写到结构体变量里会让最后一个成员 + * 变量的值不正常,因此需要手动为其成员变量赋值 */ + pd_bmp_header_init(&bmp_reader->header, buffer); + if (n < 14 || bmp_reader->header.type != 0x4D42) { + return PD_ERROR_IMAGE_HEADER_INVALID; + } + n = pd_file_reader_read(reader->file_reader, info, + sizeof(pd_bmp_info_header_t)); + if (n < sizeof(pd_bmp_info_header_t)) { + return PD_ERROR_IMAGE_HEADER_INVALID; + } + reader->header.width = info->width; + reader->header.height = info->height; + reader->header.type = PD_BMP_IMAGE; + if (info->bits == 24) { + reader->header.color_type = PD_COLOR_TYPE_RGB; + reader->header.bit_depth = 24; + } + return PD_OK; +} + +jmp_buf *pd_bmp_reader_jmpbuf(pd_image_reader_t *reader) +{ + pd_bmp_reader_t *bmp_reader = reader->reader_data; + return &bmp_reader->buf; +} + +void pd_bmp_reader_finish(pd_image_reader_t *reader) +{ +} + +void pd_bmp_reader_start(pd_image_reader_t *reader) +{ + pd_bmp_reader_t *bmp_reader = reader->reader_data; + /* 信息头中的偏移位置是相对于起始处,需要减去当前已经偏移的位置 */ + long offset = bmp_reader->header.offset - bmp_reader->info.size - 14; + pd_file_reader_skip(reader->file_reader, offset); +} + +void pd_bmp_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *graph) +{ + unsigned char *buffer, *dest; + size_t n, row, bytes_per_row; + pd_bmp_reader_t *bmp_reader = reader->reader_data; + pd_bmp_info_header_t *info = &bmp_reader->info; + + /* 暂时不实现其它色彩类型处理 */ + if (info->bits != 24) { + return; + } + bytes_per_row = (info->bits * info->width + 31) / 32 * 4; + if (bytes_per_row < graph->bytes_per_row) { + return; + } + buffer = malloc(bytes_per_row); + /* 从最后一行开始保存 */ + dest = graph->bytes + graph->bytes_per_row * (graph->height - 1); + for (row = 0; row < info->height; ++row) { + n = pd_file_reader_read(reader->file_reader, buffer, + bytes_per_row); + if (n < bytes_per_row) { + break; + } + memcpy(dest, buffer, graph->bytes_per_row); + dest -= graph->bytes_per_row; + } + free(buffer); } diff --git a/lib/pandagl/src/image/bmp.h b/lib/pandagl/src/image/bmp.h deleted file mode 100644 index 0889f452f..000000000 --- a/lib/pandagl/src/image/bmp.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * lib/pandagl/src/image/bmp.h - * - * Copyright (c) 2023-2024, Liu Chao All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * This file is part of LCUI, distributed under the MIT License found in the - * LICENSE.TXT file in the root directory of this source tree. - */ - -int pd_bmp_reader_init(pd_image_reader_t* reader); - -int pd_bmp_reader_read_header(pd_image_reader_t* reader); - -int pd_bmp_reader_read_data(pd_image_reader_t* reader, pd_canvas_t *graph); diff --git a/lib/pandagl/src/image/bmp_private.h b/lib/pandagl/src/image/bmp_private.h new file mode 100644 index 000000000..a34ba5a00 --- /dev/null +++ b/lib/pandagl/src/image/bmp_private.h @@ -0,0 +1,18 @@ +/* + * lib/pandagl/src/image/bmp.h + * + * Copyright (c) 2023-2024, Liu Chao All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * This file is part of LCUI, distributed under the MIT License found in the + * LICENSE.TXT file in the root directory of this source tree. + */ + +void pd_bmp_reader_create(pd_image_reader_t *reader); +void pd_bmp_reader_destroy(pd_image_reader_t *reader); +jmp_buf *pd_bmp_reader_jmpbuf(pd_image_reader_t *reader); +pd_error_t pd_bmp_reader_read_header(pd_image_reader_t *reader); +void pd_bmp_reader_start(pd_image_reader_t *reader); +void pd_bmp_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *graph); +void pd_bmp_reader_finish(pd_image_reader_t *reader); diff --git a/lib/pandagl/src/image/jpeg.c b/lib/pandagl/src/image/jpeg.c index 5f641679d..446cd0635 100644 --- a/lib/pandagl/src/image/jpeg.c +++ b/lib/pandagl/src/image/jpeg.c @@ -9,11 +9,13 @@ * LICENSE.TXT file in the root directory of this source tree. */ +#include #include #include #include #include -#include "jpeg.h" +#include +#include "jpeg_private.h" #ifdef PANDAGL_HAS_LIBJPEG @@ -24,53 +26,41 @@ typedef struct pd_jpeg_error_t { struct jpeg_error_mgr pub; - pd_image_reader_t* reader; + pd_image_reader_t *reader; } pd_jpeg_error_t; typedef struct pd_jpeg_reader_t { struct jpeg_source_mgr src; + struct jpeg_decompress_struct cinfo; + jmp_buf env; pd_jpeg_error_t err; pd_bool_t start_of_file; - pd_image_reader_t* base; + pd_image_reader_t *base; unsigned char buffer[BUFFER_SIZE]; } pd_jpeg_reader_t; -static void pd_jpeg_reader_destroy(void *data) -{ - j_decompress_ptr cinfo = data; - jpeg_destroy_decompress(cinfo); - free(data); -} - METHODDEF(void) pd_jpeg_reader_on_error_exit(j_common_ptr cinfo) { - char msg[JMSG_LENGTH_MAX]; - pd_jpeg_error_t * err = (pd_jpeg_error_t *)cinfo->err; - cinfo->err->format_message(cinfo, msg); - logger_error("%s\n", msg); - longjmp(*err->reader->env, 1); + pd_jpeg_error_t *err = (pd_jpeg_error_t *)cinfo->err; + pd_jpeg_reader_t *reader = err->reader->reader_data; + cinfo->err->format_message(cinfo, err->reader->error_message); + longjmp(reader->env, PD_ERROR_IMAGE_READING); } static void pd_jpeg_reader_on_init(j_decompress_ptr cinfo) { - pd_image_reader_t* reader; - pd_jpeg_reader_t * jpeg_reader; + pd_jpeg_reader_t *jpeg_reader; jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; jpeg_reader->start_of_file = PD_TRUE; - reader = jpeg_reader->base; - if (reader->fn_begin) { - reader->fn_begin(reader->stream_data); - } } static boolean pd_jpeg_reader_on_read(j_decompress_ptr cinfo) { size_t size; - pd_image_reader_t* reader; - pd_jpeg_reader_t * jpeg_reader; - jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; - reader = jpeg_reader->base; - size = reader->fn_read(reader->stream_data, jpeg_reader->buffer, + pd_jpeg_reader_t *jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; + pd_image_reader_t *reader = jpeg_reader->base; + + size = pd_file_reader_read(reader->file_reader, jpeg_reader->buffer, BUFFER_SIZE); if (size <= 0) { /* 将空的输入文件视为致命错误 */ @@ -87,28 +77,15 @@ static boolean pd_jpeg_reader_on_read(j_decompress_ptr cinfo) jpeg_reader->src.next_input_byte = jpeg_reader->buffer; jpeg_reader->src.bytes_in_buffer = size; jpeg_reader->start_of_file = PD_FALSE; - return PD_TRUE; + return true; } -static void pd_jpeg_reader_OnSkip(j_decompress_ptr cinfo, long num_bytes) +static void pd_jpeg_reader_on_skip(j_decompress_ptr cinfo, long num_bytes) { - pd_image_reader_t* reader; - pd_jpeg_reader_t * jpeg_reader; struct jpeg_source_mgr *src = cinfo->src; - jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; - reader = jpeg_reader->base; - if (!reader->fn_skip) { - if (num_bytes <= 0) { - return; - } - while (num_bytes > (long)src->bytes_in_buffer) { - num_bytes -= (long)src->bytes_in_buffer; - src->fill_input_buffer(cinfo); - } - src->next_input_byte += (size_t)num_bytes; - src->bytes_in_buffer -= (size_t)num_bytes; - return; - } + pd_jpeg_reader_t * jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; + pd_image_reader_t *reader = jpeg_reader->base; + /* 如果跳过的字节数小于当前缓存的数据大小,则直接移动数据读取指针 */ if (src->bytes_in_buffer > (size_t)num_bytes) { src->next_input_byte += num_bytes; @@ -116,149 +93,117 @@ static void pd_jpeg_reader_OnSkip(j_decompress_ptr cinfo, long num_bytes) return; } num_bytes -= (long)src->bytes_in_buffer; - reader->fn_skip(reader->stream_data, num_bytes); + pd_file_reader_skip(reader->file_reader, num_bytes); /* 重置当前缓存 */ src->bytes_in_buffer = 0; src->next_input_byte = jpeg_reader->buffer; } -static void pd_jpeg_reader_OnTerminate(j_decompress_ptr cinfo) +static void pd_jpeg_reader_on_terminate(j_decompress_ptr cinfo) { - pd_image_reader_t* reader; - pd_jpeg_reader_t * jpeg_reader; - jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; - reader = jpeg_reader->base; - if (reader->fn_end) { - reader->fn_end(reader->stream_data); - } } -static void *jpeg_malloc(j_decompress_ptr cinfo, size_t size) + +void pd_jpeg_reader_create(pd_image_reader_t *reader) +{ + pd_jpeg_reader_t *jpeg_reader; + + jpeg_reader = malloc(sizeof(pd_jpeg_reader_t)); + jpeg_reader->src.init_source = pd_jpeg_reader_on_init; + jpeg_reader->src.term_source = pd_jpeg_reader_on_terminate; + jpeg_reader->src.skip_input_data = pd_jpeg_reader_on_skip; + jpeg_reader->src.fill_input_buffer = pd_jpeg_reader_on_read; + jpeg_reader->src.resync_to_restart = jpeg_resync_to_restart; + jpeg_reader->src.bytes_in_buffer = 0; + jpeg_reader->src.next_input_byte = NULL; + jpeg_reader->err.pub.error_exit = pd_jpeg_reader_on_error_exit; + jpeg_reader->err.reader = reader; + jpeg_reader->base = reader; + jpeg_create_decompress(&jpeg_reader->cinfo); + jpeg_reader->cinfo.src = &jpeg_reader->src; + jpeg_reader->cinfo.err = jpeg_std_error(&jpeg_reader->err.pub); + reader->reader_data = jpeg_reader; +} + +void pd_jpeg_reader_start(pd_image_reader_t *reader) +{ + pd_jpeg_reader_t *jpeg_reader = reader->reader_data; + jpeg_start_decompress(&jpeg_reader->cinfo); +} + +void pd_jpeg_reader_finish(pd_image_reader_t *reader) +{ + pd_jpeg_reader_t *jpeg_reader = reader->reader_data; + + jpeg_finish_decompress(&jpeg_reader->cinfo); +} + +void pd_jpeg_reader_destroy(pd_image_reader_t *reader) { - return cinfo->mem->alloc_small((j_common_ptr)cinfo, JPOOL_PERMANENT, - size); + pd_jpeg_reader_t *jpeg_reader = reader->reader_data; + jpeg_destroy_decompress(&jpeg_reader->cinfo); + free(jpeg_reader); } -int pd_jpeg_reader_read_header(pd_image_reader_t* reader) +jmp_buf *pd_jpeg_reader_jmpbuf(pd_image_reader_t *reader) +{ + pd_jpeg_reader_t *jpeg_reader = reader->reader_data; + return &jpeg_reader->env; +} + +pd_error_t pd_jpeg_reader_read_header(pd_image_reader_t *reader) { size_t size; short int *buffer; - j_decompress_ptr cinfo; - pd_jpeg_reader_t * jpeg_reader; - pd_image_header_t* header = &reader->header; - if (reader->type != PD_JPEG_READER) { - return -EINVAL; - } - cinfo = reader->data; - jpeg_reader = (pd_jpeg_reader_t *)cinfo->src; - size = reader->fn_read(reader->stream_data, jpeg_reader->buffer, + pd_image_header_t *header = &reader->header; + pd_jpeg_reader_t *jpeg_reader = reader->reader_data; + j_decompress_ptr cinfo = &jpeg_reader->cinfo; + + size = pd_file_reader_read(reader->file_reader, jpeg_reader->buffer, sizeof(short int)); if (size < sizeof(short int)) { - return -2; + return PD_ERROR_IMAGE_HEADER_INVALID; } jpeg_reader->src.bytes_in_buffer = sizeof(short int); jpeg_reader->src.next_input_byte = jpeg_reader->buffer; buffer = (short int *)jpeg_reader->buffer; if (buffer[0] != -9985) { - return -2; + return PD_ERROR_IMAGE_HEADER_INVALID; + } + if (setjmp(jpeg_reader->env)) { + return PD_ERROR_IMAGE_HEADER_INVALID; } jpeg_read_header(cinfo, PD_TRUE); header->width = cinfo->image_width; header->height = cinfo->image_height; - header->type = PD_JPEG_READER; + header->type = PD_JPEG_IMAGE; + header->color_type = PD_COLOR_TYPE_RGB; if (cinfo->jpeg_color_space == JCS_RGB) { - header->color_type = PD_COLOR_TYPE_RGB; header->bit_depth = 24; - } else { - header->color_type = 0; } - return 0; + return PD_OK; } -#endif - -int pd_jpeg_reader_init(pd_image_reader_t* reader) +void pd_jpeg_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *out) { -#ifdef PANDAGL_HAS_LIBJPEG - j_decompress_ptr cinfo; - pd_jpeg_reader_t * jpeg_reader; - cinfo = malloc(sizeof(struct jpeg_decompress_struct)); - jpeg_create_decompress(cinfo); - jpeg_reader = jpeg_malloc(cinfo, sizeof(pd_jpeg_reader_t)); - jpeg_reader->src.init_source = pd_jpeg_reader_on_init; - jpeg_reader->src.term_source = pd_jpeg_reader_OnTerminate; - jpeg_reader->src.skip_input_data = pd_jpeg_reader_OnSkip; - jpeg_reader->src.fill_input_buffer = pd_jpeg_reader_on_read; - jpeg_reader->src.resync_to_restart = jpeg_resync_to_restart; - jpeg_reader->src.bytes_in_buffer = 0; - jpeg_reader->src.next_input_byte = NULL; - jpeg_reader->base = reader; - reader->data = cinfo; - reader->type = PD_JPEG_READER; - reader->header.type = PD_UNKNOWN_IMAGE; - reader->destructor = pd_jpeg_reader_destroy; - reader->env = &reader->env_src; - cinfo->src = (struct jpeg_source_mgr *)jpeg_reader; - cinfo->err = jpeg_std_error(&jpeg_reader->err.pub); - jpeg_reader->err.pub.error_exit = pd_jpeg_reader_on_error_exit; - jpeg_reader->err.reader = reader; - return 0; -#else - logger_warning("warning: not JPEG support!"); -#endif - return -1; -} - -int pd_jpeg_reader_read_data(pd_image_reader_t* reader, pd_canvas_t *graph) -{ -#ifdef PANDAGL_HAS_LIBJPEG uint8_t *bytep; JSAMPARRAY buffer; - j_decompress_ptr cinfo; - int k, row_stride; + pd_jpeg_reader_t *jpeg_reader = reader->reader_data; + j_decompress_ptr cinfo = &jpeg_reader->cinfo; + int row_stride = cinfo->output_width * cinfo->output_components; + int k; unsigned x; - if (reader->type != PD_JPEG_READER) { - return -EINVAL; - } - if (reader->header.type == PD_UNKNOWN_IMAGE) { - if (pd_jpeg_reader_read_header(reader) != 0) { - return -2; - } - } - cinfo = reader->data; - jpeg_start_decompress(cinfo); - /* 暂时不处理其它色彩类型的图像 */ - if (cinfo->num_components != 3) { - return -ENOSYS; - } - graph->color_type = PD_COLOR_TYPE_RGB; - if (0 != - pd_canvas_create(graph, cinfo->output_width, cinfo->output_height)) { - return -ENOMEM; - } - row_stride = cinfo->output_width * cinfo->output_components; buffer = cinfo->mem->alloc_sarray((j_common_ptr)cinfo, JPOOL_IMAGE, row_stride, 1); - while (cinfo->output_scanline < cinfo->output_height) { - bytep = graph->bytes; - bytep += cinfo->output_scanline * graph->bytes_per_row; - jpeg_read_scanlines(cinfo, buffer, 1); - for (x = 0; x < graph->width; ++x) { - k = x * 3; - *bytep++ = buffer[0][k + 2]; - *bytep++ = buffer[0][k + 1]; - *bytep++ = buffer[0][k]; - } - if (reader->fn_prog) { - reader->fn_prog(reader->prog_arg, - 100.0f * cinfo->output_scanline / - cinfo->output_height); - } + bytep = out->bytes + cinfo->output_scanline * out->bytes_per_row; + jpeg_read_scanlines(cinfo, buffer, 1); + for (x = 0; x < out->width; ++x) { + k = x * 3; + *bytep++ = buffer[0][k + 2]; + *bytep++ = buffer[0][k + 1]; + *bytep++ = buffer[0][k]; } - return 0; -#else - logger_warning("warning: not JPEG support!"); -#endif - return -ENOSYS; } + +#endif diff --git a/lib/pandagl/src/image/jpeg.h b/lib/pandagl/src/image/jpeg.h deleted file mode 100644 index 2040a4ba2..000000000 --- a/lib/pandagl/src/image/jpeg.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * lib/pandagl/src/image/jpeg.h - * - * Copyright (c) 2023-2024, Liu Chao All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * This file is part of LCUI, distributed under the MIT License found in the - * LICENSE.TXT file in the root directory of this source tree. - */ - -int pd_jpeg_reader_read_header(pd_image_reader_t* reader); - -int pd_jpeg_reader_init(pd_image_reader_t* reader); - -int pd_jpeg_reader_read_data(pd_image_reader_t* reader, pd_canvas_t *graph); diff --git a/lib/pandagl/src/image/jpeg_private.h b/lib/pandagl/src/image/jpeg_private.h new file mode 100644 index 000000000..b6ed600d4 --- /dev/null +++ b/lib/pandagl/src/image/jpeg_private.h @@ -0,0 +1,18 @@ +/* + * lib/pandagl/src/image/jpeg.h + * + * Copyright (c) 2023-2024, Liu Chao All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * This file is part of LCUI, distributed under the MIT License found in the + * LICENSE.TXT file in the root directory of this source tree. + */ + +void pd_jpeg_reader_create(pd_image_reader_t *reader); +void pd_jpeg_reader_destroy(pd_image_reader_t *reader); +jmp_buf *pd_jpeg_reader_jmpbuf(pd_image_reader_t *reader); +pd_error_t pd_jpeg_reader_read_header(pd_image_reader_t *reader); +void pd_jpeg_reader_start(pd_image_reader_t *reader); +void pd_jpeg_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *graph); +void pd_jpeg_reader_finish(pd_image_reader_t *reader); diff --git a/lib/pandagl/src/image/png-private.h b/lib/pandagl/src/image/png-private.h deleted file mode 100644 index 39e7ec77d..000000000 --- a/lib/pandagl/src/image/png-private.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * lib/pandagl/src/image/png-private.h - * - * Copyright (c) 2023-2024, Liu Chao All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * This file is part of LCUI, distributed under the MIT License found in the - * LICENSE.TXT file in the root directory of this source tree. - */ - -int pd_png_reader_init(pd_image_reader_t* reader); - -int pd_png_reader_read_header(pd_image_reader_t* reader); - -int pd_png_reader_read_data(pd_image_reader_t* reader, pd_canvas_t *graph); - -int pd_write_png_file(const char *file_name, const pd_canvas_t *graph); diff --git a/lib/pandagl/src/image/png.c b/lib/pandagl/src/image/png.c index 9c32c052d..6df2fa24d 100644 --- a/lib/pandagl/src/image/png.c +++ b/lib/pandagl/src/image/png.c @@ -13,294 +13,240 @@ #include #include #include -#include "png-private.h" - -#ifdef ASSERT -#undef ASSERT -#endif -#define ASSERT(X) \ - if (!(X)) \ - goto error; +#include "png_private.h" #ifdef PANDAGL_HAS_LIBPNG #include -#include #define PNG_BYTES_TO_CHECK 4 typedef struct pd_png_reader_t { - png_structp png_ptr; - png_infop info_ptr; + pd_error_t error; + pd_file_reader_t *file_reader; + png_structp png_ptr; + png_infop info_ptr; + jmp_buf *env; + jmp_buf env_src; } pd_png_reader_t; -static void DestroyPNGReader(void *data) +static void pd_png_reader_on_read(png_structp png_ptr, png_bytep buffer, + png_size_t size) { - pd_png_reader_t *reader = data; - if (reader->png_ptr) { - png_destroy_read_struct(&reader->png_ptr, &reader->info_ptr, - NULL); - } - free(reader); + size_t read_size; + pd_image_reader_t *reader = png_get_io_ptr(png_ptr); + pd_png_reader_t *png_reader = reader->reader_data; + + if (!reader || !png_reader || png_reader->error != PD_OK) { + return; + } + read_size = pd_file_reader_read(reader->file_reader, buffer, size); + if (read_size != size) { + png_reader->error = PD_ERROR_IMAGE_HEADER_INVALID; + } } -static void PNGReader_OnRead(png_structp png_ptr, png_bytep buffer, - png_size_t size) +void pd_png_reader_create(pd_image_reader_t *reader) { - size_t read_size; - pd_image_reader_t *reader = png_get_io_ptr(png_ptr); - if (!reader || reader->has_error) { - return; - } - read_size = reader->fn_read(reader->stream_data, buffer, size); - if (read_size != size) { - reader->has_error = PD_TRUE; - if (reader->fn_end) { - reader->fn_end(reader->stream_data); - } - } + png_structp png_ptr; + png_infop info_ptr; + pd_png_reader_t *png_reader = reader->reader_data; + + png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + return; + } + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, + (png_infopp)NULL); + return; + } + png_set_read_fn(png_ptr, reader, pd_png_reader_on_read); + png_reader = calloc(1, sizeof(pd_png_reader_t)); + png_reader->png_ptr = png_ptr; + png_reader->info_ptr = info_ptr; + reader->reader_data = png_reader; } -#endif -int pd_png_reader_init(pd_image_reader_t *reader) +void pd_png_reader_destroy(pd_image_reader_t *reader) { -#ifdef PANDAGL_HAS_LIBPNG - pd_png_reader_t *png_reader; - - png_reader = malloc(sizeof(pd_png_reader_t)); - png_reader->png_ptr = - png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - ASSERT(png_reader->png_ptr); - png_reader->info_ptr = png_create_info_struct(png_reader->png_ptr); - ASSERT(png_reader->info_ptr); - png_set_read_fn(png_reader->png_ptr, reader, PNGReader_OnRead); - reader->destructor = DestroyPNGReader; - reader->has_error = PD_FALSE; - reader->data = png_reader; - reader->type = PD_PNG_READER; - reader->header.type = PD_UNKNOWN_IMAGE; - reader->env = &png_jmpbuf(png_reader->png_ptr); - return 0; - -error: - pd_image_reader_destroy(reader); -#else - logger_warning("warning: not PNG support!"); -#endif - return -1; + pd_png_reader_t *png_reader = reader->reader_data; + if (png_reader->png_ptr) { + png_destroy_read_struct(&png_reader->png_ptr, + &png_reader->info_ptr, NULL); + } + png_reader->png_ptr = NULL; + free(png_reader); } -int pd_png_reader_read_header(pd_image_reader_t *reader) +void pd_png_reader_start(pd_image_reader_t *reader) { -#ifdef PANDAGL_HAS_LIBPNG - size_t n; - png_infop info_ptr; - png_structp png_ptr; - png_byte buf[PNG_BYTES_TO_CHECK]; - pd_png_reader_t *png_reader; - if (reader->type != PD_PNG_READER) { - return -EINVAL; - } - png_reader = reader->data; - png_ptr = png_reader->png_ptr; - info_ptr = png_reader->info_ptr; - n = reader->fn_read(reader->stream_data, buf, PNG_BYTES_TO_CHECK); - if (n < PNG_BYTES_TO_CHECK) { - return -2; - } - /* 检测数据是否为PNG的签名 */ - if (!png_check_sig(buf, PNG_BYTES_TO_CHECK)) { - return -2; - } - png_set_sig_bytes(png_reader->png_ptr, PNG_BYTES_TO_CHECK); - /* 读取PNG图片信息 */ - png_read_info(png_ptr, info_ptr); - reader->header.width = png_get_image_width(png_ptr, info_ptr); - reader->header.height = png_get_image_height(png_ptr, info_ptr); - reader->header.bit_depth = png_get_bit_depth(png_ptr, info_ptr); - reader->header.color_type = png_get_color_type(png_ptr, info_ptr); - reader->header.type = PD_PNG_IMAGE; - switch (reader->header.color_type) { - case PNG_COLOR_TYPE_RGB_ALPHA: - reader->header.color_type = PD_COLOR_TYPE_ARGB; - break; - case PNG_COLOR_TYPE_RGB: - reader->header.color_type = PD_COLOR_TYPE_RGB; - break; - default: - reader->header.color_type = 0; - break; - } - return 0; -#else - logger_warning("warning: not PNG support!"); - return -ENOSYS; -#endif + pd_png_reader_t *png_reader = reader->reader_data; + reader->passes = png_set_interlace_handling(png_reader->png_ptr); + png_set_bgr(png_reader->png_ptr); + png_set_expand(png_reader->png_ptr); + png_read_update_info(png_reader->png_ptr, png_reader->info_ptr); } -int pd_png_reader_read_data(pd_image_reader_t *reader, pd_canvas_t *graph) +void pd_png_reader_finish(pd_image_reader_t *reader) { -#ifdef PANDAGL_HAS_LIBPNG - png_uint_32 i; - png_bytep row; - png_infop info_ptr; - png_structp png_ptr; - pd_image_header_t *header; - pd_png_reader_t *png_reader; - int pass, number_passes, ret = 0; - float progress; + pd_png_reader_t *png_reader = reader->reader_data; + png_read_end(png_reader->png_ptr, png_reader->info_ptr); +} - if (reader->type != PD_PNG_READER) { - return -EINVAL; - } - header = &reader->header; - png_reader = reader->data; - png_ptr = png_reader->png_ptr; - info_ptr = png_reader->info_ptr; - if (header->type == PD_UNKNOWN_IMAGE) { - if (pd_png_reader_read_header(reader) != 0) { - return -2; - } - } - /* 根据不同的色彩类型进行相应处理 */ - switch (header->color_type) { - case PD_COLOR_TYPE_ARGB: - graph->color_type = PD_COLOR_TYPE_ARGB; - ret = pd_canvas_create(graph, header->width, header->height); - if (ret != 0) { - ret = -ENOMEM; - break; - } - break; - case PD_COLOR_TYPE_RGB: - graph->color_type = PD_COLOR_TYPE_RGB; - ret = pd_canvas_create(graph, header->width, header->height); - if (ret != 0) { - ret = -ENOMEM; - break; - } - break; - default: - /* 其它色彩类型的图像就不处理了 */ - return -2; - } - png_set_bgr(png_ptr); - png_set_expand(png_ptr); - number_passes = png_set_interlace_handling(png_ptr); - png_read_update_info(png_ptr, info_ptr); - for (pass = 0; pass < number_passes; ++pass) { - for (i = 0; i < graph->height; ++i) { - row = graph->bytes + i * graph->bytes_per_row; - png_read_row(png_ptr, row, NULL); - if (reader->fn_prog) { - progress = 100.0f * i / graph->height; - reader->fn_prog(reader->prog_arg, progress); - } - } - } - return ret; +jmp_buf *pd_png_reader_jmpbuf(pd_image_reader_t *reader) +{ + pd_png_reader_t *png_reader = reader->reader_data; +#ifdef PNG_SETJMP_SUPPORTED + return png_set_longjmp_fn(png_reader->png_ptr, longjmp, + sizeof(jmp_buf)); #else - logger_warning("warning: not PNG support!"); - return -ENOSYS; + return NULL; #endif } -int pd_write_png_file(const char *file_name, const pd_canvas_t *graph) +pd_error_t pd_png_reader_read_header(pd_image_reader_t *reader) { -#ifdef PANDAGL_HAS_LIBPNG - - png_byte color_type; - png_structp png_ptr; - png_infop info_ptr; - png_bytep *row_pointers; - - FILE *fp; - unsigned char *p; - int y, x; - size_t row_size; - pd_rect_t rect; - - if (!pd_canvas_is_valid(graph)) { - logger_error("canvas is not valid\n"); - return -1; - } - /* create file */ - fp = fopen(file_name, "wb"); - if (!fp) { - logger_error("file %s could not be opened for writing\n", - file_name); - return -1; - } - /* initialize stuff */ - png_ptr = - png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - fclose(fp); - logger_error("png_create_write_struct failed\n"); - return -1; - } - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - fclose(fp); - logger_error("png_create_info_struct failed\n"); - png_destroy_write_struct(&png_ptr, &info_ptr); - return -1; - } - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(fp); - logger_error("error during init_io\n"); - png_destroy_write_struct(&png_ptr, &info_ptr); - return -1; - } - png_init_io(png_ptr, fp); - if (graph->color_type == PD_COLOR_TYPE_ARGB8888) { - color_type = PNG_COLOR_TYPE_RGB_ALPHA; - } else { - color_type = PNG_COLOR_TYPE_RGB; - } - /* write header */ - png_set_IHDR(png_ptr, info_ptr, graph->width, graph->height, 8, - color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); + size_t n; + pd_png_reader_t *png_reader = reader->reader_data; + png_infop info_ptr = png_reader->info_ptr; + png_structp png_ptr = png_reader->png_ptr; + png_byte buf[PNG_BYTES_TO_CHECK]; + + n = pd_file_reader_read(reader->file_reader, buf, PNG_BYTES_TO_CHECK); + if (n < PNG_BYTES_TO_CHECK) { + return PD_ERROR_IMAGE_HEADER_INVALID; + } + /* 检测数据是否为PNG的签名 */ + if (!png_check_sig(buf, PNG_BYTES_TO_CHECK)) { + return PD_ERROR_IMAGE_TYPE_INCORRECT; + } + if (setjmp(png_jmpbuf(png_ptr))) { + return PD_ERROR_IMAGE_READING; + } + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + /* 读取PNG图片信息 */ + png_read_info(png_ptr, info_ptr); + reader->header.width = png_get_image_width(png_ptr, info_ptr); + reader->header.height = png_get_image_height(png_ptr, info_ptr); + reader->header.bit_depth = png_get_bit_depth(png_ptr, info_ptr); + reader->header.color_type = png_get_color_type(png_ptr, info_ptr); + reader->header.type = PD_PNG_IMAGE; + switch (reader->header.color_type) { + case PNG_COLOR_TYPE_RGB_ALPHA: + reader->header.color_type = PD_COLOR_TYPE_ARGB; + break; + case PNG_COLOR_TYPE_RGB: + reader->header.color_type = PD_COLOR_TYPE_RGB; + break; + default: + return PD_ERROR_IMAGE_DATA_NOT_SUPPORTED; + } + return PD_OK; +} - png_write_info(png_ptr, info_ptr); - /* write bytes */ +void pd_png_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *data) +{ + pd_png_reader_t *png_reader = reader->reader_data; + png_read_row(png_reader->png_ptr, + data->bytes + reader->read_row_index * data->bytes_per_row, + NULL); +} - pd_canvas_get_quote_rect(graph, &rect); - graph = pd_canvas_get_quote_source_readonly(graph); - row_size = png_get_rowbytes(png_ptr, info_ptr); - row_pointers = (png_bytep *)malloc(rect.height * sizeof(png_bytep)); +int pd_write_png_file(const char *file_name, const pd_canvas_t *data) +{ + png_byte color_type; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers; + + FILE *fp; + unsigned char *p; + int y, x; + size_t row_size; + pd_rect_t rect; + + if (!pd_canvas_is_valid(data)) { + logger_error("canvas is not valid\n"); + return -1; + } + /* create file */ + fp = fopen(file_name, "wb"); + if (!fp) { + logger_error("file %s could not be opened for writing\n", + file_name); + return -1; + } + /* initialize stuff */ + png_ptr = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(fp); + logger_error("png_create_write_struct failed\n"); + return -1; + } + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(fp); + logger_error("png_create_info_struct failed\n"); + png_destroy_write_struct(&png_ptr, &info_ptr); + return -1; + } + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(fp); + logger_error("error during init_io\n"); + png_destroy_write_struct(&png_ptr, &info_ptr); + return -1; + } + png_init_io(png_ptr, fp); + if (data->color_type == PD_COLOR_TYPE_ARGB8888) { + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } else { + color_type = PNG_COLOR_TYPE_RGB; + } + /* write header */ + png_set_IHDR(png_ptr, info_ptr, data->width, data->height, 8, + color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + /* write bytes */ + + pd_canvas_get_quote_rect(data, &rect); + data = pd_canvas_get_quote_source_readonly(data); + row_size = png_get_rowbytes(png_ptr, info_ptr); + row_pointers = (png_bytep *)malloc(rect.height * sizeof(png_bytep)); + + for (y = 0; y < rect.height; ++y) { + p = pd_canvas_pixel_at(data, rect.x, rect.y + y); + row_pointers[y] = png_malloc(png_ptr, row_size); + if (data->color_type == PD_COLOR_TYPE_ARGB) { + for (x = 0; x < row_size; p += 4) { + row_pointers[y][x++] = p[2]; // r + row_pointers[y][x++] = p[1]; // g + row_pointers[y][x++] = p[0]; // b + row_pointers[y][x++] = p[3]; // a + } + } else { + for (x = 0; x < row_size; p += 3) { + row_pointers[y][x++] = p[2]; // r + row_pointers[y][x++] = p[1]; // g + row_pointers[y][x++] = p[0]; // b + } + } + } + png_write_image(png_ptr, row_pointers); + /* cleanup heap allocation */ + for (y = 0; y < rect.height; ++y) { + free(row_pointers[y]); + } + free(row_pointers); + /* end write */ + png_write_end(png_ptr, NULL); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return PD_OK; +} - for (y = 0; y < rect.height; ++y) { - p = pd_canvas_pixel_at(graph, rect.x, rect.y + y); - row_pointers[y] = png_malloc(png_ptr, row_size); - if (graph->color_type == PD_COLOR_TYPE_ARGB) { - for (x = 0; x < row_size; p += 4) { - row_pointers[y][x++] = p[2]; // r - row_pointers[y][x++] = p[1]; // g - row_pointers[y][x++] = p[0]; // b - row_pointers[y][x++] = p[3]; // a - } - } else { - for (x = 0; x < row_size; p += 3) { - row_pointers[y][x++] = p[2]; // r - row_pointers[y][x++] = p[1]; // g - row_pointers[y][x++] = p[0]; // b - } - } - } - png_write_image(png_ptr, row_pointers); - /* cleanup heap allocation */ - for (y = 0; y < rect.height; ++y) { - free(row_pointers[y]); - } - free(row_pointers); - /* end write */ - png_write_end(png_ptr, NULL); - png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); - return 0; -#else - logger_warning("warning: not PNG support!"); - return -ENOSYS; #endif -} diff --git a/lib/pandagl/src/image/png_private.h b/lib/pandagl/src/image/png_private.h new file mode 100644 index 000000000..216fcf50b --- /dev/null +++ b/lib/pandagl/src/image/png_private.h @@ -0,0 +1,7 @@ +void pd_png_reader_create(pd_image_reader_t *reader); +void pd_png_reader_destroy(pd_image_reader_t *reader); +jmp_buf *pd_png_reader_jmpbuf(pd_image_reader_t *reader); +pd_error_t pd_png_reader_read_header(pd_image_reader_t *reader); +void pd_png_reader_start(pd_image_reader_t *reader); +void pd_png_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *graph); +void pd_png_reader_finish(pd_image_reader_t *reader); diff --git a/lib/pandagl/src/image/reader.c b/lib/pandagl/src/image/reader.c index f9bb37b90..86106a6cb 100644 --- a/lib/pandagl/src/image/reader.c +++ b/lib/pandagl/src/image/reader.c @@ -14,193 +14,237 @@ #include #include #include -#include "bmp.h" -#include "png.h" -#include "jpeg.h" - -typedef struct pd_image_interface_t { - const char *suffix; - int (*init)(pd_image_reader_t *); - int (*read_header)(pd_image_reader_t *); - int (*read)(pd_image_reader_t *, pd_canvas_t *); -} pd_image_interface_t; - -static const pd_image_interface_t pd_image_interfaces[] = { +#include "bmp_private.h" +#include "png_private.h" +#include "jpeg_private.h" + +typedef struct { + const char *suffix; + pd_image_reader_type_t type; + void (*create)(pd_image_reader_t *); + void (*destroy)(pd_image_reader_t *); + jmp_buf *(*jmpbuf)(pd_image_reader_t *); + pd_error_t (*reade_header)(pd_image_reader_t *); + void (*start)(pd_image_reader_t *); + void (*read_row)(pd_image_reader_t *, pd_canvas_t *); + void (*finish)(pd_image_reader_t *); +} pd_image_reader_methods_t; + +static pd_image_reader_methods_t pd_image_readers[] = { #ifdef PANDAGL_HAS_LIBPNG - { ".png", pd_png_reader_init, pd_png_reader_read_header, - pd_png_reader_read_data }, + { ".png", PD_PNG_READER, pd_png_reader_create, pd_png_reader_destroy, + pd_png_reader_jmpbuf, pd_png_reader_read_header, pd_png_reader_start, + pd_png_reader_read_row, pd_png_reader_finish }, #endif #ifdef PANDAGL_HAS_LIBJPEG - { ".jpeg .jpg", pd_jpeg_reader_init, pd_jpeg_reader_read_header, - pd_jpeg_reader_read_data }, + { ".jpeg .jpg", PD_JPEG_READER, pd_jpeg_reader_create, + pd_jpeg_reader_destroy, pd_jpeg_reader_jmpbuf, + pd_jpeg_reader_read_header, pd_jpeg_reader_start, + pd_jpeg_reader_read_row, pd_jpeg_reader_finish }, #endif - { ".bmp", pd_bmp_reader_init, pd_bmp_reader_read_header, - pd_bmp_reader_read_data } + { ".bmp", PD_BMP_READER, pd_bmp_reader_create, pd_bmp_reader_destroy, + pd_bmp_reader_jmpbuf, pd_bmp_reader_read_header, pd_bmp_reader_start, + pd_bmp_reader_read_row, pd_bmp_reader_finish }, }; -#define INTERFACES_COUNT \ - (sizeof(pd_image_interfaces) / sizeof(pd_image_interface_t)) +static pd_image_reader_type_t pd_image_reader_detect_suffix( + const char *filename) +{ + int i; + const char *suffix = NULL; + + for (i = 0; filename[i]; ++i) { + if (filename[i] == '.') { + suffix = filename + i; + } + } + if (!suffix) { + return PD_UNKNOWN_READER; + } + for (i = 0; i < sizeof(pd_image_readers) / sizeof(pd_image_readers[0]); + ++i) { + if (strstr(pd_image_readers[i].suffix, suffix)) { + return pd_image_readers[i].type; + } + } + return PD_UNKNOWN_READER; +} -static size_t pd_file_stream_on_read(void *data, void *buffer, size_t size) +static pd_image_reader_methods_t *pd_image_reader_get_methods( + pd_image_reader_t *reader) { - return fread(buffer, 1, size, data); + int i; + for (i = 0; i < sizeof(pd_image_readers) / sizeof(pd_image_readers[0]); + ++i) { + if (pd_image_readers[i].type == reader->type) { + return pd_image_readers + i; + } + } + return NULL; } -static void pd_file_stream_on_skip(void *data, long offset) +pd_image_reader_t *pd_image_reader_create(void) { - fseek(data, offset, SEEK_CUR); + pd_image_reader_t *reader = calloc(1, sizeof(pd_image_reader_t)); + reader->passes = 1; + return reader; } -static void pd_file_stream_on_rewind(void *data) +pd_image_reader_t *pd_image_reader_create_from_file(const char *filename) { - rewind(data); + pd_image_reader_t *reader; + pd_file_reader_t *file_reader; + + file_reader = pd_file_reader_create_from_file(filename); + if (!file_reader) { + return NULL; + } + reader = pd_image_reader_create(); + reader->type = pd_image_reader_detect_suffix(filename); + reader->file_reader = file_reader; + return reader; } -void pd_image_reader_set_file(pd_image_reader_t *reader, FILE *fp) +void pd_image_reader_destroy(pd_image_reader_t *reader) { - reader->stream_data = fp; - reader->fn_skip = pd_file_stream_on_skip; - reader->fn_read = pd_file_stream_on_read; - reader->fn_rewind = pd_file_stream_on_rewind; + if (reader->reader_data) { + pd_image_reader_get_methods(reader)->destroy(reader); + } + pd_file_reader_destroy(reader->file_reader); + reader->type = PD_UNKNOWN_READER; + reader->header.type = PD_UNKNOWN_IMAGE; + reader->reader_data = NULL; + reader->file_reader = NULL; + free(reader); } -static int pd_detech_image_type(const char *filename) +static pd_error_t pd_image_reader_try_read_header(pd_image_reader_t *reader) { - int i; - const char *suffix = NULL; - - for (i = 0; filename[i]; ++i) { - if (filename[i] == '.') { - suffix = filename + i; - } - } - if (!suffix) { - return -1; - } - for (i = 0; i < INTERFACES_COUNT; ++i) { - if (strstr(pd_image_interfaces[i].suffix, suffix)) { - return i; - } - } - return -1; + pd_error_t err; + pd_image_reader_methods_t *methods = + pd_image_reader_get_methods(reader); + + if (methods) { + methods->create(reader); + err = methods->reade_header(reader); + if (err != PD_OK) { + methods->destroy(reader); + reader->reader_data = NULL; + } + return err; + } + return PD_ERROR_IMAGE_HEADER_INVALID; } -static int pd_image_reader_init_for_type(pd_image_reader_t *reader, int type) +pd_error_t pd_image_reader_read_header(pd_image_reader_t *reader) { - int ret; - reader->fn_rewind(reader->stream_data); - ret = pd_image_interfaces[type].init(reader); - if (ret != 0) { - return -2; - } - if (pd_image_reader_set_jump(reader)) { - return -2; - } - return pd_image_interfaces[type].read_header(reader); + pd_error_t err; + pd_image_reader_type_t type = reader->type; + pd_image_reader_methods_t *methods; + + if (!reader->reader_data) { + if (reader->type != PD_UNKNOWN_READER && + pd_image_reader_try_read_header(reader) == PD_OK) { + return PD_OK; + } + type = reader->type; + for (reader->type = PD_PNG_READER; + reader->type < PD_READER_COUNT; ++reader->type) { + if (reader->type == type) { + continue; + } + pd_file_reader_rewind(reader->file_reader); + err = pd_image_reader_try_read_header(reader); + if (err == PD_OK) { + break; + } + } + } + methods = pd_image_reader_get_methods(reader); + if (methods) { + return methods->reade_header(reader); + } + reader->type = PD_UNKNOWN_READER; + return PD_ERROR_IMAGE_TYPE_INCORRECT; } -int pd_image_reader_init(pd_image_reader_t *reader) +pd_error_t pd_image_reader_create_buffer(pd_image_reader_t *reader, + pd_canvas_t *out) { - int ret, i; - for (i = 0; i < INTERFACES_COUNT; ++i) { - reader->fn_rewind(reader->stream_data); - ret = pd_image_reader_init_for_type(reader, i); - if (ret == 0) { - return 0; - } - pd_image_reader_destroy(reader); - } - reader->fn_rewind(reader->stream_data); - reader->type = PD_UNKNOWN_READER; - return -ENOENT; + switch (reader->header.color_type) { + case PD_COLOR_TYPE_ARGB: + out->color_type = PD_COLOR_TYPE_ARGB; + break; + case PD_COLOR_TYPE_RGB: + out->color_type = PD_COLOR_TYPE_RGB; + break; + default: + return PD_ERROR_IMAGE_DATA_NOT_SUPPORTED; + } + return pd_canvas_create(out, reader->header.width, + reader->header.height); } -void pd_image_reader_destroy(pd_image_reader_t *reader) +jmp_buf *pd_image_reader_jmpbuf(pd_image_reader_t *reader) +{ + return pd_image_reader_get_methods(reader)->jmpbuf(reader); +} + +void pd_image_reader_start(pd_image_reader_t *reader) { - if (reader->data) { - reader->destructor(reader->data); - } - reader->data = NULL; - reader->type = PD_UNKNOWN_READER; - reader->header.type = PD_UNKNOWN_IMAGE; + pd_image_reader_get_methods(reader)->start(reader); } -int pd_image_reader_read_header(pd_image_reader_t *reader) +void pd_image_reader_finish(pd_image_reader_t *reader) { - int i = reader->type - 1; - if (reader->header.type != PD_UNKNOWN_IMAGE) { - return 0; - } - if (i < INTERFACES_COUNT && i >= 0) { - return pd_image_interfaces[i].read_header(reader); - } - return -2; + pd_image_reader_get_methods(reader)->finish(reader); } -int pd_image_reader_read_data(pd_image_reader_t *reader, pd_canvas_t *out) +void pd_image_reader_read_row(pd_image_reader_t *reader, pd_canvas_t *data) { - int i = reader->type - 1; - if (i < INTERFACES_COUNT && i >= 0) { - return pd_image_interfaces[i].read(reader, out); - } - return -2; + pd_image_reader_get_methods(reader)->read_row(reader, data); + reader->read_row_index++; } -int pd_read_image_from_file(const char *filepath, pd_canvas_t *out) +pd_error_t pd_image_reader_read_data(pd_image_reader_t *reader, + pd_canvas_t *out) { - int ret; - FILE *fp; - pd_image_reader_t reader = { 0 }; - - fp = fopen(filepath, "rb"); - if (!fp) { - return -ENOENT; - } - ret = pd_detech_image_type(filepath); - pd_image_reader_set_file(&reader, fp); - if (ret >= 0) { - ret = pd_image_reader_init_for_type(&reader, ret); - } - if (ret < 0) { - if (pd_image_reader_init(&reader) != 0) { - fclose(fp); - return -2; - } - } - if (pd_image_reader_set_jump(&reader)) { - ret = -2; - } else { - ret = pd_image_reader_read_data(&reader, out); - } - pd_image_reader_destroy(&reader); - fclose(fp); - return ret; + pd_image_reader_methods_t *methods = + pd_image_reader_get_methods(reader); + + if (!methods) { + return PD_ERROR_IMAGE_TYPE_INCORRECT; + } + if (setjmp(*pd_image_reader_jmpbuf(reader))) { + return PD_ERROR_IMAGE_READING; + } + if (pd_image_reader_create_buffer(reader, out) != PD_OK) { + return PD_ERROR_IMAGE_DATA_NOT_SUPPORTED; + } + pd_image_reader_start(reader); + for (reader->pass = 0; reader->pass < reader->passes; ++reader->pass) { + reader->read_row_index = 0; + while (reader->read_row_index < reader->header.height) { + pd_image_reader_read_row(reader, out); + } + } + pd_image_reader_finish(reader); + return PD_OK; } -int pd_read_image_size_from_file(const char *filepath, int *width, int *height) +pd_error_t pd_read_image_from_file(const char *filepath, pd_canvas_t *out) { - int ret; - FILE *fp; - pd_image_reader_t reader = { 0 }; - - fp = fopen(filepath, "rb"); - if (!fp) { - return -ENOENT; - } - ret = pd_detech_image_type(filepath); - pd_image_reader_set_file(&reader, fp); - if (ret >= 0) { - ret = pd_image_reader_init_for_type(&reader, ret); - } - if (ret < 0) { - if (pd_image_reader_init(&reader) != 0) { - fclose(fp); - return -2; - } - } - *width = reader.header.width; - *height = reader.header.height; - pd_image_reader_destroy(&reader); - fclose(fp); - return 0; + pd_error_t err; + pd_image_reader_t *reader; + + reader = pd_image_reader_create_from_file(filepath); + if (!reader) { + return PD_ERROR_NOT_FOUND; + } + err = pd_image_reader_read_header(reader); + if (err == PD_OK) { + err = pd_image_reader_read_data(reader, out); + } + pd_image_reader_destroy(reader); + return err; } diff --git a/lib/ui/src/ui_image.c b/lib/ui/src/ui_image.c index 98fc3539b..66f389a77 100644 --- a/lib/ui/src/ui_image.c +++ b/lib/ui/src/ui_image.c @@ -9,6 +9,7 @@ * LICENSE.TXT file in the root directory of this source tree. */ +#include #include #include #include @@ -24,6 +25,7 @@ typedef struct ui_image_event_listener_t { struct ui_image_t { pd_canvas_t data; bool loaded; + int error; char *path; size_t refs_count; /** list_t */ @@ -91,13 +93,14 @@ static void ui_image_dispatch_events(ui_image_t *image) static void ui_image_load(ui_image_t *image) { - if (!image->loaded) { - if (pd_read_image_from_file(image->path, &image->data) != 0) { - pd_canvas_init(&image->data); - } + if (image->loaded) { + return; + } + pd_canvas_init(&image->data); + image->error = pd_read_image_from_file(image->path, &image->data); + if (image->error == PD_OK) { image->loaded = true; } - ui_image_dispatch_events(image); } ui_image_t *ui_get_image(const char *path) @@ -195,6 +198,7 @@ void ui_load_images(void) list_concat(&list, &ui_image_loader.images); for (list_each(node, &list)) { ui_image_load(node->data); + ui_image_dispatch_events(node->data); } } diff --git a/tests/cases/test_image_reader.c b/tests/cases/test_image_reader.c index a567913f5..cb237f7a6 100644 --- a/tests/cases/test_image_reader.c +++ b/tests/cases/test_image_reader.c @@ -15,24 +15,20 @@ void test_image_reader(void) { - pd_canvas_t img; - int i, width, height; - char file[256], *formats[] = { "png", "bmp", "jpg" }; + pd_canvas_t img; + int i; + char file[256], *formats[] = { "png", "bmp", "jpg" }; - for (i = 0; i < 3; ++i) { - width = height = 0; - pd_canvas_init(&img); - snprintf(file, 255, "test_image_reader.%s", formats[i]); - logger_debug("image file: %s\n", file); - ctest_equal_int("check pd_read_image_from_file", pd_read_image_from_file(file, &img), - 0); - ctest_equal_int("check image width with ReadImageFile", img.width, 91); - ctest_equal_int("check image height with ReadImageFile", img.height, 69); - ctest_equal_int("check pd_read_image_size_from_file", - pd_read_image_size_from_file(file, &width, &height), 0); - logger_debug("image size: (%d, %d)\n", width, height); - ctest_equal_int("check image width with GetImageSize", width, 91); - ctest_equal_int("check image height with GetImageSize", height, 69); - pd_canvas_destroy(&img); - } + for (i = 0; i < 3; ++i) { + pd_canvas_init(&img); + snprintf(file, 255, "test_image_reader.%s", formats[i]); + logger_debug("image file: %s\n", file); + ctest_equal_int("check pd_read_image_from_file", + pd_read_image_from_file(file, &img), 0); + ctest_equal_int("check image width with ReadImageFile", + img.width, 91); + ctest_equal_int("check image height with ReadImageFile", + img.height, 69); + pd_canvas_destroy(&img); + } }