39#define N_ICC_MIN_BYTES 132u
47#define N_ICC_FIRST_READ (256u * 1024u)
50#define N_ICC_TIFF_TAG 34675
53static const char n_icc_jpeg_id[12] = {
'I',
'C',
'C',
'_',
'P',
'R',
'O',
'F',
'I',
'L',
'E', 0};
56static size_t _be16(
const unsigned char* p) {
57 return ((
size_t)p[0] << 8) | (size_t)p[1];
61static size_t _be32(
const unsigned char* p) {
62 return ((
size_t)p[0] << 24) | ((size_t)p[1] << 16) | ((
size_t)p[2] << 8) | (size_t)p[3];
66static size_t _le32(
const unsigned char* p) {
67 return ((
size_t)p[3] << 24) | ((size_t)p[2] << 16) | ((
size_t)p[1] << 8) | (size_t)p[0];
75 declared =
_be32(data);
76 if (declared < N_ICC_MIN_BYTES || declared > len)
78 return memcmp(data + 36,
"acsp", 4) == 0;
83static int _give(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
84 unsigned char* copy = NULL;
87 n_log(
LOG_ERR,
"icc: an embedded profile of %zu bytes is not a valid profile", len);
93 memcpy(copy, data, len);
100static int _from_jpeg(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
102 const unsigned char* chunk[256];
103 size_t chunk_len[256];
104 size_t at = 2, total = 0;
105 int count = 0, seen = 0, n = 0;
106 unsigned char* out = NULL;
110 memset(chunk, 0,
sizeof(chunk));
111 memset(chunk_len, 0,
sizeof(chunk_len));
112 while (at + 4 <= len) {
116 if (data[at] != 0xFF) {
122 while (at < len && data[at] == 0xFF)
128 if (marker == 0xDA || marker == 0xD9) {
133 if (marker == 0x01 || (marker >= 0xD0 && marker <= 0xD7))
137 seglen =
_be16(data + at);
142 if (at + seglen > len)
144 if (marker == 0xE2 && seglen >= 2 + 12 + 2 && memcmp(data + at + 2,
n_icc_jpeg_id, 12) == 0) {
145 int seq = data[at + 14], of = data[at + 15];
147 if (seq < 1 || of < 1 || seq > of || (count && of != count) || chunk[seq]) {
148 n_log(
LOG_ERR,
"icc: JPEG profile chunks do not agree");
152 chunk[seq] = data + at + 16;
153 chunk_len[seq] = seglen - 16;
154 total += chunk_len[seq];
159 if (!complete && seen != count)
164 n_log(
LOG_ERR,
"icc: JPEG profile has %d of %d chunks", seen, count);
167 out = malloc(total ? total : 1);
171 for (n = 1; n <= count; n++) {
172 memcpy(out + total, chunk[n], chunk_len[n]);
173 total += chunk_len[n];
176 n_log(
LOG_ERR,
"icc: the JPEG's profile chunks do not make a profile");
186static int _from_png(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
189 while (at + 12 <= len) {
190 size_t clen =
_be32(data + at);
191 const unsigned char*
type = data + at + 4;
192 const unsigned char* body = data + at + 8;
194 if (clen > len - at - 12)
196 if (memcmp(
type,
"IDAT", 4) == 0 || memcmp(
type,
"IEND", 4) == 0)
198 if (memcmp(
type,
"iCCP", 4) == 0) {
201 unsigned char* out = NULL;
202 size_t room = 64u * 1024u;
207 while (name < clen && name < 80 && body[name])
209 if (name == 0 || name >= 80 || name + 2 > clen || body[name + 1] != 0) {
210 n_log(
LOG_ERR,
"icc: a PNG iCCP chunk with a broken header");
213 memset(&zip, 0,
sizeof(zip));
214 if (inflateInit(&zip) != Z_OK)
221 zip.next_in = (
unsigned char*)(body + name + 2);
222 zip.avail_in = (unsigned)(clen - name - 2);
223 while (zerr == Z_OK) {
224 if (zip.total_out == room) {
225 unsigned char* more = NULL;
229 more = realloc(out, room * 2u);
235 zip.next_out = out + zip.total_out;
236 zip.avail_out = (unsigned)(room - zip.total_out);
237 zerr = inflate(&zip, Z_NO_FLUSH);
241 n_log(
LOG_ERR,
"icc: a PNG iCCP chunk that does not inflate to a profile");
246 *icc_len = zip.total_out;
255static int _from_webp(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
258 while (at + 8 <= len) {
259 size_t clen =
_le32(data + at + 4);
261 if (clen > len - at - 8)
264 if (memcmp(data + at,
"VP8 ", 4) == 0 || memcmp(data + at,
"VP8L", 4) == 0 ||
265 memcmp(data + at,
"ANIM", 4) == 0)
267 if (memcmp(data + at,
"ICCP", 4) == 0)
268 return _give(data + at + 8, clen, icc, icc_len);
270 at += 8 + clen + (clen & 1u);
276static int _from_tiff(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
285 if (entry && (entry->
type == 7 || entry->
type == 1))
293static int _from_buffer(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
294 static const unsigned char png_sig[8] = {0x89,
'P',
'N',
'G', 0x0D, 0x0A, 0x1A, 0x0A};
298 if (len >= 4 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF)
300 if (len >= 8 && memcmp(data, png_sig, 8) == 0)
301 return _from_png(data, len, icc, icc_len);
302 if (len >= 12 && memcmp(data,
"RIFF", 4) == 0 && memcmp(data + 8,
"WEBP", 4) == 0)
304 if (len >= 8 && ((data[0] ==
'I' && data[1] ==
'I') || (data[0] ==
'M' && data[1] ==
'M')))
309int n_icc_from_buffer(
const unsigned char* data,
size_t len,
unsigned char** icc,
size_t* icc_len) {
322static int _tiff_file(FILE* file,
unsigned char** icc,
size_t* icc_len) {
323 unsigned char header[8], entry[12];
325 size_t ifd = 0, count = 0, i = 0;
327 if (fseek(file, 0, SEEK_SET) != 0 || fread(header, 1, 8, file) != 8)
329 big = header[0] ==
'M';
330 ifd = big ?
_be32(header + 4) :
_le32(header + 4);
331 if (fseek(file, (
long)ifd, SEEK_SET) != 0 || fread(entry, 1, 2, file) != 2)
333 count = big ?
_be16(entry) : (size_t)entry[0] | ((
size_t)entry[1] << 8);
336 for (i = 0; i < count; i++) {
337 size_t tag = 0,
type = 0, n = 0, offset = 0;
338 unsigned char* out = NULL;
340 if (fread(entry, 1, 12, file) != 12)
342 tag = big ?
_be16(entry) : (size_t)entry[0] | ((
size_t)entry[1] << 8);
345 type = big ?
_be16(entry + 2) : (size_t)entry[2] | ((
size_t)entry[3] << 8);
346 n = big ?
_be32(entry + 4) :
_le32(entry + 4);
347 offset = big ?
_be32(entry + 8) :
_le32(entry + 8);
353 if (fseek(file, (
long)offset, SEEK_SET) != 0 || fread(out, 1, n, file) != n ||
355 n_log(
LOG_ERR,
"icc: the TIFF's profile tag does not hold a profile");
368 unsigned char* head = NULL;
377 file = fopen(path,
"rb");
384 unsigned char* more = realloc(head, room);
391 got += fread(head + got, 1, room - got, file);
392 int at_end = got < room;
393 if (got >= 8 && ((head[0] ==
'I' && head[1] ==
'I') || (head[0] ==
'M' && head[1] ==
'M'))) {
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
#define __n_assert(__ptr, __ret)
macro to assert things
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
#define N_ICC_MAX_BYTES
Largest profile returned.
#define N_ICC_HEAD_BYTES
Bytes of a file n_icc_from_file() reads to look for a JPEG, PNG or WebP profile, which those formats ...
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,...
size_t offset
where the values start in the buffer, already checked to hold them
uint16_t type
TIFF field type, 1 BYTE to 12 DOUBLE.
uint32_t count
number of values
N_TIFF * n_tiff_parse(const unsigned char *data, size_t len)
Parse the first directory of a TIFF structured buffer.
const N_TIFF_ENTRY * n_tiff_find(const N_TIFF *tiff, uint16_t tag)
Find an entry by tag.
void n_tiff_free(N_TIFF **tiff)
Release a parse, and set the pointer to NULL.
The first directory of a TIFF structured buffer.
Common headers and low-level functions & define.
static int _from_webp(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
helper: the profile of a WebP, from its ICCP chunk
static size_t _le32(const unsigned char *p)
helper: a little endian 32 bit value
static int _from_buffer(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
helper: n_icc_from_buffer(), with N_ICC_MORE when the bytes ran out before the answer
static int _from_tiff(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
helper: the profile of a TIFF held whole in memory
static int _from_png(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
helper: the profile of a PNG, from its iCCP chunk, inflated
static size_t _be16(const unsigned char *p)
helper: a big endian 16 bit value
#define N_ICC_TIFF_TAG
TIFF tag of an embedded ICC profile.
static const char n_icc_jpeg_id[12]
JPEG APP2 identifier of a profile chunk, terminator included.
static int _from_jpeg(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
helper: the profile of a JPEG, from its APP2 chunks put back in order
#define N_ICC_FIRST_READ
Bytes n_icc_from_file() reads first; a profile, when there is one, is almost always in them.
#define N_ICC_MORE
What a parser answers when it ran off the end of the bytes it was given before it could say: the file...
static int _give(const unsigned char *data, size_t len, unsigned char **icc, size_t *icc_len)
helper: hand a found profile back, a copy of len bytes at data, checked
static int _tiff_file(FILE *file, unsigned char **icc, size_t *icc_len)
helper: read the TIFF profile of a file where its directory says, so a large TIFF is not read whole t...
#define N_ICC_MIN_BYTES
Smallest ICC profile: its 128 byte header and a tag count.
static size_t _be32(const unsigned char *p)
helper: a big endian 32 bit value
Finding the ICC colour profile embedded in an image file.
Reading the tags of a TIFF structured buffer.