rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

commit 77fecf49100c5f98c513a07b90ae8ee6ae65045a
parent 0f3db1ca56a848b4157250011586562213eb1514
Author: vaplv <vaplv@free.fr>
Date:   Wed, 29 Mar 2017 17:52:29 +0200

Add a image_ppm_read_stream function

Test the image ppm API.

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/image.c | 190++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/image.h | 16++++++++++++++++
Asrc/test_image.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 345 insertions(+), 5 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -183,6 +183,7 @@ if(NOT NO_TEST) new_test(test_free_list rsys) new_test(test_func_name) new_test(test_hash_table rsys) + new_test(test_image rsys) new_test(test_library rsys) new_test(test_list rsys) new_test(test_logger rsys) diff --git a/src/image.c b/src/image.c @@ -14,7 +14,9 @@ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ #define _POSIX_C_SOURCE 200112L /* snprintf support */ +#include "cstr.h" #include "image.h" +#include "mem_allocator.h" #include <stdio.h> #include <string.h> @@ -22,6 +24,123 @@ #include "io_c99.h" #endif +enum ppm_id { P3, P6 }; + +struct parser { + char buf[512]; + char* tk; + FILE* stream; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE size_t +sizeof_format(const enum image_format fmt) +{ + switch(fmt) { + case IMAGE_RGB8: return sizeof(uint8_t[3]); + case IMAGE_RGB16: return sizeof(uint16_t[3]); + default: FATAL("Unreachable code.\n"); break; + } +} + +static void +parser_init(struct parser* parser, FILE* stream) +{ + ASSERT(parser && stream); + parser->tk = NULL; + parser->stream = stream; +} + +static char* +parser_next_token(struct parser* parser) +{ + do { + if(parser->tk) parser->tk = strtok(NULL, " \t\n"); + + if(!parser->tk) { + char* line = fgets(parser->buf, (int)sizeof(parser->buf), parser->stream); + if(line) parser->tk = strtok(line, " \t\n"); + } + + if(parser->tk && parser->tk[0] == '#') + parser->tk = NULL; + + } while(!parser->tk && !feof(parser->stream)); + return parser->tk; +} + +static INLINE res_T +parse_ppm_id(const char* str, enum ppm_id* id) +{ + ASSERT(str && id); + if(!strcmp(str, "P3")) { + *id = P3; + } else if(!strcmp(str, "P6")) { + *id = P6; + } else { + return RES_BAD_ARG; + } + return RES_OK; +} + +static res_T +parse_raw_pixels + (struct parser* parser, + const size_t width, + const size_t height, + const enum image_format fmt, + char* buffer) +{ + size_t i, n; + res_T res = RES_OK; + ASSERT(parser && width > 0 && height > 0 && buffer); + + n = (size_t)(width * height * 3/*#channels*/); + FOR_EACH(i, 0, n) { + unsigned val; + + res = cstr_to_uint(parser_next_token(parser), &val); + if(res != RES_OK) return res; + + switch(fmt) { + case IMAGE_RGB8: + if(val > UINT8_MAX) return RES_BAD_ARG; + ((uint8_t*)buffer)[i] = (uint8_t)val; + break; + case IMAGE_RGB16: + if(val > UINT16_MAX) return RES_BAD_ARG; + ((uint16_t*)buffer)[i] = (uint16_t)val; + break; + default: FATAL("Unreachable code.\n"); break; + } + } + return RES_OK; +} + +static INLINE res_T +parse_bin_pixels + (struct parser* parser, + const size_t width, + const size_t height, + const enum image_format fmt, + char* buffer) +{ + size_t n, size; + ASSERT(parser && width > 0 && height > 0 && buffer); + switch(fmt) { + case IMAGE_RGB8: size = 1; break; + case IMAGE_RGB16: size = 2; break; + default: FATAL("Unreachable code.\n"); break; + } + n = (size_t)(width * height * 3/*#channels*/); + return (n == fread(buffer, size, n, parser->stream)) ? RES_OK : RES_BAD_ARG; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ res_T image_ppm_write (const char* path, @@ -60,11 +179,7 @@ image_ppm_write_stream char buf[BUFSIZ]; res_T res = RES_OK; - if(width && height && Bpp && !buffer) { - res = RES_BAD_ARG; - goto error; - } - if(!fp) { + if(!width || !height || !Bpp || !buffer || !fp) { res = RES_BAD_ARG; goto error; } @@ -108,3 +223,68 @@ error: goto exit; } +res_T +image_ppm_read_stream + (FILE* fp, + struct mem_allocator* mem_allocator, + size_t* out_width, + size_t* out_height, + enum image_format* out_fmt, + char** out_buffer) +{ + struct parser parser; + struct mem_allocator* allocator; + char* buffer = NULL; + unsigned long width=0, height=0, max_val; + enum ppm_id id; + enum image_format fmt = IMAGE_RGB8; + res_T res = RES_OK; + + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + if(!fp || !out_width || !out_height || !out_fmt || !out_buffer) { + res = RES_BAD_ARG; + goto error; + } + + parser_init(&parser, fp); + + /* Read header */ + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + CALL(parse_ppm_id(parser_next_token(&parser), &id)); + CALL(cstr_to_ulong(parser_next_token(&parser), &width)); + CALL(cstr_to_ulong(parser_next_token(&parser), &height)); + CALL(cstr_to_ulong(parser_next_token(&parser), &max_val)); + #undef CALL + + /* Check header */ + if(!width || !height || !max_val || max_val >= 65536) { + res = RES_BAD_ARG; + goto error; + } + + /* Allocate the image buffer */ + fmt = max_val <= 255 ? IMAGE_RGB8 : IMAGE_RGB16; + buffer = MEM_ALLOC(allocator, width * height * sizeof_format(fmt)); + if(!buffer) { + res = RES_MEM_ERR; + goto error; + } + + /* Read pixel data */ + switch(id) { + case P3: res = parse_raw_pixels(&parser, width, height, fmt, buffer); break; + case P6: res = parse_bin_pixels(&parser, width, height, fmt, buffer); break; + default: FATAL("Unreachable code.\n"); break; + } + +exit: + if(out_width) *out_width = width; + if(out_height) *out_height = height; + if(out_fmt) *out_fmt = fmt; + if(out_buffer) *out_buffer = buffer; + return res; +error: + if(buffer) MEM_RM(allocator, buffer), buffer = NULL; + goto exit; +} + diff --git a/src/image.h b/src/image.h @@ -18,6 +18,13 @@ #include "rsys.h" +struct mem_allocator; + +enum image_format { + IMAGE_RGB8, + IMAGE_RGB16 +}; + BEGIN_DECLS RSYS_API res_T @@ -36,6 +43,15 @@ image_ppm_write_stream const int bytes_per_pixel, const unsigned char* buffer); +RSYS_API res_T +image_ppm_read_stream + (FILE* stream, + struct mem_allocator* mem_allocator, + size_t* width, + size_t* height, + enum image_format* fmt, + char** buffer); + END_DECLS #endif /* IMAGE_H */ diff --git a/src/test_image.c b/src/test_image.c @@ -0,0 +1,143 @@ +/* Copyright (C) 2013-2017 Vincent Forest (vaplv@free.fr) + * + * The RSys library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSys library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ + +#include "image.h" +#include "mem_allocator.h" + +#define WIDTH 64 +#define HEIGHT 32 + +int +main(int argc, char** argv) +{ + unsigned char lut[4][4][3] = { + {{0xFF,0xFF,0x00}, {0xFF,0x00,0x00 }, {0xFF,0x00,0x00}, { 0xFF,0xFF,0x00}}, + {{0xFF,0xFF,0xFF}, {0x00,0x00,0x00 }, {0x00,0x00,0x00}, { 0xFF,0xFF,0xFF}}, + {{0x00,0xFF,0x00}, {0x00,0xFF,0xFF }, {0x00,0xFF,0xFF}, { 0x00,0xFF,0x00}}, + {{0x00,0x00,0xFF}, {0xFF,0x00,0xFF }, {0xFF,0x00,0xFF}, { 0x00,0x00,0xFF}} + }; + unsigned char* pixels; + char* buf; + FILE* fp; + size_t w, h; + enum image_format fmt; + int x, y, i = 0; + (void)argc, (void)argv; + + fp = tmpfile(); + NCHECK(fp, NULL); + + pixels = mem_alloc(WIDTH*HEIGHT*3); + NCHECK(pixels, NULL); + + i = 0; + FOR_EACH(y, 0, HEIGHT) { + FOR_EACH(x, 0, WIDTH) { + int j = ((x/32) & 1) + ((y/16)&1) * 2; + int k = ((x/8) & 1) + ((y/8)&1) * 2; + + pixels[i++] = lut[j][k][0]; + pixels[i++] = lut[j][k][1]; + pixels[i++] = lut[j][k][2]; + }} + + CHECK(image_ppm_write_stream(NULL, 0, 0, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, 0, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, 0, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, 0, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, HEIGHT, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, HEIGHT, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, HEIGHT, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, HEIGHT, 0, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, 0, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, 0, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, 0, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, 0, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, HEIGHT, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, HEIGHT, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, HEIGHT, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, HEIGHT, 3, NULL), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, 0, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, 0, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, 0, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, 0, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, HEIGHT, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, HEIGHT, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, HEIGHT, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, HEIGHT, 0, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, 0, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, 0, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, 0, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, 0, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, 0, HEIGHT, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, 0, HEIGHT, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(NULL, WIDTH, HEIGHT, 3, pixels), RES_BAD_ARG); + CHECK(image_ppm_write_stream(fp, WIDTH, HEIGHT, 3, pixels), RES_OK); + rewind(fp); + + CHECK(image_ppm_read_stream(NULL, NULL, NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, &h, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, &h, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, &h, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, &h, NULL, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, NULL, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, NULL, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, NULL, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, NULL, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, &h, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, &h, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, &h, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, &h, &fmt, NULL), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, NULL, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, NULL, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, NULL, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, NULL, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, &h, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, &h, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, &h, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, &h, NULL, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, NULL, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, NULL, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, NULL, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, NULL, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, NULL, &h, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, NULL, &h, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(NULL, NULL, &w, &h, &fmt, &buf), RES_BAD_ARG); + CHECK(image_ppm_read_stream(fp, NULL, &w, &h, &fmt, &buf), RES_OK); + NCHECK(buf, NULL); + CHECK(w, WIDTH); + CHECK(h, HEIGHT); + CHECK(fmt, IMAGE_RGB8); + + i = 0; + FOR_EACH(y, 0, HEIGHT) { + FOR_EACH(x, 0, WIDTH) { + CHECK(((unsigned char*)buf)[i], pixels[i]), ++i; + CHECK(((unsigned char*)buf)[i], pixels[i]), ++i; + CHECK(((unsigned char*)buf)[i], pixels[i]), ++i; + }} + + CHECK(image_ppm_write_stream + (stdout, WIDTH, HEIGHT, 3, (unsigned char*)buf), RES_OK); + + mem_rm(buf); + fclose(fp); + mem_rm(pixels); + CHECK(mem_allocated_size(), 0); + return 0; +}