46static void check(
const char* what,
int ok) {
49 fprintf(stderr,
"FAIL %s\n", what);
54#define PROFILE_LEN 301
63 out[i] = (
unsigned char)(i * 7 + 3);
68 memcpy(out + 36, ok ?
"acsp" :
"nope", 4);
76static void add(
unsigned char* buf,
size_t* len,
const void* data,
size_t n) {
77 memcpy(buf + *len, data, n);
87static void segment(
unsigned char* buf,
size_t* len,
unsigned marker,
const unsigned char* body,
size_t n) {
88 unsigned char head[4];
91 head[1] = (
unsigned char)marker;
92 head[2] = (
unsigned char)((n + 2) >> 8);
93 head[3] = (
unsigned char)((n + 2) & 0xff);
94 add(buf, len, head, 4);
95 add(buf, len, body, n);
105static void icc_chunk(
unsigned char* buf,
size_t* len,
int seq,
int of,
const unsigned char* part,
size_t n) {
108 memcpy(body,
"ICC_PROFILE", 12);
109 body[12] = (
unsigned char)seq;
110 body[13] = (
unsigned char)of;
111 memcpy(body + 14, part, n);
112 segment(buf, len, 0xE2, body, n + 14);
122static size_t make_jpeg(
unsigned char* buf,
const unsigned char* profile,
int chunks,
int padding) {
123 static unsigned char filler[60000];
124 const unsigned char soi[2] = {0xFF, 0xD8}, sos[4] = {0xFF, 0xDA, 0x00, 0x02};
128 add(buf, &len, soi, 2);
129 segment(buf, &len, 0xE0, (
const unsigned char*)
"JFIF\0\1\1\0\0\1\0\1\0\0", 14);
130 for (i = 0; i < padding; i++)
131 segment(buf, &len, 0xE1, filler,
sizeof(filler));
135 icc_chunk(buf, &len, 1, 2, profile, 100);
137 add(buf, &len, sos, 4);
144static void be32(
unsigned char* p,
size_t v) {
145 p[0] = (
unsigned char)(v >> 24);
146 p[1] = (
unsigned char)(v >> 16);
147 p[2] = (
unsigned char)(v >> 8);
148 p[3] = (
unsigned char)v;
157static void png_chunk(
unsigned char* buf,
size_t* len,
const char*
type,
const unsigned char* body,
size_t n) {
158 unsigned char head[8], crc[4] = {0, 0, 0, 0};
161 memcpy(head + 4,
type, 4);
162 add(buf, len, head, 8);
163 add(buf, len, body, n);
164 add(buf, len, crc, 4);
173static size_t make_png(
unsigned char* buf,
const unsigned char* profile,
int after) {
174 static const unsigned char sig[8] = {0x89,
'P',
'N',
'G', 0x0D, 0x0A, 0x1A, 0x0A};
175 unsigned char ihdr[13], iccp[64 + 2 *
PROFILE_LEN], idat[4] = {0, 0, 0, 0};
179 memset(ihdr, 0,
sizeof(ihdr));
180 memcpy(iccp,
"Test profile", 13);
182 compress2(iccp + 14, &packed, profile,
PROFILE_LEN, 9);
183 add(buf, &len, sig, 8);
186 png_chunk(buf, &len,
"iCCP", iccp, 14 + packed);
189 png_chunk(buf, &len,
"iCCP", iccp, 14 + packed);
197static void le32(
unsigned char* p,
size_t v) {
198 p[0] = (
unsigned char)v;
199 p[1] = (
unsigned char)(v >> 8);
200 p[2] = (
unsigned char)(v >> 16);
201 p[3] = (
unsigned char)(v >> 24);
208static size_t make_webp(
unsigned char* buf,
const unsigned char* profile) {
209 unsigned char head[8], vp8x[10], pad = 0;
212 memset(vp8x, 0,
sizeof(vp8x));
213 add(buf, &len,
"RIFF\0\0\0\0WEBP", 12);
214 memcpy(head,
"VP8X", 4);
215 le32(head + 4,
sizeof(vp8x));
216 add(buf, &len, head, 8);
217 add(buf, &len, vp8x,
sizeof(vp8x));
218 memcpy(head,
"ICCP", 4);
220 add(buf, &len, head, 8);
222 add(buf, &len, &pad, 1);
223 memcpy(head,
"VP8L", 4);
225 add(buf, &len, head, 8);
226 le32(buf + 4, len - 8);
235static size_t make_tiff(
unsigned char* buf,
const unsigned char* profile,
int be) {
237 size_t len = 8 + 2 + 12 + 4;
241 buf[0] = buf[1] = be ?
'M' :
'I';
262 buf[len + (
size_t)i] = profile[i];
272static void expect(
const char* what,
const unsigned char* buf,
size_t len,
const unsigned char* want,
int expect_result) {
273 unsigned char* icc = NULL;
279 snprintf(label,
sizeof(label),
"%s: from a buffer", what);
280 check(label, got == expect_result &&
285 file = fopen(
"ex_icc.tmp",
"wb");
287 fwrite(buf, 1, len, file);
290 snprintf(label,
sizeof(label),
"%s: from a file", what);
291 check(label, got == expect_result &&
294 remove(
"ex_icc.tmp");
306int main(
int argc,
char** argv) {
308 unsigned char* buf = malloc(1024 * 1024);
316 while ((opt = getopt(argc, argv,
"V:")) != -1) {
318 fprintf(stderr,
"Usage: %s [-V LOG_LEVEL] [file]\n", argv[0]);
324 const char* path = argv[optind];
325 unsigned char* icc = NULL;
330 printf(
"%s: a profile of %zu bytes\n", path, icc_len);
332 printf(
"%s: %s\n", path, got == 0 ?
"no profile" :
"a broken profile, or unreadable");
335 return got < 0 ? 1 : 0;
344 expect(
"JPEG, chunks out of order", buf, len, profile, 1);
346 expect(
"JPEG, the profile past the first read", buf, len, profile, 1);
348 expect(
"JPEG without a profile", buf, len, NULL, 0);
350 expect(
"JPEG with a chunk missing", buf, len, NULL, -1);
353 expect(
"a JPEG cut short", buf, len - 60, NULL, 0);
356 expect(
"PNG", buf, len, profile, 1);
358 expect(
"PNG with iCCP after the image data", buf, len, NULL, 0);
361 expect(
"WebP", buf, len, profile, 1);
364 expect(
"TIFF little endian", buf, len, profile, 1);
366 expect(
"TIFF big endian", buf, len, profile, 1);
368 expect(
"TIFF with a broken profile", buf, len, NULL, -1);
370 check(
"a buffer of another kind has none",
371 n_icc_from_buffer((
const unsigned char*)
"GIF89a..", 8, &(
unsigned char*){NULL}, &(size_t){0}) == 0);
372 check(
"a missing file is an error",
n_icc_from_file(
"/nonexistent/ex_icc", &(
unsigned char*){NULL}, &(size_t){0}) == -1);
376 fprintf(stderr,
"ex_icc: %d failure(s)\n",
failures);
379 printf(
"ex_icc: ok\n");
static int expect(BITMAP *bmp, int x, int y, int r, int g, int b, const char *what)
static unsigned char * make_png(int w, int h, size_t *out_len)
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
static void make_profile(unsigned char *out, int ok)
A profile shaped buffer: its size, "acsp" at 36, a pattern after.
static size_t make_tiff(unsigned char *buf, const unsigned char *profile, int be)
A TIFF shaped buffer with the profile in tag 34675.
static void png_chunk(unsigned char *buf, size_t *len, const char *type, const unsigned char *body, size_t n)
A PNG chunk, CRC left zero: nothing here checks it.
static void segment(unsigned char *buf, size_t *len, unsigned marker, const unsigned char *body, size_t n)
A JPEG APP segment.
static void icc_chunk(unsigned char *buf, size_t *len, int seq, int of, const unsigned char *part, size_t n)
One ICC chunk of a JPEG.
static void be32(unsigned char *p, size_t v)
A big endian 32 bit value.
#define PROFILE_LEN
the test profile's length, odd so the WebP padding is exercised
static size_t make_webp(unsigned char *buf, const unsigned char *profile)
A WebP shaped buffer: VP8X, ICCP (odd length, padded), VP8L.
static void add(unsigned char *buf, size_t *len, const void *data, size_t n)
Append bytes to a growing buffer.
static void check(const char *what, int ok)
Count and print a failed condition.
static size_t make_jpeg(unsigned char *buf, const unsigned char *profile, int chunks, int padding)
A JPEG shaped buffer: SOI, an APP0, padding segments, the profile in two chunks, second first,...
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NULL
no log output
int n_icc_from_file(const char *path, unsigned char **icc, size_t *icc_len)
Find the profile embedded in an image file.
int n_icc_from_buffer(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
Find the profile embedded in an image file held in memory.
int n_icc_is_profile(const unsigned char *data, size_t len)
Does a buffer look like an ICC profile: at least a header, a size field no larger than the buffer,...
Common headers and low-level functions & define.
Finding the ICC colour profile embedded in an image file.
static uint32_t le32(const unsigned char *p)
helper: a little endian 32 bit value