Upgrading lodepng, QOI, and stb_image.h/stb_image_write.h, and adding the pvpng reader from basisu for benchmarking/comparison purposes
This commit is contained in:
@@ -68,6 +68,7 @@ set(FPNG_SRC_LIST ${COMMON_SRC_LIST}
|
|||||||
src/fpng.cpp
|
src/fpng.cpp
|
||||||
src/fpng_test.cpp
|
src/fpng_test.cpp
|
||||||
src/lodepng.cpp
|
src/lodepng.cpp
|
||||||
|
src/pvpngreader.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
@@ -147,6 +147,7 @@
|
|||||||
<ClCompile Include="src/fpng.cpp" />
|
<ClCompile Include="src/fpng.cpp" />
|
||||||
<ClCompile Include="src/fpng_test.cpp" />
|
<ClCompile Include="src/fpng_test.cpp" />
|
||||||
<ClCompile Include="src/lodepng.cpp" />
|
<ClCompile Include="src/lodepng.cpp" />
|
||||||
|
<ClCompile Include="src\pvpngreader.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src/fpng.h" />
|
<ClInclude Include="src/fpng.h" />
|
||||||
@@ -154,6 +155,8 @@
|
|||||||
<ClInclude Include="src/qoi.h" />
|
<ClInclude Include="src/qoi.h" />
|
||||||
<ClInclude Include="src/stb_image.h" />
|
<ClInclude Include="src/stb_image.h" />
|
||||||
<ClInclude Include="src/stb_image_write.h" />
|
<ClInclude Include="src/stb_image_write.h" />
|
||||||
|
<ClInclude Include="src\basisu_miniz.h" />
|
||||||
|
<ClInclude Include="src\pvpngreader.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
@@ -24,6 +24,9 @@
|
|||||||
<ClCompile Include="src/lodepng.cpp">
|
<ClCompile Include="src/lodepng.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\pvpngreader.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src/fpng.h">
|
<ClInclude Include="src/fpng.h">
|
||||||
@@ -41,5 +44,11 @@
|
|||||||
<ClInclude Include="src/lodepng.h">
|
<ClInclude Include="src/lodepng.h">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\pvpngreader.h">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\basisu_miniz.h">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -27,6 +27,9 @@
|
|||||||
#define WUFFS_CONFIG__STATIC_FUNCTIONS
|
#define WUFFS_CONFIG__STATIC_FUNCTIONS
|
||||||
#include "wuffs-v0.3.c"
|
#include "wuffs-v0.3.c"
|
||||||
|
|
||||||
|
#include "basisu_miniz.h"
|
||||||
|
#include "pvpngreader.h"
|
||||||
|
|
||||||
typedef std::vector<uint8_t> uint8_vec;
|
typedef std::vector<uint8_t> uint8_vec;
|
||||||
|
|
||||||
typedef uint64_t timer_ticks;
|
typedef uint64_t timer_ticks;
|
||||||
@@ -1228,7 +1231,7 @@ int main(int arg_c, char **arg_v)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
double fpng_decode_time = 0.0f, lodepng_decode_time = 0.0f, stbi_decode_time = 0.0f, qoi_decode_time = 0.0f, wuffs_decode_time = 0.0f;
|
double fpng_decode_time = 0.0f, lodepng_decode_time = 0.0f, stbi_decode_time = 0.0f, qoi_decode_time = 0.0f, wuffs_decode_time = 0.0f, pvpng_decode_time = 0.0f;
|
||||||
|
|
||||||
// Decode the file using our decompressor
|
// Decode the file using our decompressor
|
||||||
{
|
{
|
||||||
@@ -1542,21 +1545,65 @@ int main(int arg_c, char **arg_v)
|
|||||||
|
|
||||||
// Validate QOI's output file
|
// Validate QOI's output file
|
||||||
{
|
{
|
||||||
|
qoi_decode_time = 1e+9f;
|
||||||
|
|
||||||
qoi_desc qddesc;
|
qoi_desc qddesc;
|
||||||
tm.start();
|
for (uint32_t i = 0; i < NUM_TIMES_TO_ENCODE; i++)
|
||||||
void* pQOI_decomp_data = qoi_decode(pQOI_data, qoi_len, &qddesc, 4);
|
|
||||||
qoi_decode_time = tm.get_elapsed_secs();
|
|
||||||
|
|
||||||
if (memcmp(pQOI_decomp_data, pSource_pixels32, total_source_pixels * 4) != 0)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "QOI verification failure!\n");
|
tm.start();
|
||||||
return EXIT_FAILURE;
|
void* pQOI_decomp_data = qoi_decode(pQOI_data, qoi_len, &qddesc, 4);
|
||||||
|
|
||||||
|
qoi_decode_time = minimum(qoi_decode_time, tm.get_elapsed_secs());
|
||||||
|
|
||||||
|
if (memcmp(pQOI_decomp_data, pSource_pixels32, total_source_pixels * 4) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "QOI verification failure!\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pQOI_decomp_data);
|
||||||
}
|
}
|
||||||
free(pQOI_decomp_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(pQOI_data);
|
free(pQOI_data);
|
||||||
pQOI_data = nullptr;
|
pQOI_data = nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Decode the PNG file using pvpng, which ships with BasisU and uses miniz for decompression.
|
||||||
|
|
||||||
|
pvpng_decode_time = 1e+9f;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < NUM_TIMES_TO_ENCODE; i++)
|
||||||
|
{
|
||||||
|
uint32_t width = 0, height = 0, num_chans = 0;
|
||||||
|
|
||||||
|
tm.start();
|
||||||
|
|
||||||
|
void* pImage_data = pv_png::load_png(fpng_file_buf.data(), fpng_file_buf.size(), source_chans, width, height, num_chans);
|
||||||
|
|
||||||
|
pvpng_decode_time = minimum(pvpng_decode_time, tm.get_elapsed_secs());
|
||||||
|
|
||||||
|
if (!pImage_data)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed decoding using pvpng! (1)\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((num_chans != source_chans) || (width != source_width) || (height != source_height))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed decoding using pvpng! (2)\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp((source_chans == 3) ? (const void*)pSource_pixels24 : (const void*)pSource_pixels32, pImage_data, width * height * source_chans) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed decoding using pvpng! (3)\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pImage_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!csv_flag)
|
if (!csv_flag)
|
||||||
{
|
{
|
||||||
@@ -1565,6 +1612,7 @@ int main(int arg_c, char **arg_v)
|
|||||||
printf("lodepng: %3.6f secs, %4.3f MP/sec\n", lodepng_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / lodepng_decode_time);
|
printf("lodepng: %3.6f secs, %4.3f MP/sec\n", lodepng_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / lodepng_decode_time);
|
||||||
printf("stbi: %3.6f secs, %4.3f MP/sec\n", stbi_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / stbi_decode_time);
|
printf("stbi: %3.6f secs, %4.3f MP/sec\n", stbi_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / stbi_decode_time);
|
||||||
printf("wuffs: %3.6f secs, %4.3f MP/sec\n", wuffs_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / wuffs_decode_time);
|
printf("wuffs: %3.6f secs, %4.3f MP/sec\n", wuffs_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / wuffs_decode_time);
|
||||||
|
printf("pvpng: %3.6f secs, %4.3f MP/sec\n", pvpng_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / pvpng_decode_time);
|
||||||
printf("qoi: %3.6f secs, %4.3f MP/sec\n", qoi_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / qoi_decode_time);
|
printf("qoi: %3.6f secs, %4.3f MP/sec\n", qoi_decode_time, (total_source_pixels / (1024.0f * 1024.0f)) / qoi_decode_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1574,12 +1622,13 @@ int main(int arg_c, char **arg_v)
|
|||||||
|
|
||||||
const double source_megapixels = total_source_pixels / (1024.0f * 1024.0f);
|
const double source_megapixels = total_source_pixels / (1024.0f * 1024.0f);
|
||||||
|
|
||||||
printf("%s, %u, %u, %u, %f, %f, %f, %4.1f, %4.1f, %f, %f, %f, %4.1f, %4.1f, %f, %f, %f, %4.1f, %4.1f, %f, %f, %f, %4.1f, %4.1f\n",
|
printf("%s, %u, %u, %u, %f, %f, %f, %4.3f, %4.3f, %f, %f, %f, %4.3f, %4.3f, %f, %f, %f, %4.3f, %4.3f, %f, %f, %f, %4.3f, %4.3f, %4.3f, %4.3f\n",
|
||||||
pFilename, source_width, source_height, source_chans,
|
pFilename, source_width, source_height, source_chans,
|
||||||
qoi_best_time, (double)qoi_len / MB, qoi_decode_time, source_megapixels / qoi_best_time, source_megapixels / qoi_decode_time,
|
qoi_best_time, (double)qoi_len / MB, qoi_decode_time, source_megapixels / qoi_best_time, source_megapixels / qoi_decode_time,
|
||||||
fpng_best_time, (double)fpng_file_buf.size() / MB, fpng_decode_time, source_megapixels / fpng_best_time, source_megapixels / fpng_decode_time,
|
fpng_best_time, (double)fpng_file_buf.size() / MB, fpng_decode_time, source_megapixels / fpng_best_time, source_megapixels / fpng_decode_time,
|
||||||
lodepng_best_time, (double)lodepng_file_buf.size() / MB, lodepng_decode_time, source_megapixels / lodepng_best_time, source_megapixels / lodepng_decode_time,
|
lodepng_best_time, (double)lodepng_file_buf.size() / MB, lodepng_decode_time, source_megapixels / lodepng_best_time, source_megapixels / lodepng_decode_time,
|
||||||
stbi_best_time, (double)stbi_file_buf.size() / MB, stbi_decode_time, source_megapixels / stbi_best_time, source_megapixels / stbi_decode_time
|
stbi_best_time, (double)stbi_file_buf.size() / MB, stbi_decode_time, source_megapixels / stbi_best_time, source_megapixels / stbi_decode_time,
|
||||||
|
pvpng_decode_time, source_megapixels / pvpng_decode_time
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
888
src/lodepng.cpp
888
src/lodepng.cpp
File diff suppressed because it is too large
Load Diff
112
src/lodepng.h
112
src/lodepng.h
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
LodePNG version 20210627
|
LodePNG version 20230410
|
||||||
|
|
||||||
Copyright (c) 2005-2021 Lode Vandevenne
|
Copyright (c) 2005-2023 Lode Vandevenne
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
This software is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the authors be held liable for any damages
|
warranty. In no event will the authors be held liable for any damages
|
||||||
@@ -35,43 +35,50 @@ The following #defines are used to create code sections. They can be disabled
|
|||||||
to disable code sections, which can give faster compile time and smaller binary.
|
to disable code sections, which can give faster compile time and smaller binary.
|
||||||
The "NO_COMPILE" defines are designed to be used to pass as defines to the
|
The "NO_COMPILE" defines are designed to be used to pass as defines to the
|
||||||
compiler command to disable them without modifying this header, e.g.
|
compiler command to disable them without modifying this header, e.g.
|
||||||
-DLODEPNG_NO_COMPILE_ZLIB for gcc.
|
-DLODEPNG_NO_COMPILE_ZLIB for gcc or clang.
|
||||||
In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to
|
|
||||||
allow implementing a custom lodepng_crc32.
|
|
||||||
*/
|
*/
|
||||||
/*deflate & zlib. If disabled, you must specify alternative zlib functions in
|
/*deflate & zlib. If disabled, you must specify alternative zlib functions in
|
||||||
the custom_zlib field of the compress and decompress settings*/
|
the custom_zlib field of the compress and decompress settings*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_ZLIB
|
#ifndef LODEPNG_NO_COMPILE_ZLIB
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_ZLIB to the compiler to disable this, or comment out LODEPNG_COMPILE_ZLIB below*/
|
||||||
#define LODEPNG_COMPILE_ZLIB
|
#define LODEPNG_COMPILE_ZLIB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*png encoder and png decoder*/
|
/*png encoder and png decoder*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_PNG
|
#ifndef LODEPNG_NO_COMPILE_PNG
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_PNG to the compiler to disable this, or comment out LODEPNG_COMPILE_PNG below*/
|
||||||
#define LODEPNG_COMPILE_PNG
|
#define LODEPNG_COMPILE_PNG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*deflate&zlib decoder and png decoder*/
|
/*deflate&zlib decoder and png decoder*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_DECODER
|
#ifndef LODEPNG_NO_COMPILE_DECODER
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_DECODER to the compiler to disable this, or comment out LODEPNG_COMPILE_DECODER below*/
|
||||||
#define LODEPNG_COMPILE_DECODER
|
#define LODEPNG_COMPILE_DECODER
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*deflate&zlib encoder and png encoder*/
|
/*deflate&zlib encoder and png encoder*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_ENCODER
|
#ifndef LODEPNG_NO_COMPILE_ENCODER
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_ENCODER to the compiler to disable this, or comment out LODEPNG_COMPILE_ENCODER below*/
|
||||||
#define LODEPNG_COMPILE_ENCODER
|
#define LODEPNG_COMPILE_ENCODER
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*the optional built in harddisk file loading and saving functions*/
|
/*the optional built in harddisk file loading and saving functions*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_DISK
|
#ifndef LODEPNG_NO_COMPILE_DISK
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_DISK to the compiler to disable this, or comment out LODEPNG_COMPILE_DISK below*/
|
||||||
#define LODEPNG_COMPILE_DISK
|
#define LODEPNG_COMPILE_DISK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
|
/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
|
#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS to the compiler to disable this,
|
||||||
|
or comment out LODEPNG_COMPILE_ANCILLARY_CHUNKS below*/
|
||||||
#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*ability to convert error numerical codes to English text string*/
|
/*ability to convert error numerical codes to English text string*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
|
#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_ERROR_TEXT to the compiler to disable this,
|
||||||
|
or comment out LODEPNG_COMPILE_ERROR_TEXT below*/
|
||||||
#define LODEPNG_COMPILE_ERROR_TEXT
|
#define LODEPNG_COMPILE_ERROR_TEXT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -79,12 +86,27 @@ the custom_zlib field of the compress and decompress settings*/
|
|||||||
you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
|
you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
|
||||||
source files with custom allocators.*/
|
source files with custom allocators.*/
|
||||||
#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
|
#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler to disable the built-in ones,
|
||||||
|
or comment out LODEPNG_COMPILE_ALLOCATORS below*/
|
||||||
#define LODEPNG_COMPILE_ALLOCATORS
|
#define LODEPNG_COMPILE_ALLOCATORS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*Disable built-in CRC function, in that case a custom implementation of
|
||||||
|
lodepng_crc32 must be defined externally so that it can be linked in.
|
||||||
|
The default built-in CRC code comes with 8KB of lookup tables, so for memory constrained environment you may want it
|
||||||
|
disabled and provide a much smaller implementation externally as said above. You can find such an example implementation
|
||||||
|
in a comment in the lodepng.c(pp) file in the 'else' case of the searchable LODEPNG_COMPILE_CRC section.*/
|
||||||
|
#ifndef LODEPNG_NO_COMPILE_CRC
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_CRC to the compiler to disable the built-in one,
|
||||||
|
or comment out LODEPNG_COMPILE_CRC below*/
|
||||||
|
#define LODEPNG_COMPILE_CRC
|
||||||
|
#endif
|
||||||
|
|
||||||
/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
|
/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#ifndef LODEPNG_NO_COMPILE_CPP
|
#ifndef LODEPNG_NO_COMPILE_CPP
|
||||||
|
/*pass -DLODEPNG_NO_COMPILE_CPP to the compiler to disable C++ (not needed if a C-only compiler),
|
||||||
|
or comment out LODEPNG_COMPILE_CPP below*/
|
||||||
#define LODEPNG_COMPILE_CPP
|
#define LODEPNG_COMPILE_CPP
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -374,8 +396,10 @@ typedef struct LodePNGColorMode {
|
|||||||
|
|
||||||
The alpha channels must be set as well, set them to 255 for opaque images.
|
The alpha channels must be set as well, set them to 255 for opaque images.
|
||||||
|
|
||||||
When decoding, by default you can ignore this palette, since LodePNG already
|
When decoding, with the default settings you can ignore this palette, since
|
||||||
fills the palette colors in the pixels of the raw RGBA output.
|
LodePNG already fills the palette colors in the pixels of the raw RGBA output,
|
||||||
|
but when decoding to the original PNG color mode it is needed to reconstruct
|
||||||
|
the colors.
|
||||||
|
|
||||||
The palette is only supported for color type 3.
|
The palette is only supported for color type 3.
|
||||||
*/
|
*/
|
||||||
@@ -465,10 +489,12 @@ typedef struct LodePNGInfo {
|
|||||||
with values truncated to the bit depth in the unsigned integer.
|
with values truncated to the bit depth in the unsigned integer.
|
||||||
|
|
||||||
For grayscale and palette PNGs, the value is stored in background_r. The values
|
For grayscale and palette PNGs, the value is stored in background_r. The values
|
||||||
in background_g and background_b are then unused.
|
in background_g and background_b are then unused. The decoder will set them
|
||||||
|
equal to background_r, the encoder ignores them in this case.
|
||||||
|
|
||||||
So when decoding, you may get these in a different color mode than the one you requested
|
When decoding, you may get these in a different color mode than the one you requested
|
||||||
for the raw pixels.
|
for the raw pixels: the colortype and bitdepth defined by info_png.color, that is the
|
||||||
|
ones defined in the header of the PNG image, are used.
|
||||||
|
|
||||||
When encoding with auto_convert, you must use the color model defined in info_png.color for
|
When encoding with auto_convert, you must use the color model defined in info_png.color for
|
||||||
these values. The encoder normally ignores info_png.color when auto_convert is on, but will
|
these values. The encoder normally ignores info_png.color when auto_convert is on, but will
|
||||||
@@ -535,7 +561,7 @@ typedef struct LodePNGInfo {
|
|||||||
unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
|
unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Color profile related chunks: gAMA, cHRM, sRGB, iCPP
|
Color profile related chunks: gAMA, cHRM, sRGB, iCPP, sBIT
|
||||||
|
|
||||||
LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
|
LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
|
||||||
profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
|
profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
|
||||||
@@ -598,6 +624,45 @@ typedef struct LodePNGInfo {
|
|||||||
unsigned char* iccp_profile;
|
unsigned char* iccp_profile;
|
||||||
unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
|
unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
|
||||||
|
|
||||||
|
/*
|
||||||
|
sBIT chunk: significant bits. Optional metadata, only set this if needed.
|
||||||
|
|
||||||
|
If defined, these values give the bit depth of the original data. Since PNG only stores 1, 2, 4, 8 or 16-bit
|
||||||
|
per channel data, the significant bits value can be used to indicate the original encoded data has another
|
||||||
|
sample depth, such as 10 or 12.
|
||||||
|
|
||||||
|
Encoders using this value, when storing the pixel data, should use the most significant bits
|
||||||
|
of the data to store the original bits, and use a good sample depth scaling method such as
|
||||||
|
"left bit replication" to fill in the least significant bits, rather than fill zeroes.
|
||||||
|
|
||||||
|
Decoders using this value, if able to work with data that's e.g. 10-bit or 12-bit, should right
|
||||||
|
shift the data to go back to the original bit depth, but decoders are also allowed to ignore
|
||||||
|
sbit and work e.g. with the 8-bit or 16-bit data from the PNG directly, since thanks
|
||||||
|
to the encoder contract, the values encoded in PNG are in valid range for the PNG bit depth.
|
||||||
|
|
||||||
|
For grayscale images, sbit_g and sbit_b are not used, and for images that don't use color
|
||||||
|
type RGBA or grayscale+alpha, sbit_a is not used (it's not used even for palette images with
|
||||||
|
translucent palette values, or images with color key). The values that are used must be
|
||||||
|
greater than zero and smaller than or equal to the PNG bit depth.
|
||||||
|
|
||||||
|
The color type from the header in the PNG image defines these used and unused fields: if
|
||||||
|
decoding with a color mode conversion, such as always decoding to RGBA, this metadata still
|
||||||
|
only uses the color type of the original PNG, and may e.g. lack the alpha channel info
|
||||||
|
if the PNG was RGB. When encoding with auto_convert (as well as without), also always the
|
||||||
|
color model defined in info_png.color determines this.
|
||||||
|
|
||||||
|
NOTE: enabling sbit can hurt compression, because the encoder can then not always use
|
||||||
|
auto_convert to choose a more optimal color mode for the data, because the PNG format has
|
||||||
|
strict requirements for the allowed sbit values in combination with color modes.
|
||||||
|
For example, setting these fields to 10-bit will force the encoder to keep using a 16-bit per channel
|
||||||
|
color mode, even if the pixel data would in fact fit in a more efficient 8-bit mode.
|
||||||
|
*/
|
||||||
|
unsigned sbit_defined; /*is significant bits given? if not, the values below are unused*/
|
||||||
|
unsigned sbit_r; /*red or gray component of significant bits*/
|
||||||
|
unsigned sbit_g; /*green component of significant bits*/
|
||||||
|
unsigned sbit_b; /*blue component of significant bits*/
|
||||||
|
unsigned sbit_a; /*alpha component of significant bits*/
|
||||||
|
|
||||||
/* End of color profile related chunks */
|
/* End of color profile related chunks */
|
||||||
|
|
||||||
|
|
||||||
@@ -770,7 +835,11 @@ typedef struct LodePNGEncoderSettings {
|
|||||||
const unsigned char* predefined_filters;
|
const unsigned char* predefined_filters;
|
||||||
|
|
||||||
/*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
|
/*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
|
||||||
If colortype is 3, PLTE is _always_ created.*/
|
If colortype is 3, PLTE is always created. If color type is explicitely set
|
||||||
|
to a grayscale type (1 or 4), this is not done and is ignored. If enabling this,
|
||||||
|
a palette must be present in the info_png.
|
||||||
|
NOTE: enabling this may worsen compression if auto_convert is used to choose
|
||||||
|
optimal color mode, because it cannot use grayscale color modes in this case*/
|
||||||
unsigned force_palette;
|
unsigned force_palette;
|
||||||
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
|
||||||
/*add LodePNG identifier and version as a text chunk, for debugging*/
|
/*add LodePNG identifier and version as a text chunk, for debugging*/
|
||||||
@@ -824,8 +893,8 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h,
|
|||||||
#endif /*LODEPNG_COMPILE_DECODER*/
|
#endif /*LODEPNG_COMPILE_DECODER*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it
|
Reads one metadata chunk (other than IHDR, which is handled by lodepng_inspect)
|
||||||
read in the state. Returns error code on failure.
|
of the PNG file and outputs what it read in the state. Returns error code on failure.
|
||||||
Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
|
Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
|
||||||
to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
|
to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
|
||||||
chunk_pointer - start_of_file as pos).
|
chunk_pointer - start_of_file as pos).
|
||||||
@@ -1103,7 +1172,7 @@ TODO:
|
|||||||
[.] check compatibility with various compilers - done but needs to be redone for every newer version
|
[.] check compatibility with various compilers - done but needs to be redone for every newer version
|
||||||
[X] converting color to 16-bit per channel types
|
[X] converting color to 16-bit per channel types
|
||||||
[X] support color profile chunk types (but never let them touch RGB values by default)
|
[X] support color profile chunk types (but never let them touch RGB values by default)
|
||||||
[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST)
|
[ ] support all public PNG chunk types (almost done except sPLT and hIST)
|
||||||
[ ] make sure encoder generates no chunks with size > (2^31)-1
|
[ ] make sure encoder generates no chunks with size > (2^31)-1
|
||||||
[ ] partial decoding (stream processing)
|
[ ] partial decoding (stream processing)
|
||||||
[X] let the "isFullyOpaque" function check color keys and transparent palettes too
|
[X] let the "isFullyOpaque" function check color keys and transparent palettes too
|
||||||
@@ -1230,18 +1299,16 @@ The following features are supported by the decoder:
|
|||||||
gAMA: RGB gamma correction
|
gAMA: RGB gamma correction
|
||||||
iCCP: ICC color profile
|
iCCP: ICC color profile
|
||||||
sRGB: rendering intent
|
sRGB: rendering intent
|
||||||
|
sBIT: significant bits
|
||||||
|
|
||||||
1.2. features not supported
|
1.2. features not supported
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
The following features are _not_ supported:
|
The following features are not (yet) supported:
|
||||||
|
|
||||||
*) some features needed to make a conformant PNG-Editor might be still missing.
|
*) some features needed to make a conformant PNG-Editor might be still missing.
|
||||||
*) partial loading/stream processing. All data must be available and is processed in one call.
|
*) partial loading/stream processing. All data must be available and is processed in one call.
|
||||||
*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG:
|
*) The hIST and sPLT public chunks are not (yet) supported but treated as unknown chunks
|
||||||
sBIT
|
|
||||||
hIST
|
|
||||||
sPLT
|
|
||||||
|
|
||||||
|
|
||||||
2. C and C++ version
|
2. C and C++ version
|
||||||
@@ -1845,6 +1912,9 @@ symbol.
|
|||||||
Not all changes are listed here, the commit history in github lists more:
|
Not all changes are listed here, the commit history in github lists more:
|
||||||
https://github.com/lvandeve/lodepng
|
https://github.com/lvandeve/lodepng
|
||||||
|
|
||||||
|
*) 10 apr 2023: faster CRC32 implementation, but with larger lookup table.
|
||||||
|
*) 13 jun 2022: added support for the sBIT chunk.
|
||||||
|
*) 09 jan 2022: minor decoder speed improvements.
|
||||||
*) 27 jun 2021: added warnings that file reading/writing functions don't support
|
*) 27 jun 2021: added warnings that file reading/writing functions don't support
|
||||||
wide-character filenames (support for this is not planned, opening files is
|
wide-character filenames (support for this is not planned, opening files is
|
||||||
not the core part of PNG decoding/decoding and is platform dependent).
|
not the core part of PNG decoding/decoding and is platform dependent).
|
||||||
@@ -2015,5 +2085,5 @@ Domain: gmail dot com.
|
|||||||
Account: lode dot vandevenne.
|
Account: lode dot vandevenne.
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) 2005-2021 Lode Vandevenne
|
Copyright (c) 2005-2022 Lode Vandevenne
|
||||||
*/
|
*/
|
||||||
|
215
src/qoi.h
215
src/qoi.h
@@ -1,39 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
QOI - The "Quite OK Image" format for fast, lossless image compression
|
QOI - The "Quite OK Image" format for fast, lossless image compression
|
||||||
|
|
||||||
Dominic Szablewski - https://phoboslab.org
|
|
||||||
|
|
||||||
|
|
||||||
-- LICENSE: The MIT License(MIT)
|
|
||||||
|
|
||||||
Copyright(c) 2021 Dominic Szablewski
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files(the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions :
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
-- About
|
-- About
|
||||||
|
|
||||||
QOI encodes and decodes images in a lossless format. An encoded QOI image is
|
QOI encodes and decodes images in a lossless format. Compared to stb_image and
|
||||||
usually around 10--30% larger than a decently optimized PNG image.
|
stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and
|
||||||
|
20% better compression.
|
||||||
QOI outperforms simpler PNG encoders in compression ratio and performance. QOI
|
|
||||||
images are typically 20% smaller than PNGs written with stbi_image. Encoding is
|
|
||||||
25-50x faster and decoding is 3-4x faster than stbi_image or libpng.
|
|
||||||
|
|
||||||
|
|
||||||
-- Synopsis
|
-- Synopsis
|
||||||
@@ -48,7 +25,7 @@ images are typically 20% smaller than PNGs written with stbi_image. Encoding is
|
|||||||
// the input pixel data.
|
// the input pixel data.
|
||||||
qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
|
qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
|
||||||
.width = 1920,
|
.width = 1920,
|
||||||
.height = 1080,
|
.height = 1080,
|
||||||
.channels = 4,
|
.channels = 4,
|
||||||
.colorspace = QOI_SRGB
|
.colorspace = QOI_SRGB
|
||||||
});
|
});
|
||||||
@@ -77,14 +54,14 @@ QOI_NO_STDIO before including this library.
|
|||||||
This library uses malloc() and free(). To supply your own malloc implementation
|
This library uses malloc() and free(). To supply your own malloc implementation
|
||||||
you can define QOI_MALLOC and QOI_FREE before including this library.
|
you can define QOI_MALLOC and QOI_FREE before including this library.
|
||||||
|
|
||||||
This library uses memset() to zero-initialize the index. To supply your own
|
This library uses memset() to zero-initialize the index. To supply your own
|
||||||
implementation you can define QOI_ZEROARR before including this library.
|
implementation you can define QOI_ZEROARR before including this library.
|
||||||
|
|
||||||
|
|
||||||
-- Data Format
|
-- Data Format
|
||||||
|
|
||||||
A QOI file has a 14 byte header, followed by any number of data "chunks" and 8
|
A QOI file has a 14 byte header, followed by any number of data "chunks" and an
|
||||||
zero-bytes to mark the end of the data stream.
|
8-byte end marker.
|
||||||
|
|
||||||
struct qoi_header_t {
|
struct qoi_header_t {
|
||||||
char magic[4]; // magic bytes "qoif"
|
char magic[4]; // magic bytes "qoif"
|
||||||
@@ -94,33 +71,36 @@ struct qoi_header_t {
|
|||||||
uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
|
uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
|
||||||
};
|
};
|
||||||
|
|
||||||
The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous
|
Images are encoded row by row, left to right, top to bottom. The decoder and
|
||||||
pixel value. Pixels are either encoded as
|
encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An
|
||||||
|
image is complete when all pixels specified by width * height have been covered.
|
||||||
|
|
||||||
|
Pixels are encoded as
|
||||||
- a run of the previous pixel
|
- a run of the previous pixel
|
||||||
- an index into an array of previously seen pixels
|
- an index into an array of previously seen pixels
|
||||||
- a difference to the previous pixel value in r,g,b
|
- a difference to the previous pixel value in r,g,b
|
||||||
- full r,g,b or r,g,b,a values
|
- full r,g,b or r,g,b,a values
|
||||||
|
|
||||||
The color channels are assumed to not be premultiplied with the alpha channel
|
The color channels are assumed to not be premultiplied with the alpha channel
|
||||||
("un-premultiplied alpha").
|
("un-premultiplied alpha").
|
||||||
|
|
||||||
A running array[64] (zero-initialized) of previously seen pixel values is
|
A running array[64] (zero-initialized) of previously seen pixel values is
|
||||||
maintained by the encoder and decoder. Each pixel that is seen by the encoder
|
maintained by the encoder and decoder. Each pixel that is seen by the encoder
|
||||||
and decoder is put into this array at the position formed by a hash function of
|
and decoder is put into this array at the position formed by a hash function of
|
||||||
the color value. In the encoder, if the pixel value at the index matches the
|
the color value. In the encoder, if the pixel value at the index matches the
|
||||||
current pixel, this index position is written to the stream as QOI_OP_INDEX.
|
current pixel, this index position is written to the stream as QOI_OP_INDEX.
|
||||||
The hash function for the index is:
|
The hash function for the index is:
|
||||||
|
|
||||||
index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
||||||
|
|
||||||
Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
|
Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
|
||||||
bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
|
bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
|
||||||
values encoded in these data bits have the most significant bit on the left.
|
values encoded in these data bits have the most significant bit on the left.
|
||||||
|
|
||||||
The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
|
The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
|
||||||
presence of an 8-bit tag first.
|
presence of an 8-bit tag first.
|
||||||
|
|
||||||
The byte stream is padded with 8 zero-bytes at the end.
|
The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte.
|
||||||
|
|
||||||
|
|
||||||
The possible chunks are:
|
The possible chunks are:
|
||||||
@@ -135,8 +115,11 @@ The possible chunks are:
|
|||||||
2-bit tag b00
|
2-bit tag b00
|
||||||
6-bit index into the color index array: 0..63
|
6-bit index into the color index array: 0..63
|
||||||
|
|
||||||
|
A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the
|
||||||
|
same index. QOI_OP_RUN should be used instead.
|
||||||
|
|
||||||
.- QOI_OP_DIFF -----------.
|
|
||||||
|
.- QOI_OP_DIFF -----------.
|
||||||
| Byte[0] |
|
| Byte[0] |
|
||||||
| 7 6 5 4 3 2 1 0 |
|
| 7 6 5 4 3 2 1 0 |
|
||||||
|-------+-----+-----+-----|
|
|-------+-----+-----+-----|
|
||||||
@@ -147,14 +130,16 @@ The possible chunks are:
|
|||||||
2-bit green channel difference from the previous pixel between -2..1
|
2-bit green channel difference from the previous pixel between -2..1
|
||||||
2-bit blue channel difference from the previous pixel between -2..1
|
2-bit blue channel difference from the previous pixel between -2..1
|
||||||
|
|
||||||
The difference to the current channel values are using a wraparound operation,
|
The difference to the current channel values are using a wraparound operation,
|
||||||
so "1 - 2" will result in 255, while "255 + 1" will result in 0.
|
so "1 - 2" will result in 255, while "255 + 1" will result in 0.
|
||||||
|
|
||||||
Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
|
Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
|
||||||
0 (b00). 1 is stored as 3 (b11).
|
0 (b00). 1 is stored as 3 (b11).
|
||||||
|
|
||||||
|
The alpha value remains unchanged from the previous pixel.
|
||||||
|
|
||||||
.- QOI_OP_LUMA -------------------------------------.
|
|
||||||
|
.- QOI_OP_LUMA -------------------------------------.
|
||||||
| Byte[0] | Byte[1] |
|
| Byte[0] | Byte[1] |
|
||||||
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
|
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
|
||||||
|-------+-----------------+-------------+-----------|
|
|-------+-----------------+-------------+-----------|
|
||||||
@@ -165,18 +150,20 @@ Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
|
|||||||
4-bit red channel difference minus green channel difference -8..7
|
4-bit red channel difference minus green channel difference -8..7
|
||||||
4-bit blue channel difference minus green channel difference -8..7
|
4-bit blue channel difference minus green channel difference -8..7
|
||||||
|
|
||||||
The green channel is used to indicate the general direction of change and is
|
The green channel is used to indicate the general direction of change and is
|
||||||
encoded in 6 bits. The red and green channels (dr and db) base their diffs off
|
encoded in 6 bits. The red and blue channels (dr and db) base their diffs off
|
||||||
of the green channel difference and are encoded in 4 bits. I.e.:
|
of the green channel difference and are encoded in 4 bits. I.e.:
|
||||||
dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g)
|
dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
|
||||||
db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g)
|
db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g)
|
||||||
|
|
||||||
The difference to the current channel values are using a wraparound operation,
|
The difference to the current channel values are using a wraparound operation,
|
||||||
so "10 - 13" will result in 253, while "250 + 7" will result in 1.
|
so "10 - 13" will result in 253, while "250 + 7" will result in 1.
|
||||||
|
|
||||||
Values are stored as unsigned integers with a bias of 32 for the green channel
|
Values are stored as unsigned integers with a bias of 32 for the green channel
|
||||||
and a bias of 8 for the red and blue channel.
|
and a bias of 8 for the red and blue channel.
|
||||||
|
|
||||||
|
The alpha value remains unchanged from the previous pixel.
|
||||||
|
|
||||||
|
|
||||||
.- QOI_OP_RUN ------------.
|
.- QOI_OP_RUN ------------.
|
||||||
| Byte[0] |
|
| Byte[0] |
|
||||||
@@ -187,8 +174,8 @@ and a bias of 8 for the red and blue channel.
|
|||||||
2-bit tag b11
|
2-bit tag b11
|
||||||
6-bit run-length repeating the previous pixel: 1..62
|
6-bit run-length repeating the previous pixel: 1..62
|
||||||
|
|
||||||
The run-length is stored with a bias of 1. Note that the run-lengths 63 and 64
|
The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64
|
||||||
(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
|
(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
|
||||||
QOI_OP_RGBA tags.
|
QOI_OP_RGBA tags.
|
||||||
|
|
||||||
|
|
||||||
@@ -203,6 +190,8 @@ QOI_OP_RGBA tags.
|
|||||||
8-bit green channel value
|
8-bit green channel value
|
||||||
8-bit blue channel value
|
8-bit blue channel value
|
||||||
|
|
||||||
|
The alpha value remains unchanged from the previous pixel.
|
||||||
|
|
||||||
|
|
||||||
.- QOI_OP_RGBA ---------------------------------------------------.
|
.- QOI_OP_RGBA ---------------------------------------------------.
|
||||||
| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
|
| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
|
||||||
@@ -216,13 +205,6 @@ QOI_OP_RGBA tags.
|
|||||||
8-bit blue channel value
|
8-bit blue channel value
|
||||||
8-bit alpha channel value
|
8-bit alpha channel value
|
||||||
|
|
||||||
|
|
||||||
The byte stream is padded at the end with 8 zero bytes. Since the longest legal
|
|
||||||
chunk is 5 bytes (QOI_OP_RGBA), with this padding it is possible to check for an
|
|
||||||
overrun only once per decode loop iteration. These 0x00 bytes also mark the end
|
|
||||||
of the data stream, as an encoder should never produce 8 consecutive zero bytes
|
|
||||||
within the stream.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -236,17 +218,17 @@ Header - Public functions */
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
|
/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
|
||||||
It describes either the input format (for qoi_write and qoi_encode), or is
|
It describes either the input format (for qoi_write and qoi_encode), or is
|
||||||
filled with the description read from the file header (for qoi_read and
|
filled with the description read from the file header (for qoi_read and
|
||||||
qoi_decode).
|
qoi_decode).
|
||||||
|
|
||||||
The colorspace in this qoi_desc is an enum where
|
The colorspace in this qoi_desc is an enum where
|
||||||
0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
|
0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
|
||||||
1 = all channels are linear
|
1 = all channels are linear
|
||||||
You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
|
You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
|
||||||
informative. It will be saved to the file header, but does not affect
|
informative. It will be saved to the file header, but does not affect
|
||||||
en-/decoding in any way. */
|
how chunks are en-/decoded. */
|
||||||
|
|
||||||
#define QOI_SRGB 0
|
#define QOI_SRGB 0
|
||||||
#define QOI_LINEAR 1
|
#define QOI_LINEAR 1
|
||||||
@@ -260,11 +242,11 @@ typedef struct {
|
|||||||
|
|
||||||
#ifndef QOI_NO_STDIO
|
#ifndef QOI_NO_STDIO
|
||||||
|
|
||||||
/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
|
/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
|
||||||
system. The qoi_desc struct must be filled with the image width, height,
|
system. The qoi_desc struct must be filled with the image width, height,
|
||||||
number of channels (3 = RGB, 4 = RGBA) and the colorspace.
|
number of channels (3 = RGB, 4 = RGBA) and the colorspace.
|
||||||
|
|
||||||
The function returns 0 on failure (invalid parameters, or fopen or malloc
|
The function returns 0 on failure (invalid parameters, or fopen or malloc
|
||||||
failed) or the number of bytes written on success. */
|
failed) or the number of bytes written on success. */
|
||||||
|
|
||||||
int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
|
int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
|
||||||
@@ -275,7 +257,7 @@ number of channels from the file header is used. If channels is 3 or 4 the
|
|||||||
output format will be forced into this number of channels.
|
output format will be forced into this number of channels.
|
||||||
|
|
||||||
The function either returns NULL on failure (invalid data, or malloc or fopen
|
The function either returns NULL on failure (invalid data, or malloc or fopen
|
||||||
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
|
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
|
||||||
will be filled with the description from the file header.
|
will be filled with the description from the file header.
|
||||||
|
|
||||||
The returned pixel data should be free()d after use. */
|
The returned pixel data should be free()d after use. */
|
||||||
@@ -287,8 +269,8 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels);
|
|||||||
|
|
||||||
/* Encode raw RGB or RGBA pixels into a QOI image in memory.
|
/* Encode raw RGB or RGBA pixels into a QOI image in memory.
|
||||||
|
|
||||||
The function either returns NULL on failure (invalid parameters or malloc
|
The function either returns NULL on failure (invalid parameters or malloc
|
||||||
failed) or a pointer to the encoded data on success. On success the out_len
|
failed) or a pointer to the encoded data on success. On success the out_len
|
||||||
is set to the size in bytes of the encoded data.
|
is set to the size in bytes of the encoded data.
|
||||||
|
|
||||||
The returned qoi data should be free()d after use. */
|
The returned qoi data should be free()d after use. */
|
||||||
@@ -298,8 +280,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len);
|
|||||||
|
|
||||||
/* Decode a QOI image from memory.
|
/* Decode a QOI image from memory.
|
||||||
|
|
||||||
The function either returns NULL on failure (invalid parameters or malloc
|
The function either returns NULL on failure (invalid parameters or malloc
|
||||||
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
|
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
|
||||||
is filled with the description from the file header.
|
is filled with the description from the file header.
|
||||||
|
|
||||||
The returned pixel data should be free()d after use. */
|
The returned pixel data should be free()d after use. */
|
||||||
@@ -342,21 +324,28 @@ Implementation */
|
|||||||
(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
|
(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
|
||||||
((unsigned int)'i') << 8 | ((unsigned int)'f'))
|
((unsigned int)'i') << 8 | ((unsigned int)'f'))
|
||||||
#define QOI_HEADER_SIZE 14
|
#define QOI_HEADER_SIZE 14
|
||||||
#define QOI_PADDING 8
|
|
||||||
|
/* 2GB is the max file size that this implementation can safely handle. We guard
|
||||||
|
against anything larger than that, assuming the worst case with 5 bytes per
|
||||||
|
pixel, rounded down to a nice clean value. 400 million pixels ought to be
|
||||||
|
enough for anybody. */
|
||||||
|
#define QOI_PIXELS_MAX ((unsigned int)400000000)
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct { unsigned char r, g, b, a; } rgba;
|
struct { unsigned char r, g, b, a; } rgba;
|
||||||
unsigned int v;
|
unsigned int v;
|
||||||
} qoi_rgba_t;
|
} qoi_rgba_t;
|
||||||
|
|
||||||
void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) {
|
static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
|
||||||
|
|
||||||
|
static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) {
|
||||||
bytes[(*p)++] = (0xff000000 & v) >> 24;
|
bytes[(*p)++] = (0xff000000 & v) >> 24;
|
||||||
bytes[(*p)++] = (0x00ff0000 & v) >> 16;
|
bytes[(*p)++] = (0x00ff0000 & v) >> 16;
|
||||||
bytes[(*p)++] = (0x0000ff00 & v) >> 8;
|
bytes[(*p)++] = (0x0000ff00 & v) >> 8;
|
||||||
bytes[(*p)++] = (0x000000ff & v);
|
bytes[(*p)++] = (0x000000ff & v);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int qoi_read_32(const unsigned char *bytes, int *p) {
|
static unsigned int qoi_read_32(const unsigned char *bytes, int *p) {
|
||||||
unsigned int a = bytes[(*p)++];
|
unsigned int a = bytes[(*p)++];
|
||||||
unsigned int b = bytes[(*p)++];
|
unsigned int b = bytes[(*p)++];
|
||||||
unsigned int c = bytes[(*p)++];
|
unsigned int c = bytes[(*p)++];
|
||||||
@@ -376,14 +365,15 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
|||||||
data == NULL || out_len == NULL || desc == NULL ||
|
data == NULL || out_len == NULL || desc == NULL ||
|
||||||
desc->width == 0 || desc->height == 0 ||
|
desc->width == 0 || desc->height == 0 ||
|
||||||
desc->channels < 3 || desc->channels > 4 ||
|
desc->channels < 3 || desc->channels > 4 ||
|
||||||
desc->colorspace > 2
|
desc->colorspace > 1 ||
|
||||||
|
desc->height >= QOI_PIXELS_MAX / desc->width
|
||||||
) {
|
) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
max_size =
|
max_size =
|
||||||
desc->width * desc->height * (desc->channels + 1) +
|
desc->width * desc->height * (desc->channels + 1) +
|
||||||
QOI_HEADER_SIZE + QOI_PADDING;
|
QOI_HEADER_SIZE + sizeof(qoi_padding);
|
||||||
|
|
||||||
p = 0;
|
p = 0;
|
||||||
bytes = (unsigned char *) QOI_MALLOC(max_size);
|
bytes = (unsigned char *) QOI_MALLOC(max_size);
|
||||||
@@ -408,19 +398,18 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
|||||||
px_prev.rgba.b = 0;
|
px_prev.rgba.b = 0;
|
||||||
px_prev.rgba.a = 255;
|
px_prev.rgba.a = 255;
|
||||||
px = px_prev;
|
px = px_prev;
|
||||||
|
|
||||||
px_len = desc->width * desc->height * desc->channels;
|
px_len = desc->width * desc->height * desc->channels;
|
||||||
px_end = px_len - desc->channels;
|
px_end = px_len - desc->channels;
|
||||||
channels = desc->channels;
|
channels = desc->channels;
|
||||||
|
|
||||||
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
||||||
|
px.rgba.r = pixels[px_pos + 0];
|
||||||
|
px.rgba.g = pixels[px_pos + 1];
|
||||||
|
px.rgba.b = pixels[px_pos + 2];
|
||||||
|
|
||||||
if (channels == 4) {
|
if (channels == 4) {
|
||||||
px = *(qoi_rgba_t *)(pixels + px_pos);
|
px.rgba.a = pixels[px_pos + 3];
|
||||||
}
|
|
||||||
else {
|
|
||||||
px.rgba.r = pixels[px_pos + 0];
|
|
||||||
px.rgba.g = pixels[px_pos + 1];
|
|
||||||
px.rgba.b = pixels[px_pos + 2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (px.v == px_prev.v) {
|
if (px.v == px_prev.v) {
|
||||||
@@ -456,14 +445,14 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
vr > -3 && vr < 2 &&
|
vr > -3 && vr < 2 &&
|
||||||
vg > -3 && vg < 2 &&
|
vg > -3 && vg < 2 &&
|
||||||
vb > -3 && vb < 2
|
vb > -3 && vb < 2
|
||||||
) {
|
) {
|
||||||
bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
|
bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
vg_r > -9 && vg_r < 8 &&
|
vg_r > -9 && vg_r < 8 &&
|
||||||
vg > -33 && vg < 32 &&
|
vg > -33 && vg < 32 &&
|
||||||
vg_b > -9 && vg_b < 8
|
vg_b > -9 && vg_b < 8
|
||||||
) {
|
) {
|
||||||
bytes[p++] = QOI_OP_LUMA | (vg + 32);
|
bytes[p++] = QOI_OP_LUMA | (vg + 32);
|
||||||
@@ -488,8 +477,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
|||||||
px_prev = px;
|
px_prev = px;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < QOI_PADDING; i++) {
|
for (i = 0; i < (int)sizeof(qoi_padding); i++) {
|
||||||
bytes[p++] = 0;
|
bytes[p++] = qoi_padding[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_len = p;
|
*out_len = p;
|
||||||
@@ -502,13 +491,13 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
|||||||
unsigned char *pixels;
|
unsigned char *pixels;
|
||||||
qoi_rgba_t index[64];
|
qoi_rgba_t index[64];
|
||||||
qoi_rgba_t px;
|
qoi_rgba_t px;
|
||||||
int px_len, chunks_len, px_pos;
|
int px_len, chunks_len, px_pos;
|
||||||
int p = 0, run = 0;
|
int p = 0, run = 0;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data == NULL || desc == NULL ||
|
data == NULL || desc == NULL ||
|
||||||
(channels != 0 && channels != 3 && channels != 4) ||
|
(channels != 0 && channels != 3 && channels != 4) ||
|
||||||
size < QOI_HEADER_SIZE + QOI_PADDING
|
size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
|
||||||
) {
|
) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -522,10 +511,11 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
|||||||
desc->colorspace = bytes[p++];
|
desc->colorspace = bytes[p++];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
desc->width == 0 || desc->height == 0 ||
|
desc->width == 0 || desc->height == 0 ||
|
||||||
desc->channels < 3 || desc->channels > 4 ||
|
desc->channels < 3 || desc->channels > 4 ||
|
||||||
desc->colorspace > 2 ||
|
desc->colorspace > 1 ||
|
||||||
header_magic != QOI_MAGIC
|
header_magic != QOI_MAGIC ||
|
||||||
|
desc->height >= QOI_PIXELS_MAX / desc->width
|
||||||
) {
|
) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -546,7 +536,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
|||||||
px.rgba.b = 0;
|
px.rgba.b = 0;
|
||||||
px.rgba.a = 255;
|
px.rgba.a = 255;
|
||||||
|
|
||||||
chunks_len = size - QOI_PADDING;
|
chunks_len = size - (int)sizeof(qoi_padding);
|
||||||
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
||||||
if (run > 0) {
|
if (run > 0) {
|
||||||
run--;
|
run--;
|
||||||
@@ -587,13 +577,12 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
|||||||
index[QOI_COLOR_HASH(px) % 64] = px;
|
index[QOI_COLOR_HASH(px) % 64] = px;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channels == 4) {
|
pixels[px_pos + 0] = px.rgba.r;
|
||||||
*(qoi_rgba_t*)(pixels + px_pos) = px;
|
pixels[px_pos + 1] = px.rgba.g;
|
||||||
}
|
pixels[px_pos + 2] = px.rgba.b;
|
||||||
else {
|
|
||||||
pixels[px_pos + 0] = px.rgba.r;
|
if (channels == 4) {
|
||||||
pixels[px_pos + 1] = px.rgba.g;
|
pixels[px_pos + 3] = px.rgba.a;
|
||||||
pixels[px_pos + 2] = px.rgba.b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,11 +605,11 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) {
|
|||||||
if (!encoded) {
|
if (!encoded) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(encoded, 1, size, f);
|
fwrite(encoded, 1, size, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
QOI_FREE(encoded);
|
QOI_FREE(encoded);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -636,6 +625,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
|
|||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
size = ftell(f);
|
size = ftell(f);
|
||||||
|
if (size <= 0) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
data = QOI_MALLOC(size);
|
data = QOI_MALLOC(size);
|
||||||
@@ -653,4 +646,4 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif /* QOI_NO_STDIO */
|
#endif /* QOI_NO_STDIO */
|
||||||
#endif /* QOI_IMPLEMENTATION */
|
#endif /* QOI_IMPLEMENTATION */
|
||||||
|
146
src/stb_image.h
146
src/stb_image.h
@@ -1,4 +1,4 @@
|
|||||||
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
|
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
|
||||||
no warranty implied; use at your own risk
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
Do this:
|
Do this:
|
||||||
@@ -48,6 +48,7 @@ LICENSE
|
|||||||
|
|
||||||
RECENT REVISION HISTORY:
|
RECENT REVISION HISTORY:
|
||||||
|
|
||||||
|
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
|
||||||
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
||||||
2.26 (2020-07-13) many minor fixes
|
2.26 (2020-07-13) many minor fixes
|
||||||
2.25 (2020-02-02) fix warnings
|
2.25 (2020-02-02) fix warnings
|
||||||
@@ -108,7 +109,7 @@ RECENT REVISION HISTORY:
|
|||||||
Cass Everitt Ryamond Barbiero github:grim210
|
Cass Everitt Ryamond Barbiero github:grim210
|
||||||
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
|
||||||
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
|
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
|
||||||
Josh Tobin Matthew Gregan github:poppolopoppo
|
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
|
||||||
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
|
||||||
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
|
||||||
Brad Weinberger Matvey Cherevko github:mosra
|
Brad Weinberger Matvey Cherevko github:mosra
|
||||||
@@ -140,7 +141,7 @@ RECENT REVISION HISTORY:
|
|||||||
// // ... x = width, y = height, n = # 8-bit components per pixel ...
|
// // ... x = width, y = height, n = # 8-bit components per pixel ...
|
||||||
// // ... replace '0' with '1'..'4' to force that many components per pixel
|
// // ... replace '0' with '1'..'4' to force that many components per pixel
|
||||||
// // ... but 'n' will always be the number that it would have been if you said 0
|
// // ... but 'n' will always be the number that it would have been if you said 0
|
||||||
// stbi_image_free(data)
|
// stbi_image_free(data);
|
||||||
//
|
//
|
||||||
// Standard parameters:
|
// Standard parameters:
|
||||||
// int *x -- outputs image width in pixels
|
// int *x -- outputs image width in pixels
|
||||||
@@ -635,7 +636,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
|
||||||
typedef unsigned short stbi__uint16;
|
typedef unsigned short stbi__uint16;
|
||||||
typedef signed short stbi__int16;
|
typedef signed short stbi__int16;
|
||||||
typedef unsigned int stbi__uint32;
|
typedef unsigned int stbi__uint32;
|
||||||
@@ -1063,6 +1064,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
|
||||||
|
static int stbi__addints_valid(int a, int b)
|
||||||
|
{
|
||||||
|
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
|
||||||
|
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
|
||||||
|
return a <= INT_MAX - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns 1 if the product of two signed shorts is valid, 0 on overflow.
|
||||||
|
static int stbi__mul2shorts_valid(short a, short b)
|
||||||
|
{
|
||||||
|
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
|
||||||
|
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
|
||||||
|
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
|
||||||
|
return a >= SHRT_MIN / b;
|
||||||
|
}
|
||||||
|
|
||||||
// stbi__err - error
|
// stbi__err - error
|
||||||
// stbi__errpf - error returning pointer to float
|
// stbi__errpf - error returning pointer to float
|
||||||
// stbi__errpuc - error returning pointer to unsigned char
|
// stbi__errpuc - error returning pointer to unsigned char
|
||||||
@@ -1985,9 +2003,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
|
|||||||
int i,j,k=0;
|
int i,j,k=0;
|
||||||
unsigned int code;
|
unsigned int code;
|
||||||
// build size list for each symbol (from JPEG spec)
|
// build size list for each symbol (from JPEG spec)
|
||||||
for (i=0; i < 16; ++i)
|
for (i=0; i < 16; ++i) {
|
||||||
for (j=0; j < count[i]; ++j)
|
for (j=0; j < count[i]; ++j) {
|
||||||
h->size[k++] = (stbi_uc) (i+1);
|
h->size[k++] = (stbi_uc) (i+1);
|
||||||
|
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
|
||||||
|
}
|
||||||
|
}
|
||||||
h->size[k] = 0;
|
h->size[k] = 0;
|
||||||
|
|
||||||
// compute actual symbols (from jpeg spec)
|
// compute actual symbols (from jpeg spec)
|
||||||
@@ -2112,6 +2133,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
|
|||||||
|
|
||||||
// convert the huffman code to the symbol id
|
// convert the huffman code to the symbol id
|
||||||
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
|
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
|
||||||
|
if(c < 0 || c >= 256) // symbol id out of bounds!
|
||||||
|
return -1;
|
||||||
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
|
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
|
||||||
|
|
||||||
// convert the id to a symbol
|
// convert the id to a symbol
|
||||||
@@ -2130,6 +2153,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
|
|||||||
unsigned int k;
|
unsigned int k;
|
||||||
int sgn;
|
int sgn;
|
||||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||||
|
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||||
|
|
||||||
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
|
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
|
||||||
k = stbi_lrot(j->code_buffer, n);
|
k = stbi_lrot(j->code_buffer, n);
|
||||||
@@ -2144,6 +2168,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
|
|||||||
{
|
{
|
||||||
unsigned int k;
|
unsigned int k;
|
||||||
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
|
||||||
|
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||||
k = stbi_lrot(j->code_buffer, n);
|
k = stbi_lrot(j->code_buffer, n);
|
||||||
j->code_buffer = k & ~stbi__bmask[n];
|
j->code_buffer = k & ~stbi__bmask[n];
|
||||||
k &= stbi__bmask[n];
|
k &= stbi__bmask[n];
|
||||||
@@ -2155,6 +2180,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
|
|||||||
{
|
{
|
||||||
unsigned int k;
|
unsigned int k;
|
||||||
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
|
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
|
||||||
|
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
|
||||||
k = j->code_buffer;
|
k = j->code_buffer;
|
||||||
j->code_buffer <<= 1;
|
j->code_buffer <<= 1;
|
||||||
--j->code_bits;
|
--j->code_bits;
|
||||||
@@ -2192,8 +2218,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
|||||||
memset(data,0,64*sizeof(data[0]));
|
memset(data,0,64*sizeof(data[0]));
|
||||||
|
|
||||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||||
|
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
|
||||||
dc = j->img_comp[b].dc_pred + diff;
|
dc = j->img_comp[b].dc_pred + diff;
|
||||||
j->img_comp[b].dc_pred = dc;
|
j->img_comp[b].dc_pred = dc;
|
||||||
|
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||||
data[0] = (short) (dc * dequant[0]);
|
data[0] = (short) (dc * dequant[0]);
|
||||||
|
|
||||||
// decode AC components, see JPEG spec
|
// decode AC components, see JPEG spec
|
||||||
@@ -2207,6 +2235,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
|
|||||||
if (r) { // fast-AC path
|
if (r) { // fast-AC path
|
||||||
k += (r >> 4) & 15; // run
|
k += (r >> 4) & 15; // run
|
||||||
s = r & 15; // combined length
|
s = r & 15; // combined length
|
||||||
|
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
|
||||||
j->code_buffer <<= s;
|
j->code_buffer <<= s;
|
||||||
j->code_bits -= s;
|
j->code_bits -= s;
|
||||||
// decode into unzigzag'd location
|
// decode into unzigzag'd location
|
||||||
@@ -2246,8 +2275,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
|
|||||||
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||||
diff = t ? stbi__extend_receive(j, t) : 0;
|
diff = t ? stbi__extend_receive(j, t) : 0;
|
||||||
|
|
||||||
|
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
|
||||||
dc = j->img_comp[b].dc_pred + diff;
|
dc = j->img_comp[b].dc_pred + diff;
|
||||||
j->img_comp[b].dc_pred = dc;
|
j->img_comp[b].dc_pred = dc;
|
||||||
|
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
|
||||||
data[0] = (short) (dc * (1 << j->succ_low));
|
data[0] = (short) (dc * (1 << j->succ_low));
|
||||||
} else {
|
} else {
|
||||||
// refinement scan for DC coefficient
|
// refinement scan for DC coefficient
|
||||||
@@ -2282,6 +2313,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
|
|||||||
if (r) { // fast-AC path
|
if (r) { // fast-AC path
|
||||||
k += (r >> 4) & 15; // run
|
k += (r >> 4) & 15; // run
|
||||||
s = r & 15; // combined length
|
s = r & 15; // combined length
|
||||||
|
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
|
||||||
j->code_buffer <<= s;
|
j->code_buffer <<= s;
|
||||||
j->code_bits -= s;
|
j->code_bits -= s;
|
||||||
zig = stbi__jpeg_dezigzag[k++];
|
zig = stbi__jpeg_dezigzag[k++];
|
||||||
@@ -3102,6 +3134,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
|
|||||||
sizes[i] = stbi__get8(z->s);
|
sizes[i] = stbi__get8(z->s);
|
||||||
n += sizes[i];
|
n += sizes[i];
|
||||||
}
|
}
|
||||||
|
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
|
||||||
L -= 17;
|
L -= 17;
|
||||||
if (tc == 0) {
|
if (tc == 0) {
|
||||||
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
|
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
|
||||||
@@ -3351,6 +3384,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
||||||
|
{
|
||||||
|
// some JPEGs have junk at end, skip over it but if we find what looks
|
||||||
|
// like a valid marker, resume there
|
||||||
|
while (!stbi__at_eof(j->s)) {
|
||||||
|
int x = stbi__get8(j->s);
|
||||||
|
while (x == 255) { // might be a marker
|
||||||
|
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
|
||||||
|
x = stbi__get8(j->s);
|
||||||
|
if (x != 0x00 && x != 0xff) {
|
||||||
|
// not a stuffed zero or lead-in to another marker, looks
|
||||||
|
// like an actual marker, return it
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
// stuffed zero has x=0 now which ends the loop, meaning we go
|
||||||
|
// back to regular scan loop.
|
||||||
|
// repeated 0xff keeps trying to read the next byte of the marker.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return STBI__MARKER_none;
|
||||||
|
}
|
||||||
|
|
||||||
// decode image to YCbCr format
|
// decode image to YCbCr format
|
||||||
static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
||||||
{
|
{
|
||||||
@@ -3367,25 +3422,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
|
|||||||
if (!stbi__process_scan_header(j)) return 0;
|
if (!stbi__process_scan_header(j)) return 0;
|
||||||
if (!stbi__parse_entropy_coded_data(j)) return 0;
|
if (!stbi__parse_entropy_coded_data(j)) return 0;
|
||||||
if (j->marker == STBI__MARKER_none ) {
|
if (j->marker == STBI__MARKER_none ) {
|
||||||
// handle 0s at the end of image data from IP Kamera 9060
|
j->marker = stbi__skip_jpeg_junk_at_end(j);
|
||||||
while (!stbi__at_eof(j->s)) {
|
|
||||||
int x = stbi__get8(j->s);
|
|
||||||
if (x == 255) {
|
|
||||||
j->marker = stbi__get8(j->s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
|
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
|
||||||
}
|
}
|
||||||
|
m = stbi__get_marker(j);
|
||||||
|
if (STBI__RESTART(m))
|
||||||
|
m = stbi__get_marker(j);
|
||||||
} else if (stbi__DNL(m)) {
|
} else if (stbi__DNL(m)) {
|
||||||
int Ld = stbi__get16be(j->s);
|
int Ld = stbi__get16be(j->s);
|
||||||
stbi__uint32 NL = stbi__get16be(j->s);
|
stbi__uint32 NL = stbi__get16be(j->s);
|
||||||
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
|
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
|
||||||
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
|
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
|
||||||
|
m = stbi__get_marker(j);
|
||||||
} else {
|
} else {
|
||||||
if (!stbi__process_marker(j, m)) return 0;
|
if (!stbi__process_marker(j, m)) return 1;
|
||||||
|
m = stbi__get_marker(j);
|
||||||
}
|
}
|
||||||
m = stbi__get_marker(j);
|
|
||||||
}
|
}
|
||||||
if (j->progressive)
|
if (j->progressive)
|
||||||
stbi__jpeg_finish(j);
|
stbi__jpeg_finish(j);
|
||||||
@@ -3976,6 +4028,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
|
|||||||
unsigned char* result;
|
unsigned char* result;
|
||||||
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
|
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
|
||||||
if (!j) return stbi__errpuc("outofmem", "Out of memory");
|
if (!j) return stbi__errpuc("outofmem", "Out of memory");
|
||||||
|
memset(j, 0, sizeof(stbi__jpeg));
|
||||||
STBI_NOTUSED(ri);
|
STBI_NOTUSED(ri);
|
||||||
j->s = s;
|
j->s = s;
|
||||||
stbi__setup_jpeg(j);
|
stbi__setup_jpeg(j);
|
||||||
@@ -3989,6 +4042,7 @@ static int stbi__jpeg_test(stbi__context *s)
|
|||||||
int r;
|
int r;
|
||||||
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
|
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
|
||||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||||
|
memset(j, 0, sizeof(stbi__jpeg));
|
||||||
j->s = s;
|
j->s = s;
|
||||||
stbi__setup_jpeg(j);
|
stbi__setup_jpeg(j);
|
||||||
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
|
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
|
||||||
@@ -4014,6 +4068,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
|
|||||||
int result;
|
int result;
|
||||||
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
|
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
|
||||||
if (!j) return stbi__err("outofmem", "Out of memory");
|
if (!j) return stbi__err("outofmem", "Out of memory");
|
||||||
|
memset(j, 0, sizeof(stbi__jpeg));
|
||||||
j->s = s;
|
j->s = s;
|
||||||
result = stbi__jpeg_info_raw(j, x, y, comp);
|
result = stbi__jpeg_info_raw(j, x, y, comp);
|
||||||
STBI_FREE(j);
|
STBI_FREE(j);
|
||||||
@@ -4256,11 +4311,12 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
|||||||
a->zout = zout;
|
a->zout = zout;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
|
||||||
z -= 257;
|
z -= 257;
|
||||||
len = stbi__zlength_base[z];
|
len = stbi__zlength_base[z];
|
||||||
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
|
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
|
||||||
z = stbi__zhuffman_decode(a, &a->z_distance);
|
z = stbi__zhuffman_decode(a, &a->z_distance);
|
||||||
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
|
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
|
||||||
dist = stbi__zdist_base[z];
|
dist = stbi__zdist_base[z];
|
||||||
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
|
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
|
||||||
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
||||||
@@ -4955,7 +5011,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
|
|||||||
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
|
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
|
||||||
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
|
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
|
||||||
|
|
||||||
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
|
||||||
{
|
{
|
||||||
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
|
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
|
||||||
stbi__unpremultiply_on_load_set = 1;
|
stbi__unpremultiply_on_load_set = 1;
|
||||||
@@ -5064,14 +5120,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||||||
if (!pal_img_n) {
|
if (!pal_img_n) {
|
||||||
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
|
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
|
||||||
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
|
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
|
||||||
if (scan == STBI__SCAN_header) return 1;
|
|
||||||
} else {
|
} else {
|
||||||
// if paletted, then pal_n is our final components, and
|
// if paletted, then pal_n is our final components, and
|
||||||
// img_n is # components to decompress/filter.
|
// img_n is # components to decompress/filter.
|
||||||
s->img_n = 1;
|
s->img_n = 1;
|
||||||
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
|
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
|
||||||
// if SCAN_header, have to scan to see if we have a tRNS
|
|
||||||
}
|
}
|
||||||
|
// even with SCAN_header, have to scan to see if we have a tRNS
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5103,6 +5158,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||||||
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
|
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
|
||||||
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
|
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
|
||||||
has_trans = 1;
|
has_trans = 1;
|
||||||
|
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
|
||||||
|
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
|
||||||
if (z->depth == 16) {
|
if (z->depth == 16) {
|
||||||
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||||
} else {
|
} else {
|
||||||
@@ -5115,7 +5172,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||||||
case STBI__PNG_TYPE('I','D','A','T'): {
|
case STBI__PNG_TYPE('I','D','A','T'): {
|
||||||
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
|
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
|
||||||
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
|
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
|
||||||
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
|
if (scan == STBI__SCAN_header) {
|
||||||
|
// header scan definitely stops at first IDAT
|
||||||
|
if (pal_img_n)
|
||||||
|
s->img_n = pal_img_n;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
|
||||||
if ((int)(ioff + c.length) < (int)ioff) return 0;
|
if ((int)(ioff + c.length) < (int)ioff) return 0;
|
||||||
if (ioff + c.length > idata_limit) {
|
if (ioff + c.length > idata_limit) {
|
||||||
stbi__uint32 idata_limit_old = idata_limit;
|
stbi__uint32 idata_limit_old = idata_limit;
|
||||||
@@ -5498,8 +5561,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
|||||||
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
psize = (info.offset - info.extra_read - info.hsz) >> 2;
|
||||||
}
|
}
|
||||||
if (psize == 0) {
|
if (psize == 0) {
|
||||||
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
|
// accept some number of extra bytes after the header, but if the offset points either to before
|
||||||
return stbi__errpuc("bad offset", "Corrupt BMP");
|
// the header ends or implies a large amount of extra data, reject the file as malformed
|
||||||
|
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
|
||||||
|
int header_limit = 1024; // max we actually read is below 256 bytes currently.
|
||||||
|
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
|
||||||
|
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
|
||||||
|
return stbi__errpuc("bad header", "Corrupt BMP");
|
||||||
|
}
|
||||||
|
// we established that bytes_read_so_far is positive and sensible.
|
||||||
|
// the first half of this test rejects offsets that are either too small positives, or
|
||||||
|
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
|
||||||
|
// ensures the number computed in the second half of the test can't overflow.
|
||||||
|
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
|
||||||
|
return stbi__errpuc("bad offset", "Corrupt BMP");
|
||||||
|
} else {
|
||||||
|
stbi__skip(s, info.offset - bytes_read_so_far);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7187,12 +7264,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
|
|||||||
// Run
|
// Run
|
||||||
value = stbi__get8(s);
|
value = stbi__get8(s);
|
||||||
count -= 128;
|
count -= 128;
|
||||||
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||||
for (z = 0; z < count; ++z)
|
for (z = 0; z < count; ++z)
|
||||||
scanline[i++ * 4 + k] = value;
|
scanline[i++ * 4 + k] = value;
|
||||||
} else {
|
} else {
|
||||||
// Dump
|
// Dump
|
||||||
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
|
||||||
for (z = 0; z < count; ++z)
|
for (z = 0; z < count; ++z)
|
||||||
scanline[i++ * 4 + k] = stbi__get8(s);
|
scanline[i++ * 4 + k] = stbi__get8(s);
|
||||||
}
|
}
|
||||||
@@ -7446,10 +7523,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
|
|||||||
|
|
||||||
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
|
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
|
||||||
if (!out) return stbi__errpuc("outofmem", "Out of memory");
|
if (!out) return stbi__errpuc("outofmem", "Out of memory");
|
||||||
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
|
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
|
||||||
|
STBI_FREE(out);
|
||||||
|
return stbi__errpuc("bad PNM", "PNM file truncated");
|
||||||
|
}
|
||||||
|
|
||||||
if (req_comp && req_comp != s->img_n) {
|
if (req_comp && req_comp != s->img_n) {
|
||||||
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
if (ri->bits_per_channel == 16) {
|
||||||
|
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||||
|
} else {
|
||||||
|
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
|
||||||
|
}
|
||||||
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
@@ -7486,6 +7570,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
|
|||||||
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
|
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
|
||||||
value = value*10 + (*c - '0');
|
value = value*10 + (*c - '0');
|
||||||
*c = (char) stbi__get8(s);
|
*c = (char) stbi__get8(s);
|
||||||
|
if((value > 214748364) || (value == 214748364 && *c > '7'))
|
||||||
|
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -7516,9 +7602,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
|
|||||||
stbi__pnm_skip_whitespace(s, &c);
|
stbi__pnm_skip_whitespace(s, &c);
|
||||||
|
|
||||||
*x = stbi__pnm_getinteger(s, &c); // read width
|
*x = stbi__pnm_getinteger(s, &c); // read width
|
||||||
|
if(*x == 0)
|
||||||
|
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
|
||||||
stbi__pnm_skip_whitespace(s, &c);
|
stbi__pnm_skip_whitespace(s, &c);
|
||||||
|
|
||||||
*y = stbi__pnm_getinteger(s, &c); // read height
|
*y = stbi__pnm_getinteger(s, &c); // read height
|
||||||
|
if (*y == 0)
|
||||||
|
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
|
||||||
stbi__pnm_skip_whitespace(s, &c);
|
stbi__pnm_skip_whitespace(s, &c);
|
||||||
|
|
||||||
maxv = stbi__pnm_getinteger(s, &c); // read max value
|
maxv = stbi__pnm_getinteger(s, &c); // read max value
|
||||||
@@ -7894,4 +7984,4 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|||||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user