d6cfb1f1创建于 4月27日历史提交
/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// APNG stub implementations
// These functions manually parse APNG chunks when libpng doesn't have APNG support
//

#include "apng.h"
#include "png.h"
#include <cstring>
#include <map>

// APNG chunk signatures (as 4-byte arrays)
static const png_byte PNG_CHUNK_ACTL[5] = {'a', 'c', 'T', 'L', '\0'};
static const png_byte PNG_CHUNK_FCTL[5] = {'f', 'c', 'T', 'L', '\0'};
static const png_byte PNG_CHUNK_FDAT[5] = {'f', 'd', 'A', 'T', '\0'};
// PNG chunk name 长度
constexpr size_t CHUNK_NAME_SIZE = 4;

// Global state to track current frame index for fcTL reading
// Use void* as key to avoid restrict qualifier issues
static std::map<void *, png_uint_32> g_frameIndexMap;

// Helper function to read chunk from unknown chunks
static png_uint_32 ReadChunkData(png_const_structrp pngPtr, png_inforp infoPtr, const png_byte *chunkName,
    png_bytep data, png_uint_32 length)
{
    png_unknown_chunkp unknowns;
    int numUnknowns = png_get_unknown_chunks(const_cast<png_structrp>(pngPtr), infoPtr, &unknowns);

    for (int i = 0; i < numUnknowns; i++) {
        if (memcmp(unknowns[i].name, chunkName, CHUNK_NAME_SIZE) == 0) {
            if (unknowns[i].size >= length) {
                std::copy_n(unknowns[i].data, length, data);
                return 1;
            }
        }
    }
    return 0;
}

// Helper function to read nth fcTL chunk (0-based index)
static png_uint_32 ReadFcTLChunk(png_structrp pngPtr, png_inforp infoPtr, png_uint_32 frameIndex, png_bytep data,
    png_uint_32 length)
{
    png_unknown_chunkp unknowns;
    int numUnknowns = png_get_unknown_chunks(pngPtr, infoPtr, &unknowns);

    png_uint_32 tlCount = 0;
    for (int i = 0; i < numUnknowns; i++) {
        if (memcmp(unknowns[i].name, PNG_CHUNK_FCTL, CHUNK_NAME_SIZE) == 0) {
            if (tlCount == frameIndex && unknowns[i].size >= length) {
                std::copy_n(unknowns[i].data, length, data);
                return 1;
            }
            tlCount++;
        }
    }
    return 0;
}

constexpr png_uint_32 ACTL_DATA_BYTE_COUNT = 8;
constexpr size_t ACTL_NUM_FRAMES_OFFSET = 0;
constexpr size_t ACTL_NUM_PLAYS_OFFSET = 4;

// fcTL chunk data layout (stub buffer matches fields read by AssignFcTLFromBuffer)
constexpr png_uint_32 FCTL_DATA_BYTE_COUNT = 26;
constexpr size_t FCTL_WIDTH_OFFSET = 0;
constexpr size_t FCTL_HEIGHT_OFFSET = 4;
constexpr size_t FCTL_X_OFFSET_OFFSET = 8;
constexpr size_t FCTL_Y_OFFSET_OFFSET = 12;
constexpr size_t FCTL_DELAY_NUM_OFFSET = 16;
constexpr size_t FCTL_DELAY_DEN_OFFSET = 18;
constexpr size_t FCTL_DISPOSE_OP_OFFSET = 20;
constexpr size_t FCTL_BLEND_OP_OFFSET = 21;

struct FcTlOutputSlots {
    png_uint_32 *width;
    png_uint_32 *height;
    png_uint_32 *xOffset;
    png_uint_32 *yOffset;
    png_uint_16 *delayNum;
    png_uint_16 *delayDen;
    png_byte *disposeOp;
    png_byte *blendOp;
};

namespace {

void AssignFcTLFromBuffer(const png_byte *data, const FcTlOutputSlots& out)
{
    if (out.width) {
        *out.width = png_get_uint_32(data + FCTL_WIDTH_OFFSET);
    }
    if (out.height) {
        *out.height = png_get_uint_32(data + FCTL_HEIGHT_OFFSET);
    }
    if (out.xOffset) {
        *out.xOffset = png_get_uint_32(data + FCTL_X_OFFSET_OFFSET);
    }
    if (out.yOffset) {
        *out.yOffset = png_get_uint_32(data + FCTL_Y_OFFSET_OFFSET);
    }
    if (out.delayNum) {
        *out.delayNum = png_get_uint_16(data + FCTL_DELAY_NUM_OFFSET);
    }
    if (out.delayDen) {
        *out.delayDen = png_get_uint_16(data + FCTL_DELAY_DEN_OFFSET);
    }
    if (out.disposeOp) {
        *out.disposeOp = data[FCTL_DISPOSE_OP_OFFSET];
    }
    if (out.blendOp) {
        *out.blendOp = data[FCTL_BLEND_OP_OFFSET];
    }
}

png_uint_32 ReadAndApplyNextFcTL(png_structrp pngPtr, png_inforp infoPtr, const FcTlOutputSlots& out)
{
    png_uint_32 frameIndex = 0;
    void *key = const_cast<void *>(reinterpret_cast<const void *>(pngPtr));
    auto it = g_frameIndexMap.find(key);
    if (it != g_frameIndexMap.end()) {
        frameIndex = it->second;
    }

    png_byte data[FCTL_DATA_BYTE_COUNT];
    if (ReadFcTLChunk(pngPtr, infoPtr, frameIndex, data, FCTL_DATA_BYTE_COUNT)) {
        AssignFcTLFromBuffer(data, out);
        g_frameIndexMap[key] = frameIndex + 1;
        return 1;
    }
    return 0;
}

} // namespace

PNG_EXPORT_TYPE(png_uint_32)
PNGAPI png_get_acTL PNGARG((png_const_structrp pngPtr, png_inforp infoPtr, png_uint_32 *numFrames,
    png_uint_32 *numPlays))
{
    if (!pngPtr || !infoPtr || !numFrames || !numPlays) {
        return 0;
    }

    png_byte data[ACTL_DATA_BYTE_COUNT];
    if (ReadChunkData(pngPtr, infoPtr, PNG_CHUNK_ACTL, data, ACTL_DATA_BYTE_COUNT)) {
        *numFrames = png_get_uint_32(data + ACTL_NUM_FRAMES_OFFSET);
        *numPlays = png_get_uint_32(data + ACTL_NUM_PLAYS_OFFSET);
        return 1;
    }

    return 0;
}

PNG_EXPORT_TYPE(png_uint_32)
PNGAPI png_get_next_frame_fcTL PNGARG((png_structrp pngPtr, png_inforp infoPtr, png_uint_32 *width,
    png_uint_32 *height, png_uint_32 *xOffset, png_uint_32 *yOffset, png_uint_16 *delayNum, png_uint_16 *delayDen,
    png_byte *disposeOp, png_byte *blendOp))
{
    if (!pngPtr || !infoPtr) {
        return 0;
    }
    const FcTlOutputSlots out = { width, height, xOffset, yOffset, delayNum, delayDen, disposeOp, blendOp };
    return ReadAndApplyNextFcTL(pngPtr, infoPtr, out);
}

PNG_EXPORT_TYPE(void) PNGAPI png_read_frame_head PNGARG((png_structrp pngPtr, png_inforp infoPtr))
{
    // For stub implementation, we need to read the next chunk sequence
    // This function is called before png_get_next_frame_fcTL
    // The frame index is managed by png_get_next_frame_fcTL
    // This is a no-op as the frame reading logic handles chunk reading
    if (pngPtr && infoPtr) {
        // The actual chunk reading happens in png_read_image
        // which reads IDAT or fdAT chunks
    }
}

PNG_EXPORT_TYPE(png_uint_32)
PNGAPI png_get_first_frame_is_hidden PNGARG((png_const_structrp pngPtr, png_const_inforp infoPtr))
{
    // Check if first frame is hidden by looking at the first fcTL chunk
    // For simplicity, return 0 (not hidden)
    // A full implementation would check the sequence_number field
    return 0;
}