Skip to content

Commit f2c3362

Browse files
committed
unknown chunk support, tests & docs
1 parent 0303358 commit f2c3362

File tree

8 files changed

+183
-8
lines changed

8 files changed

+183
-8
lines changed

docs/chunk.md

+18
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,21 @@ Set image offset
310310
int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif)
311311
```
312312
Set EXIF data
313+
314+
# spng_get_unknown_chunks()
315+
```c
316+
int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks)
317+
```
318+
319+
Copies unknown chunk information to `chunks`.
320+
321+
`n_chunks` should be greater than or equal to the number of stored unknown chunks.
322+
323+
If `chunks` is NULL and `n_chunks` is non-NULL then `n_chunks` is set to the number
324+
of stored chunks.
325+
326+
!!! note
327+
To retrieve all unknown chunks call this functions after `spng_decode_image()`.
328+
329+
!!! warning
330+
Chunk data is freed when calling `spng_ctx_free()`.

spng/spng.c

+91-1
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ struct spng_chunk_bitfield
171171
unsigned time: 1;
172172
unsigned offs: 1;
173173
unsigned exif: 1;
174+
unsigned unknown: 1;
174175
};
175176

176177
/* Packed sample iterator */
@@ -272,6 +273,9 @@ struct spng_ctx
272273
struct spng_offs offs;
273274
struct spng_exif exif;
274275

276+
uint32_t n_chunks;
277+
struct spng_unknown_chunk *chunk_list;
278+
275279
struct spng_subimage subimage[7];
276280

277281
z_stream zstream;
@@ -1696,6 +1700,20 @@ static void text_undo(spng_ctx *ctx)
16961700
ctx->n_text--;
16971701
}
16981702

1703+
static void chunk_undo(spng_ctx *ctx)
1704+
{
1705+
struct spng_unknown_chunk *chunk = &ctx->chunk_list[ctx->n_chunks - 1];
1706+
1707+
spng__free(ctx, chunk->data);
1708+
1709+
decrease_cache_usage(ctx, chunk->length);
1710+
decrease_cache_usage(ctx, sizeof(struct spng_unknown_chunk));
1711+
1712+
chunk->data = NULL;
1713+
1714+
ctx->n_chunks--;
1715+
}
1716+
16991717
static int read_non_idat_chunks(spng_ctx *ctx)
17001718
{
17011719
int ret;
@@ -2389,6 +2407,48 @@ static int read_non_idat_chunks(spng_ctx *ctx)
23892407

23902408
ctx->stored.splt = 1;
23912409
}
2410+
else /* Unknown chunk */
2411+
{
2412+
ctx->file.unknown = 1;
2413+
2414+
if(ctx->user.unknown) goto discard;
2415+
2416+
if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_unknown_chunk))) return SPNG_ECHUNK_LIMITS;
2417+
2418+
ctx->n_chunks++;
2419+
if(ctx->n_chunks < 1) return SPNG_EOVERFLOW;
2420+
if(sizeof(struct spng_unknown_chunk) > SIZE_MAX / ctx->n_chunks) return SPNG_EOVERFLOW;
2421+
2422+
void *buf = spng__realloc(ctx, ctx->chunk_list, ctx->n_chunks * sizeof(struct spng_unknown_chunk));
2423+
if(buf == NULL) return SPNG_EMEM;
2424+
ctx->chunk_list = buf;
2425+
2426+
struct spng_unknown_chunk *chunks = &ctx->chunk_list[ctx->n_chunks - 1];
2427+
2428+
memset(chunks, 0, sizeof(struct spng_unknown_chunk));
2429+
2430+
ctx->undo = chunk_undo;
2431+
2432+
memcpy(chunks->type, chunk.type, 4);
2433+
2434+
if(chunk.length > 0)
2435+
{
2436+
void *t = spng__malloc(ctx, chunk.length);
2437+
if(t == NULL) return SPNG_EMEM;
2438+
2439+
ret = read_chunk_bytes2(ctx, t, chunk.length);
2440+
if(ret)
2441+
{
2442+
spng__free(ctx, t);
2443+
return ret;
2444+
}
2445+
2446+
chunks->length = chunk.length;
2447+
chunks->data = t;
2448+
}
2449+
2450+
ctx->stored.unknown = 1;
2451+
}
23922452

23932453
discard:
23942454
ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);
@@ -3321,6 +3381,15 @@ void spng_ctx_free(spng_ctx *ctx)
33213381
spng__free(ctx, ctx->text_list);
33223382
}
33233383

3384+
if(ctx->chunk_list != NULL && !ctx->user.unknown)
3385+
{
3386+
for(i=0; i< ctx->n_chunks; i++)
3387+
{
3388+
spng__free(ctx, ctx->chunk_list[i].data);
3389+
}
3390+
spng__free(ctx, ctx->chunk_list);
3391+
}
3392+
33243393
inflateEnd(&ctx->zstream);
33253394

33263395
spng__free(ctx, ctx->gamma_lut16);
@@ -4092,6 +4161,27 @@ int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif)
40924161
return 0;
40934162
}
40944163

4164+
int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks)
4165+
{
4166+
if(ctx == NULL) return 1;
4167+
int ret = read_chunks(ctx, 0);
4168+
if(ret) return ret;
4169+
if(!ctx->stored.unknown) return SPNG_ECHUNKAVAIL;
4170+
if(n_chunks == NULL) return 1;
4171+
4172+
if(chunks == NULL)
4173+
{
4174+
*n_chunks = ctx->n_chunks;
4175+
return 0;
4176+
}
4177+
4178+
if(*n_chunks < ctx->n_chunks) return 1;
4179+
4180+
memcpy(chunks, ctx->chunk_list, sizeof(struct spng_unknown_chunk));
4181+
4182+
return 0;
4183+
}
4184+
40954185
const char *spng_strerror(int err)
40964186
{
40974187
switch(err)
@@ -4185,7 +4275,7 @@ const char *spng_strerror(int err)
41854275

41864276
const char *spng_version_string(void)
41874277
{
4188-
return SPNG_VERSION_STRING "-rc1"
4278+
return SPNG_VERSION_STRING "-rc1";
41894279
}
41904280

41914281
#if defined(_MSC_VER)

spng/spng.h

+9
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,13 @@ struct spng_chunk
348348
uint32_t crc;
349349
};
350350

351+
struct spng_unknown_chunk
352+
{
353+
uint8_t type[4];
354+
size_t length;
355+
void *data;
356+
};
357+
351358
typedef void* spng_malloc_fn(size_t size);
352359
typedef void* spng_realloc_fn(void* ptr, size_t size);
353360
typedef void* spng_calloc_fn(size_t count, size_t size);
@@ -441,6 +448,8 @@ SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time);
441448
SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs);
442449
SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif);
443450

451+
SPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks);
452+
444453
SPNG_API const char *spng_strerror(int err);
445454
SPNG_API const char *spng_version_string(void);
446455

tests/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ test('cpp_test', cpp_exe)
3131

3232
subdir('images')
3333
subdir('crashers')
34+
subdir('misc')
3435

3536
if get_option('oss_fuzz') == false
3637
subdir_done()

tests/misc/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test('unknown_chunk', test_exe, args : files('unknown.png'))

tests/misc/unknown.png

164 Bytes
Loading

tests/test_png.h

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ png_structp init_libpng(FILE *file, int flags, png_infop *iptr)
3939
return NULL;
4040
}
4141

42+
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
43+
4244
png_read_info(png_ptr, info_ptr);
4345

4446
*iptr = info_ptr;

tests/testsuite.c

+61-7
Original file line numberDiff line numberDiff line change
@@ -370,19 +370,19 @@ static int compare_chunks(spng_ctx *ctx, png_infop info_ptr, png_structp png_ptr
370370
spngt_chunk_bitfield spng_have = { 0 };
371371
spngt_chunk_bitfield png_have = { 0 };
372372

373+
int i, ret = 0;
373374
struct spng_plte plte;
374375
struct spng_trns trns;
375376
struct spng_chrm chrm;
376-
struct spng_chrm_int chrm_int;
377377
double gamma;
378378
struct spng_iccp iccp;
379379
struct spng_sbit sbit;
380380
uint8_t srgb_rendering_intent;
381-
struct spng_text *text;
381+
struct spng_text *text = NULL;
382382
struct spng_bkgd bkgd;
383383
struct spng_hist hist;
384384
struct spng_phys phys;
385-
struct spng_splt *splt;
385+
struct spng_splt *splt = NULL;
386386
struct spng_time time;
387387
uint32_t n_text = 0, n_splt = 0;
388388
struct spng_offs offs;
@@ -404,9 +404,9 @@ static int compare_chunks(spng_ctx *ctx, png_infop info_ptr, png_structp png_ptr
404404
if(!spng_get_offs(ctx, &offs)) spng_have.offs = 1;
405405
if(!spng_get_exif(ctx, &exif)) spng_have.exif = 1;
406406

407-
png_text *libpng_text;
407+
png_text *png_text;
408408
int png_n_text;
409-
png_sPLT_tp png_splt_entries;
409+
png_sPLT_t *png_splt_entries;
410410

411411
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) png_have.plte = 1;
412412
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_have.trns = 1;
@@ -415,7 +415,7 @@ static int compare_chunks(spng_ctx *ctx, png_infop info_ptr, png_structp png_ptr
415415
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) png_have.iccp = 1;
416416
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) png_have.sbit = 1;
417417
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) png_have.srgb = 1;
418-
if(png_get_text(png_ptr, info_ptr, &libpng_text, &png_n_text)) png_have.text = 1;
418+
if(png_get_text(png_ptr, info_ptr, &png_text, &png_n_text)) png_have.text = 1;
419419
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) png_have.bkgd = 1;
420420
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_hIST)) png_have.hist = 1;
421421
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) png_have.phys = 1;
@@ -447,7 +447,59 @@ static int compare_chunks(spng_ctx *ctx, png_infop info_ptr, png_structp png_ptr
447447
return 1;
448448
}
449449

450-
return 0;
450+
if(n_text)
451+
{
452+
text = malloc(n_text * sizeof(struct spng_text));
453+
if(!text) return 1;
454+
455+
spng_get_text(ctx, text, &n_text);
456+
457+
for(i=0; i < n_text; i++)
458+
{
459+
if((strcmp(png_text[i].text, text[i].text)))
460+
{
461+
printf("text[%d]: text mismatch!\nspng: %s\n\nlibpng: %s\n", i, text[i].text, png_text[i].text);
462+
ret = 1;
463+
}
464+
}
465+
}
466+
467+
uint32_t n_spng_chunks = 0;
468+
struct spng_unknown_chunk *spng_chunks = NULL;
469+
470+
int n_png_chunks;
471+
png_unknown_chunkp png_chunks;
472+
473+
if(!spng_get_unknown_chunks(ctx, NULL, &n_spng_chunks))
474+
{
475+
spng_chunks = malloc(n_spng_chunks * sizeof(struct spng_unknown_chunk));
476+
spng_get_unknown_chunks(ctx, spng_chunks, &n_spng_chunks);
477+
}
478+
479+
n_png_chunks = png_get_unknown_chunks(png_ptr, info_ptr, &png_chunks);
480+
481+
if(n_png_chunks != n_spng_chunks)
482+
{
483+
printf("unknown chunk count mismatch: %u(spng), %d(libpng)\n", n_spng_chunks, n_png_chunks);
484+
ret = 1;
485+
goto cleanup;
486+
}
487+
488+
for(i=0; i < n_spng_chunks; i++)
489+
{
490+
if(spng_chunks[i].length != png_chunks[i].size)
491+
{
492+
printf("chunk[%d]: size mismatch %" PRIu64 "(spng) %" PRIu64" (libpng)\n", i, spng_chunks[i].length, png_chunks[i].size);
493+
ret = 1;
494+
}
495+
}
496+
497+
cleanup:
498+
free(splt);
499+
free(text);
500+
free(spng_chunks);
501+
502+
return ret;
451503
}
452504

453505
static int decode_and_compare(const char *filename, int fmt, int flags, int test_flags)
@@ -670,6 +722,8 @@ int main(int argc, char **argv)
670722
it emulates the behavior of their old PNG loader which used libpng. */
671723
add_test_case(SPNGT_FMT_VIPS, SPNG_DECODE_TRNS, 0);
672724

725+
printf("%d test cases\n", n_test_cases);
726+
673727
int ret = 0;
674728
int i;
675729
for(i=0; i < n_test_cases; i++)

0 commit comments

Comments
 (0)