rsys

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

commit 698959233eb56ca2f15f880ea9e5bcd52aeed9c4
parent 237771176e55b35a4c9ef9eec966c11522cbdf52
Author: vaplv <vincent.forest@meso-star.com>
Date:   Fri, 31 Mar 2017 10:57:18 +0200

Extend the image API

Add the image_setup function that setups its internal memory layout and
the image_write_ppm[_stream] functions that write the image content wrt
the PPM file format. Add the image_read_ppm function that reads the
image content from a PPM file.

Diffstat:
Msrc/image.c | 164++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/image.h | 37+++++++++++++++++++++++++++----------
Msrc/test_image.c | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
3 files changed, 264 insertions(+), 41 deletions(-)

diff --git a/src/image.c b/src/image.c @@ -151,10 +151,81 @@ image_release(struct image* img) } res_T +image_setup + (struct image* img, + const size_t width, + const size_t height, + const size_t pitch, + const enum image_format format, + char* pixels) +{ + size_t size; + char* buffer = NULL; + res_T res = RES_OK; + + if(!img || !width || !height || !pitch || pitch < width) { + res = RES_BAD_ARG; + goto error; + } + + size = height * pitch; + if(size != img->height * img->pitch) { + buffer = MEM_ALLOC(img->allocator, size); + if(!buffer) { + res = RES_MEM_ERR; + goto error; + } + if(img->pixels) MEM_RM(img->allocator, img->pixels); + img->pixels = buffer; + } + + if(pixels) { + memcpy(img->pixels, pixels, size); + } + + img->width = width; + img->height = height; + img->pitch = pitch; + img->format = format; + +exit: + return res; +error: + if(buffer) MEM_RM(img->allocator, buffer); + goto exit; +} + +res_T +image_read_ppm(struct image* img, const char* filename) +{ + FILE* stream = NULL; + res_T res = RES_OK; + + if(!img || !filename) { + res = RES_OK; + goto error; + } + + stream = fopen(filename, "w"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + + res = image_read_ppm_stream(img, stream); + if(res != RES_OK) goto error; + +exit: + if(stream) fclose(stream); + return res; +error: + goto exit; +} + +res_T image_read_ppm_stream(struct image* img, FILE* stream) { struct parser parser; - char* buffer = NULL; size_t pitch; unsigned long width=0, height=0, max_val=0; enum ppm_id id; @@ -174,7 +245,7 @@ image_read_ppm_stream(struct image* img, FILE* stream) #undef CALL /* Check header */ - if(!width || !height || !max_val || max_val >= 65536) { + if(!width || !height || !max_val || max_val > 65535) { res = RES_BAD_ARG; goto error; } @@ -182,31 +253,90 @@ image_read_ppm_stream(struct image* img, FILE* stream) /* Allocate the image buffer */ fmt = max_val <= 255 ? IMAGE_RGB8 : IMAGE_RGB16; pitch = width * sizeof_image_format(fmt); - buffer = MEM_ALLOC(img->allocator, pitch*height); - if(!buffer) { - res = RES_MEM_ERR; - goto error; - } + res = image_setup(img, width, height, pitch, fmt, NULL); + if(res != RES_OK) 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; + case P3: + res = parse_raw_pixels(&parser, width, height, fmt, img->pixels); + break; + case P6: + res = parse_bin_pixels(&parser, width, height, fmt, img->pixels); + break; default: FATAL("Unreachable code.\n"); break; } - /* Setup the image layout */ - if(img->pixels) MEM_RM(img->allocator, img->pixels); - img->pixels = buffer; - img->width = width; - img->height = height; - img->format = fmt; - img->pitch = pitch; +exit: + return res; +error: + goto exit; +} + +res_T +image_write_ppm(const struct image* img, const char* filename) +{ + FILE* stream = NULL; + res_T res = RES_OK; + + if(!img || !filename) { + res = RES_OK; + goto error; + } + + stream = fopen(filename, "r"); + if(!stream) { + res = RES_IO_ERR; + goto error; + } + + res = image_write_ppm_stream(img, stream); + if(res != RES_OK) goto error; + +exit: + if(stream) fclose(stream); + return res; +error: + goto exit; +} + +res_T +image_write_ppm_stream(const struct image* img, FILE* stream) +{ + size_t x, y; + res_T res = RES_OK; + + if(!img || !stream) { + res = RES_BAD_ARG; + goto error; + } + + fprintf(stream, "P3 %lu %lu\n", img->width, img->height); + switch(img->format) { /* Write Max val */ + case IMAGE_RGB8: fprintf(stream, "255\n"); break; + case IMAGE_RGB16: fprintf(stream, "65535\n"); break; + default: FATAL("Unreachable code.\n"); break; + } + + FOR_EACH(y, 0, img->height) { + const char* row = img->pixels + y * img->pitch; + FOR_EACH(x, 0, img->width) { + const char* pixel = row + x * sizeof_image_format(img->format); + switch(img->format) { + case IMAGE_RGB8: + fprintf(stream, "%u %u %u\n", SPLIT3((uint8_t*)pixel)); + break; + case IMAGE_RGB16: + fprintf(stream, "%u %u %u\n", SPLIT3(((uint16_t*)pixel))); + break; + default: FATAL("Unreachable code.\n"); break; + } + } + } exit: return res; error: - if(buffer) MEM_RM(img->allocator, buffer); goto exit; } diff --git a/src/image.h b/src/image.h @@ -28,9 +28,11 @@ enum image_format { struct image { size_t width; size_t height; - size_t pitch; + size_t pitch; enum image_format format; char* pixels; + + /* Internal data */ struct mem_allocator* allocator; }; @@ -56,11 +58,35 @@ image_release (struct image* img); RSYS_API res_T +image_setup + (struct image* image, + const size_t width, + const size_t height, + const size_t pitch, + const enum image_format format, + char* pixels); /* May be NULL */ + +RSYS_API res_T +image_read_ppm + (struct image* image, + const char* filename); + +RSYS_API res_T image_read_ppm_stream (struct image* image, FILE* stream); RSYS_API res_T +image_write_ppm + (const struct image* image, + const char* filename); + +RSYS_API res_T +image_write_ppm_stream + (const struct image* image, + FILE* stream); + +RSYS_API res_T image_ppm_write (const char* path, const int width, @@ -76,15 +102,6 @@ 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 @@ -20,6 +20,44 @@ #define WIDTH 64 #define HEIGHT 32 +static void +check_image + (const struct image* img, + const char* ref_pixels, + const size_t ref_width, + const size_t ref_height, + const enum image_format ref_fmt) +{ + size_t x, y; + size_t i; + + CHECK(img->format, ref_fmt); + CHECK(img->height, ref_height); + CHECK(img->width, ref_width); + CHECK(img->pitch >= img->width, 1); + + i = 0; + FOR_EACH(y, 0, img->height) { + const char* row = img->pixels + img->pitch * (size_t)y; + FOR_EACH(x, 0, img->width) { + const char* pixel = row + (size_t)x*sizeof_image_format(img->format); + switch(img->format) { + case IMAGE_RGB8: + CHECK(((uint8_t*)pixel)[0], ((uint8_t*)ref_pixels)[i]), ++i; + CHECK(((uint8_t*)pixel)[1], ((uint8_t*)ref_pixels)[i]), ++i; + CHECK(((uint8_t*)pixel)[2], ((uint8_t*)ref_pixels)[i]), ++i; + break; + case IMAGE_RGB16: + CHECK(((uint16_t*)pixel)[0], ((uint16_t*)ref_pixels)[i]), ++i; + CHECK(((uint16_t*)pixel)[1], ((uint16_t*)ref_pixels)[i]), ++i; + CHECK(((uint16_t*)pixel)[2], ((uint16_t*)ref_pixels)[i]), ++i; + break; + default: FATAL("Unreachable code.\n"); break; + } + } + } +} + int main(int argc, char** argv) { @@ -32,6 +70,7 @@ main(int argc, char** argv) struct mem_allocator allocator; struct image img; unsigned char* pixels; + size_t pitch; FILE* fp; int x, y, i = 0; (void)argc, (void)argv; @@ -96,26 +135,63 @@ main(int argc, char** argv) CHECK(image_release(&img), RES_OK); CHECK(image_init(&allocator, &img), RES_OK); + pitch = WIDTH * sizeof_image_format(IMAGE_RGB8); + #define SETUP image_setup + CHECK(SETUP(NULL, 0, 0, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, 0, 0, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, 0, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, 0, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, 0, HEIGHT, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, 0, HEIGHT, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, HEIGHT, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, HEIGHT, 0, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, 0, 0, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, 0, 0, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, 0, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, 0, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, 0, HEIGHT, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, 0, HEIGHT, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, HEIGHT, pitch, IMAGE_RGB8, NULL), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, HEIGHT, pitch, IMAGE_RGB8, NULL), RES_OK); + CHECK(SETUP(NULL, 0, 0, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, 0, 0, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, 0, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, 0, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, 0, HEIGHT, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, 0, HEIGHT, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, HEIGHT, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, HEIGHT, 0, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, 0, 0, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, 0, 0, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, 0, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, 0, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, 0, HEIGHT, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, 0, HEIGHT, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(NULL, WIDTH, HEIGHT, pitch, IMAGE_RGB8, (char*)pixels), RES_BAD_ARG); + CHECK(SETUP(&img, WIDTH, HEIGHT, pitch, IMAGE_RGB8, (char*)pixels), RES_OK); + #undef SETUP + + CHECK(image_release(&img), RES_OK); + CHECK(image_init(&allocator, &img), RES_OK); + CHECK(image_read_ppm_stream(NULL, NULL), RES_BAD_ARG); CHECK(image_read_ppm_stream(&img, NULL), RES_BAD_ARG); CHECK(image_read_ppm_stream(NULL, fp), RES_BAD_ARG); CHECK(image_read_ppm_stream(&img, fp), RES_OK); - CHECK(img.format, IMAGE_RGB8); - CHECK(img.height, HEIGHT); - CHECK(img.width, WIDTH); - CHECK(img.pitch >= img.width, 1); + check_image(&img, (char*)pixels, WIDTH, HEIGHT, IMAGE_RGB8); - i = 0; - FOR_EACH(y, 0, HEIGHT) { - const char* row = img.pixels + img.pitch * (size_t)y; - FOR_EACH(x, 0, WIDTH) { - const char* pixel = row + (size_t)x*sizeof_image_format(img.format); - CHECK(((uint8_t*)pixel)[0], pixels[i]), ++i; - CHECK(((uint8_t*)pixel)[1], pixels[i]), ++i; - CHECK(((uint8_t*)pixel)[2], pixels[i]), ++i; - } - } + rewind(fp); + CHECK(image_write_ppm_stream(NULL, NULL), RES_BAD_ARG); + CHECK(image_write_ppm_stream(&img, NULL), RES_BAD_ARG); + CHECK(image_write_ppm_stream(NULL, fp), RES_BAD_ARG); + CHECK(image_write_ppm_stream(&img, fp), RES_OK); + CHECK(image_read_ppm_stream(&img, fp), RES_BAD_ARG); + rewind(fp); + CHECK(image_write_ppm_stream(&img, fp), RES_OK); + check_image(&img, (char*)pixels, WIDTH, HEIGHT, IMAGE_RGB8); + + CHECK(image_write_ppm_stream(&img, stdout), RES_OK); CHECK(image_release(&img), RES_OK);