diff --git a/docs/decode.md b/docs/decode.md index b487725..b0cf4d3 100644 --- a/docs/decode.md +++ b/docs/decode.md @@ -322,6 +322,7 @@ If the image is not interlaced this function's behavior is identical to | `SPNG_IMG_COMPRESSION_LEVEL` | `-1` | May expose an estimate (0-9) after `spng_decode_image()` | | `SPNG_IMG_WINDOW_BITS` | `15`* | Set zlib window bits used for image decompression | | `SPNG_CHUNK_COUNT_LIMIT` | `1000` | Limit shared by both known and unknown chunks | +| `SPNG_CHECK_PALETTE_INDEX` | `0` | Check image for invalid palette indices | \* Option may be optimized if not set explicitly. diff --git a/docs/encode.md b/docs/encode.md index fe57ab2..4f4fcfa 100644 --- a/docs/encode.md +++ b/docs/encode.md @@ -186,18 +186,19 @@ On success the buffer must be freed by the user. # Encode options -| Option | Default value | Description | -|----------------------------------|---------------------------|-----------------------------------| -| `SPNG_IMG_COMPRESSION_LEVEL` | `Z_DEFAULT_COMPRESSION` | Set image compression level (0-9) | -| `SPNG_IMG_WINDOW_BITS` | `15`* | Set image zlib window bits (9-15) | -| `SPNG_IMG_MEM_LEVEL` | `8` | Set zlib `memLevel` for images | -| `SPNG_IMG_COMPRESSION_STRATEGY` | `Z_FILTERED`* | Set image compression strategy | -| `SPNG_TEXT_COMPRESSION_LEVEL` | `Z_DEFAULT_COMPRESSION` | Set text compression level (0-9) | -| `SPNG_TEXT_WINDOW_BITS` | `15` | Set text zlib window bits (9-15) | -| `SPNG_TEXT_MEM_LEVEL` | `8` | Set zlib `memLevel` for text | -| `SPNG_TEXT_COMPRESSION_STRATEGY` | `Z_DEFAULT_STRATEGY` | Set text compression strategy | -| `SPNG_FILTER_CHOICE` | `SPNG_FILTER_CHOICE_ALL`* | Configure or disable filtering | -| `SPNG_ENCODE_TO_BUFFER` | `0` | Encode to internal buffer | +| Option | Default value | Description | +|----------------------------------|---------------------------|-----------------------------------------| +| `SPNG_IMG_COMPRESSION_LEVEL` | `Z_DEFAULT_COMPRESSION` | Set image compression level (0-9) | +| `SPNG_IMG_WINDOW_BITS` | `15`* | Set image zlib window bits (9-15) | +| `SPNG_IMG_MEM_LEVEL` | `8` | Set zlib `memLevel` for images | +| `SPNG_IMG_COMPRESSION_STRATEGY` | `Z_FILTERED`* | Set image compression strategy | +| `SPNG_TEXT_COMPRESSION_LEVEL` | `Z_DEFAULT_COMPRESSION` | Set text compression level (0-9) | +| `SPNG_TEXT_WINDOW_BITS` | `15` | Set text zlib window bits (9-15) | +| `SPNG_TEXT_MEM_LEVEL` | `8` | Set zlib `memLevel` for text | +| `SPNG_TEXT_COMPRESSION_STRATEGY` | `Z_DEFAULT_STRATEGY` | Set text compression strategy | +| `SPNG_FILTER_CHOICE` | `SPNG_FILTER_CHOICE_ALL`* | Configure or disable filtering | +| `SPNG_ENCODE_TO_BUFFER` | `0` | Encode to internal buffer | +| `SPNG_CHECK_PALETTE_INDEX` | `1` | Check image for invalid palette indices | \* Option may be optimized if not set explicitly. diff --git a/docs/migrate-libpng.md b/docs/migrate-libpng.md index b1b3cd6..5a7124a 100644 --- a/docs/migrate-libpng.md +++ b/docs/migrate-libpng.md @@ -389,3 +389,4 @@ Filter choice flags for [`spng_set_option()`](context.md#spng_set_option) when u | `png_write_chunk_start()` | N/A | Not supported | | `png_write_chunk_data()` | N/A | Not supported | | `png_write_chunk_end()` | N/A | Not supported | +| `png_set_check_for_invalid_index()` | `spng_set_option()` | `SPNG_CHECK_PALETTE_INDEX` | \ No newline at end of file diff --git a/spng/spng.c b/spng/spng.c index 277e595..8635fb9 100644 --- a/spng/spng.c +++ b/spng/spng.c @@ -263,6 +263,7 @@ struct spng_ctx unsigned streaming: 1; unsigned internal_buffer: 1; /* encoding to internal buffer */ + unsigned check_indices: 1; unsigned inflate: 1; unsigned deflate: 1; @@ -1782,6 +1783,33 @@ static inline void gamma_correct_row(unsigned char *row, uint32_t pixels, int fm } } +static int check_indices(unsigned char *scanline, uint32_t width, unsigned bit_depth, uint32_t n_entries) +{ + int i; + unsigned int max = n_entries - 1; + + if(bit_depth < 8) + { + struct spng__iter iter = spng__iter_init(bit_depth, scanline); + + for(i=0; i < width; i++) + { + uint8_t sample = get_sample(&iter); + + if(sample > max) return SPNG_EPLTE_IDX; + } + } + else + { + for(i=0; i < width; i++) + { + if(scanline[i] > max) return SPNG_EPLTE_IDX; + } + } + + return 0; +} + /* Apply transparency to output row */ static inline void trns_row(unsigned char *row, const unsigned char *scanline, @@ -3332,6 +3360,13 @@ int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len) scanline = ctx->scanline; + if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED && ctx->check_indices) + { + ret = check_indices(scanline, scanline_width, ihdr->bit_depth, ctx->plte.n_entries); + + if(ret) return decode_err(ctx, ret); + } + for(k=0; k < width; k++) { pixel = (unsigned char*)out + pixel_offset; @@ -4575,6 +4610,13 @@ static int encode_scanline(spng_ctx *ctx, const void *scanline, size_t len) /* encode_row() interlaces directly to ctx->scanline */ if(scanline != ctx->scanline) memcpy(ctx->scanline, scanline, scanline_width - 1); + if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED && ctx->check_indices) + { + ret = check_indices(ctx->scanline, scanline_width, ctx->ihdr.bit_depth, ctx->plte.n_entries); + + if(ret) return encode_err(ctx, ret); + } + if(f.to_bigendian) u16_row_to_bigendian(ctx->scanline, scanline_width - 1); const int requires_previous = f.filter_choice & (SPNG_FILTER_CHOICE_UP | SPNG_FILTER_CHOICE_AVG | SPNG_FILTER_CHOICE_PAETH); @@ -5272,6 +5314,15 @@ int spng_set_option(spng_ctx *ctx, enum spng_option option, int value) break; } + case SPNG_CHECK_PALETTE_INDEX: + { + if(value < 0) return 1; + + if(!value) ctx->check_indices = 0; + else ctx->check_indices = 1; + + break; + } default: return 1; } @@ -5350,6 +5401,13 @@ int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value) break; } + case SPNG_CHECK_PALETTE_INDEX: + { + if(ctx->check_indices) *value = 1; + else *value = 0; + + break; + } default: return 1; } diff --git a/spng/spng.h b/spng/spng.h index 5937d6c..a6593a3 100644 --- a/spng/spng.h +++ b/spng/spng.h @@ -409,6 +409,7 @@ enum spng_option SPNG_FILTER_CHOICE, SPNG_CHUNK_COUNT_LIMIT, SPNG_ENCODE_TO_BUFFER, + SPNG_CHECK_PALETTE_INDEX, }; typedef void* SPNG_CDECL spng_malloc_fn(size_t size);