/*
 * Copyright (C) 2022-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.
 */

#include "image_packer.h"

#include "buffer_packer_stream.h"
#include "file_packer_stream.h"
#include "image/abs_image_encoder.h"
#include "image_log.h"
#include "image_mime_type.h"
#include "image_trace.h"
#include "image_utils.h"
#include "media_errors.h"
#include "ostream_packer_stream.h"
#include "plugin_server.h"
#include "string_ex.h"
#if defined(SUPPORT_LIBTIFF)
#include "tiff_encoder.h"
#endif
#include "hdr_helper.h"
#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
#include "include/jpeg_encoder.h"
#endif
#ifdef HEIF_HW_ENCODE_ENABLE
#include "image/v2_1/icodec_image.h"
#include "image/v2_1/codec_image_type.h"
#include "v4_0/codec_types.h"
#include "v4_0/icodec_component_manager.h"
#endif

#undef LOG_DOMAIN
#define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE

#undef LOG_TAG
#define LOG_TAG "ImagePacker"

namespace OHOS {
namespace Media {
using namespace ImagePlugin;
using namespace MultimediaPlugin;
static constexpr uint8_t QUALITY_MAX = 100;
const static std::string EXTENDED_ENCODER = "image/jpeg,image/png,image/webp";
static constexpr size_t SIZE_ZERO = 0;
static constexpr uint8_t BITS_PER_BYTE = 8;

PluginServer &ImagePacker::pluginServer_ = ImageUtils::GetPluginServer();

#ifdef HEIF_HW_ENCODE_ENABLE
static bool IsEncodeSecureMode(const std::string &name)
{
    std::string prefix = ".secure";
    if (name.length() <= prefix.length()) {
        return false;
    }
    return name.rfind(prefix) == (name.length() - prefix.length());
}
#endif

static bool IsSupportHeifEncode()
{
#ifdef HEIF_HW_ENCODE_ENABLE
    sptr<HDI::Codec::Image::V2_1::ICodecImage> image =
            HDI::Codec::Image::V2_1::ICodecImage::Get(false);
    if (image == nullptr) {
        return false;
    }
    std::vector<HDI::Codec::Image::V2_1::CodecImageCapability> capList;
    int32_t ret = image->GetImageCapability(capList);
    if (ret != HDF_SUCCESS || capList.empty()) {
        return false;
    }
    for (const auto& cap : capList) {
        if (cap.role == HDI::Codec::Image::V2_1::CODEC_IMAGE_HEIF &&
            cap.type == HDI::Codec::Image::V2_1::CODEC_IMAGE_TYPE_ENCODER && !IsEncodeSecureMode(cap.name)) {
            return true;
        }
    }
#endif
    return false;
}

uint32_t ImagePacker::GetSupportedFormats(std::set<std::string> &formats)
{
    formats.clear();
    std::vector<ClassInfo> classInfos;
    uint32_t ret =
        pluginServer_.PluginServerGetClassInfo<AbsImageEncoder>(AbsImageEncoder::SERVICE_DEFAULT, classInfos);
    CHECK_ERROR_RETURN_RET_LOG(ret != SUCCESS, ret,
        "get class info from plugin server failed, ret:%{public}u.", ret);
    for (auto &info : classInfos) {
        std::map<std::string, AttrData> &capbility = info.capabilities;
        auto iter = capbility.find(IMAGE_ENCODE_FORMAT);
        if (iter == capbility.end()) {
            continue;
        }
        AttrData &attr = iter->second;
        std::string format;
        if (attr.GetValue(format) != SUCCESS) {
            IMAGE_LOGE("attr data get format failed.");
            continue;
        }
        std::vector<std::string> splitedVector;
        SplitStr(format, ",", splitedVector);
        for (std::string item : splitedVector) {
            formats.insert(item);
        }
    }
    static bool isSupportHeif = IsSupportHeifEncode();
    if (isSupportHeif) {
        formats.insert(ImageUtils::GetEncodedHeifFormat());
    }
    return SUCCESS;
}

uint32_t ImagePacker::StartPackingImpl(const PackOption &option)
{
    if (packerStream_ == nullptr || packerStream_.get() == nullptr) {
        IMAGE_LOGE("make buffer packer stream failed.");
        return ERR_IMAGE_DATA_ABNORMAL;
    }
    if (!GetEncoderPlugin(option)) {
        IMAGE_LOGE("StartPackingImpl get encoder plugin failed.");
        return ERR_IMAGE_MISMATCHED_FORMAT;
    }
    encodeToSdr_ = ((option.desiredDynamicRange == EncodeDynamicRange::SDR) ||
        (option.format != IMAGE_JPEG_FORMAT && option.format != IMAGE_HEIF_FORMAT &&
            option.format != IMAGE_HEIC_FORMAT));
    format_ = option.format;
    PlEncodeOptions plOpts;
    CopyOptionsToPlugin(option, plOpts);
    return DoEncodingFunc([this, &plOpts](ImagePlugin::AbsImageEncoder* encoder) {
        return encoder->StartEncode(*packerStream_.get(), plOpts);
    });
}

uint32_t ImagePacker::StartPacking(uint8_t *outputData, uint32_t maxSize, const PackOption &option)
{
    ImageTrace imageTrace("ImagePacker::StartPacking by outputData");
    if (!IsPackOptionValid(option)) {
        IMAGE_LOGE("array startPacking option invalid %{public}s, %{public}u.", option.format.c_str(),
            option.quality);
        return ERR_IMAGE_INVALID_PARAMETER;
    }

    CHECK_ERROR_RETURN_RET_LOG(outputData == nullptr,
        ERR_IMAGE_INVALID_PARAMETER, "output buffer is null.");
    BufferPackerStream *stream = new (std::nothrow) BufferPackerStream(outputData, maxSize);
    CHECK_ERROR_RETURN_RET_LOG(stream == nullptr,
        ERR_IMAGE_DATA_ABNORMAL, "make buffer packer stream failed.");
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<BufferPackerStream>(stream);
    return StartPackingImpl(option);
}

uint32_t ImagePacker::StartPacking(const std::string &filePath, const PackOption &option)
{
    ImageTrace imageTrace("ImagePacker::StartPacking by filePath");
    if (!IsPackOptionValid(option)) {
        IMAGE_LOGE("filepath startPacking option invalid %{public}s, %{public}u.", option.format.c_str(),
            option.quality);
        return ERR_IMAGE_INVALID_PARAMETER;
    }
    FilePackerStream *stream = new (std::nothrow) FilePackerStream(filePath);
    CHECK_ERROR_RETURN_RET_LOG(stream == nullptr,
        ERR_IMAGE_DATA_ABNORMAL, "make file packer stream failed.");
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<FilePackerStream>(stream);
    return StartPackingImpl(option);
}

uint32_t ImagePacker::StartPacking(const int &fd, const PackOption &option)
{
    ImageTrace imageTrace("ImagePacker::StartPacking by fd");
    if (!IsPackOptionValid(option)) {
        IMAGE_LOGE("fd startPacking option invalid %{public}s, %{public}u.", option.format.c_str(), option.quality);
        return ERR_IMAGE_INVALID_PARAMETER;
    }
    FilePackerStream *stream = new (std::nothrow) FilePackerStream(fd);
    bool cond = (stream == nullptr);
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_DATA_ABNORMAL, "make file packer stream failed");
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<FilePackerStream>(stream);
    return StartPackingImpl(option);
}

uint32_t ImagePacker::StartPacking(std::ostream &outputStream, const PackOption &option)
{
    ImageTrace imageTrace("ImagePacker::StartPacking by outputStream");
    if (!IsPackOptionValid(option)) {
        IMAGE_LOGE("outputStream startPacking option invalid %{public}s, %{public}u.", option.format.c_str(),
            option.quality);
        return ERR_IMAGE_INVALID_PARAMETER;
    }
    OstreamPackerStream *stream = new (std::nothrow) OstreamPackerStream(outputStream);
    CHECK_ERROR_RETURN_RET_LOG(stream == nullptr,
        ERR_IMAGE_DATA_ABNORMAL, "make ostream packer stream failed.");
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<OstreamPackerStream>(stream);
    return StartPackingImpl(option);
}

// JNI adapter method, this method be called by jni and the outputStream be created by jni, here we manage the lifecycle
// of the outputStream
uint32_t ImagePacker::StartPackingAdapter(PackerStream &outputStream, const PackOption &option)
{
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<PackerStream>(&outputStream);

    bool cond = !IsPackOptionValid(option);
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
                               "packer stream option invalid %{public}s, %{public}u.",
                               option.format.c_str(), option.quality);
    return StartPackingImpl(option);
}

uint32_t ImagePacker::AddImage(PixelMap &pixelMap)
{
    ImageUtils::DumpPixelMapBeforeEncode(pixelMap);
    ImageTrace imageTrace("ImagePacker::AddImage by pixelMap");

    bool cond = pixelMap.GetPixelFormat() == PixelFormat::Y8 && format_ != IMAGE_TIFF_FORMAT;
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
        "Y8 format only supported via TIFF plugin.");

    return DoEncodingFunc([this, &pixelMap](ImagePlugin::AbsImageEncoder* encoder) {
        return encoder->AddImage(pixelMap);
    });
}

uint32_t ImagePacker::AddImage(ImageSource &source)
{
    ImageTrace imageTrace("ImagePacker::AddImage by imageSource");
    return AddImage(source, 0);
}

uint32_t ImagePacker::AddImage(ImageSource &source, uint32_t index)
{
    ImageTrace imageTrace("ImagePacker::AddImage by imageSource and index %u", index);
    uint32_t ret = SUCCESS;
    DecodeOptions decodeOpts;
    decodeOpts.desiredDynamicRange = encodeToSdr_ ? DecodeDynamicRange::SDR : DecodeDynamicRange::AUTO;
    bool isHdr = source.IsDecodeHdrImage(decodeOpts);
#if !defined(CROSS_PLATFORM)
    if (isHdr && source.CheckHdrType() == ImageHdrType::HDR_VIVID_DUAL) {
        if (picture_ != nullptr) {
            picture_.reset();  // release old inner picture
        }
        DecodingOptionsForPicture decodeOptsForPicture;
        decodeOptsForPicture.desiredPixelFormat = PixelFormat::NV12;
        decodeOptsForPicture.desireAuxiliaryPictures = { AuxiliaryPictureType::GAINMAP };
        picture_ = source.CreatePicture(decodeOptsForPicture, ret);
        if (ret == SUCCESS && picture_ != nullptr && picture_.get() != nullptr) {
            IMAGE_LOGD("image source create picture success.");
            return AddPicture(*picture_.get());
        }
    }
#endif
    if (pixelMap_ != nullptr) {
        pixelMap_.reset();  // release old inner pixelmap
    }
    pixelMap_ = source.CreatePixelMapEx(index, decodeOpts, ret);
    CHECK_ERROR_RETURN_RET_LOG(ret != SUCCESS, ret,
        "image source create pixel map failed.");
    bool cond = pixelMap_ == nullptr || pixelMap_.get() == nullptr;
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_MALLOC_ABNORMAL, "create the pixel map unique_ptr fail.");

    return AddImage(*pixelMap_.get());
}

#if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
uint32_t ImagePacker::AddPicture(Picture &picture)
{
    Picture::DumpPictureIfDumpEnabled(picture, "picture_encode_before");
    return DoEncodingFunc([this, &picture](ImagePlugin::AbsImageEncoder* encoder) {
        return encoder->AddPicture(picture);
    });
}
#endif

uint32_t ImagePacker::FinalizePacking()
{
    return DoEncodingFunc([](ImagePlugin::AbsImageEncoder* encoder) {
        auto res = encoder->FinalizeEncode();
        if (res != SUCCESS) {
            IMAGE_LOGE("FinalizePacking failed %{public}d.", res);
        }
        return res;
        }, false);
}

uint32_t ImagePacker::FinalizePacking(int64_t &packedSize)
{
    uint32_t ret = FinalizePacking();
    if (packerStream_ != nullptr) {
        packerStream_->Flush();
    }
    packedSize = (packerStream_ != nullptr) ? packerStream_->BytesWritten() : 0;
    return ret;
}

static ImagePlugin::AbsImageEncoder* GetEncoder(PluginServer &pluginServer, std::string format)
{
    std::map<std::string, AttrData> capabilities;
    capabilities.insert(std::map<std::string, AttrData>::value_type(IMAGE_ENCODE_FORMAT, AttrData(format)));
    return pluginServer.CreateObject<AbsImageEncoder>(AbsImageEncoder::SERVICE_DEFAULT, capabilities);
}

bool ImagePacker::GetEncoderPlugin(const PackOption &option)
{
    encoders_.clear();
    IMAGE_LOGD("GetEncoderPlugin current encoder plugin size %{public}zu.", encoders_.size());
    auto encoder = GetEncoder(pluginServer_, EXTENDED_ENCODER);
    if (encoder != nullptr) {
        encoders_.emplace_back(std::unique_ptr<ImagePlugin::AbsImageEncoder>(encoder));
    } else {
        IMAGE_LOGE("GetEncoderPlugin get ext_encoder plugin failed.");
    }
    encoder = GetEncoder(pluginServer_, option.format);
    if (encoder != nullptr) {
        encoders_.emplace_back(std::unique_ptr<ImagePlugin::AbsImageEncoder>(encoder));
    } else {
        IMAGE_LOGD("GetEncoderPlugin get %{public}s plugin failed, use ext_encoder plugin",
            option.format.c_str());
    }
    return encoders_.size() != SIZE_ZERO;
}

void ImagePacker::CopyOptionsToPlugin(const PackOption &opts, PlEncodeOptions &plOpts)
{
    plOpts.delayTimes = opts.delayTimes;
    plOpts.loop = opts.loop;
    plOpts.numberHint = opts.numberHint;
    plOpts.quality = opts.quality;
    plOpts.format = opts.format;
    plOpts.disposalTypes = opts.disposalTypes;
    plOpts.needsPackProperties = opts.needsPackProperties;
    plOpts.needsPackDfxData = opts.needsPackDfxData;
    plOpts.desiredDynamicRange = opts.desiredDynamicRange;
    plOpts.isEditScene = opts.isEditScene;
    plOpts.maxEmbedThumbnailDimension = opts.maxEmbedThumbnailDimension;
    plOpts.backgroundColor = opts.backgroundColor;
    plOpts.sizeLimit.maxSize = opts.sizeLimit.maxSize;
    plOpts.sizeLimit.antiAliasingLevel = opts.sizeLimit.antiAliasingLevel;
    plOpts.needsPackGPS = opts.needsPackGPS;
    plOpts.astcPackingOption.enableGPUEncode = opts.astcPackingOption.enableGPUEncode;

    if (opts.format == IMAGE_TIFF_FORMAT) {
        CopyTiffPackingOptions(opts.tiffPackingOption, plOpts.tiffPackingOption);
    }
}

void ImagePacker::CopyTiffPackingOptions(const PackingOptionsForTiff &src,
                                         ImagePlugin::PlPackingOptionsForTiff &dst)
{
    dst.compression = src.compression;
    dst.orientation = src.orientation;
    dst.xResolution = src.xResolution;
    dst.yResolution = src.yResolution;
    dst.resolutionUnit = src.resolutionUnit;
}

void ImagePacker::FreeOldPackerStream()
{
    if (packerStream_ != nullptr) {
        packerStream_.reset();
    }
}

bool ImagePacker::IsPackOptionValid(const PackOption &option)
{
    return !(option.quality > QUALITY_MAX || option.format.empty());
}

uint32_t ImagePacker::DoEncodingFunc(std::function<uint32_t(ImagePlugin::AbsImageEncoder*)> func, bool forAll)
{
    if (encoders_.size() == SIZE_ZERO) {
        IMAGE_LOGE("DoEncodingFunc encoders is empty.");
        return ERR_IMAGE_DECODE_ABNORMAL;
    }
    std::vector<uint32_t> rets;
    rets.resize(SIZE_ZERO);
    bool isSuccessOnce = false;
    for (size_t i = SIZE_ZERO; i < encoders_.size(); i++) {
        if (!forAll && isSuccessOnce) {
            IMAGE_LOGD("DoEncodingFunc encoding successed, reset other encoder.");
            encoders_.at(i).reset();
            continue;
        }
        auto iterRes = func(encoders_.at(i).get());
        rets.emplace_back(iterRes);
        if (iterRes == SUCCESS) {
            isSuccessOnce = true;
        }
        if (!forAll && !isSuccessOnce) {
            IMAGE_LOGD("DoEncodingFunc failed.");
        }
    }
    if (isSuccessOnce) {
        return SUCCESS;
    }
    return (rets.size() == SIZE_ZERO)?ERR_IMAGE_DECODE_ABNORMAL:rets.front();
}

#if defined(SUPPORT_LIBTIFF)
uint32_t ImagePacker::ValidateBinaryImageBufferInfo(const PixelBufferInfo &bufferInfo, const char *funcName)
{
    bool cond = (bufferInfo.data == nullptr) || (bufferInfo.dataSize == 0);
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
        "[ImagePacker] %{public}s failed, invalid buffer data", funcName);
    cond = (bufferInfo.width == 0) || (bufferInfo.height == 0);
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
        "[ImagePacker] %{public}s failed, invalid dimensions: width or height cannot be zero", funcName);

    // Get and validate rowBytes for Y1 format (1 bit per pixel, packed into bytes)
    uint64_t minRowBytes = (static_cast<uint64_t>(bufferInfo.width) + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
    uint64_t rowBytes = minRowBytes;
    if (bufferInfo.bytesPerRow > 0) {
        cond = bufferInfo.bytesPerRow < minRowBytes;
        CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
            "[ImagePacker] %{public}s failed, bytesPerRow too small: %{public}u < required %{public}llu",
            funcName, bufferInfo.bytesPerRow, static_cast<unsigned long long>(minRowBytes));
        rowBytes = bufferInfo.bytesPerRow;
    }
    // Check for overflow when calculating required size
    CHECK_ERROR_RETURN_RET_LOG(rowBytes > std::numeric_limits<uint64_t>::max() / bufferInfo.height,
        ERR_IMAGE_INVALID_PARAMETER,
        "[ImagePacker] %{public}s failed, dimensions cause overflow: %{public}ux%{public}u",
        funcName, bufferInfo.width, bufferInfo.height);
    // Check for buffer size
    uint64_t requiredSize = rowBytes * bufferInfo.height;
    static constexpr uint64_t maxDataSize = std::numeric_limits<uint32_t>::max();
    cond = bufferInfo.dataSize < requiredSize;
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
        "[ImagePacker] %{public}s failed, dataSize too small: %{public}llu < required %{public}llu",
        funcName, static_cast<unsigned long long>(bufferInfo.dataSize),
        static_cast<unsigned long long>(requiredSize));
    cond = bufferInfo.dataSize > maxDataSize;
    CHECK_ERROR_RETURN_RET_LOG(cond, ERR_IMAGE_INVALID_PARAMETER,
        "[ImagePacker] %{public}s failed, dataSize too large: %{public}llu > max %{public}llu",
        funcName, static_cast<unsigned long long>(bufferInfo.dataSize),
        static_cast<unsigned long long>(maxDataSize));
    return SUCCESS;
}

// Common helper function to encode binary image to TIFF using the provided stream
uint32_t ImagePacker::EncodeBinaryImageToTiffStream(const PixelBufferInfo &bufferInfo,
                                                    PackerStream &packerStream,
                                                    const PackingOptionsForTiff &option,
                                                    const char *funcName)
{
    auto tiffEncoder = std::make_unique<TiffEncoder>();
    CHECK_ERROR_RETURN_RET_LOG((tiffEncoder == nullptr), ERR_IMAGE_ENCODE_FAILED, "make TiffEncoder failed!");

    ImagePlugin::PlPackingOptionsForTiff tiffOption;
    CopyTiffPackingOptions(option, tiffOption);
    uint32_t ret = tiffEncoder->EncodeBinaryImageToTiff(&bufferInfo, packerStream, tiffOption);
    if (ret != SUCCESS) {
        IMAGE_LOGE("[ImagePacker] %{public}s failed, EncodeBinaryImageToTiff error: %{public}u", funcName, ret);
        return ret;
    }
    return SUCCESS;
}
#endif

uint32_t ImagePacker::PackBinaryImageToTiffFile(const PixelBufferInfo &bufferInfo, const int &fd,
                                                const PackingOptionsForTiff &option)
{
#if defined(SUPPORT_LIBTIFF)
    uint32_t ret = ValidateBinaryImageBufferInfo(bufferInfo, "PackBinaryImageToTiffFile");
    CHECK_ERROR_RETURN_RET(ret != SUCCESS, ret);
    CHECK_ERROR_RETURN_RET_LOG(fd < 0, ERR_IMAGE_INVALID_PARAMETER,
        "[ImagePacker] PackBinaryImageToTiffFile failed, invalid fd: %{public}d", fd);

    FilePackerStream *packerStream = new (std::nothrow) FilePackerStream(fd);
    CHECK_ERROR_RETURN_RET_LOG(!packerStream, ERR_IMAGE_INVALID_PARAMETER, "make file packer stream failed");
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<FilePackerStream>(packerStream);
    ret = EncodeBinaryImageToTiffStream(bufferInfo, *packerStream_, option, "PackBinaryImageToTiffFile");
    CHECK_ERROR_RETURN_RET(ret != SUCCESS, ret);

    return SUCCESS;
#else
    IMAGE_LOGE("[ImagePacker] PackBinaryImageToTiffFile failed, TIFF encoding is not supported");
    return ERR_IMAGE_ENCODE_FAILED;
#endif
}

uint32_t ImagePacker::PackBinaryImageToTiffData(const PixelBufferInfo &bufferInfo,
                                                const PackingOptionsForTiff &option,
                                                uint8_t *outputData, uint32_t &outputSize)
{
#if defined(SUPPORT_LIBTIFF)
    uint32_t ret = ValidateBinaryImageBufferInfo(bufferInfo, "PackBinaryImageToTiffData");
    if (ret != SUCCESS) {
        return ret;
    }
    if (outputData == nullptr || outputSize == 0) {
        IMAGE_LOGE("[ImagePacker] PackBinaryImageToTiffData failed, invalid output buffer");
        return ERR_IMAGE_INVALID_PARAMETER;
    }

    BufferPackerStream *packerStream = new (std::nothrow) BufferPackerStream(outputData, outputSize);
    CHECK_ERROR_RETURN_RET_LOG(!packerStream, ERR_IMAGE_INVALID_PARAMETER, "make buffer packer stream failed");
    FreeOldPackerStream();
    packerStream_ = std::unique_ptr<BufferPackerStream>(packerStream);
    ret = EncodeBinaryImageToTiffStream(bufferInfo, *packerStream_, option, "PackBinaryImageToTiffData");
    if (ret != SUCCESS) {
        return ret;
    }

    size_t actualSize = 0;
    packerStream_->GetRealWrittenSize(actualSize);
    outputSize = static_cast<uint32_t>(actualSize);

    IMAGE_LOGD("[ImagePacker] PackBinaryImageToTiffData success, outputSize: %{public}u", outputSize);
    return SUCCESS;
#else
    IMAGE_LOGE("[ImagePacker] PackBinaryImageToTiffData failed, TIFF encoding is not supported");
    return ERR_IMAGE_ENCODE_FAILED;
#endif
}

// class reference need explicit constructor and destructor, otherwise unique_ptr<T> use unnormal
ImagePacker::ImagePacker()
{}

ImagePacker::~ImagePacker()
{}
} // namespace Media
} // namespace OHOS