commit 1ab15ecc0b7027970c9b1d4e6f64a8692a91e86f
parent 0f3db1ca56a848b4157250011586562213eb1514
Author: vaplv <vaplv@free.fr>
Date: Sat, 1 Apr 2017 09:40:29 +0200
Merge branch 'feature_image_ppm'
Diffstat:
| M | cmake/CMakeLists.txt | | | 1 | + |
| M | src/image.c | | | 392 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
| M | src/image.h | | | 70 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
| M | src/rsys.h | | | 17 | ++++++++++++++++- |
| A | src/test_image.c | | | 300 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 755 insertions(+), 25 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,361 @@
#include "io_c99.h"
#endif
+enum ppm_id { P3, P6 };
+
+struct parser {
+ char buf[512];
+ char* tk;
+ FILE* stream;
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+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(id);
+ if(!str) return RES_BAD_ARG;
+ 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;
+}
+
+static res_T
+write_bin_ppm(const struct image* img, FILE* stream)
+{
+ size_t y;
+ ASSERT(img && stream);
+
+ FOR_EACH(y, 0, img->height) {
+ const char* row = img->pixels + y * img->pitch;
+ size_t n;
+ n = fwrite(row, sizeof_image_format(img->format), img->width, stream);
+ if(n < img->width) return RES_IO_ERR;
+ }
+ return RES_OK;
+}
+
+static res_T
+write_raw_ppm(const struct image* img, FILE* stream)
+{
+ size_t x, y;
+ ASSERT(img && stream);
+
+ 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;
+ }
+ }
+ }
+ return RES_OK;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+image_init(struct mem_allocator* mem_allocator, struct image* img)
+{
+ struct mem_allocator* allocator;
+ if(!img) return RES_BAD_ARG;
+ allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
+ memset(img, 0, sizeof(struct image));
+ img->allocator = allocator;
+ return RES_OK;
+}
+
+res_T
+image_release(struct image* img)
+{
+ if(!img) return RES_BAD_ARG;
+ if(img->pixels) MEM_RM(img->allocator, img->pixels);
+ return RES_OK;
+}
+
+res_T
+image_setup
+ (struct image* img,
+ const size_t width,
+ const size_t height,
+ const size_t pitch,
+ const enum image_format format,
+ const 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_BAD_ARG;
+ goto error;
+ }
+
+ stream = fopen(filename, "r");
+ 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;
+ size_t pitch;
+ unsigned long width=0, height=0, max_val=0;
+ enum ppm_id id;
+ enum image_format fmt;
+ res_T res = RES_OK;
+
+ if(!img || !stream) return RES_BAD_ARG;
+
+ parser_init(&parser, stream);
+
+ /* 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 > 65535) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Allocate the image buffer */
+ fmt = max_val <= 255 ? IMAGE_RGB8 : IMAGE_RGB16;
+ pitch = width * sizeof_image_format(fmt);
+ 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, img->pixels);
+ break;
+ case P6:
+ res = parse_bin_pixels(&parser, width, height, fmt, img->pixels);
+ break;
+ default: FATAL("Unreachable code.\n"); break;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+image_write_ppm
+ (const struct image* img,
+ const int binary,
+ const char* filename)
+{
+ FILE* stream = NULL;
+ res_T res = RES_OK;
+
+ if(!img || !filename) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ stream = fopen(filename, "w");
+ if(!stream) {
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ res = image_write_ppm_stream(img, binary, 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,
+ const int bin,
+ FILE* stream)
+{
+ res_T res = RES_OK;
+
+ if(!img || !stream) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ fprintf(stream, "%s %lu %lu\n", bin ? "P6" : "P3",
+ (unsigned long)img->width,
+ (unsigned long)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;
+ }
+
+ if(bin) {
+ res = write_bin_ppm(img, stream);
+ } else {
+ res = write_raw_ppm(img, stream);
+ }
+ if(res != RES_OK) goto error;
+
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
res_T
image_ppm_write
(const char* path,
@@ -57,32 +414,20 @@ image_ppm_write_stream
const int Bpp,
const unsigned char* buffer)
{
- char buf[BUFSIZ];
+ int i;
res_T res = RES_OK;
- if(width && height && Bpp && !buffer) {
+ if(!width || !height || !Bpp || !buffer || !fp) {
res = RES_BAD_ARG;
goto error;
}
- if(!fp) {
- res = RES_BAD_ARG;
+
+ i = fprintf(fp, "P3\n\n%i %i\n%i\n", width, height, 255);
+ if(i < 0) {
+ res = RES_IO_ERR;
goto error;
}
- #define FWRITE(Fp, String) \
- { \
- const size_t i = fwrite(String, sizeof(char), strlen(String), Fp); \
- if(i != strlen(String) * sizeof(char)) { res = RES_MEM_ERR; goto error; }\
- } (void)0
- #define SNPRINTF(Buf, Sz, Str, Arg0, Arg1, Arg2) \
- { \
- const int i = snprintf(Buf, Sz, Str, Arg0, Arg1, Arg2); \
- if( i >= BUFSIZ ) { res = RES_MEM_ERR; goto error; } \
- } (void)0
-
- SNPRINTF(buf, BUFSIZ, "P3\n\n%i %i\n%i\n", width, height, 255);
- FWRITE(fp, buf);
-
if(Bpp) {
const long pitch = width * Bpp;
int x, y;
@@ -90,17 +435,18 @@ image_ppm_write_stream
const unsigned char* row = buffer + y * pitch;
for(x = 0; x < width; ++x) {
const unsigned char* pixel = row + x * Bpp;
- SNPRINTF(buf, BUFSIZ, "%u %u %u\n",
+ i = fprintf(fp, "%u %u %u\n",
pixel[0],
Bpp > 1 ? pixel[1] : pixel[0],
Bpp > 2 ? pixel[2] : pixel[0]);
- FWRITE(fp, buf);
+ if(i < 0) {
+ res = RES_IO_ERR;
+ goto error;
+ }
}
- FWRITE(fp, "\n");
+ fprintf(fp, "\n");
}
}
- #undef SNPRINTF
- #undef FWRITE
exit:
return res;
diff --git a/src/image.h b/src/image.h
@@ -18,9 +18,77 @@
#include "rsys.h"
+struct mem_allocator;
+
+enum image_format {
+ IMAGE_RGB8,
+ IMAGE_RGB16
+};
+
+struct image {
+ size_t width;
+ size_t height;
+ size_t pitch;
+ enum image_format format;
+ char* pixels;
+
+ /* Internal data */
+ struct mem_allocator* allocator;
+};
+
+static FINLINE size_t
+sizeof_image_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;
+ }
+}
+
BEGIN_DECLS
RSYS_API res_T
+image_init
+ (struct mem_allocator* allocator, /* May be NULL */
+ struct image* img);
+
+RSYS_API res_T
+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,
+ const 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 int binary,
+ const char* filename);
+
+RSYS_API res_T
+image_write_ppm_stream
+ (const struct image* image,
+ const int binary,
+ FILE* stream);
+
+DEPRECATED RSYS_API res_T
image_ppm_write
(const char* path,
const int width,
@@ -28,7 +96,7 @@ image_ppm_write
const int bytes_per_pixel,
const unsigned char* buffer);
-RSYS_API res_T
+DEPRECATED RSYS_API res_T
image_ppm_write_stream
(FILE* stream,
const int width,
diff --git a/src/rsys.h b/src/rsys.h
@@ -343,7 +343,14 @@ typedef int res_T;
#define CONTAINER_OF(Ptr, Type, Member) \
((Type*)((uintptr_t)Ptr - offsetof(Type, Member)))
-#define RESTRICT __restrict__
+#ifdef COMPILER_CL
+ #define RESTRICT __restrict
+#elif defined COMPILER_GCC
+ #define RESTRICT __restrict__
+#else
+ #define RESTRICT
+#endif
+
#define COUNTER __COUNTER__
#define SWAP(Type, A, B) \
@@ -369,6 +376,14 @@ typedef int res_T;
? (uintptr_t)(B) < ((uintptr_t)(A) + (SzA)) \
: (uintptr_t)(A) < ((uintptr_t)(B) + (SzB)))
+#ifdef COMPILER_CL
+ #define DEPRECATED __declspec(deprecated)
+#elif defined COMPILER_GCC
+ #define DEPRECATED __attribute__((deprecated))
+#else
+ #define DEPRECATED
+#endif
+
#ifdef __cplusplus
#define BEGIN_DECLS extern "C" {
#define END_DECLS }
diff --git a/src/test_image.c b/src/test_image.c
@@ -0,0 +1,300 @@
+/* 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"
+#include "test_utils.h"
+
+#define WIDTH 64
+#define HEIGHT 32
+
+static void
+check_image_eq
+ (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;
+ }
+ }
+ }
+}
+
+static void
+check_image_read
+ (FILE* fp,
+ const size_t width,
+ const size_t height,
+ const enum image_format fmt,
+ struct mem_allocator* allocator)
+{
+ struct image img;
+ size_t max_val;
+ int is_bin;
+ char id[2];
+
+ CHECK(image_init(allocator, &img), RES_OK);
+
+ CHECK(fseek(fp, 0, SEEK_SET), 0);
+ CHECK(image_read_ppm_stream(&img, fp), RES_OK);
+
+ CHECK(fseek(fp, 0, SEEK_SET), 0);
+ CHECK(fread(id, 1, 2, fp), 2);
+ CHECK(id[0], 'P');
+ CHECK(id[1] == '3' || id[1] == '6', 1);
+ is_bin = id[1] == '6';
+
+ switch(fmt) {
+ case IMAGE_RGB8: max_val = 255; break;
+ case IMAGE_RGB16: max_val = 65535; break;
+ default: FATAL("Unreachable code.\n"); break;
+ }
+
+ CHECK(fseek(fp, 3, SEEK_SET), 0);
+ fprintf(fp, "%lu %lu\n", (unsigned long)width+1, (unsigned long)height);
+ CHECK(fseek(fp, 0, SEEK_SET), 0);
+ CHECK(image_read_ppm_stream(&img, fp), RES_BAD_ARG);
+ CHECK(fseek(fp, 3, SEEK_SET), 0);
+ fprintf(fp, "%lu %lu\n", (unsigned long)width, (unsigned long)height+1);
+ CHECK(fseek(fp, 0, SEEK_SET), 0);
+ CHECK(image_read_ppm_stream(&img, fp), RES_BAD_ARG);
+ CHECK(fseek(fp, 3, SEEK_SET), 0);
+ fprintf(fp, "%lu %lu\n", (unsigned long)width, (unsigned long)height);
+ fprintf(fp, "%lu\n", (unsigned long)max_val+1);
+ CHECK(fseek(fp, 0, SEEK_SET), 0);
+
+ if(is_bin) {
+ CHECK(image_read_ppm_stream(&img, fp), RES_BAD_ARG);
+ } else {
+ switch(fmt) {
+ case IMAGE_RGB8:
+ CHECK(image_read_ppm_stream(&img, fp), RES_OK);
+ break;
+ case IMAGE_RGB16:
+ CHECK(image_read_ppm_stream(&img, fp), RES_BAD_ARG);
+ break;
+ default: FATAL("Unreachable code.\n"); break;
+ }
+ }
+
+ CHECK(fseek(fp, 3, SEEK_SET), 0);
+ fprintf(fp, "%lu %lu\n", (unsigned long)width, (unsigned long)height);
+ fprintf(fp, "%lu\n", (unsigned long)max_val);
+ CHECK(fseek(fp, 0, SEEK_SET), 0);
+ CHECK(image_read_ppm_stream(&img, fp), RES_OK);
+
+ CHECK(image_release(&img), RES_OK);
+}
+
+static void
+check_image
+ (const char* pixels,
+ const size_t width,
+ const size_t height,
+ const enum image_format fmt,
+ struct mem_allocator* allocator)
+{
+ struct image img;
+ size_t pitch;
+ FILE* fp;
+
+ CHECK(image_init(NULL, NULL), RES_BAD_ARG);
+ CHECK(image_init(allocator, NULL), RES_BAD_ARG);
+ CHECK(image_init(NULL, &img), RES_OK);
+ CHECK(image_release(NULL), RES_BAD_ARG);
+ CHECK(image_release(&img), RES_OK);
+ CHECK(image_init(allocator, &img), RES_OK);
+
+ pitch = width * sizeof_image_format(fmt);
+
+ CHECK(image_setup(NULL, 0, 0, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, 0, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, 0, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, 0, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, 0, height, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, height, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, height, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, height, 0, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, 0, 0, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, 0, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, 0, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, 0, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, 0, height, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, height, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, height, pitch, fmt, NULL), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, height, pitch, fmt, NULL), RES_OK);
+ CHECK(image_setup(NULL, 0, 0, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, 0, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, 0, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, 0, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, 0, height, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, height, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, height, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, height, 0, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, 0, 0, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, 0, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, 0, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, 0, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, 0, height, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, 0, height, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(NULL, width, height, pitch, fmt, pixels), RES_BAD_ARG);
+ CHECK(image_setup(&img, width, height, pitch, fmt, pixels), RES_OK);
+
+ fp = tmpfile();
+ NCHECK(fp, NULL);
+
+ CHECK(image_write_ppm_stream(NULL, 0, NULL), RES_BAD_ARG);
+ CHECK(image_write_ppm_stream(&img, 0, NULL), RES_BAD_ARG);
+ CHECK(image_write_ppm_stream(NULL, 0, fp), RES_BAD_ARG);
+ CHECK(image_write_ppm_stream(&img, 0, fp), 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_BAD_ARG);
+ rewind(fp);
+ CHECK(image_read_ppm_stream(&img, fp), RES_OK);
+ check_image_eq(&img, pixels, width, height, fmt);
+
+ rewind(fp);
+ CHECK(image_write_ppm_stream(&img, 1, fp), RES_OK);
+ rewind(fp);
+ CHECK(image_read_ppm_stream(&img, fp), RES_OK);
+ check_image_eq(&img, pixels, width, height, fmt);
+
+ CHECK(image_write_ppm(NULL, 0, NULL), RES_BAD_ARG);
+ CHECK(image_write_ppm(&img, 0, NULL), RES_BAD_ARG);
+ CHECK(image_write_ppm(NULL, 0, "test.ppm"), RES_BAD_ARG);
+ CHECK(image_write_ppm(&img, 0, "test.ppm"), RES_OK);
+
+ CHECK(image_read_ppm(NULL, NULL), RES_BAD_ARG);
+ CHECK(image_read_ppm(&img, NULL), RES_BAD_ARG);
+ CHECK(image_read_ppm(NULL, "test_bad.ppm"), RES_BAD_ARG);
+ CHECK(image_read_ppm(&img, "test_bad.ppm"), RES_IO_ERR);
+ CHECK(image_read_ppm(&img, "test.ppm"), RES_OK);
+
+ check_image_eq(&img, pixels, width, height, fmt);
+
+ CHECK(image_write_ppm(&img, 1, "test.ppm"), RES_OK);
+ CHECK(image_read_ppm(&img, "test.ppm"), RES_OK);
+ check_image_eq(&img, pixels, width, height, fmt);
+
+ CHECK(image_write_ppm_stream(&img, 1, stdout), RES_OK);
+
+ fclose(fp);
+
+ fp = tmpfile();
+ NCHECK(fp, NULL);
+ CHECK(image_write_ppm_stream(&img, 0, fp), RES_OK);
+ check_image_read(fp, width, height, fmt, allocator);
+ fclose(fp);
+
+ fp = tmpfile();
+ NCHECK(fp, NULL);
+ CHECK(image_write_ppm_stream(&img, 1, fp), RES_OK);
+ check_image_read(fp, width, height, fmt, allocator);
+ fclose(fp);
+
+ CHECK(image_release(&img), RES_OK);
+}
+
+int
+main(int argc, char** argv)
+{
+ uint8_t lut8[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}}
+ };
+ uint16_t lut16[4][4][3] = {
+ { {0x0000,0x0000,0xFFFF}, {0xFFFF,0x0000,0xFFFF},
+ {0xFFFF,0x0000,0xFFFF}, {0x0000,0x0000,0xFFFF} },
+ { {0x0000,0xFFFF,0x0000}, {0x0000,0xFFFF,0xFFFF},
+ {0x0000,0xFFFF,0xFFFF}, {0x0000,0xFFFF,0x0000} },
+ { {0xFFFF,0xFFFF,0xFFFF}, {0x0000,0x0000,0x0000},
+ {0x0000,0x0000,0x0000}, {0xFFFF,0xFFFF,0xFFFF} },
+ { {0xFFFF,0xFFFF,0x0000}, {0xFFFF,0x0000,0x0000},
+ {0xFFFF,0x0000,0x0000}, {0xFFFF,0xFFFF,0x0000} }
+ };
+ struct mem_allocator allocator;
+ char* pixels;
+ int x, y, i = 0;
+ (void)argc, (void)argv;
+
+ CHECK(mem_init_proxy_allocator(&allocator, &mem_default_allocator), RES_OK);
+
+ pixels = MEM_ALLOC(&allocator, WIDTH*sizeof_image_format(IMAGE_RGB8)*HEIGHT);
+ 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;
+
+ ((uint8_t*)pixels)[i++] = lut8[j][k][0];
+ ((uint8_t*)pixels)[i++] = lut8[j][k][1];
+ ((uint8_t*)pixels)[i++] = lut8[j][k][2];
+ }}
+ check_image(pixels, WIDTH, HEIGHT, IMAGE_RGB8, &allocator);
+ MEM_RM(&allocator, pixels);
+
+ pixels = MEM_ALLOC(&allocator, WIDTH*sizeof_image_format(IMAGE_RGB16)*HEIGHT);
+ 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;
+ ((uint16_t*)pixels)[i++] = lut16[j][k][0];
+ ((uint16_t*)pixels)[i++] = lut16[j][k][1];
+ ((uint16_t*)pixels)[i++] = lut16[j][k][2];
+ }}
+ check_image(pixels, WIDTH, HEIGHT, IMAGE_RGB16, &allocator);
+ MEM_RM(&allocator, pixels);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}