Table of Contents
- examples/ex_icc.c
- Functions
- Attributes
- Defines
- Functions Documentation
- function segment
- function png_chunk
- function make_webp
- function make_tiff
- function make_profile
- function make_png
- function make_jpeg
- function main
- function le32
- function icc_chunk
- function expect
- function check
- function be32
- function add
- Attributes Documentation
- Macros Documentation
- Source code
examples/ex_icc.c
Functions
| Name | |
|---|---|
| void | segment(unsigned char * buf, size_t * len, unsigned marker, const unsigned char * body, size_t n) A JPEG APP segment. |
| 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. |
| size_t | make_webp(unsigned char * buf, const unsigned char * profile) A WebP shaped buffer: VP8X, ICCP (odd length, padded), VP8L. |
| size_t | make_tiff(unsigned char * buf, const unsigned char * profile, int be) A TIFF shaped buffer with the profile in tag 34675. |
| void | make_profile(unsigned char * out, int ok) A profile shaped buffer: its size, "acsp" at 36, a pattern after. |
| size_t | make_png(unsigned char * buf, const unsigned char * profile, int after) A PNG shaped buffer with the profile in iCCP, before or after IDAT. |
| 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, and a start of scan. |
| int | main(int argc, char ** argv) run the checks, or report a file's profile |
| void | le32(unsigned char * p, size_t v) A little endian 32 bit value. |
| 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. |
| void | expect(const char * what, const unsigned char * buf, size_t len, const unsigned char * want, int expect_result) Find a profile in a buffer, and in the same bytes as a file. |
| void | check(const char * what, int ok) Count and print a failed condition. |
| void | be32(unsigned char * p, size_t v) A big endian 32 bit value. |
| void | add(unsigned char * buf, size_t * len, const void * data, size_t n) Append bytes to a growing buffer. |
Attributes
| Name | |
|---|---|
| int | failures failures counted by check() |
Defines
| Name | |
|---|---|
| PROFILE_LEN the test profile's length, odd so the WebP padding is exercised |
Functions Documentation
function segment
static void segment(
unsigned char * buf,
size_t * len,
unsigned marker,
const unsigned char * body,
size_t n
)
A JPEG APP segment.
Parameters:
- buf the buffer
- len its length, updated
- marker the marker byte, 0xE0 to 0xEF
- body the segment's contents
- n their length
function png_chunk
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.
Parameters:
- buf the buffer
- len its length, updated
- type the four letters
- body the contents
- n their length
function make_webp
static size_t make_webp(
unsigned char * buf,
const unsigned char * profile
)
A WebP shaped buffer: VP8X, ICCP (odd length, padded), VP8L.
Parameters:
- buf receives it
- profile the profile
Return: its length
function make_tiff
static size_t make_tiff(
unsigned char * buf,
const unsigned char * profile,
int be
)
A TIFF shaped buffer with the profile in tag 34675.
Parameters:
- buf receives it
- profile the profile
- be non-zero for big endian
Return: its length
function make_profile
static void make_profile(
unsigned char * out,
int ok
)
A profile shaped buffer: its size, "acsp" at 36, a pattern after.
Parameters:
- out receives PROFILE_LEN bytes
- ok zero to leave the signature out
function make_png
static size_t make_png(
unsigned char * buf,
const unsigned char * profile,
int after
)
A PNG shaped buffer with the profile in iCCP, before or after IDAT.
Parameters:
- buf receives it
- profile the profile
- after non-zero to put the iCCP after the image data, where it no longer counts
Return: its length
function make_jpeg
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, and a start of scan.
Parameters:
- buf receives it
- profile the profile, or NULL for none
- chunks 2 for both chunks, 1 to leave the second out
- padding APP1 segments of 60000 bytes before the profile
Return: its length
function main
int main(
int argc,
char ** argv
)
run the checks, or report a file's profile
Parameters:
- argc argument count
- argv arguments
Return: 0 on success
-V LEVEL is accepted and ignored, as run_tests.sh passes it to every example; it used to be taken for the name of a file.
function le32
static void le32(
unsigned char * p,
size_t v
)
A little endian 32 bit value.
Parameters:
- p where
- v the value
function icc_chunk
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.
Parameters:
- buf the buffer
- len its length, updated
- seq its number
- of how many
- part its share of the profile
- n that share's length
function expect
static void expect(
const char * what,
const unsigned char * buf,
size_t len,
const unsigned char * want,
int expect_result
)
Find a profile in a buffer, and in the same bytes as a file.
Parameters:
- what the case
- buf the bytes
- len how many
- want the profile expected, or NULL for none
- expect_result what n_icc_from_buffer() should answer
function check
static void check(
const char * what,
int ok
)
Count and print a failed condition.
Parameters:
- what what was checked
- ok the condition
function be32
static void be32(
unsigned char * p,
size_t v
)
A big endian 32 bit value.
Parameters:
- p where
- v the value
function add
static void add(
unsigned char * buf,
size_t * len,
const void * data,
size_t n
)
Append bytes to a growing buffer.
Parameters:
- buf the buffer
- len its length, updated
- data what to add
- n how many
Attributes Documentation
variable failures
static int failures = 0;
failures counted by check()
Macros Documentation
define PROFILE_LEN
#define PROFILE_LEN 301
the test profile's length, odd so the WebP padding is exercised
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_common.h"
#include "nilorea/n_icc.h"
#include "nilorea/n_log.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
static int failures = 0;
static void check(const char* what, int ok) {
if (!ok) {
failures++;
fprintf(stderr, "FAIL %s\n", what);
}
}
#define PROFILE_LEN 301
static void make_profile(unsigned char* out, int ok) {
int i = 0;
for (i = 0; i < PROFILE_LEN; i++)
out[i] = (unsigned char)(i * 7 + 3);
out[0] = 0;
out[1] = 0;
out[2] = (unsigned char)(PROFILE_LEN >> 8);
out[3] = (unsigned char)(PROFILE_LEN & 0xff);
memcpy(out + 36, ok ? "acsp" : "nope", 4);
}
static void add(unsigned char* buf, size_t* len, const void* data, size_t n) {
memcpy(buf + *len, data, n);
*len += n;
}
static void segment(unsigned char* buf, size_t* len, unsigned marker, const unsigned char* body, size_t n) {
unsigned char head[4];
head[0] = 0xFF;
head[1] = (unsigned char)marker;
head[2] = (unsigned char)((n + 2) >> 8);
head[3] = (unsigned char)((n + 2) & 0xff);
add(buf, len, head, 4);
add(buf, len, body, n);
}
static void icc_chunk(unsigned char* buf, size_t* len, int seq, int of, const unsigned char* part, size_t n) {
unsigned char body[16 + PROFILE_LEN];
memcpy(body, "ICC_PROFILE", 12);
body[12] = (unsigned char)seq;
body[13] = (unsigned char)of;
memcpy(body + 14, part, n);
segment(buf, len, 0xE2, body, n + 14);
}
static size_t make_jpeg(unsigned char* buf, const unsigned char* profile, int chunks, int padding) {
static unsigned char filler[60000];
const unsigned char soi[2] = {0xFF, 0xD8}, sos[4] = {0xFF, 0xDA, 0x00, 0x02};
size_t len = 0;
int i = 0;
add(buf, &len, soi, 2);
segment(buf, &len, 0xE0, (const unsigned char*)"JFIF\0\1\1\0\0\1\0\1\0\0", 14);
for (i = 0; i < padding; i++)
segment(buf, &len, 0xE1, filler, sizeof(filler));
if (profile) {
if (chunks == 2)
icc_chunk(buf, &len, 2, 2, profile + 100, PROFILE_LEN - 100);
icc_chunk(buf, &len, 1, 2, profile, 100);
}
add(buf, &len, sos, 4);
return len;
}
static void be32(unsigned char* p, size_t v) {
p[0] = (unsigned char)(v >> 24);
p[1] = (unsigned char)(v >> 16);
p[2] = (unsigned char)(v >> 8);
p[3] = (unsigned char)v;
}
static void png_chunk(unsigned char* buf, size_t* len, const char* type, const unsigned char* body, size_t n) {
unsigned char head[8], crc[4] = {0, 0, 0, 0};
be32(head, n);
memcpy(head + 4, type, 4);
add(buf, len, head, 8);
add(buf, len, body, n);
add(buf, len, crc, 4);
}
static size_t make_png(unsigned char* buf, const unsigned char* profile, int after) {
static const unsigned char sig[8] = {0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A};
unsigned char ihdr[13], iccp[64 + 2 * PROFILE_LEN], idat[4] = {0, 0, 0, 0};
uLongf packed = 2 * PROFILE_LEN;
size_t len = 0;
memset(ihdr, 0, sizeof(ihdr));
memcpy(iccp, "Test profile", 13);
iccp[13] = 0;
compress2(iccp + 14, &packed, profile, PROFILE_LEN, 9);
add(buf, &len, sig, 8);
png_chunk(buf, &len, "IHDR", ihdr, 13);
if (!after)
png_chunk(buf, &len, "iCCP", iccp, 14 + packed);
png_chunk(buf, &len, "IDAT", idat, 4);
if (after)
png_chunk(buf, &len, "iCCP", iccp, 14 + packed);
png_chunk(buf, &len, "IEND", idat, 0);
return len;
}
static void le32(unsigned char* p, size_t v) {
p[0] = (unsigned char)v;
p[1] = (unsigned char)(v >> 8);
p[2] = (unsigned char)(v >> 16);
p[3] = (unsigned char)(v >> 24);
}
static size_t make_webp(unsigned char* buf, const unsigned char* profile) {
unsigned char head[8], vp8x[10], pad = 0;
size_t len = 0;
memset(vp8x, 0, sizeof(vp8x));
add(buf, &len, "RIFF\0\0\0\0WEBP", 12);
memcpy(head, "VP8X", 4);
le32(head + 4, sizeof(vp8x));
add(buf, &len, head, 8);
add(buf, &len, vp8x, sizeof(vp8x));
memcpy(head, "ICCP", 4);
le32(head + 4, PROFILE_LEN);
add(buf, &len, head, 8);
add(buf, &len, profile, PROFILE_LEN);
add(buf, &len, &pad, 1);
memcpy(head, "VP8L", 4);
le32(head + 4, 0);
add(buf, &len, head, 8);
le32(buf + 4, len - 8);
return len;
}
static size_t make_tiff(unsigned char* buf, const unsigned char* profile, int be) {
/* Header, then one directory of one entry, then the profile. */
size_t len = 8 + 2 + 12 + 4;
int i = 0;
memset(buf, 0, len);
buf[0] = buf[1] = be ? 'M' : 'I';
if (be) {
buf[3] = 42;
be32(buf + 4, 8);
buf[9] = 1;
buf[10] = 0x87;
buf[11] = 0x73;
buf[13] = 7;
be32(buf + 14, PROFILE_LEN);
be32(buf + 18, len);
} else {
buf[2] = 42;
le32(buf + 4, 8);
buf[8] = 1;
buf[10] = 0x73;
buf[11] = 0x87;
buf[12] = 7;
le32(buf + 14, PROFILE_LEN);
le32(buf + 18, len);
}
for (i = 0; i < PROFILE_LEN; i++)
buf[len + (size_t)i] = profile[i];
return len + PROFILE_LEN;
}
static void expect(const char* what, const unsigned char* buf, size_t len, const unsigned char* want, int expect_result) {
unsigned char* icc = NULL;
size_t icc_len = 0;
int got = n_icc_from_buffer(buf, len, &icc, &icc_len);
char label[160];
FILE* file = NULL;
snprintf(label, sizeof(label), "%s: from a buffer", what);
check(label, got == expect_result &&
(!want || (icc && icc_len == PROFILE_LEN && memcmp(icc, want, PROFILE_LEN) == 0)) &&
(want || !icc));
free(icc);
icc = NULL;
file = fopen("ex_icc.tmp", "wb");
if (file) {
fwrite(buf, 1, len, file);
fclose(file);
got = n_icc_from_file("ex_icc.tmp", &icc, &icc_len);
snprintf(label, sizeof(label), "%s: from a file", what);
check(label, got == expect_result &&
(!want || (icc && icc_len == PROFILE_LEN && memcmp(icc, want, PROFILE_LEN) == 0)));
free(icc);
remove("ex_icc.tmp");
}
}
int main(int argc, char** argv) {
unsigned char profile[PROFILE_LEN], bad[PROFILE_LEN];
unsigned char* buf = malloc(1024 * 1024);
size_t len = 0;
int opt = 0;
set_log_level(LOG_NULL);
if (!buf)
return 1;
while ((opt = getopt(argc, argv, "V:")) != -1) {
if (opt != 'V') {
fprintf(stderr, "Usage: %s [-V LOG_LEVEL] [file]\n", argv[0]);
free(buf);
return 2;
}
}
if (optind < argc) {
const char* path = argv[optind];
unsigned char* icc = NULL;
size_t icc_len = 0;
int got = n_icc_from_file(path, &icc, &icc_len);
if (got == 1)
printf("%s: a profile of %zu bytes\n", path, icc_len);
else
printf("%s: %s\n", path, got == 0 ? "no profile" : "a broken profile, or unreadable");
free(icc);
free(buf);
return got < 0 ? 1 : 0;
}
make_profile(profile, 1);
make_profile(bad, 0);
check("a profile is a profile", n_icc_is_profile(profile, PROFILE_LEN));
check("one without its signature is not", !n_icc_is_profile(bad, PROFILE_LEN));
check("nor one shorter than it says", !n_icc_is_profile(profile, PROFILE_LEN - 1));
len = make_jpeg(buf, profile, 2, 0);
expect("JPEG, chunks out of order", buf, len, profile, 1);
len = make_jpeg(buf, profile, 2, 6);
expect("JPEG, the profile past the first read", buf, len, profile, 1);
len = make_jpeg(buf, NULL, 0, 0);
expect("JPEG without a profile", buf, len, NULL, 0);
len = make_jpeg(buf, profile, 1, 0);
expect("JPEG with a chunk missing", buf, len, NULL, -1);
len = make_jpeg(buf, profile, 2, 0);
/* Cut inside the first chunk: only the second is whole. */
expect("a JPEG cut short", buf, len - 60, NULL, 0);
len = make_png(buf, profile, 0);
expect("PNG", buf, len, profile, 1);
len = make_png(buf, profile, 1);
expect("PNG with iCCP after the image data", buf, len, NULL, 0);
len = make_webp(buf, profile);
expect("WebP", buf, len, profile, 1);
len = make_tiff(buf, profile, 0);
expect("TIFF little endian", buf, len, profile, 1);
len = make_tiff(buf, profile, 1);
expect("TIFF big endian", buf, len, profile, 1);
len = make_tiff(buf, bad, 0);
expect("TIFF with a broken profile", buf, len, NULL, -1);
check("a buffer of another kind has none",
n_icc_from_buffer((const unsigned char*)"GIF89a..", 8, &(unsigned char*){NULL}, &(size_t){0}) == 0);
check("a missing file is an error", n_icc_from_file("/nonexistent/ex_icc", &(unsigned char*){NULL}, &(size_t){0}) == -1);
free(buf);
if (failures) {
fprintf(stderr, "ex_icc: %d failure(s)\n", failures);
return 1;
}
printf("ex_icc: ok\n");
return 0;
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB