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:
| M | src/image.c | | | 164 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
| M | src/image.h | | | 37 | +++++++++++++++++++++++++++---------- |
| M | src/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);