diff options
Diffstat (limited to 'lib/extras/codec_apng.cc')
-rw-r--r-- | lib/extras/codec_apng.cc | 409 |
1 files changed, 0 insertions, 409 deletions
diff --git a/lib/extras/codec_apng.cc b/lib/extras/codec_apng.cc deleted file mode 100644 index 196bddf..0000000 --- a/lib/extras/codec_apng.cc +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/extras/codec_apng.h" - -// Parts of this code are taken from apngdis, which has the following license: -/* APNG Disassembler 2.8 - * - * Deconstructs APNG files into individual frames. - * - * http://apngdis.sourceforge.net - * - * Copyright (c) 2010-2015 Max Stepin - * maxst at users.sourceforge.net - * - * zlib license - * ------------ - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - */ - -#include <stdio.h> -#include <string.h> - -#include <algorithm> -#include <string> -#include <utility> -#include <vector> - -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/color_management.h" -#include "lib/jxl/frame_header.h" -#include "lib/jxl/headers.h" -#include "lib/jxl/image.h" -#include "lib/jxl/image_bundle.h" -#include "lib/jxl/luminance.h" -#include "png.h" /* original (unpatched) libpng is ok */ - -namespace jxl { -namespace extras { - -namespace { - -constexpr bool isAbc(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -} -#define notabc(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) - -constexpr uint32_t kId_IHDR = 0x52444849; -constexpr uint32_t kId_acTL = 0x4C546361; -constexpr uint32_t kId_fcTL = 0x4C546366; -constexpr uint32_t kId_IDAT = 0x54414449; -constexpr uint32_t kId_fdAT = 0x54416466; -constexpr uint32_t kId_IEND = 0x444E4549; - -struct CHUNK { - unsigned char* p; - unsigned int size; -}; - -struct APNGFrame { - unsigned char *p, **rows; - unsigned int w, h, delay_num, delay_den; -}; - -struct Reader { - const uint8_t* next; - const uint8_t* last; - bool Read(void* data, size_t len) { - size_t cap = last - next; - size_t to_copy = std::min(cap, len); - memcpy(data, next, to_copy); - next += to_copy; - return (len == to_copy); - } - bool Eof() { return next == last; } -}; - -const unsigned long cMaxPNGSize = 1000000UL; -const size_t kMaxPNGChunkSize = 100000000; // 100 MB - -void info_fn(png_structp png_ptr, png_infop info_ptr) { - png_set_expand(png_ptr); - png_set_strip_16(png_ptr); - png_set_gray_to_rgb(png_ptr); - png_set_palette_to_rgb(png_ptr); - png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); - (void)png_set_interlace_handling(png_ptr); - png_read_update_info(png_ptr, info_ptr); -} - -void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, - int pass) { - APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr); - png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row); -} - -inline unsigned int read_chunk(Reader* r, CHUNK* pChunk) { - unsigned char len[4]; - pChunk->size = 0; - pChunk->p = 0; - if (r->Read(&len, 4)) { - const auto size = png_get_uint_32(len); - // Check first, to avoid overflow. - if (size > kMaxPNGChunkSize) { - JXL_WARNING("APNG chunk size is too big"); - return 0; - } - pChunk->size = size + 12; - pChunk->p = new unsigned char[pChunk->size]; - memcpy(pChunk->p, len, 4); - if (r->Read(pChunk->p + 4, pChunk->size - 4)) { - return *(unsigned int*)(pChunk->p + 4); - } - } - return 0; -} - -int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr, - bool hasInfo, CHUNK& chunkIHDR, - std::vector<CHUNK>& chunksInfo) { - unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - info_ptr = png_create_info_struct(png_ptr); - if (!png_ptr || !info_ptr) return 1; - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, 0); - return 1; - } - - png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); - png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL); - - png_process_data(png_ptr, info_ptr, header, 8); - png_process_data(png_ptr, info_ptr, chunkIHDR.p, chunkIHDR.size); - - if (hasInfo) { - for (unsigned int i = 0; i < chunksInfo.size(); i++) { - png_process_data(png_ptr, info_ptr, chunksInfo[i].p, chunksInfo[i].size); - } - } - return 0; -} - -int processing_data(png_structp png_ptr, png_infop info_ptr, unsigned char* p, - unsigned int size) { - if (!png_ptr || !info_ptr) return 1; - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, 0); - return 1; - } - - png_process_data(png_ptr, info_ptr, p, size); - return 0; -} - -int processing_finish(png_structp png_ptr, png_infop info_ptr) { - unsigned char footer[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130}; - - if (!png_ptr || !info_ptr) return 1; - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, 0); - return 1; - } - - png_process_data(png_ptr, info_ptr, footer, 12); - png_destroy_read_struct(&png_ptr, &info_ptr, 0); - - return 0; -} - -} // namespace - -Status DecodeImageAPNG(Span<const uint8_t> bytes, const ColorHints& color_hints, - ThreadPool* pool, CodecInOut* io) { - Reader r; - unsigned int id, i, j, w, h, w0, h0, x0, y0; - unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize; - unsigned char sig[8]; - png_structp png_ptr; - png_infop info_ptr; - CHUNK chunk; - CHUNK chunkIHDR; - std::vector<CHUNK> chunksInfo; - bool isAnimated = false; - bool skipFirst = false; - bool hasInfo = false; - bool all_dispose_bg = true; - APNGFrame frameRaw = {}; - - r = {bytes.data(), bytes.data() + bytes.size()}; - // Not an aPNG => not an error - unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) { - return false; - } - id = read_chunk(&r, &chunkIHDR); - - io->frames.clear(); - io->dec_pixels = 0; - io->metadata.m.SetUintSamples(8); - io->metadata.m.SetAlphaBits(8); - io->metadata.m.color_encoding = - ColorEncoding::SRGB(); // todo: get data from png metadata - JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/true, - /*is_gray=*/false, io)); - - bool errorstate = true; - if (id == kId_IHDR && chunkIHDR.size == 25) { - w0 = w = png_get_uint_32(chunkIHDR.p + 8); - h0 = h = png_get_uint_32(chunkIHDR.p + 12); - - if (w > cMaxPNGSize || h > cMaxPNGSize) { - return false; - } - - x0 = 0; - y0 = 0; - delay_num = 1; - delay_den = 10; - dop = 0; - bop = 0; - rowbytes = w * 4; - imagesize = h * rowbytes; - - frameRaw.p = new unsigned char[imagesize]; - frameRaw.rows = new png_bytep[h * sizeof(png_bytep)]; - for (j = 0; j < h; j++) frameRaw.rows[j] = frameRaw.p + j * rowbytes; - - if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo, - chunkIHDR, chunksInfo)) { - bool last_base_was_none = true; - while (!r.Eof()) { - id = read_chunk(&r, &chunk); - if (!id) break; - JXL_ASSERT(chunk.p != nullptr); - - if (id == kId_acTL && !hasInfo && !isAnimated) { - isAnimated = true; - skipFirst = true; - io->metadata.m.have_animation = true; - io->metadata.m.animation.tps_numerator = 1000; - } else if (id == kId_IEND || - (id == kId_fcTL && (!hasInfo || isAnimated))) { - if (hasInfo) { - if (!processing_finish(png_ptr, info_ptr)) { - ImageBundle bundle(&io->metadata.m); - bundle.duration = delay_num * 1000 / delay_den; - bundle.origin.x0 = x0; - bundle.origin.y0 = y0; - // TODO(veluca): this could in principle be implemented. - if (last_base_was_none && !all_dispose_bg && - (x0 != 0 || y0 != 0 || w0 != w || h0 != h || bop != 0)) { - return JXL_FAILURE( - "APNG with dispose-to-0 is not supported for non-full or " - "blended frames"); - } - switch (dop) { - case 0: - bundle.use_for_next_frame = true; - last_base_was_none = false; - all_dispose_bg = false; - break; - case 2: - bundle.use_for_next_frame = false; - all_dispose_bg = false; - break; - default: - bundle.use_for_next_frame = false; - last_base_was_none = true; - } - bundle.blend = bop != 0; - io->dec_pixels += w0 * h0; - - Image3F sub_frame(w0, h0); - ImageF sub_frame_alpha(w0, h0); - for (size_t y = 0; y < h0; ++y) { - float* const JXL_RESTRICT row_r = sub_frame.PlaneRow(0, y); - float* const JXL_RESTRICT row_g = sub_frame.PlaneRow(1, y); - float* const JXL_RESTRICT row_b = sub_frame.PlaneRow(2, y); - float* const JXL_RESTRICT row_alpha = sub_frame_alpha.Row(y); - uint8_t* const f = frameRaw.rows[y]; - for (size_t x = 0; x < w0; ++x) { - if (f[4 * x + 3] == 0) { - row_alpha[x] = 0; - row_r[x] = 0; - row_g[x] = 0; - row_b[x] = 0; - continue; - } - row_r[x] = f[4 * x + 0] * (1.f / 255); - row_g[x] = f[4 * x + 1] * (1.f / 255); - row_b[x] = f[4 * x + 2] * (1.f / 255); - row_alpha[x] = f[4 * x + 3] * (1.f / 255); - } - } - bundle.SetFromImage(std::move(sub_frame), ColorEncoding::SRGB()); - bundle.SetAlpha(std::move(sub_frame_alpha), - /*alpha_is_premultiplied=*/false); - io->frames.push_back(std::move(bundle)); - } else { - delete[] chunk.p; - break; - } - } - - if (id == kId_IEND) { - errorstate = false; - break; - } - // At this point the old frame is done. Let's start a new one. - w0 = png_get_uint_32(chunk.p + 12); - h0 = png_get_uint_32(chunk.p + 16); - x0 = png_get_uint_32(chunk.p + 20); - y0 = png_get_uint_32(chunk.p + 24); - delay_num = png_get_uint_16(chunk.p + 28); - delay_den = png_get_uint_16(chunk.p + 30); - dop = chunk.p[32]; - bop = chunk.p[33]; - - if (!delay_den) delay_den = 100; - - if (w0 > cMaxPNGSize || h0 > cMaxPNGSize || x0 > cMaxPNGSize || - y0 > cMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 || - bop > 1) { - delete[] chunk.p; - break; - } - - if (hasInfo) { - memcpy(chunkIHDR.p + 8, chunk.p + 12, 8); - if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo, - chunkIHDR, chunksInfo)) { - delete[] chunk.p; - break; - } - } else - skipFirst = false; - - if (io->frames.size() == (skipFirst ? 1 : 0)) { - bop = 0; - if (dop == 2) dop = 1; - } - } else if (id == kId_IDAT) { - hasInfo = true; - if (processing_data(png_ptr, info_ptr, chunk.p, chunk.size)) { - delete[] chunk.p; - break; - } - } else if (id == kId_fdAT && isAnimated) { - png_save_uint_32(chunk.p + 4, chunk.size - 16); - memcpy(chunk.p + 8, "IDAT", 4); - if (processing_data(png_ptr, info_ptr, chunk.p + 4, chunk.size - 4)) { - delete[] chunk.p; - break; - } - } else if (!isAbc(chunk.p[4]) || !isAbc(chunk.p[5]) || - !isAbc(chunk.p[6]) || !isAbc(chunk.p[7])) { - delete[] chunk.p; - break; - } else if (!hasInfo) { - if (processing_data(png_ptr, info_ptr, chunk.p, chunk.size)) { - delete[] chunk.p; - break; - } - chunksInfo.push_back(chunk); - continue; - } - delete[] chunk.p; - } - } - delete[] frameRaw.rows; - delete[] frameRaw.p; - } - - for (i = 0; i < chunksInfo.size(); i++) delete[] chunksInfo[i].p; - - chunksInfo.clear(); - delete[] chunkIHDR.p; - - if (errorstate) return false; - SetIntensityTarget(io); - return true; -} - -} // namespace extras -} // namespace jxl |