diff options
Diffstat (limited to 'core/fxcodec')
59 files changed, 20770 insertions, 0 deletions
diff --git a/core/fxcodec/DEPS b/core/fxcodec/DEPS new file mode 100644 index 0000000000..a88b3294a9 --- /dev/null +++ b/core/fxcodec/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + '+third_party/lcms2-2.6', + '+third_party/libjpeg/jpeglib.h', + # For non-standalone builds that may use libjpeg_turbo. + '+third_party/libjpeg_turbo/jpeglib.h', + '+third_party/libopenjpeg20', + '+third_party/zlib_v128', +] diff --git a/core/fxcodec/codec/DEPS b/core/fxcodec/codec/DEPS new file mode 100644 index 0000000000..495d288992 --- /dev/null +++ b/core/fxcodec/codec/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + '+third_party/libpng16/png.h', + '+third_party/libtiff/tiffiop.h', +] diff --git a/core/fxcodec/codec/codec_int.h b/core/fxcodec/codec/codec_int.h new file mode 100644 index 0000000000..d2f44dd674 --- /dev/null +++ b/core/fxcodec/codec/codec_int.h @@ -0,0 +1,428 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_CODEC_CODEC_INT_H_ +#define CORE_FXCODEC_CODEC_CODEC_INT_H_ + +#include <limits.h> + +#include <list> +#include <map> +#include <memory> +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_Context.h" +#include "core/include/fxcodec/fx_codec.h" +#include "third_party/libopenjpeg20/openjpeg.h" // For OPJ_SIZE_T. + +class CFX_IccProfileCache; +class CFX_IccTransformCache; +class CPDF_ColorSpace; + +class CCodec_BasicModule : public ICodec_BasicModule { + public: + // ICodec_BasicModule: + FX_BOOL RunLengthEncode(const uint8_t* src_buf, + FX_DWORD src_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size) override; + FX_BOOL A85Encode(const uint8_t* src_buf, + FX_DWORD src_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size) override; + ICodec_ScanlineDecoder* CreateRunLengthDecoder(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc) override; +}; + +class CCodec_ScanlineDecoder : public ICodec_ScanlineDecoder { + public: + CCodec_ScanlineDecoder(); + ~CCodec_ScanlineDecoder() override; + + // ICodec_ScanlineDecoder + FX_DWORD GetSrcOffset() override { return -1; } + void DownScale(int dest_width, int dest_height) override; + const uint8_t* GetScanline(int line) override; + FX_BOOL SkipToScanline(int line, IFX_Pause* pPause) override; + int GetWidth() override { return m_OutputWidth; } + int GetHeight() override { return m_OutputHeight; } + int CountComps() override { return m_nComps; } + int GetBPC() override { return m_bpc; } + FX_BOOL IsColorTransformed() override { return m_bColorTransformed; } + void ClearImageData() override { m_pDataCache.reset(); } + + protected: + class ImageDataCache { + public: + ImageDataCache(int width, int height, FX_DWORD pitch); + ~ImageDataCache(); + + bool AllocateCache(); + void AppendLine(const uint8_t* line); + + int NumLines() const { return m_nCachedLines; } + const uint8_t* GetLine(int line) const; + bool IsSameDimensions(int width, int height) const { + return width == m_Width && height == m_Height; + } + + private: + bool IsValid() const { return m_Data.get() != nullptr; } + + const int m_Width; + const int m_Height; + const FX_DWORD m_Pitch; + int m_nCachedLines; + std::unique_ptr<uint8_t, FxFreeDeleter> m_Data; + }; + + virtual FX_BOOL v_Rewind() = 0; + virtual uint8_t* v_GetNextLine() = 0; + virtual void v_DownScale(int dest_width, int dest_height) = 0; + + uint8_t* ReadNextLine(); + + int m_OrigWidth; + int m_OrigHeight; + int m_DownScale; + int m_OutputWidth; + int m_OutputHeight; + int m_nComps; + int m_bpc; + FX_DWORD m_Pitch; + FX_BOOL m_bColorTransformed; + int m_NextLine; + uint8_t* m_pLastScanline; + std::unique_ptr<ImageDataCache> m_pDataCache; +}; + +class CCodec_FaxModule : public ICodec_FaxModule { + public: + // ICodec_FaxModule: + ICodec_ScanlineDecoder* CreateDecoder(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int K, + FX_BOOL EndOfLine, + FX_BOOL EncodedByteAlign, + FX_BOOL BlackIs1, + int Columns, + int Rows) override; + FX_BOOL Encode(const uint8_t* src_buf, + int width, + int height, + int pitch, + uint8_t*& dest_buf, + FX_DWORD& dest_size) override; +}; + +class CCodec_FlateModule : public ICodec_FlateModule { + public: + virtual ICodec_ScanlineDecoder* CreateDecoder(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc, + int predictor, + int Colors, + int BitsPerComponent, + int Columns); + virtual FX_DWORD FlateOrLZWDecode(FX_BOOL bLZW, + const uint8_t* src_buf, + FX_DWORD src_size, + FX_BOOL bEarlyChange, + int predictor, + int Colors, + int BitsPerComponent, + int Columns, + FX_DWORD estimated_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size); + virtual FX_BOOL Encode(const uint8_t* src_buf, + FX_DWORD src_size, + int predictor, + int Colors, + int BitsPerComponent, + int Columns, + uint8_t*& dest_buf, + FX_DWORD& dest_size); + virtual FX_BOOL Encode(const uint8_t* src_buf, + FX_DWORD src_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size); +}; + +class CCodec_JpegModule : public ICodec_JpegModule { + public: + CCodec_JpegModule() {} + ICodec_ScanlineDecoder* CreateDecoder(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + FX_BOOL ColorTransform) override; + FX_BOOL LoadInfo(const uint8_t* src_buf, + FX_DWORD src_size, + int& width, + int& height, + int& num_components, + int& bits_per_components, + FX_BOOL& color_transform, + uint8_t** icc_buf_ptr, + FX_DWORD* icc_length) override; + FX_BOOL Encode(const CFX_DIBSource* pSource, + uint8_t*& dest_buf, + FX_STRSIZE& dest_size, + int quality, + const uint8_t* icc_buf, + FX_DWORD icc_length) override; + void* Start() override; + void Finish(void* pContext) override; + void Input(void* pContext, + const uint8_t* src_buf, + FX_DWORD src_size) override; +#ifndef PDF_ENABLE_XFA + int ReadHeader(void* pContext, int* width, int* height, int* nComps) override; +#else // PDF_ENABLE_XFA + int ReadHeader(void* pContext, + int* width, + int* height, + int* nComps, + CFX_DIBAttribute* pAttribute) override; +#endif // PDF_ENABLE_XFA + int StartScanline(void* pContext, int down_scale) override; + FX_BOOL ReadScanline(void* pContext, uint8_t* dest_buf) override; + FX_DWORD GetAvailInput(void* pContext, uint8_t** avail_buf_ptr) override; +}; + +#ifdef PDF_ENABLE_XFA +#define PNG_ERROR_SIZE 256 +class CCodec_PngModule : public ICodec_PngModule { + public: + CCodec_PngModule() { FXSYS_memset(m_szLastError, '\0', PNG_ERROR_SIZE); } + + virtual void* Start(void* pModule); + virtual void Finish(void* pContext); + virtual FX_BOOL Input(void* pContext, + const uint8_t* src_buf, + FX_DWORD src_size, + CFX_DIBAttribute* pAttribute); + + protected: + FX_CHAR m_szLastError[PNG_ERROR_SIZE]; +}; +class CCodec_GifModule : public ICodec_GifModule { + public: + CCodec_GifModule() { FXSYS_memset(m_szLastError, '\0', 256); } + virtual void* Start(void* pModule); + virtual void Finish(void* pContext); + virtual FX_DWORD GetAvailInput(void* pContext, uint8_t** avail_buf_ptr); + virtual void Input(void* pContext, const uint8_t* src_buf, FX_DWORD src_size); + + virtual int32_t ReadHeader(void* pContext, + int* width, + int* height, + int* pal_num, + void** pal_pp, + int* bg_index, + CFX_DIBAttribute* pAttribute); + + virtual int32_t LoadFrameInfo(void* pContext, int* frame_num); + + virtual int32_t LoadFrame(void* pContext, + int frame_num, + CFX_DIBAttribute* pAttribute); + + protected: + FX_CHAR m_szLastError[256]; +}; +class CCodec_BmpModule : public ICodec_BmpModule { + public: + CCodec_BmpModule() { FXSYS_memset(m_szLastError, 0, sizeof(m_szLastError)); } + void* Start(void* pModule) override; + void Finish(void* pContext) override; + FX_DWORD GetAvailInput(void* pContext, uint8_t** avail_buf_ptr) override; + void Input(void* pContext, + const uint8_t* src_buf, + FX_DWORD src_size) override; + int32_t ReadHeader(void* pContext, + int32_t* width, + int32_t* height, + FX_BOOL* tb_flag, + int32_t* components, + int32_t* pal_num, + FX_DWORD** pal_pp, + CFX_DIBAttribute* pAttribute) override; + int32_t LoadImage(void* pContext) override; + + protected: + FX_CHAR m_szLastError[256]; +}; +#endif // PDF_ENABLE_XFA + +class CCodec_IccModule : public ICodec_IccModule { + public: + ~CCodec_IccModule() override; + + // ICodec_IccModule: + IccCS GetProfileCS(const uint8_t* pProfileData, + unsigned int dwProfileSize) override; + IccCS GetProfileCS(IFX_FileRead* pFile) override; + void* CreateTransform(ICodec_IccModule::IccParam* pInputParam, + ICodec_IccModule::IccParam* pOutputParam, + ICodec_IccModule::IccParam* pProofParam = NULL, + FX_DWORD dwIntent = Icc_INTENT_PERCEPTUAL, + FX_DWORD dwFlag = Icc_FLAGS_DEFAULT, + FX_DWORD dwPrfIntent = Icc_INTENT_ABSOLUTE_COLORIMETRIC, + FX_DWORD dwPrfFlag = Icc_FLAGS_SOFTPROOFING) override; + void* CreateTransform_sRGB( + const uint8_t* pProfileData, + FX_DWORD dwProfileSize, + FX_DWORD& nComponents, + int32_t intent = 0, + FX_DWORD dwSrcFormat = Icc_FORMAT_DEFAULT) override; + void* CreateTransform_CMYK( + const uint8_t* pSrcProfileData, + FX_DWORD dwSrcProfileSize, + FX_DWORD& nSrcComponents, + const uint8_t* pDstProfileData, + FX_DWORD dwDstProfileSize, + int32_t intent = 0, + FX_DWORD dwSrcFormat = Icc_FORMAT_DEFAULT, + FX_DWORD dwDstFormat = Icc_FORMAT_DEFAULT) override; + void DestroyTransform(void* pTransform) override; + void Translate(void* pTransform, + FX_FLOAT* pSrcValues, + FX_FLOAT* pDestValues) override; + void TranslateScanline(void* pTransform, + uint8_t* pDest, + const uint8_t* pSrc, + int pixels) override; + void SetComponents(FX_DWORD nComponents) override { + m_nComponents = nComponents; + } + + protected: + enum Icc_CLASS { + Icc_CLASS_INPUT = 0, + Icc_CLASS_OUTPUT, + Icc_CLASS_PROOF, + Icc_CLASS_MAX + }; + void* CreateProfile(ICodec_IccModule::IccParam* pIccParam, + Icc_CLASS ic, + CFX_BinaryBuf* pTransformKey); + + FX_DWORD m_nComponents; + std::map<CFX_ByteString, CFX_IccTransformCache*> m_MapTranform; + std::map<CFX_ByteString, CFX_IccProfileCache*> m_MapProfile; +}; + +class CCodec_JpxModule : public ICodec_JpxModule { + public: + CCodec_JpxModule(); + ~CCodec_JpxModule() override; + + // ICodec_JpxModule: + CJPX_Decoder* CreateDecoder(const uint8_t* src_buf, + FX_DWORD src_size, + CPDF_ColorSpace* cs) override; + void GetImageInfo(CJPX_Decoder* pDecoder, + FX_DWORD* width, + FX_DWORD* height, + FX_DWORD* components) override; + bool Decode(CJPX_Decoder* pDecoder, + uint8_t* dest_data, + int pitch, + const std::vector<uint8_t>& offsets) override; + void DestroyDecoder(CJPX_Decoder* pDecoder) override; +}; + +#ifdef PDF_ENABLE_XFA +class CCodec_TiffModule : public ICodec_TiffModule { + public: + // ICodec_TiffModule + void* CreateDecoder(IFX_FileRead* file_ptr) override; + void GetFrames(void* ctx, int32_t& frames) override; + FX_BOOL LoadFrameInfo(void* ctx, + int32_t frame, + FX_DWORD& width, + FX_DWORD& height, + FX_DWORD& comps, + FX_DWORD& bpc, + CFX_DIBAttribute* pAttribute) override; + FX_BOOL Decode(void* ctx, class CFX_DIBitmap* pDIBitmap) override; + void DestroyDecoder(void* ctx) override; + + protected: + ~CCodec_TiffModule() override {} +}; +#endif // PDF_ENABLE_XFA + +class CCodec_Jbig2Context { + public: + CCodec_Jbig2Context(); + ~CCodec_Jbig2Context() {} + + FX_DWORD m_width; + FX_DWORD m_height; + CPDF_StreamAcc* m_pGlobalStream; + CPDF_StreamAcc* m_pSrcStream; + uint8_t* m_dest_buf; + FX_DWORD m_dest_pitch; + IFX_Pause* m_pPause; + CJBig2_Context* m_pContext; + CJBig2_Image* m_dest_image; +}; +class CCodec_Jbig2Module : public ICodec_Jbig2Module { + public: + CCodec_Jbig2Module() {} + ~CCodec_Jbig2Module() override; + + // ICodec_Jbig2Module + void* CreateJbig2Context() override; + FXCODEC_STATUS StartDecode(void* pJbig2Context, + CFX_PrivateData* pPrivateData, + FX_DWORD width, + FX_DWORD height, + CPDF_StreamAcc* src_stream, + CPDF_StreamAcc* global_stream, + uint8_t* dest_buf, + FX_DWORD dest_pitch, + IFX_Pause* pPause) override; + FXCODEC_STATUS ContinueDecode(void* pJbig2Context, + IFX_Pause* pPause) override; + void DestroyJbig2Context(void* pJbig2Context) override; +}; + +struct DecodeData { + public: + DecodeData(unsigned char* src_data, OPJ_SIZE_T src_size) + : src_data(src_data), src_size(src_size), offset(0) {} + unsigned char* src_data; + OPJ_SIZE_T src_size; + OPJ_SIZE_T offset; +}; + +void sycc420_to_rgb(opj_image_t* img); + +/* Wrappers for C-style callbacks. */ +OPJ_SIZE_T opj_read_from_memory(void* p_buffer, + OPJ_SIZE_T nb_bytes, + void* p_user_data); +OPJ_SIZE_T opj_write_from_memory(void* p_buffer, + OPJ_SIZE_T nb_bytes, + void* p_user_data); +OPJ_OFF_T opj_skip_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data); +OPJ_BOOL opj_seek_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data); + +#endif // CORE_FXCODEC_CODEC_CODEC_INT_H_ diff --git a/core/fxcodec/codec/fx_codec.cpp b/core/fxcodec/codec/fx_codec.cpp new file mode 100644 index 0000000000..f1298f9fb3 --- /dev/null +++ b/core/fxcodec/codec/fx_codec.cpp @@ -0,0 +1,487 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/include/fxcodec/fx_codec.h" + +#include <cmath> +#include <utility> + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcrt/fx_ext.h" +#include "core/include/fxcrt/fx_safe_types.h" +#include "third_party/base/logging.h" + +CCodec_ModuleMgr::CCodec_ModuleMgr() + : m_pBasicModule(new CCodec_BasicModule), + m_pFaxModule(new CCodec_FaxModule), + m_pJpegModule(new CCodec_JpegModule), + m_pJpxModule(new CCodec_JpxModule), + m_pJbig2Module(new CCodec_Jbig2Module), + m_pIccModule(new CCodec_IccModule), +#ifdef PDF_ENABLE_XFA + m_pPngModule(new CCodec_PngModule), + m_pGifModule(new CCodec_GifModule), + m_pBmpModule(new CCodec_BmpModule), + m_pTiffModule(new CCodec_TiffModule), +#endif // PDF_ENABLE_XFA + m_pFlateModule(new CCodec_FlateModule) { +} + +CCodec_ScanlineDecoder::ImageDataCache::ImageDataCache(int width, + int height, + FX_DWORD pitch) + : m_Width(width), m_Height(height), m_Pitch(pitch), m_nCachedLines(0) {} + +CCodec_ScanlineDecoder::ImageDataCache::~ImageDataCache() {} + +bool CCodec_ScanlineDecoder::ImageDataCache::AllocateCache() { + if (m_Pitch == 0 || m_Height < 0) + return false; + + FX_SAFE_SIZE_T size = m_Pitch; + size *= m_Height; + if (!size.IsValid()) + return false; + + m_Data.reset(FX_TryAlloc(uint8_t, size.ValueOrDie())); + return IsValid(); +} + +void CCodec_ScanlineDecoder::ImageDataCache::AppendLine(const uint8_t* line) { + // If the callers adds more lines than there is room, fail. + if (m_Pitch == 0 || m_nCachedLines >= m_Height) { + NOTREACHED(); + return; + } + + size_t offset = m_Pitch; + FXSYS_memcpy(m_Data.get() + offset * m_nCachedLines, line, m_Pitch); + ++m_nCachedLines; +} + +const uint8_t* CCodec_ScanlineDecoder::ImageDataCache::GetLine(int line) const { + if (m_Pitch == 0 || line < 0 || line >= m_nCachedLines) + return nullptr; + + size_t offset = m_Pitch; + return m_Data.get() + offset * line; +} + +CCodec_ScanlineDecoder::CCodec_ScanlineDecoder() + : m_NextLine(-1), m_pLastScanline(nullptr) {} + +CCodec_ScanlineDecoder::~CCodec_ScanlineDecoder() {} + +const uint8_t* CCodec_ScanlineDecoder::GetScanline(int line) { + if (m_pDataCache && line < m_pDataCache->NumLines()) + return m_pDataCache->GetLine(line); + + if (m_NextLine == line + 1) + return m_pLastScanline; + + if (m_NextLine < 0 || m_NextLine > line) { + if (!v_Rewind()) + return nullptr; + m_NextLine = 0; + } + while (m_NextLine < line) { + ReadNextLine(); + m_NextLine++; + } + m_pLastScanline = ReadNextLine(); + m_NextLine++; + return m_pLastScanline; +} + +FX_BOOL CCodec_ScanlineDecoder::SkipToScanline(int line, IFX_Pause* pPause) { + if (m_pDataCache && line < m_pDataCache->NumLines()) + return FALSE; + + if (m_NextLine == line || m_NextLine == line + 1) + return FALSE; + + if (m_NextLine < 0 || m_NextLine > line) { + v_Rewind(); + m_NextLine = 0; + } + m_pLastScanline = nullptr; + while (m_NextLine < line) { + m_pLastScanline = ReadNextLine(); + m_NextLine++; + if (pPause && pPause->NeedToPauseNow()) { + return TRUE; + } + } + return FALSE; +} + +uint8_t* CCodec_ScanlineDecoder::ReadNextLine() { + uint8_t* pLine = v_GetNextLine(); + if (!pLine) + return nullptr; + + if (m_pDataCache && m_NextLine == m_pDataCache->NumLines()) + m_pDataCache->AppendLine(pLine); + return pLine; +} + +void CCodec_ScanlineDecoder::DownScale(int dest_width, int dest_height) { + dest_width = std::abs(dest_width); + dest_height = std::abs(dest_height); + v_DownScale(dest_width, dest_height); + + if (m_pDataCache && + m_pDataCache->IsSameDimensions(m_OutputWidth, m_OutputHeight)) { + return; + } + + std::unique_ptr<ImageDataCache> cache( + new ImageDataCache(m_OutputWidth, m_OutputHeight, m_Pitch)); + if (!cache->AllocateCache()) + return; + + m_pDataCache = std::move(cache); +} + +FX_BOOL CCodec_BasicModule::RunLengthEncode(const uint8_t* src_buf, + FX_DWORD src_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size) { + return FALSE; +} + +#define EXPONENT_DETECT(ptr) \ + for (;; ptr++) { \ + if (!std::isdigit(*ptr)) { \ + if (endptr) \ + *endptr = (char*)ptr; \ + break; \ + } else { \ + exp_ret *= 10; \ + exp_ret += FXSYS_toDecimalDigit(*ptr); \ + continue; \ + } \ + } + +extern "C" double FXstrtod(const char* nptr, char** endptr) { + double ret = 0.0; + const char* ptr = nptr; + const char* exp_ptr = NULL; + int e_number = 0, e_signal = 0, e_point = 0, is_negative = 0; + int exp_ret = 0, exp_sig = 1, fra_ret = 0, fra_count = 0, fra_base = 1; + if (!nptr) { + return 0.0; + } + for (;; ptr++) { + if (!e_number && !e_point && (*ptr == '\t' || *ptr == ' ')) + continue; + + if (std::isdigit(*ptr)) { + if (!e_number) + e_number = 1; + + if (!e_point) { + ret *= 10; + ret += FXSYS_toDecimalDigit(*ptr); + } else { + fra_count++; + fra_ret *= 10; + fra_ret += FXSYS_toDecimalDigit(*ptr); + } + continue; + } + if (!e_point && *ptr == '.') { + e_point = 1; + continue; + } + if (!e_number && !e_point && !e_signal) { + switch (*ptr) { + case '-': + is_negative = 1; + case '+': + e_signal = 1; + continue; + } + } + if (e_number && (*ptr == 'e' || *ptr == 'E')) { + exp_ptr = ptr++; + if (*ptr == '+' || *ptr == '-') { + exp_sig = (*ptr++ == '+') ? 1 : -1; + if (!std::isdigit(*ptr)) { + if (endptr) { + *endptr = (char*)exp_ptr; + } + break; + } + EXPONENT_DETECT(ptr); + } else if (std::isdigit(*ptr)) { + EXPONENT_DETECT(ptr); + } else { + if (endptr) { + *endptr = (char*)exp_ptr; + } + break; + } + break; + } + if (ptr != nptr && !e_number) { + if (endptr) { + *endptr = (char*)nptr; + } + break; + } + if (endptr) { + *endptr = (char*)ptr; + } + break; + } + while (fra_count--) { + fra_base *= 10; + } + ret += (double)fra_ret / (double)fra_base; + if (exp_sig == 1) { + while (exp_ret--) { + ret *= 10.0; + } + } else { + while (exp_ret--) { + ret /= 10.0; + } + } + return is_negative ? -ret : ret; +} +#undef EXPONENT_DETECT + +FX_BOOL CCodec_BasicModule::A85Encode(const uint8_t* src_buf, + FX_DWORD src_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size) { + return FALSE; +} + +#ifdef PDF_ENABLE_XFA +CFX_DIBAttribute::CFX_DIBAttribute() + : m_nXDPI(-1), + m_nYDPI(-1), + m_fAspectRatio(-1.0f), + m_wDPIUnit(0), + m_nGifLeft(0), + m_nGifTop(0), + m_pGifLocalPalette(nullptr), + m_nGifLocalPalNum(0), + m_nBmpCompressType(0) { + FXSYS_memset(m_strTime, 0, sizeof(m_strTime)); +} +CFX_DIBAttribute::~CFX_DIBAttribute() { + for (const auto& pair : m_Exif) + FX_Free(pair.second); +} +#endif // PDF_ENABLE_XFA + +class CCodec_RLScanlineDecoder : public CCodec_ScanlineDecoder { + public: + CCodec_RLScanlineDecoder(); + ~CCodec_RLScanlineDecoder() override; + + FX_BOOL Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc); + + // CCodec_ScanlineDecoder + void v_DownScale(int dest_width, int dest_height) override {} + FX_BOOL v_Rewind() override; + uint8_t* v_GetNextLine() override; + FX_DWORD GetSrcOffset() override { return m_SrcOffset; } + + protected: + FX_BOOL CheckDestSize(); + void GetNextOperator(); + void UpdateOperator(uint8_t used_bytes); + + uint8_t* m_pScanline; + const uint8_t* m_pSrcBuf; + FX_DWORD m_SrcSize; + FX_DWORD m_dwLineBytes; + FX_DWORD m_SrcOffset; + FX_BOOL m_bEOD; + uint8_t m_Operator; +}; +CCodec_RLScanlineDecoder::CCodec_RLScanlineDecoder() + : m_pScanline(NULL), + m_pSrcBuf(NULL), + m_SrcSize(0), + m_dwLineBytes(0), + m_SrcOffset(0), + m_bEOD(FALSE), + m_Operator(0) {} +CCodec_RLScanlineDecoder::~CCodec_RLScanlineDecoder() { + FX_Free(m_pScanline); +} +FX_BOOL CCodec_RLScanlineDecoder::CheckDestSize() { + FX_DWORD i = 0; + FX_DWORD old_size = 0; + FX_DWORD dest_size = 0; + while (i < m_SrcSize) { + if (m_pSrcBuf[i] < 128) { + old_size = dest_size; + dest_size += m_pSrcBuf[i] + 1; + if (dest_size < old_size) { + return FALSE; + } + i += m_pSrcBuf[i] + 2; + } else if (m_pSrcBuf[i] > 128) { + old_size = dest_size; + dest_size += 257 - m_pSrcBuf[i]; + if (dest_size < old_size) { + return FALSE; + } + i += 2; + } else { + break; + } + } + if (((FX_DWORD)m_OrigWidth * m_nComps * m_bpc * m_OrigHeight + 7) / 8 > + dest_size) { + return FALSE; + } + return TRUE; +} +FX_BOOL CCodec_RLScanlineDecoder::Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc) { + m_pSrcBuf = src_buf; + m_SrcSize = src_size; + m_OutputWidth = m_OrigWidth = width; + m_OutputHeight = m_OrigHeight = height; + m_nComps = nComps; + m_bpc = bpc; + m_bColorTransformed = FALSE; + m_DownScale = 1; + // Aligning the pitch to 4 bytes requires an integer overflow check. + FX_SAFE_DWORD pitch = width; + pitch *= nComps; + pitch *= bpc; + pitch += 31; + pitch /= 32; + pitch *= 4; + if (!pitch.IsValid()) { + return FALSE; + } + m_Pitch = pitch.ValueOrDie(); + // Overflow should already have been checked before this is called. + m_dwLineBytes = (static_cast<FX_DWORD>(width) * nComps * bpc + 7) / 8; + m_pScanline = FX_Alloc(uint8_t, m_Pitch); + return CheckDestSize(); +} +FX_BOOL CCodec_RLScanlineDecoder::v_Rewind() { + FXSYS_memset(m_pScanline, 0, m_Pitch); + m_SrcOffset = 0; + m_bEOD = FALSE; + m_Operator = 0; + return TRUE; +} +uint8_t* CCodec_RLScanlineDecoder::v_GetNextLine() { + if (m_SrcOffset == 0) { + GetNextOperator(); + } else { + if (m_bEOD) { + return NULL; + } + } + FXSYS_memset(m_pScanline, 0, m_Pitch); + FX_DWORD col_pos = 0; + FX_BOOL eol = FALSE; + while (m_SrcOffset < m_SrcSize && !eol) { + if (m_Operator < 128) { + FX_DWORD copy_len = m_Operator + 1; + if (col_pos + copy_len >= m_dwLineBytes) { + copy_len = m_dwLineBytes - col_pos; + eol = TRUE; + } + if (copy_len >= m_SrcSize - m_SrcOffset) { + copy_len = m_SrcSize - m_SrcOffset; + m_bEOD = TRUE; + } + FXSYS_memcpy(m_pScanline + col_pos, m_pSrcBuf + m_SrcOffset, copy_len); + col_pos += copy_len; + UpdateOperator((uint8_t)copy_len); + } else if (m_Operator > 128) { + int fill = 0; + if (m_SrcOffset - 1 < m_SrcSize - 1) { + fill = m_pSrcBuf[m_SrcOffset]; + } + FX_DWORD duplicate_len = 257 - m_Operator; + if (col_pos + duplicate_len >= m_dwLineBytes) { + duplicate_len = m_dwLineBytes - col_pos; + eol = TRUE; + } + FXSYS_memset(m_pScanline + col_pos, fill, duplicate_len); + col_pos += duplicate_len; + UpdateOperator((uint8_t)duplicate_len); + } else { + m_bEOD = TRUE; + break; + } + } + return m_pScanline; +} +void CCodec_RLScanlineDecoder::GetNextOperator() { + if (m_SrcOffset >= m_SrcSize) { + m_Operator = 128; + return; + } + m_Operator = m_pSrcBuf[m_SrcOffset]; + m_SrcOffset++; +} +void CCodec_RLScanlineDecoder::UpdateOperator(uint8_t used_bytes) { + if (used_bytes == 0) { + return; + } + if (m_Operator < 128) { + FXSYS_assert((FX_DWORD)m_Operator + 1 >= used_bytes); + if (used_bytes == m_Operator + 1) { + m_SrcOffset += used_bytes; + GetNextOperator(); + return; + } + m_Operator -= used_bytes; + m_SrcOffset += used_bytes; + if (m_SrcOffset >= m_SrcSize) { + m_Operator = 128; + } + return; + } + uint8_t count = 257 - m_Operator; + FXSYS_assert((FX_DWORD)count >= used_bytes); + if (used_bytes == count) { + m_SrcOffset++; + GetNextOperator(); + return; + } + count -= used_bytes; + m_Operator = 257 - count; +} +ICodec_ScanlineDecoder* CCodec_BasicModule::CreateRunLengthDecoder( + const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc) { + CCodec_RLScanlineDecoder* pRLScanlineDecoder = new CCodec_RLScanlineDecoder; + if (!pRLScanlineDecoder->Create(src_buf, src_size, width, height, nComps, + bpc)) { + delete pRLScanlineDecoder; + return NULL; + } + return pRLScanlineDecoder; +} diff --git a/core/fxcodec/codec/fx_codec_bmp.cpp b/core/fxcodec/codec/fx_codec_bmp.cpp new file mode 100644 index 0000000000..d2299d6e56 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_bmp.cpp @@ -0,0 +1,125 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/codec_int.h" +#include "core/fxcodec/lbmp/fx_bmp.h" +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxge/fx_dib.h" +struct FXBMP_Context { + bmp_decompress_struct_p bmp_ptr; + void* parent_ptr; + void* child_ptr; + + void* (*m_AllocFunc)(unsigned int); + void (*m_FreeFunc)(void*); +}; +extern "C" { +static void* bmp_alloc_func(unsigned int size) { + return FX_Alloc(char, size); +} +static void bmp_free_func(void* p) { + FX_Free(p); +} +}; +static void bmp_error_data(bmp_decompress_struct_p bmp_ptr, + const FX_CHAR* err_msg) { + FXSYS_strncpy((char*)bmp_ptr->err_ptr, err_msg, BMP_MAX_ERROR_SIZE - 1); + longjmp(bmp_ptr->jmpbuf, 1); +} +static void bmp_read_scanline(bmp_decompress_struct_p bmp_ptr, + int32_t row_num, + uint8_t* row_buf) { + FXBMP_Context* p = (FXBMP_Context*)bmp_ptr->context_ptr; + CCodec_BmpModule* pModule = (CCodec_BmpModule*)p->parent_ptr; + pModule->ReadScanlineCallback(p->child_ptr, row_num, row_buf); +} +static FX_BOOL bmp_get_data_position(bmp_decompress_struct_p bmp_ptr, + FX_DWORD rcd_pos) { + FXBMP_Context* p = (FXBMP_Context*)bmp_ptr->context_ptr; + CCodec_BmpModule* pModule = (CCodec_BmpModule*)p->parent_ptr; + return pModule->InputImagePositionBufCallback(p->child_ptr, rcd_pos); +} +void* CCodec_BmpModule::Start(void* pModule) { + FXBMP_Context* p = (FXBMP_Context*)FX_Alloc(uint8_t, sizeof(FXBMP_Context)); + if (p == NULL) { + return NULL; + } + FXSYS_memset(p, 0, sizeof(FXBMP_Context)); + if (p == NULL) { + return NULL; + } + p->m_AllocFunc = bmp_alloc_func; + p->m_FreeFunc = bmp_free_func; + p->bmp_ptr = NULL; + p->parent_ptr = (void*)this; + p->child_ptr = pModule; + p->bmp_ptr = bmp_create_decompress(); + if (p->bmp_ptr == NULL) { + FX_Free(p); + return NULL; + } + p->bmp_ptr->context_ptr = (void*)p; + p->bmp_ptr->err_ptr = m_szLastError; + p->bmp_ptr->bmp_error_fn = bmp_error_data; + p->bmp_ptr->bmp_get_row_fn = bmp_read_scanline; + p->bmp_ptr->bmp_get_data_position_fn = bmp_get_data_position; + return p; +} +void CCodec_BmpModule::Finish(void* pContext) { + FXBMP_Context* p = (FXBMP_Context*)pContext; + if (p) { + bmp_destroy_decompress(&p->bmp_ptr); + p->m_FreeFunc(p); + } +} +int32_t CCodec_BmpModule::ReadHeader(void* pContext, + int32_t* width, + int32_t* height, + FX_BOOL* tb_flag, + int32_t* components, + int32_t* pal_num, + FX_DWORD** pal_pp, + CFX_DIBAttribute* pAttribute) { + FXBMP_Context* p = (FXBMP_Context*)pContext; + if (setjmp(p->bmp_ptr->jmpbuf)) { + return 0; + } + int32_t ret = bmp_read_header(p->bmp_ptr); + if (ret != 1) { + return ret; + } + *width = p->bmp_ptr->width; + *height = p->bmp_ptr->height; + *tb_flag = p->bmp_ptr->imgTB_flag; + *components = p->bmp_ptr->components; + *pal_num = p->bmp_ptr->pal_num; + *pal_pp = p->bmp_ptr->pal_ptr; + if (pAttribute) { + pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER; + pAttribute->m_nXDPI = p->bmp_ptr->dpi_x; + pAttribute->m_nYDPI = p->bmp_ptr->dpi_y; + pAttribute->m_nBmpCompressType = p->bmp_ptr->compress_flag; + } + return 1; +} +int32_t CCodec_BmpModule::LoadImage(void* pContext) { + FXBMP_Context* p = (FXBMP_Context*)pContext; + if (setjmp(p->bmp_ptr->jmpbuf)) { + return 0; + } + return bmp_decode_image(p->bmp_ptr); +} +FX_DWORD CCodec_BmpModule::GetAvailInput(void* pContext, + uint8_t** avial_buf_ptr) { + FXBMP_Context* p = (FXBMP_Context*)pContext; + return bmp_get_avail_input(p->bmp_ptr, avial_buf_ptr); +} +void CCodec_BmpModule::Input(void* pContext, + const uint8_t* src_buf, + FX_DWORD src_size) { + FXBMP_Context* p = (FXBMP_Context*)pContext; + bmp_input_buffer(p->bmp_ptr, (uint8_t*)src_buf, src_size); +} diff --git a/core/fxcodec/codec/fx_codec_fax.cpp b/core/fxcodec/codec/fx_codec_fax.cpp new file mode 100644 index 0000000000..13ffcda174 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_fax.cpp @@ -0,0 +1,830 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcodec/fx_codec.h" + +namespace { + +const uint8_t OneLeadPos[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +const uint8_t ZeroLeadPos[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, +}; + +int FindBit(const uint8_t* data_buf, int max_pos, int start_pos, int bit) { + if (start_pos >= max_pos) { + return max_pos; + } + const uint8_t* leading_pos = bit ? OneLeadPos : ZeroLeadPos; + if (start_pos % 8) { + uint8_t data = data_buf[start_pos / 8]; + if (bit) { + data &= 0xff >> (start_pos % 8); + } else { + data |= 0xff << (8 - start_pos % 8); + } + if (leading_pos[data] < 8) { + return start_pos / 8 * 8 + leading_pos[data]; + } + start_pos += 7; + } + uint8_t skip = bit ? 0x00 : 0xff; + int byte_pos = start_pos / 8; + int max_byte = (max_pos + 7) / 8; + while (byte_pos < max_byte) { + if (data_buf[byte_pos] != skip) { + break; + } + byte_pos++; + } + if (byte_pos == max_byte) { + return max_pos; + } + int pos = leading_pos[data_buf[byte_pos]] + byte_pos * 8; + if (pos > max_pos) { + pos = max_pos; + } + return pos; +} + +void FaxG4FindB1B2(const uint8_t* ref_buf, + int columns, + int a0, + bool a0color, + int& b1, + int& b2) { + uint8_t first_bit = + (a0 < 0) ? 1 : ((ref_buf[a0 / 8] & (1 << (7 - a0 % 8))) != 0); + b1 = FindBit(ref_buf, columns, a0 + 1, !first_bit); + if (b1 >= columns) { + b1 = b2 = columns; + return; + } + if (first_bit == !a0color) { + b1 = FindBit(ref_buf, columns, b1 + 1, first_bit); + first_bit = !first_bit; + } + if (b1 >= columns) { + b1 = b2 = columns; + return; + } + b2 = FindBit(ref_buf, columns, b1 + 1, first_bit); +} + +void FaxFillBits(uint8_t* dest_buf, int columns, int startpos, int endpos) { + if (startpos < 0) { + startpos = 0; + } + if (endpos < 0) { + endpos = 0; + } + if (endpos >= columns) { + endpos = columns; + } + if (startpos >= endpos) { + return; + } + int first_byte = startpos / 8; + int last_byte = (endpos - 1) / 8; + if (first_byte == last_byte) { + for (int i = startpos % 8; i <= (endpos - 1) % 8; i++) { + dest_buf[first_byte] -= 1 << (7 - i); + } + return; + } + int i; + for (i = startpos % 8; i < 8; i++) { + dest_buf[first_byte] -= 1 << (7 - i); + } + for (i = 0; i <= (endpos - 1) % 8; i++) { + dest_buf[last_byte] -= 1 << (7 - i); + } + if (last_byte > first_byte + 1) { + FXSYS_memset(dest_buf + first_byte + 1, 0, last_byte - first_byte - 1); + } +} + +#define NEXTBIT \ + src_buf[bitpos / 8] & (1 << (7 - bitpos % 8)); \ + bitpos++; +#define ADDBIT(code, bit) \ + code = code << 1; \ + if (bit) \ + code++; +#define GETBIT(bitpos) src_buf[bitpos / 8] & (1 << (7 - bitpos % 8)) + +const uint8_t FaxBlackRunIns[] = { + 0, 2, 0x02, 3, 0, 0x03, + 2, 0, 2, 0x02, 1, 0, + 0x03, 4, 0, 2, 0x02, 6, + 0, 0x03, 5, 0, 1, 0x03, + 7, 0, 2, 0x04, 9, 0, + 0x05, 8, 0, 3, 0x04, 10, + 0, 0x05, 11, 0, 0x07, 12, + 0, 2, 0x04, 13, 0, 0x07, + 14, 0, 1, 0x18, 15, 0, + 5, 0x08, 18, 0, 0x0f, 64, + 0, 0x17, 16, 0, 0x18, 17, + 0, 0x37, 0, 0, 10, 0x08, + 0x00, 0x07, 0x0c, 0x40, 0x07, 0x0d, + 0x80, 0x07, 0x17, 24, 0, 0x18, + 25, 0, 0x28, 23, 0, 0x37, + 22, 0, 0x67, 19, 0, 0x68, + 20, 0, 0x6c, 21, 0, 54, + 0x12, 1984 % 256, 1984 / 256, 0x13, 2048 % 256, 2048 / 256, + 0x14, 2112 % 256, 2112 / 256, 0x15, 2176 % 256, 2176 / 256, + 0x16, 2240 % 256, 2240 / 256, 0x17, 2304 % 256, 2304 / 256, + 0x1c, 2368 % 256, 2368 / 256, 0x1d, 2432 % 256, 2432 / 256, + 0x1e, 2496 % 256, 2496 / 256, 0x1f, 2560 % 256, 2560 / 256, + 0x24, 52, 0, 0x27, 55, 0, + 0x28, 56, 0, 0x2b, 59, 0, + 0x2c, 60, 0, 0x33, 320 % 256, 320 / 256, + 0x34, 384 % 256, 384 / 256, 0x35, 448 % 256, 448 / 256, + 0x37, 53, 0, 0x38, 54, 0, + 0x52, 50, 0, 0x53, 51, 0, + 0x54, 44, 0, 0x55, 45, 0, + 0x56, 46, 0, 0x57, 47, 0, + 0x58, 57, 0, 0x59, 58, 0, + 0x5a, 61, 0, 0x5b, 256 % 256, 256 / 256, + 0x64, 48, 0, 0x65, 49, 0, + 0x66, 62, 0, 0x67, 63, 0, + 0x68, 30, 0, 0x69, 31, 0, + 0x6a, 32, 0, 0x6b, 33, 0, + 0x6c, 40, 0, 0x6d, 41, 0, + 0xc8, 128, 0, 0xc9, 192, 0, + 0xca, 26, 0, 0xcb, 27, 0, + 0xcc, 28, 0, 0xcd, 29, 0, + 0xd2, 34, 0, 0xd3, 35, 0, + 0xd4, 36, 0, 0xd5, 37, 0, + 0xd6, 38, 0, 0xd7, 39, 0, + 0xda, 42, 0, 0xdb, 43, 0, + 20, 0x4a, 640 % 256, 640 / 256, 0x4b, 704 % 256, + 704 / 256, 0x4c, 768 % 256, 768 / 256, 0x4d, 832 % 256, + 832 / 256, 0x52, 1280 % 256, 1280 / 256, 0x53, 1344 % 256, + 1344 / 256, 0x54, 1408 % 256, 1408 / 256, 0x55, 1472 % 256, + 1472 / 256, 0x5a, 1536 % 256, 1536 / 256, 0x5b, 1600 % 256, + 1600 / 256, 0x64, 1664 % 256, 1664 / 256, 0x65, 1728 % 256, + 1728 / 256, 0x6c, 512 % 256, 512 / 256, 0x6d, 576 % 256, + 576 / 256, 0x72, 896 % 256, 896 / 256, 0x73, 960 % 256, + 960 / 256, 0x74, 1024 % 256, 1024 / 256, 0x75, 1088 % 256, + 1088 / 256, 0x76, 1152 % 256, 1152 / 256, 0x77, 1216 % 256, + 1216 / 256, 0xff}; + +const uint8_t FaxWhiteRunIns[] = { + 0, 0, 0, 6, 0x07, 2, + 0, 0x08, 3, 0, 0x0B, 4, + 0, 0x0C, 5, 0, 0x0E, 6, + 0, 0x0F, 7, 0, 6, 0x07, + 10, 0, 0x08, 11, 0, 0x12, + 128, 0, 0x13, 8, 0, 0x14, + 9, 0, 0x1b, 64, 0, 9, + 0x03, 13, 0, 0x07, 1, 0, + 0x08, 12, 0, 0x17, 192, 0, + 0x18, 1664 % 256, 1664 / 256, 0x2a, 16, 0, + 0x2B, 17, 0, 0x34, 14, 0, + 0x35, 15, 0, 12, 0x03, 22, + 0, 0x04, 23, 0, 0x08, 20, + 0, 0x0c, 19, 0, 0x13, 26, + 0, 0x17, 21, 0, 0x18, 28, + 0, 0x24, 27, 0, 0x27, 18, + 0, 0x28, 24, 0, 0x2B, 25, + 0, 0x37, 256 % 256, 256 / 256, 42, 0x02, + 29, 0, 0x03, 30, 0, 0x04, + 45, 0, 0x05, 46, 0, 0x0a, + 47, 0, 0x0b, 48, 0, 0x12, + 33, 0, 0x13, 34, 0, 0x14, + 35, 0, 0x15, 36, 0, 0x16, + 37, 0, 0x17, 38, 0, 0x1a, + 31, 0, 0x1b, 32, 0, 0x24, + 53, 0, 0x25, 54, 0, 0x28, + 39, 0, 0x29, 40, 0, 0x2a, + 41, 0, 0x2b, 42, 0, 0x2c, + 43, 0, 0x2d, 44, 0, 0x32, + 61, 0, 0x33, 62, 0, 0x34, + 63, 0, 0x35, 0, 0, 0x36, + 320 % 256, 320 / 256, 0x37, 384 % 256, 384 / 256, 0x4a, + 59, 0, 0x4b, 60, 0, 0x52, + 49, 0, 0x53, 50, 0, 0x54, + 51, 0, 0x55, 52, 0, 0x58, + 55, 0, 0x59, 56, 0, 0x5a, + 57, 0, 0x5b, 58, 0, 0x64, + 448 % 256, 448 / 256, 0x65, 512 % 256, 512 / 256, 0x67, + 640 % 256, 640 / 256, 0x68, 576 % 256, 576 / 256, 16, + 0x98, 1472 % 256, 1472 / 256, 0x99, 1536 % 256, 1536 / 256, + 0x9a, 1600 % 256, 1600 / 256, 0x9b, 1728 % 256, 1728 / 256, + 0xcc, 704 % 256, 704 / 256, 0xcd, 768 % 256, 768 / 256, + 0xd2, 832 % 256, 832 / 256, 0xd3, 896 % 256, 896 / 256, + 0xd4, 960 % 256, 960 / 256, 0xd5, 1024 % 256, 1024 / 256, + 0xd6, 1088 % 256, 1088 / 256, 0xd7, 1152 % 256, 1152 / 256, + 0xd8, 1216 % 256, 1216 / 256, 0xd9, 1280 % 256, 1280 / 256, + 0xda, 1344 % 256, 1344 / 256, 0xdb, 1408 % 256, 1408 / 256, + 0, 3, 0x08, 1792 % 256, 1792 / 256, 0x0c, + 1856 % 256, 1856 / 256, 0x0d, 1920 % 256, 1920 / 256, 10, + 0x12, 1984 % 256, 1984 / 256, 0x13, 2048 % 256, 2048 / 256, + 0x14, 2112 % 256, 2112 / 256, 0x15, 2176 % 256, 2176 / 256, + 0x16, 2240 % 256, 2240 / 256, 0x17, 2304 % 256, 2304 / 256, + 0x1c, 2368 % 256, 2368 / 256, 0x1d, 2432 % 256, 2432 / 256, + 0x1e, 2496 % 256, 2496 / 256, 0x1f, 2560 % 256, 2560 / 256, + 0xff, +}; + +int FaxGetRun(const uint8_t* ins_array, + const uint8_t* src_buf, + int& bitpos, + int bitsize) { + FX_DWORD code = 0; + int ins_off = 0; + while (1) { + uint8_t ins = ins_array[ins_off++]; + if (ins == 0xff) { + return -1; + } + if (bitpos >= bitsize) { + return -1; + } + code <<= 1; + if (src_buf[bitpos / 8] & (1 << (7 - bitpos % 8))) { + code++; + } + bitpos++; + int next_off = ins_off + ins * 3; + for (; ins_off < next_off; ins_off += 3) { + if (ins_array[ins_off] == code) { + return ins_array[ins_off + 1] + ins_array[ins_off + 2] * 256; + } + } + } +} + +FX_BOOL FaxG4GetRow(const uint8_t* src_buf, + int bitsize, + int& bitpos, + uint8_t* dest_buf, + const uint8_t* ref_buf, + int columns) { + int a0 = -1; + bool a0color = true; + while (1) { + if (bitpos >= bitsize) { + return FALSE; + } + int a1, a2, b1, b2; + FaxG4FindB1B2(ref_buf, columns, a0, a0color, b1, b2); + FX_BOOL bit = NEXTBIT; + int v_delta = 0; + if (bit) { + } else { + if (bitpos >= bitsize) { + return FALSE; + } + FX_BOOL bit1 = NEXTBIT; + if (bitpos >= bitsize) { + return FALSE; + } + FX_BOOL bit2 = NEXTBIT; + if (bit1 && bit2) { + v_delta = 1; + } else if (bit1) { + v_delta = -1; + } else if (bit2) { + int run_len1 = 0; + while (1) { + int run = FaxGetRun(a0color ? FaxWhiteRunIns : FaxBlackRunIns, + src_buf, bitpos, bitsize); + run_len1 += run; + if (run < 64) { + break; + } + } + if (a0 < 0) { + run_len1++; + } + a1 = a0 + run_len1; + if (!a0color) { + FaxFillBits(dest_buf, columns, a0, a1); + } + int run_len2 = 0; + while (1) { + int run = FaxGetRun(a0color ? FaxBlackRunIns : FaxWhiteRunIns, + src_buf, bitpos, bitsize); + run_len2 += run; + if (run < 64) { + break; + } + } + a2 = a1 + run_len2; + if (a0color) { + FaxFillBits(dest_buf, columns, a1, a2); + } + a0 = a2; + if (a0 < columns) { + continue; + } + return TRUE; + } else { + if (bitpos >= bitsize) { + return FALSE; + } + bit = NEXTBIT; + if (bit) { + if (!a0color) { + FaxFillBits(dest_buf, columns, a0, b2); + } + if (b2 >= columns) { + return TRUE; + } + a0 = b2; + continue; + } else { + if (bitpos >= bitsize) { + return FALSE; + } + FX_BOOL bit1 = NEXTBIT; + if (bitpos >= bitsize) { + return FALSE; + } + FX_BOOL bit2 = NEXTBIT; + if (bit1 && bit2) { + v_delta = 2; + } else if (bit1) { + v_delta = -2; + } else if (bit2) { + if (bitpos >= bitsize) { + return FALSE; + } + bit = NEXTBIT; + if (bit) { + v_delta = 3; + } else { + v_delta = -3; + } + } else { + if (bitpos >= bitsize) { + return FALSE; + } + bit = NEXTBIT; + if (bit) { + bitpos += 3; + continue; + } else { + bitpos += 5; + return TRUE; + } + } + } + } + } + a1 = b1 + v_delta; + if (!a0color) { + FaxFillBits(dest_buf, columns, a0, a1); + } + if (a1 >= columns) { + return TRUE; + } + a0 = a1; + a0color = !a0color; + } +} + +FX_BOOL FaxSkipEOL(const uint8_t* src_buf, int bitsize, int& bitpos) { + int startbit = bitpos; + while (bitpos < bitsize) { + int bit = NEXTBIT; + if (bit) { + if (bitpos - startbit <= 11) { + bitpos = startbit; + } + return TRUE; + } + } + return FALSE; +} + +FX_BOOL FaxGet1DLine(const uint8_t* src_buf, + int bitsize, + int& bitpos, + uint8_t* dest_buf, + int columns) { + bool color = true; + int startpos = 0; + while (1) { + if (bitpos >= bitsize) { + return FALSE; + } + int run_len = 0; + while (1) { + int run = FaxGetRun(color ? FaxWhiteRunIns : FaxBlackRunIns, src_buf, + bitpos, bitsize); + if (run < 0) { + while (bitpos < bitsize) { + int bit = NEXTBIT; + if (bit) { + return TRUE; + } + } + return FALSE; + } + run_len += run; + if (run < 64) { + break; + } + } + if (!color) { + FaxFillBits(dest_buf, columns, startpos, startpos + run_len); + } + startpos += run_len; + if (startpos >= columns) { + break; + } + color = !color; + } + return TRUE; +} + +const uint8_t BlackRunTerminator[128] = { + 0x37, 10, 0x02, 3, 0x03, 2, 0x02, 2, 0x03, 3, 0x03, 4, 0x02, 4, + 0x03, 5, 0x05, 6, 0x04, 6, 0x04, 7, 0x05, 7, 0x07, 7, 0x04, 8, + 0x07, 8, 0x18, 9, 0x17, 10, 0x18, 10, 0x08, 10, 0x67, 11, 0x68, 11, + 0x6c, 11, 0x37, 11, 0x28, 11, 0x17, 11, 0x18, 11, 0xca, 12, 0xcb, 12, + 0xcc, 12, 0xcd, 12, 0x68, 12, 0x69, 12, 0x6a, 12, 0x6b, 12, 0xd2, 12, + 0xd3, 12, 0xd4, 12, 0xd5, 12, 0xd6, 12, 0xd7, 12, 0x6c, 12, 0x6d, 12, + 0xda, 12, 0xdb, 12, 0x54, 12, 0x55, 12, 0x56, 12, 0x57, 12, 0x64, 12, + 0x65, 12, 0x52, 12, 0x53, 12, 0x24, 12, 0x37, 12, 0x38, 12, 0x27, 12, + 0x28, 12, 0x58, 12, 0x59, 12, 0x2b, 12, 0x2c, 12, 0x5a, 12, 0x66, 12, + 0x67, 12, +}; + +const uint8_t BlackRunMarkup[80] = { + 0x0f, 10, 0xc8, 12, 0xc9, 12, 0x5b, 12, 0x33, 12, 0x34, 12, 0x35, 12, + 0x6c, 13, 0x6d, 13, 0x4a, 13, 0x4b, 13, 0x4c, 13, 0x4d, 13, 0x72, 13, + 0x73, 13, 0x74, 13, 0x75, 13, 0x76, 13, 0x77, 13, 0x52, 13, 0x53, 13, + 0x54, 13, 0x55, 13, 0x5a, 13, 0x5b, 13, 0x64, 13, 0x65, 13, 0x08, 11, + 0x0c, 11, 0x0d, 11, 0x12, 12, 0x13, 12, 0x14, 12, 0x15, 12, 0x16, 12, + 0x17, 12, 0x1c, 12, 0x1d, 12, 0x1e, 12, 0x1f, 12, +}; + +const uint8_t WhiteRunTerminator[128] = { + 0x35, 8, 0x07, 6, 0x07, 4, 0x08, 4, 0x0B, 4, 0x0C, 4, 0x0E, 4, 0x0F, 4, + 0x13, 5, 0x14, 5, 0x07, 5, 0x08, 5, 0x08, 6, 0x03, 6, 0x34, 6, 0x35, 6, + 0x2a, 6, 0x2B, 6, 0x27, 7, 0x0c, 7, 0x08, 7, 0x17, 7, 0x03, 7, 0x04, 7, + 0x28, 7, 0x2B, 7, 0x13, 7, 0x24, 7, 0x18, 7, 0x02, 8, 0x03, 8, 0x1a, 8, + 0x1b, 8, 0x12, 8, 0x13, 8, 0x14, 8, 0x15, 8, 0x16, 8, 0x17, 8, 0x28, 8, + 0x29, 8, 0x2a, 8, 0x2b, 8, 0x2c, 8, 0x2d, 8, 0x04, 8, 0x05, 8, 0x0a, 8, + 0x0b, 8, 0x52, 8, 0x53, 8, 0x54, 8, 0x55, 8, 0x24, 8, 0x25, 8, 0x58, 8, + 0x59, 8, 0x5a, 8, 0x5b, 8, 0x4a, 8, 0x4b, 8, 0x32, 8, 0x33, 8, 0x34, 8, +}; + +const uint8_t WhiteRunMarkup[80] = { + 0x1b, 5, 0x12, 5, 0x17, 6, 0x37, 7, 0x36, 8, 0x37, 8, 0x64, 8, + 0x65, 8, 0x68, 8, 0x67, 8, 0xcc, 9, 0xcd, 9, 0xd2, 9, 0xd3, 9, + 0xd4, 9, 0xd5, 9, 0xd6, 9, 0xd7, 9, 0xd8, 9, 0xd9, 9, 0xda, 9, + 0xdb, 9, 0x98, 9, 0x99, 9, 0x9a, 9, 0x18, 6, 0x9b, 9, 0x08, 11, + 0x0c, 11, 0x0d, 11, 0x12, 12, 0x13, 12, 0x14, 12, 0x15, 12, 0x16, 12, + 0x17, 12, 0x1c, 12, 0x1d, 12, 0x1e, 12, 0x1f, 12, +}; + +void AddBitStream(uint8_t* dest_buf, int& dest_bitpos, int data, int bitlen) { + for (int i = bitlen - 1; i >= 0; i--) { + if (data & (1 << i)) { + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + } + dest_bitpos++; + } +} + +void FaxEncodeRun(uint8_t* dest_buf, int& dest_bitpos, int run, bool bWhite) { + while (run >= 2560) { + AddBitStream(dest_buf, dest_bitpos, 0x1f, 12); + run -= 2560; + } + if (run >= 64) { + int markup = run - run % 64; + const uint8_t* p = bWhite ? WhiteRunMarkup : BlackRunMarkup; + p += (markup / 64 - 1) * 2; + AddBitStream(dest_buf, dest_bitpos, *p, p[1]); + } + run %= 64; + const uint8_t* p = bWhite ? WhiteRunTerminator : BlackRunTerminator; + p += run * 2; + AddBitStream(dest_buf, dest_bitpos, *p, p[1]); +} + +void FaxEncode2DLine(uint8_t* dest_buf, + int& dest_bitpos, + const uint8_t* src_buf, + const uint8_t* ref_buf, + int cols) { + int a0 = -1; + bool a0color = true; + while (1) { + int a1 = FindBit(src_buf, cols, a0 + 1, !a0color); + int b1, b2; + FaxG4FindB1B2(ref_buf, cols, a0, a0color, b1, b2); + if (b2 < a1) { + dest_bitpos += 3; + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + dest_bitpos++; + a0 = b2; + } else if (a1 - b1 <= 3 && b1 - a1 <= 3) { + int delta = a1 - b1; + switch (delta) { + case 0: + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + break; + case 1: + case 2: + case 3: + dest_bitpos += delta == 1 ? 1 : delta + 2; + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + dest_bitpos++; + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + break; + case -1: + case -2: + case -3: + dest_bitpos += delta == -1 ? 1 : -delta + 2; + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + dest_bitpos++; + break; + } + dest_bitpos++; + a0 = a1; + a0color = !a0color; + } else { + int a2 = FindBit(src_buf, cols, a1 + 1, a0color); + dest_bitpos++; + dest_bitpos++; + dest_buf[dest_bitpos / 8] |= 1 << (7 - dest_bitpos % 8); + dest_bitpos++; + if (a0 < 0) { + a0 = 0; + } + FaxEncodeRun(dest_buf, dest_bitpos, a1 - a0, a0color); + FaxEncodeRun(dest_buf, dest_bitpos, a2 - a1, !a0color); + a0 = a2; + } + if (a0 >= cols) { + return; + } + } +} + +} // namespace + +class CCodec_FaxDecoder : public CCodec_ScanlineDecoder { + public: + CCodec_FaxDecoder(); + ~CCodec_FaxDecoder() override; + + FX_BOOL Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int K, + FX_BOOL EndOfLine, + FX_BOOL EncodedByteAlign, + FX_BOOL BlackIs1, + int Columns, + int Rows); + + // CCodec_ScanlineDecoder + void v_DownScale(int dest_width, int dest_height) override {} + FX_BOOL v_Rewind() override; + uint8_t* v_GetNextLine() override; + FX_DWORD GetSrcOffset() override; + + int m_Encoding, m_bEndOfLine, m_bByteAlign, m_bBlack; + int bitpos; + const uint8_t* m_pSrcBuf; + FX_DWORD m_SrcSize; + uint8_t* m_pScanlineBuf; + uint8_t* m_pRefBuf; +}; + +CCodec_FaxDecoder::CCodec_FaxDecoder() { + m_pScanlineBuf = NULL; + m_pRefBuf = NULL; +} +CCodec_FaxDecoder::~CCodec_FaxDecoder() { + FX_Free(m_pScanlineBuf); + FX_Free(m_pRefBuf); +} +FX_BOOL CCodec_FaxDecoder::Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int K, + FX_BOOL EndOfLine, + FX_BOOL EncodedByteAlign, + FX_BOOL BlackIs1, + int Columns, + int Rows) { + m_Encoding = K; + m_bEndOfLine = EndOfLine; + m_bByteAlign = EncodedByteAlign; + m_bBlack = BlackIs1; + m_OrigWidth = Columns; + m_OrigHeight = Rows; + if (m_OrigWidth == 0) { + m_OrigWidth = width; + } + if (m_OrigHeight == 0) { + m_OrigHeight = height; + } + // Should not overflow. Checked by FPDFAPI_CreateFaxDecoder. + m_Pitch = (static_cast<FX_DWORD>(m_OrigWidth) + 31) / 32 * 4; + m_OutputWidth = m_OrigWidth; + m_OutputHeight = m_OrigHeight; + m_pScanlineBuf = FX_Alloc(uint8_t, m_Pitch); + m_pRefBuf = FX_Alloc(uint8_t, m_Pitch); + m_pSrcBuf = src_buf; + m_SrcSize = src_size; + m_nComps = 1; + m_bpc = 1; + m_bColorTransformed = FALSE; + return TRUE; +} +FX_BOOL CCodec_FaxDecoder::v_Rewind() { + FXSYS_memset(m_pRefBuf, 0xff, m_Pitch); + bitpos = 0; + return TRUE; +} +uint8_t* CCodec_FaxDecoder::v_GetNextLine() { + int bitsize = m_SrcSize * 8; + FaxSkipEOL(m_pSrcBuf, bitsize, bitpos); + if (bitpos >= bitsize) { + return NULL; + } + FXSYS_memset(m_pScanlineBuf, 0xff, m_Pitch); + if (m_Encoding < 0) { + FaxG4GetRow(m_pSrcBuf, bitsize, bitpos, m_pScanlineBuf, m_pRefBuf, + m_OrigWidth); + FXSYS_memcpy(m_pRefBuf, m_pScanlineBuf, m_Pitch); + } else if (m_Encoding == 0) { + FaxGet1DLine(m_pSrcBuf, bitsize, bitpos, m_pScanlineBuf, m_OrigWidth); + } else { + FX_BOOL bNext1D = m_pSrcBuf[bitpos / 8] & (1 << (7 - bitpos % 8)); + bitpos++; + if (bNext1D) { + FaxGet1DLine(m_pSrcBuf, bitsize, bitpos, m_pScanlineBuf, m_OrigWidth); + } else { + FaxG4GetRow(m_pSrcBuf, bitsize, bitpos, m_pScanlineBuf, m_pRefBuf, + m_OrigWidth); + } + FXSYS_memcpy(m_pRefBuf, m_pScanlineBuf, m_Pitch); + } + if (m_bEndOfLine) { + FaxSkipEOL(m_pSrcBuf, bitsize, bitpos); + } + if (m_bByteAlign && bitpos < bitsize) { + int bitpos0 = bitpos; + int bitpos1 = (bitpos + 7) / 8 * 8; + while (m_bByteAlign && bitpos0 < bitpos1) { + int bit = m_pSrcBuf[bitpos0 / 8] & (1 << (7 - bitpos0 % 8)); + if (bit != 0) { + m_bByteAlign = FALSE; + } else { + bitpos0++; + } + } + if (m_bByteAlign) { + bitpos = bitpos1; + } + } + if (m_bBlack) { + for (FX_DWORD i = 0; i < m_Pitch; i++) { + m_pScanlineBuf[i] = ~m_pScanlineBuf[i]; + } + } + return m_pScanlineBuf; +} +FX_DWORD CCodec_FaxDecoder::GetSrcOffset() { + FX_DWORD ret = (bitpos + 7) / 8; + if (ret > m_SrcSize) { + ret = m_SrcSize; + } + return ret; +} + +void FaxG4Decode(const uint8_t* src_buf, + FX_DWORD src_size, + int* pbitpos, + uint8_t* dest_buf, + int width, + int height, + int pitch) { + if (pitch == 0) { + pitch = (width + 7) / 8; + } + uint8_t* ref_buf = FX_Alloc(uint8_t, pitch); + FXSYS_memset(ref_buf, 0xff, pitch); + int bitpos = *pbitpos; + for (int iRow = 0; iRow < height; iRow++) { + uint8_t* line_buf = dest_buf + iRow * pitch; + FXSYS_memset(line_buf, 0xff, pitch); + FaxG4GetRow(src_buf, src_size << 3, bitpos, line_buf, ref_buf, width); + FXSYS_memcpy(ref_buf, line_buf, pitch); + } + FX_Free(ref_buf); + *pbitpos = bitpos; +} + +class CCodec_FaxEncoder { + public: + CCodec_FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch); + ~CCodec_FaxEncoder(); + void Encode(uint8_t*& dest_buf, FX_DWORD& dest_size); + void Encode2DLine(const uint8_t* scan_line); + CFX_BinaryBuf m_DestBuf; + uint8_t* m_pRefLine; + uint8_t* m_pLineBuf; + int m_Cols, m_Rows, m_Pitch; + const uint8_t* m_pSrcBuf; +}; +CCodec_FaxEncoder::CCodec_FaxEncoder(const uint8_t* src_buf, + int width, + int height, + int pitch) { + m_pSrcBuf = src_buf; + m_Cols = width; + m_Rows = height; + m_Pitch = pitch; + m_pRefLine = FX_Alloc(uint8_t, m_Pitch); + FXSYS_memset(m_pRefLine, 0xff, m_Pitch); + m_pLineBuf = FX_Alloc2D(uint8_t, m_Pitch, 8); + m_DestBuf.EstimateSize(0, 10240); +} +CCodec_FaxEncoder::~CCodec_FaxEncoder() { + FX_Free(m_pRefLine); + FX_Free(m_pLineBuf); +} +void CCodec_FaxEncoder::Encode(uint8_t*& dest_buf, FX_DWORD& dest_size) { + int dest_bitpos = 0; + uint8_t last_byte = 0; + for (int i = 0; i < m_Rows; i++) { + const uint8_t* scan_line = m_pSrcBuf + i * m_Pitch; + FXSYS_memset(m_pLineBuf, 0, m_Pitch * 8); + m_pLineBuf[0] = last_byte; + FaxEncode2DLine(m_pLineBuf, dest_bitpos, scan_line, m_pRefLine, m_Cols); + m_DestBuf.AppendBlock(m_pLineBuf, dest_bitpos / 8); + last_byte = m_pLineBuf[dest_bitpos / 8]; + dest_bitpos %= 8; + FXSYS_memcpy(m_pRefLine, scan_line, m_Pitch); + } + if (dest_bitpos) { + m_DestBuf.AppendByte(last_byte); + } + dest_size = m_DestBuf.GetSize(); + dest_buf = m_DestBuf.DetachBuffer(); +} +FX_BOOL CCodec_FaxModule::Encode(const uint8_t* src_buf, + int width, + int height, + int pitch, + uint8_t*& dest_buf, + FX_DWORD& dest_size) { + CCodec_FaxEncoder encoder(src_buf, width, height, pitch); + encoder.Encode(dest_buf, dest_size); + return TRUE; +} +ICodec_ScanlineDecoder* CCodec_FaxModule::CreateDecoder( + const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int K, + FX_BOOL EndOfLine, + FX_BOOL EncodedByteAlign, + FX_BOOL BlackIs1, + int Columns, + int Rows) { + CCodec_FaxDecoder* pDecoder = new CCodec_FaxDecoder; + pDecoder->Create(src_buf, src_size, width, height, K, EndOfLine, + EncodedByteAlign, BlackIs1, Columns, Rows); + return pDecoder; +} diff --git a/core/fxcodec/codec/fx_codec_flate.cpp b/core/fxcodec/codec/fx_codec_flate.cpp new file mode 100644 index 0000000000..49a4f34166 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_flate.cpp @@ -0,0 +1,1007 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/codec_int.h" + +#include <algorithm> +#include <memory> + +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxcodec/fx_codec_flate.h" +#include "third_party/zlib_v128/zlib.h" + +extern "C" { +static void* my_alloc_func(void* opaque, + unsigned int items, + unsigned int size) { + return FX_Alloc2D(uint8_t, items, size); +} +static void my_free_func(void* opaque, void* address) { + FX_Free(address); +} +static int FPDFAPI_FlateGetTotalOut(void* context) { + return ((z_stream*)context)->total_out; +} +static int FPDFAPI_FlateGetTotalIn(void* context) { + return ((z_stream*)context)->total_in; +} +static void FPDFAPI_FlateCompress(unsigned char* dest_buf, + unsigned long* dest_size, + const unsigned char* src_buf, + unsigned long src_size) { + compress(dest_buf, dest_size, src_buf, src_size); +} +void* FPDFAPI_FlateInit(void* (*alloc_func)(void*, unsigned int, unsigned int), + void (*free_func)(void*, void*)) { + z_stream* p = (z_stream*)alloc_func(0, 1, sizeof(z_stream)); + if (!p) { + return NULL; + } + FXSYS_memset(p, 0, sizeof(z_stream)); + p->zalloc = alloc_func; + p->zfree = free_func; + inflateInit(p); + return p; +} +void FPDFAPI_FlateInput(void* context, + const unsigned char* src_buf, + unsigned int src_size) { + ((z_stream*)context)->next_in = (unsigned char*)src_buf; + ((z_stream*)context)->avail_in = src_size; +} +int FPDFAPI_FlateOutput(void* context, + unsigned char* dest_buf, + unsigned int dest_size) { + ((z_stream*)context)->next_out = dest_buf; + ((z_stream*)context)->avail_out = dest_size; + unsigned int pre_pos = (unsigned int)FPDFAPI_FlateGetTotalOut(context); + int ret = inflate((z_stream*)context, Z_SYNC_FLUSH); + unsigned int post_pos = (unsigned int)FPDFAPI_FlateGetTotalOut(context); + unsigned int written = post_pos - pre_pos; + if (written < dest_size) { + FXSYS_memset(dest_buf + written, '\0', dest_size - written); + } + return ret; +} +int FPDFAPI_FlateGetAvailIn(void* context) { + return ((z_stream*)context)->avail_in; +} +int FPDFAPI_FlateGetAvailOut(void* context) { + return ((z_stream*)context)->avail_out; +} +void FPDFAPI_FlateEnd(void* context) { + inflateEnd((z_stream*)context); + ((z_stream*)context)->zfree(0, context); +} +} // extern "C" + +namespace { + +class CLZWDecoder { + public: + int Decode(uint8_t* output, + FX_DWORD& outlen, + const uint8_t* input, + FX_DWORD& size, + FX_BOOL bEarlyChange); + + private: + void AddCode(FX_DWORD prefix_code, uint8_t append_char); + void DecodeString(FX_DWORD code); + + FX_DWORD m_InPos; + FX_DWORD m_OutPos; + uint8_t* m_pOutput; + const uint8_t* m_pInput; + FX_BOOL m_Early; + FX_DWORD m_CodeArray[5021]; + FX_DWORD m_nCodes; + uint8_t m_DecodeStack[4000]; + FX_DWORD m_StackLen; + int m_CodeLen; +}; +void CLZWDecoder::AddCode(FX_DWORD prefix_code, uint8_t append_char) { + if (m_nCodes + m_Early == 4094) { + return; + } + m_CodeArray[m_nCodes++] = (prefix_code << 16) | append_char; + if (m_nCodes + m_Early == 512 - 258) { + m_CodeLen = 10; + } else if (m_nCodes + m_Early == 1024 - 258) { + m_CodeLen = 11; + } else if (m_nCodes + m_Early == 2048 - 258) { + m_CodeLen = 12; + } +} +void CLZWDecoder::DecodeString(FX_DWORD code) { + while (1) { + int index = code - 258; + if (index < 0 || index >= (int)m_nCodes) { + break; + } + FX_DWORD data = m_CodeArray[index]; + if (m_StackLen >= sizeof(m_DecodeStack)) { + return; + } + m_DecodeStack[m_StackLen++] = (uint8_t)data; + code = data >> 16; + } + if (m_StackLen >= sizeof(m_DecodeStack)) { + return; + } + m_DecodeStack[m_StackLen++] = (uint8_t)code; +} +int CLZWDecoder::Decode(uint8_t* dest_buf, + FX_DWORD& dest_size, + const uint8_t* src_buf, + FX_DWORD& src_size, + FX_BOOL bEarlyChange) { + m_CodeLen = 9; + m_InPos = 0; + m_OutPos = 0; + m_pInput = src_buf; + m_pOutput = dest_buf; + m_Early = bEarlyChange ? 1 : 0; + m_nCodes = 0; + FX_DWORD old_code = (FX_DWORD)-1; + uint8_t last_char; + while (1) { + if (m_InPos + m_CodeLen > src_size * 8) { + break; + } + int byte_pos = m_InPos / 8; + int bit_pos = m_InPos % 8, bit_left = m_CodeLen; + FX_DWORD code = 0; + if (bit_pos) { + bit_left -= 8 - bit_pos; + code = (m_pInput[byte_pos++] & ((1 << (8 - bit_pos)) - 1)) << bit_left; + } + if (bit_left < 8) { + code |= m_pInput[byte_pos] >> (8 - bit_left); + } else { + bit_left -= 8; + code |= m_pInput[byte_pos++] << bit_left; + if (bit_left) { + code |= m_pInput[byte_pos] >> (8 - bit_left); + } + } + m_InPos += m_CodeLen; + if (code < 256) { + if (m_OutPos == dest_size) { + return -5; + } + if (m_pOutput) { + m_pOutput[m_OutPos] = (uint8_t)code; + } + m_OutPos++; + last_char = (uint8_t)code; + if (old_code != (FX_DWORD)-1) { + AddCode(old_code, last_char); + } + old_code = code; + } else if (code == 256) { + m_CodeLen = 9; + m_nCodes = 0; + old_code = (FX_DWORD)-1; + } else if (code == 257) { + break; + } else { + if (old_code == (FX_DWORD)-1) { + return 2; + } + m_StackLen = 0; + if (code >= m_nCodes + 258) { + if (m_StackLen < sizeof(m_DecodeStack)) { + m_DecodeStack[m_StackLen++] = last_char; + } + DecodeString(old_code); + } else { + DecodeString(code); + } + if (m_OutPos + m_StackLen > dest_size) { + return -5; + } + if (m_pOutput) { + for (FX_DWORD i = 0; i < m_StackLen; i++) { + m_pOutput[m_OutPos + i] = m_DecodeStack[m_StackLen - i - 1]; + } + } + m_OutPos += m_StackLen; + last_char = m_DecodeStack[m_StackLen - 1]; + if (old_code < 256) { + AddCode(old_code, last_char); + } else if (old_code - 258 >= m_nCodes) { + dest_size = m_OutPos; + src_size = (m_InPos + 7) / 8; + return 0; + } else { + AddCode(old_code, last_char); + } + old_code = code; + } + } + dest_size = m_OutPos; + src_size = (m_InPos + 7) / 8; + return 0; +} + +uint8_t PaethPredictor(int a, int b, int c) { + int p = a + b - c; + int pa = FXSYS_abs(p - a); + int pb = FXSYS_abs(p - b); + int pc = FXSYS_abs(p - c); + if (pa <= pb && pa <= pc) { + return (uint8_t)a; + } + if (pb <= pc) { + return (uint8_t)b; + } + return (uint8_t)c; +} + +FX_BOOL PNG_PredictorEncode(uint8_t*& data_buf, + FX_DWORD& data_size, + int predictor, + int Colors, + int BitsPerComponent, + int Columns) { + const int BytesPerPixel = (Colors * BitsPerComponent + 7) / 8; + const int row_size = (Colors * BitsPerComponent * Columns + 7) / 8; + if (row_size <= 0) + return FALSE; + const int row_count = (data_size + row_size - 1) / row_size; + const int last_row_size = data_size % row_size; + uint8_t* dest_buf = FX_Alloc2D(uint8_t, row_size + 1, row_count); + int byte_cnt = 0; + uint8_t* pSrcData = data_buf; + uint8_t* pDestData = dest_buf; + for (int row = 0; row < row_count; row++) { + if (predictor == 10) { + pDestData[0] = 0; + int move_size = row_size; + if (move_size * (row + 1) > (int)data_size) { + move_size = data_size - (move_size * row); + } + FXSYS_memmove(pDestData + 1, pSrcData, move_size); + pDestData += (move_size + 1); + pSrcData += move_size; + byte_cnt += move_size; + continue; + } + for (int byte = 0; byte < row_size && byte_cnt < (int)data_size; byte++) { + switch (predictor) { + case 11: { + pDestData[0] = 1; + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pSrcData[byte - BytesPerPixel]; + } + pDestData[byte + 1] = pSrcData[byte] - left; + } break; + case 12: { + pDestData[0] = 2; + uint8_t up = 0; + if (row) { + up = pSrcData[byte - row_size]; + } + pDestData[byte + 1] = pSrcData[byte] - up; + } break; + case 13: { + pDestData[0] = 3; + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pSrcData[byte - BytesPerPixel]; + } + uint8_t up = 0; + if (row) { + up = pSrcData[byte - row_size]; + } + pDestData[byte + 1] = pSrcData[byte] - (left + up) / 2; + } break; + case 14: { + pDestData[0] = 4; + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pSrcData[byte - BytesPerPixel]; + } + uint8_t up = 0; + if (row) { + up = pSrcData[byte - row_size]; + } + uint8_t upper_left = 0; + if (byte >= BytesPerPixel && row) { + upper_left = pSrcData[byte - row_size - BytesPerPixel]; + } + pDestData[byte + 1] = + pSrcData[byte] - PaethPredictor(left, up, upper_left); + } break; + default: { pDestData[byte + 1] = pSrcData[byte]; } break; + } + byte_cnt++; + } + pDestData += (row_size + 1); + pSrcData += row_size; + } + FX_Free(data_buf); + data_buf = dest_buf; + data_size = (row_size + 1) * row_count - + (last_row_size > 0 ? (row_size - last_row_size) : 0); + return TRUE; +} + +void PNG_PredictLine(uint8_t* pDestData, + const uint8_t* pSrcData, + const uint8_t* pLastLine, + int bpc, + int nColors, + int nPixels) { + int row_size = (nPixels * bpc * nColors + 7) / 8; + int BytesPerPixel = (bpc * nColors + 7) / 8; + uint8_t tag = pSrcData[0]; + if (tag == 0) { + FXSYS_memmove(pDestData, pSrcData + 1, row_size); + return; + } + for (int byte = 0; byte < row_size; byte++) { + uint8_t raw_byte = pSrcData[byte + 1]; + switch (tag) { + case 1: { + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pDestData[byte - BytesPerPixel]; + } + pDestData[byte] = raw_byte + left; + break; + } + case 2: { + uint8_t up = 0; + if (pLastLine) { + up = pLastLine[byte]; + } + pDestData[byte] = raw_byte + up; + break; + } + case 3: { + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pDestData[byte - BytesPerPixel]; + } + uint8_t up = 0; + if (pLastLine) { + up = pLastLine[byte]; + } + pDestData[byte] = raw_byte + (up + left) / 2; + break; + } + case 4: { + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pDestData[byte - BytesPerPixel]; + } + uint8_t up = 0; + if (pLastLine) { + up = pLastLine[byte]; + } + uint8_t upper_left = 0; + if (byte >= BytesPerPixel && pLastLine) { + upper_left = pLastLine[byte - BytesPerPixel]; + } + pDestData[byte] = raw_byte + PaethPredictor(left, up, upper_left); + break; + } + default: + pDestData[byte] = raw_byte; + break; + } + } +} + +FX_BOOL PNG_Predictor(uint8_t*& data_buf, + FX_DWORD& data_size, + int Colors, + int BitsPerComponent, + int Columns) { + const int BytesPerPixel = (Colors * BitsPerComponent + 7) / 8; + const int row_size = (Colors * BitsPerComponent * Columns + 7) / 8; + if (row_size <= 0) + return FALSE; + const int row_count = (data_size + row_size) / (row_size + 1); + if (row_count <= 0) + return FALSE; + const int last_row_size = data_size % (row_size + 1); + uint8_t* dest_buf = FX_Alloc2D(uint8_t, row_size, row_count); + int byte_cnt = 0; + uint8_t* pSrcData = data_buf; + uint8_t* pDestData = dest_buf; + for (int row = 0; row < row_count; row++) { + uint8_t tag = pSrcData[0]; + byte_cnt++; + if (tag == 0) { + int move_size = row_size; + if ((row + 1) * (move_size + 1) > (int)data_size) { + move_size = last_row_size - 1; + } + FXSYS_memmove(pDestData, pSrcData + 1, move_size); + pSrcData += move_size + 1; + pDestData += move_size; + byte_cnt += move_size; + continue; + } + for (int byte = 0; byte < row_size && byte_cnt < (int)data_size; byte++) { + uint8_t raw_byte = pSrcData[byte + 1]; + switch (tag) { + case 1: { + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pDestData[byte - BytesPerPixel]; + } + pDestData[byte] = raw_byte + left; + break; + } + case 2: { + uint8_t up = 0; + if (row) { + up = pDestData[byte - row_size]; + } + pDestData[byte] = raw_byte + up; + break; + } + case 3: { + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pDestData[byte - BytesPerPixel]; + } + uint8_t up = 0; + if (row) { + up = pDestData[byte - row_size]; + } + pDestData[byte] = raw_byte + (up + left) / 2; + break; + } + case 4: { + uint8_t left = 0; + if (byte >= BytesPerPixel) { + left = pDestData[byte - BytesPerPixel]; + } + uint8_t up = 0; + if (row) { + up = pDestData[byte - row_size]; + } + uint8_t upper_left = 0; + if (byte >= BytesPerPixel && row) { + upper_left = pDestData[byte - row_size - BytesPerPixel]; + } + pDestData[byte] = raw_byte + PaethPredictor(left, up, upper_left); + break; + } + default: + pDestData[byte] = raw_byte; + break; + } + byte_cnt++; + } + pSrcData += row_size + 1; + pDestData += row_size; + } + FX_Free(data_buf); + data_buf = dest_buf; + data_size = row_size * row_count - + (last_row_size > 0 ? (row_size + 1 - last_row_size) : 0); + return TRUE; +} + +void TIFF_PredictorEncodeLine(uint8_t* dest_buf, + int row_size, + int BitsPerComponent, + int Colors, + int Columns) { + int BytesPerPixel = BitsPerComponent * Colors / 8; + if (BitsPerComponent < 8) { + uint8_t mask = 0x01; + if (BitsPerComponent == 2) { + mask = 0x03; + } else if (BitsPerComponent == 4) { + mask = 0x0F; + } + int row_bits = Colors * BitsPerComponent * Columns; + for (int i = row_bits - BitsPerComponent; i >= BitsPerComponent; + i -= BitsPerComponent) { + int col = i % 8; + int index = i / 8; + int col_pre = + (col == 0) ? (8 - BitsPerComponent) : (col - BitsPerComponent); + int index_pre = (col == 0) ? (index - 1) : index; + uint8_t cur = (dest_buf[index] >> (8 - col - BitsPerComponent)) & mask; + uint8_t left = + (dest_buf[index_pre] >> (8 - col_pre - BitsPerComponent)) & mask; + cur -= left; + cur &= mask; + cur <<= (8 - col - BitsPerComponent); + dest_buf[index] &= ~(mask << ((8 - col - BitsPerComponent))); + dest_buf[index] |= cur; + } + } else if (BitsPerComponent == 8) { + for (int i = row_size - 1; i >= BytesPerPixel; i--) { + dest_buf[i] -= dest_buf[i - BytesPerPixel]; + } + } else { + for (int i = row_size - BytesPerPixel; i >= BytesPerPixel; + i -= BytesPerPixel) { + FX_WORD pixel = (dest_buf[i] << 8) | dest_buf[i + 1]; + pixel -= + (dest_buf[i - BytesPerPixel] << 8) | dest_buf[i - BytesPerPixel + 1]; + dest_buf[i] = pixel >> 8; + dest_buf[i + 1] = (uint8_t)pixel; + } + } +} + +FX_BOOL TIFF_PredictorEncode(uint8_t*& data_buf, + FX_DWORD& data_size, + int Colors, + int BitsPerComponent, + int Columns) { + int row_size = (Colors * BitsPerComponent * Columns + 7) / 8; + if (row_size == 0) + return FALSE; + const int row_count = (data_size + row_size - 1) / row_size; + const int last_row_size = data_size % row_size; + for (int row = 0; row < row_count; row++) { + uint8_t* scan_line = data_buf + row * row_size; + if ((row + 1) * row_size > (int)data_size) { + row_size = last_row_size; + } + TIFF_PredictorEncodeLine(scan_line, row_size, BitsPerComponent, Colors, + Columns); + } + return TRUE; +} + +void TIFF_PredictLine(uint8_t* dest_buf, + FX_DWORD row_size, + int BitsPerComponent, + int Colors, + int Columns) { + if (BitsPerComponent == 1) { + int row_bits = std::min(BitsPerComponent * Colors * Columns, + pdfium::base::checked_cast<int>(row_size * 8)); + int index_pre = 0; + int col_pre = 0; + for (int i = 1; i < row_bits; i++) { + int col = i % 8; + int index = i / 8; + if (((dest_buf[index] >> (7 - col)) & 1) ^ + ((dest_buf[index_pre] >> (7 - col_pre)) & 1)) { + dest_buf[index] |= 1 << (7 - col); + } else { + dest_buf[index] &= ~(1 << (7 - col)); + } + index_pre = index; + col_pre = col; + } + return; + } + int BytesPerPixel = BitsPerComponent * Colors / 8; + if (BitsPerComponent == 16) { + for (FX_DWORD i = BytesPerPixel; i < row_size; i += 2) { + FX_WORD pixel = + (dest_buf[i - BytesPerPixel] << 8) | dest_buf[i - BytesPerPixel + 1]; + pixel += (dest_buf[i] << 8) | dest_buf[i + 1]; + dest_buf[i] = pixel >> 8; + dest_buf[i + 1] = (uint8_t)pixel; + } + } else { + for (FX_DWORD i = BytesPerPixel; i < row_size; i++) { + dest_buf[i] += dest_buf[i - BytesPerPixel]; + } + } +} + +FX_BOOL TIFF_Predictor(uint8_t*& data_buf, + FX_DWORD& data_size, + int Colors, + int BitsPerComponent, + int Columns) { + int row_size = (Colors * BitsPerComponent * Columns + 7) / 8; + if (row_size == 0) + return FALSE; + const int row_count = (data_size + row_size - 1) / row_size; + const int last_row_size = data_size % row_size; + for (int row = 0; row < row_count; row++) { + uint8_t* scan_line = data_buf + row * row_size; + if ((row + 1) * row_size > (int)data_size) { + row_size = last_row_size; + } + TIFF_PredictLine(scan_line, row_size, BitsPerComponent, Colors, Columns); + } + return TRUE; +} + +void FlateUncompress(const uint8_t* src_buf, + FX_DWORD src_size, + FX_DWORD orig_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size, + FX_DWORD& offset) { + FX_DWORD guess_size = orig_size ? orig_size : src_size * 2; + const FX_DWORD kStepSize = 10240; + FX_DWORD alloc_step = orig_size ? kStepSize : std::min(src_size, kStepSize); + static const FX_DWORD kMaxInitialAllocSize = 10000000; + if (guess_size > kMaxInitialAllocSize) { + guess_size = kMaxInitialAllocSize; + alloc_step = kMaxInitialAllocSize; + } + FX_DWORD buf_size = guess_size; + FX_DWORD last_buf_size = buf_size; + + dest_buf = nullptr; + dest_size = 0; + void* context = FPDFAPI_FlateInit(my_alloc_func, my_free_func); + if (!context) + return; + + std::unique_ptr<uint8_t, FxFreeDeleter> guess_buf( + FX_Alloc(uint8_t, guess_size + 1)); + guess_buf.get()[guess_size] = '\0'; + + FPDFAPI_FlateInput(context, src_buf, src_size); + + if (src_size < kStepSize) { + // This is the old implementation. + uint8_t* cur_buf = guess_buf.get(); + while (1) { + int32_t ret = FPDFAPI_FlateOutput(context, cur_buf, buf_size); + if (ret != Z_OK) + break; + int32_t avail_buf_size = FPDFAPI_FlateGetAvailOut(context); + if (avail_buf_size != 0) + break; + + FX_DWORD old_size = guess_size; + guess_size += alloc_step; + if (guess_size < old_size || guess_size + 1 < guess_size) { + FPDFAPI_FlateEnd(context); + return; + } + + { + uint8_t* new_buf = + FX_Realloc(uint8_t, guess_buf.release(), guess_size + 1); + guess_buf.reset(new_buf); + } + guess_buf.get()[guess_size] = '\0'; + cur_buf = guess_buf.get() + old_size; + buf_size = guess_size - old_size; + } + dest_size = FPDFAPI_FlateGetTotalOut(context); + offset = FPDFAPI_FlateGetTotalIn(context); + if (guess_size / 2 > dest_size) { + { + uint8_t* new_buf = + FX_Realloc(uint8_t, guess_buf.release(), dest_size + 1); + guess_buf.reset(new_buf); + } + guess_size = dest_size; + guess_buf.get()[guess_size] = '\0'; + } + dest_buf = guess_buf.release(); + } else { + CFX_ArrayTemplate<uint8_t*> result_tmp_bufs; + uint8_t* cur_buf = guess_buf.release(); + while (1) { + int32_t ret = FPDFAPI_FlateOutput(context, cur_buf, buf_size); + int32_t avail_buf_size = FPDFAPI_FlateGetAvailOut(context); + if (ret != Z_OK) { + last_buf_size = buf_size - avail_buf_size; + result_tmp_bufs.Add(cur_buf); + break; + } + if (avail_buf_size != 0) { + last_buf_size = buf_size - avail_buf_size; + result_tmp_bufs.Add(cur_buf); + break; + } + + result_tmp_bufs.Add(cur_buf); + cur_buf = FX_Alloc(uint8_t, buf_size + 1); + cur_buf[buf_size] = '\0'; + } + dest_size = FPDFAPI_FlateGetTotalOut(context); + offset = FPDFAPI_FlateGetTotalIn(context); + if (result_tmp_bufs.GetSize() == 1) { + dest_buf = result_tmp_bufs[0]; + } else { + uint8_t* result_buf = FX_Alloc(uint8_t, dest_size); + FX_DWORD result_pos = 0; + for (int32_t i = 0; i < result_tmp_bufs.GetSize(); i++) { + uint8_t* tmp_buf = result_tmp_bufs[i]; + FX_DWORD tmp_buf_size = buf_size; + if (i == result_tmp_bufs.GetSize() - 1) { + tmp_buf_size = last_buf_size; + } + FXSYS_memcpy(result_buf + result_pos, tmp_buf, tmp_buf_size); + result_pos += tmp_buf_size; + FX_Free(result_tmp_bufs[i]); + } + dest_buf = result_buf; + } + } + FPDFAPI_FlateEnd(context); +} + +} // namespace + +class CCodec_FlateScanlineDecoder : public CCodec_ScanlineDecoder { + public: + CCodec_FlateScanlineDecoder(); + ~CCodec_FlateScanlineDecoder() override; + + void Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc, + int predictor, + int Colors, + int BitsPerComponent, + int Columns); + void Destroy() { delete this; } + + // CCodec_ScanlineDecoder + void v_DownScale(int dest_width, int dest_height) override {} + FX_BOOL v_Rewind() override; + uint8_t* v_GetNextLine() override; + FX_DWORD GetSrcOffset() override; + + void* m_pFlate; + const uint8_t* m_SrcBuf; + FX_DWORD m_SrcSize; + uint8_t* m_pScanline; + uint8_t* m_pLastLine; + uint8_t* m_pPredictBuffer; + uint8_t* m_pPredictRaw; + int m_Predictor; + int m_Colors; + int m_BitsPerComponent; + int m_Columns; + FX_DWORD m_PredictPitch; + size_t m_LeftOver; +}; + +CCodec_FlateScanlineDecoder::CCodec_FlateScanlineDecoder() { + m_pFlate = NULL; + m_pScanline = NULL; + m_pLastLine = NULL; + m_pPredictBuffer = NULL; + m_pPredictRaw = NULL; + m_LeftOver = 0; +} +CCodec_FlateScanlineDecoder::~CCodec_FlateScanlineDecoder() { + FX_Free(m_pScanline); + FX_Free(m_pLastLine); + FX_Free(m_pPredictBuffer); + FX_Free(m_pPredictRaw); + if (m_pFlate) { + FPDFAPI_FlateEnd(m_pFlate); + } +} +void CCodec_FlateScanlineDecoder::Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc, + int predictor, + int Colors, + int BitsPerComponent, + int Columns) { + m_SrcBuf = src_buf; + m_SrcSize = src_size; + m_OutputWidth = m_OrigWidth = width; + m_OutputHeight = m_OrigHeight = height; + m_nComps = nComps; + m_bpc = bpc; + m_bColorTransformed = FALSE; + m_Pitch = (static_cast<FX_DWORD>(width) * nComps * bpc + 7) / 8; + m_pScanline = FX_Alloc(uint8_t, m_Pitch); + m_Predictor = 0; + if (predictor) { + if (predictor >= 10) { + m_Predictor = 2; + } else if (predictor == 2) { + m_Predictor = 1; + } + if (m_Predictor) { + if (BitsPerComponent * Colors * Columns == 0) { + BitsPerComponent = m_bpc; + Colors = m_nComps; + Columns = m_OrigWidth; + } + m_Colors = Colors; + m_BitsPerComponent = BitsPerComponent; + m_Columns = Columns; + m_PredictPitch = + (static_cast<FX_DWORD>(m_BitsPerComponent) * m_Colors * m_Columns + + 7) / + 8; + m_pLastLine = FX_Alloc(uint8_t, m_PredictPitch); + m_pPredictRaw = FX_Alloc(uint8_t, m_PredictPitch + 1); + m_pPredictBuffer = FX_Alloc(uint8_t, m_PredictPitch); + } + } +} +FX_BOOL CCodec_FlateScanlineDecoder::v_Rewind() { + if (m_pFlate) { + FPDFAPI_FlateEnd(m_pFlate); + } + m_pFlate = FPDFAPI_FlateInit(my_alloc_func, my_free_func); + if (!m_pFlate) { + return FALSE; + } + FPDFAPI_FlateInput(m_pFlate, m_SrcBuf, m_SrcSize); + m_LeftOver = 0; + return TRUE; +} +uint8_t* CCodec_FlateScanlineDecoder::v_GetNextLine() { + if (m_Predictor) { + if (m_Pitch == m_PredictPitch) { + if (m_Predictor == 2) { + FPDFAPI_FlateOutput(m_pFlate, m_pPredictRaw, m_PredictPitch + 1); + PNG_PredictLine(m_pScanline, m_pPredictRaw, m_pLastLine, + m_BitsPerComponent, m_Colors, m_Columns); + FXSYS_memcpy(m_pLastLine, m_pScanline, m_PredictPitch); + } else { + FPDFAPI_FlateOutput(m_pFlate, m_pScanline, m_Pitch); + TIFF_PredictLine(m_pScanline, m_PredictPitch, m_bpc, m_nComps, + m_OutputWidth); + } + } else { + size_t bytes_to_go = m_Pitch; + size_t read_leftover = + m_LeftOver > bytes_to_go ? bytes_to_go : m_LeftOver; + if (read_leftover) { + FXSYS_memcpy(m_pScanline, + m_pPredictBuffer + m_PredictPitch - m_LeftOver, + read_leftover); + m_LeftOver -= read_leftover; + bytes_to_go -= read_leftover; + } + while (bytes_to_go) { + if (m_Predictor == 2) { + FPDFAPI_FlateOutput(m_pFlate, m_pPredictRaw, m_PredictPitch + 1); + PNG_PredictLine(m_pPredictBuffer, m_pPredictRaw, m_pLastLine, + m_BitsPerComponent, m_Colors, m_Columns); + FXSYS_memcpy(m_pLastLine, m_pPredictBuffer, m_PredictPitch); + } else { + FPDFAPI_FlateOutput(m_pFlate, m_pPredictBuffer, m_PredictPitch); + TIFF_PredictLine(m_pPredictBuffer, m_PredictPitch, m_BitsPerComponent, + m_Colors, m_Columns); + } + size_t read_bytes = + m_PredictPitch > bytes_to_go ? bytes_to_go : m_PredictPitch; + FXSYS_memcpy(m_pScanline + m_Pitch - bytes_to_go, m_pPredictBuffer, + read_bytes); + m_LeftOver += m_PredictPitch - read_bytes; + bytes_to_go -= read_bytes; + } + } + } else { + FPDFAPI_FlateOutput(m_pFlate, m_pScanline, m_Pitch); + } + return m_pScanline; +} +FX_DWORD CCodec_FlateScanlineDecoder::GetSrcOffset() { + return FPDFAPI_FlateGetTotalIn(m_pFlate); +} + +ICodec_ScanlineDecoder* CCodec_FlateModule::CreateDecoder( + const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + int bpc, + int predictor, + int Colors, + int BitsPerComponent, + int Columns) { + CCodec_FlateScanlineDecoder* pDecoder = new CCodec_FlateScanlineDecoder; + pDecoder->Create(src_buf, src_size, width, height, nComps, bpc, predictor, + Colors, BitsPerComponent, Columns); + return pDecoder; +} +FX_DWORD CCodec_FlateModule::FlateOrLZWDecode(FX_BOOL bLZW, + const uint8_t* src_buf, + FX_DWORD src_size, + FX_BOOL bEarlyChange, + int predictor, + int Colors, + int BitsPerComponent, + int Columns, + FX_DWORD estimated_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size) { + dest_buf = NULL; + FX_DWORD offset = 0; + int predictor_type = 0; + if (predictor) { + if (predictor >= 10) { + predictor_type = 2; + } else if (predictor == 2) { + predictor_type = 1; + } + } + if (bLZW) { + { + std::unique_ptr<CLZWDecoder> decoder(new CLZWDecoder); + dest_size = (FX_DWORD)-1; + offset = src_size; + int err = decoder->Decode(NULL, dest_size, src_buf, offset, bEarlyChange); + if (err || dest_size == 0 || dest_size + 1 < dest_size) { + return -1; + } + } + { + std::unique_ptr<CLZWDecoder> decoder(new CLZWDecoder); + dest_buf = FX_Alloc(uint8_t, dest_size + 1); + dest_buf[dest_size] = '\0'; + decoder->Decode(dest_buf, dest_size, src_buf, offset, bEarlyChange); + } + } else { + FlateUncompress(src_buf, src_size, estimated_size, dest_buf, dest_size, + offset); + } + if (predictor_type == 0) { + return offset; + } + FX_BOOL ret = TRUE; + if (predictor_type == 2) { + ret = PNG_Predictor(dest_buf, dest_size, Colors, BitsPerComponent, Columns); + } else if (predictor_type == 1) { + ret = + TIFF_Predictor(dest_buf, dest_size, Colors, BitsPerComponent, Columns); + } + return ret ? offset : -1; +} +FX_BOOL CCodec_FlateModule::Encode(const uint8_t* src_buf, + FX_DWORD src_size, + int predictor, + int Colors, + int BitsPerComponent, + int Columns, + uint8_t*& dest_buf, + FX_DWORD& dest_size) { + if (predictor != 2 && predictor < 10) { + return Encode(src_buf, src_size, dest_buf, dest_size); + } + uint8_t* pSrcBuf = NULL; + pSrcBuf = FX_Alloc(uint8_t, src_size); + FXSYS_memcpy(pSrcBuf, src_buf, src_size); + FX_BOOL ret = TRUE; + if (predictor == 2) { + ret = TIFF_PredictorEncode(pSrcBuf, src_size, Colors, BitsPerComponent, + Columns); + } else if (predictor >= 10) { + ret = PNG_PredictorEncode(pSrcBuf, src_size, predictor, Colors, + BitsPerComponent, Columns); + } + if (ret) + ret = Encode(pSrcBuf, src_size, dest_buf, dest_size); + FX_Free(pSrcBuf); + return ret; +} +FX_BOOL CCodec_FlateModule::Encode(const uint8_t* src_buf, + FX_DWORD src_size, + uint8_t*& dest_buf, + FX_DWORD& dest_size) { + dest_size = src_size + src_size / 1000 + 12; + dest_buf = FX_Alloc(uint8_t, dest_size); + unsigned long temp_size = dest_size; + FPDFAPI_FlateCompress(dest_buf, &temp_size, src_buf, src_size); + dest_size = (FX_DWORD)temp_size; + return TRUE; +} diff --git a/core/fxcodec/codec/fx_codec_gif.cpp b/core/fxcodec/codec/fx_codec_gif.cpp new file mode 100644 index 0000000000..60ca00f9fa --- /dev/null +++ b/core/fxcodec/codec/fx_codec_gif.cpp @@ -0,0 +1,187 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/codec_int.h" +#include "core/fxcodec/lgif/fx_gif.h" +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxge/fx_dib.h" +struct FXGIF_Context { + gif_decompress_struct_p gif_ptr; + void* parent_ptr; + void* child_ptr; + + void* (*m_AllocFunc)(unsigned int); + void (*m_FreeFunc)(void*); +}; +extern "C" { +static void* gif_alloc_func(unsigned int size) { + return FX_Alloc(char, size); +} +static void gif_free_func(void* p) { + FX_Free(p); +} +}; +static void gif_error_data(gif_decompress_struct_p gif_ptr, + const FX_CHAR* err_msg) { + FXSYS_strncpy((char*)gif_ptr->err_ptr, err_msg, GIF_MAX_ERROR_SIZE - 1); + longjmp(gif_ptr->jmpbuf, 1); +} +static uint8_t* gif_ask_buf_for_pal(gif_decompress_struct_p gif_ptr, + int32_t pal_size) { + FXGIF_Context* p = (FXGIF_Context*)gif_ptr->context_ptr; + CCodec_GifModule* pModule = (CCodec_GifModule*)p->parent_ptr; + return pModule->AskLocalPaletteBufCallback( + p->child_ptr, gif_get_frame_num(gif_ptr), pal_size); +} +static void gif_record_current_position(gif_decompress_struct_p gif_ptr, + FX_DWORD* cur_pos_ptr) { + FXGIF_Context* p = (FXGIF_Context*)gif_ptr->context_ptr; + CCodec_GifModule* pModule = (CCodec_GifModule*)p->parent_ptr; + pModule->RecordCurrentPositionCallback(p->child_ptr, *cur_pos_ptr); +} +static void gif_read_scanline(gif_decompress_struct_p gif_ptr, + int32_t row_num, + uint8_t* row_buf) { + FXGIF_Context* p = (FXGIF_Context*)gif_ptr->context_ptr; + CCodec_GifModule* pModule = (CCodec_GifModule*)p->parent_ptr; + pModule->ReadScanlineCallback(p->child_ptr, row_num, row_buf); +} +static FX_BOOL gif_get_record_position(gif_decompress_struct_p gif_ptr, + FX_DWORD cur_pos, + int32_t left, + int32_t top, + int32_t width, + int32_t height, + int32_t pal_num, + void* pal_ptr, + int32_t delay_time, + FX_BOOL user_input, + int32_t trans_index, + int32_t disposal_method, + FX_BOOL interlace) { + FXGIF_Context* p = (FXGIF_Context*)gif_ptr->context_ptr; + CCodec_GifModule* pModule = (CCodec_GifModule*)p->parent_ptr; + return pModule->InputRecordPositionBufCallback( + p->child_ptr, cur_pos, FX_RECT(left, top, left + width, top + height), + pal_num, pal_ptr, delay_time, user_input, trans_index, disposal_method, + interlace); +} +void* CCodec_GifModule::Start(void* pModule) { + FXGIF_Context* p = (FXGIF_Context*)FX_Alloc(uint8_t, sizeof(FXGIF_Context)); + if (p == NULL) { + return NULL; + } + FXSYS_memset(p, 0, sizeof(FXGIF_Context)); + p->m_AllocFunc = gif_alloc_func; + p->m_FreeFunc = gif_free_func; + p->gif_ptr = NULL; + p->parent_ptr = (void*)this; + p->child_ptr = pModule; + p->gif_ptr = gif_create_decompress(); + if (p->gif_ptr == NULL) { + FX_Free(p); + return NULL; + } + p->gif_ptr->context_ptr = (void*)p; + p->gif_ptr->err_ptr = m_szLastError; + p->gif_ptr->gif_error_fn = gif_error_data; + p->gif_ptr->gif_ask_buf_for_pal_fn = gif_ask_buf_for_pal; + p->gif_ptr->gif_record_current_position_fn = gif_record_current_position; + p->gif_ptr->gif_get_row_fn = gif_read_scanline; + p->gif_ptr->gif_get_record_position_fn = gif_get_record_position; + return p; +} +void CCodec_GifModule::Finish(void* pContext) { + FXGIF_Context* p = (FXGIF_Context*)pContext; + if (p) { + gif_destroy_decompress(&p->gif_ptr); + p->m_FreeFunc(p); + } +} +int32_t CCodec_GifModule::ReadHeader(void* pContext, + int* width, + int* height, + int* pal_num, + void** pal_pp, + int* bg_index, + CFX_DIBAttribute* pAttribute) { + FXGIF_Context* p = (FXGIF_Context*)pContext; + if (setjmp(p->gif_ptr->jmpbuf)) { + return 0; + } + int32_t ret = gif_read_header(p->gif_ptr); + if (ret != 1) { + return ret; + } + if (pAttribute) { + } + *width = p->gif_ptr->width; + *height = p->gif_ptr->height; + *pal_num = p->gif_ptr->global_pal_num; + *pal_pp = p->gif_ptr->global_pal_ptr; + *bg_index = p->gif_ptr->bc_index; + return 1; +} +int32_t CCodec_GifModule::LoadFrameInfo(void* pContext, int* frame_num) { + FXGIF_Context* p = (FXGIF_Context*)pContext; + if (setjmp(p->gif_ptr->jmpbuf)) { + return 0; + } + int32_t ret = gif_get_frame(p->gif_ptr); + if (ret != 1) { + return ret; + } + *frame_num = gif_get_frame_num(p->gif_ptr); + return 1; +} +int32_t CCodec_GifModule::LoadFrame(void* pContext, + int frame_num, + CFX_DIBAttribute* pAttribute) { + FXGIF_Context* p = (FXGIF_Context*)pContext; + if (setjmp(p->gif_ptr->jmpbuf)) { + return 0; + } + int32_t ret = gif_load_frame(p->gif_ptr, frame_num); + if (ret == 1) { + if (pAttribute) { + pAttribute->m_nGifLeft = + p->gif_ptr->img_ptr_arr_ptr->GetAt(frame_num)->image_info_ptr->left; + pAttribute->m_nGifTop = + p->gif_ptr->img_ptr_arr_ptr->GetAt(frame_num)->image_info_ptr->top; + pAttribute->m_fAspectRatio = p->gif_ptr->pixel_aspect; + if (p->gif_ptr->cmt_data_ptr) { + const uint8_t* buf = + (const uint8_t*)p->gif_ptr->cmt_data_ptr->GetBuffer(0); + FX_DWORD len = p->gif_ptr->cmt_data_ptr->GetLength(); + if (len > 21) { + uint8_t size = *buf++; + if (size) { + pAttribute->m_strAuthor = CFX_ByteString(buf, size); + } else { + pAttribute->m_strAuthor.Empty(); + } + buf += size; + size = *buf++; + if (size == 20) { + FXSYS_memcpy(pAttribute->m_strTime, buf, size); + } + } + } + } + } + return ret; +} +FX_DWORD CCodec_GifModule::GetAvailInput(void* pContext, + uint8_t** avial_buf_ptr) { + FXGIF_Context* p = (FXGIF_Context*)pContext; + return gif_get_avail_input(p->gif_ptr, avial_buf_ptr); +} +void CCodec_GifModule::Input(void* pContext, + const uint8_t* src_buf, + FX_DWORD src_size) { + FXGIF_Context* p = (FXGIF_Context*)pContext; + gif_input_buffer(p->gif_ptr, (uint8_t*)src_buf, src_size); +} diff --git a/core/fxcodec/codec/fx_codec_icc.cpp b/core/fxcodec/codec/fx_codec_icc.cpp new file mode 100644 index 0000000000..fc8113e4e6 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_icc.cpp @@ -0,0 +1,1986 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcodec/fx_codec.h" +#include "third_party/lcms2-2.6/include/lcms2.h" + +const FX_DWORD N_COMPONENT_LAB = 3; +const FX_DWORD N_COMPONENT_GRAY = 1; +const FX_DWORD N_COMPONENT_RGB = 3; +const FX_DWORD N_COMPONENT_CMYK = 4; +const FX_DWORD N_COMPONENT_DEFAULT = 3; + +FX_BOOL MD5ComputeID(const void* buf, FX_DWORD dwSize, uint8_t ID[16]) { + return cmsMD5computeIDExt(buf, dwSize, ID); +} +struct CLcmsCmm { + cmsHTRANSFORM m_hTransform; + int m_nSrcComponents; + int m_nDstComponents; + FX_BOOL m_bLab; +}; +extern "C" { +int ourHandler(int ErrorCode, const char* ErrorText) { + return TRUE; +} +}; +FX_BOOL CheckComponents(cmsColorSpaceSignature cs, + int nComponents, + FX_BOOL bDst) { + if (nComponents <= 0 || nComponents > 15) { + return FALSE; + } + switch (cs) { + case cmsSigLabData: + if (nComponents < 3) { + return FALSE; + } + break; + case cmsSigGrayData: + if (bDst && nComponents != 1) { + return FALSE; + } + if (!bDst && nComponents > 2) { + return FALSE; + } + break; + case cmsSigRgbData: + if (bDst && nComponents != 3) { + return FALSE; + } + break; + case cmsSigCmykData: + if (bDst && nComponents != 4) { + return FALSE; + } + break; + default: + if (nComponents != 3) { + return FALSE; + } + break; + } + return TRUE; +} + +FX_DWORD GetCSComponents(cmsColorSpaceSignature cs) { + FX_DWORD components; + switch (cs) { + case cmsSigLabData: + components = N_COMPONENT_LAB; + break; + case cmsSigGrayData: + components = N_COMPONENT_GRAY; + break; + case cmsSigRgbData: + components = N_COMPONENT_RGB; + break; + case cmsSigCmykData: + components = N_COMPONENT_CMYK; + break; + default: + components = N_COMPONENT_DEFAULT; + break; + } + return components; +} + +void* IccLib_CreateTransform(const unsigned char* pSrcProfileData, + FX_DWORD dwSrcProfileSize, + FX_DWORD& nSrcComponents, + const unsigned char* pDstProfileData, + FX_DWORD dwDstProfileSize, + int32_t nDstComponents, + int intent, + FX_DWORD dwSrcFormat = Icc_FORMAT_DEFAULT, + FX_DWORD dwDstFormat = Icc_FORMAT_DEFAULT) { + cmsHPROFILE srcProfile = NULL; + cmsHPROFILE dstProfile = NULL; + cmsHTRANSFORM hTransform = NULL; + CLcmsCmm* pCmm = NULL; + nSrcComponents = 0; + srcProfile = cmsOpenProfileFromMem((void*)pSrcProfileData, dwSrcProfileSize); + if (!srcProfile) { + return NULL; + } + if (!pDstProfileData && dwDstProfileSize == 0 && nDstComponents == 3) { + dstProfile = cmsCreate_sRGBProfile(); + } else { + dstProfile = + cmsOpenProfileFromMem((void*)pDstProfileData, dwDstProfileSize); + } + if (!dstProfile) { + cmsCloseProfile(srcProfile); + return NULL; + } + int srcFormat; + FX_BOOL bLab = FALSE; + cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile); + nSrcComponents = GetCSComponents(srcCS); + if (srcCS == cmsSigLabData) { + srcFormat = + COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0); + bLab = TRUE; + } else { + srcFormat = + COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1); + if (srcCS == cmsSigRgbData && T_DOSWAP(dwSrcFormat)) { + srcFormat |= DOSWAP_SH(1); + } + } + cmsColorSpaceSignature dstCS = cmsGetColorSpace(dstProfile); + if (!CheckComponents(dstCS, nDstComponents, TRUE)) { + cmsCloseProfile(srcProfile); + cmsCloseProfile(dstProfile); + return NULL; + } + switch (dstCS) { + case cmsSigGrayData: + hTransform = cmsCreateTransform(srcProfile, srcFormat, dstProfile, + TYPE_GRAY_8, intent, 0); + break; + case cmsSigRgbData: + hTransform = cmsCreateTransform(srcProfile, srcFormat, dstProfile, + TYPE_BGR_8, intent, 0); + break; + case cmsSigCmykData: + hTransform = cmsCreateTransform( + srcProfile, srcFormat, dstProfile, + T_DOSWAP(dwDstFormat) ? TYPE_KYMC_8 : TYPE_CMYK_8, intent, 0); + break; + default: + break; + } + if (!hTransform) { + cmsCloseProfile(srcProfile); + cmsCloseProfile(dstProfile); + return NULL; + } + pCmm = new CLcmsCmm; + pCmm->m_nSrcComponents = nSrcComponents; + pCmm->m_nDstComponents = nDstComponents; + pCmm->m_hTransform = hTransform; + pCmm->m_bLab = bLab; + cmsCloseProfile(srcProfile); + cmsCloseProfile(dstProfile); + return pCmm; +} +void* IccLib_CreateTransform_sRGB(const unsigned char* pProfileData, + FX_DWORD dwProfileSize, + FX_DWORD& nComponents, + int32_t intent, + FX_DWORD dwSrcFormat) { + return IccLib_CreateTransform(pProfileData, dwProfileSize, nComponents, NULL, + 0, 3, intent, dwSrcFormat); +} +void IccLib_DestroyTransform(void* pTransform) { + if (!pTransform) { + return; + } + cmsDeleteTransform(((CLcmsCmm*)pTransform)->m_hTransform); + delete (CLcmsCmm*)pTransform; +} +void IccLib_Translate(void* pTransform, + FX_DWORD nSrcComponents, + FX_FLOAT* pSrcValues, + FX_FLOAT* pDestValues) { + if (!pTransform) { + return; + } + CLcmsCmm* p = (CLcmsCmm*)pTransform; + uint8_t output[4]; + if (p->m_bLab) { + CFX_FixedBufGrow<double, 16> inputs(nSrcComponents); + double* input = inputs; + for (FX_DWORD i = 0; i < nSrcComponents; i++) { + input[i] = pSrcValues[i]; + } + cmsDoTransform(p->m_hTransform, input, output, 1); + } else { + CFX_FixedBufGrow<uint8_t, 16> inputs(nSrcComponents); + uint8_t* input = inputs; + for (FX_DWORD i = 0; i < nSrcComponents; i++) { + if (pSrcValues[i] > 1.0f) { + input[i] = 255; + } else if (pSrcValues[i] < 0) { + input[i] = 0; + } else { + input[i] = (int)(pSrcValues[i] * 255.0f); + } + } + cmsDoTransform(p->m_hTransform, input, output, 1); + } + switch (p->m_nDstComponents) { + case 1: + pDestValues[0] = output[0] / 255.0f; + break; + case 3: + pDestValues[0] = output[2] / 255.0f; + pDestValues[1] = output[1] / 255.0f; + pDestValues[2] = output[0] / 255.0f; + break; + case 4: + pDestValues[0] = output[0] / 255.0f; + pDestValues[1] = output[1] / 255.0f; + pDestValues[2] = output[2] / 255.0f; + pDestValues[3] = output[3] / 255.0f; + break; + } +} +void IccLib_TranslateImage(void* pTransform, + unsigned char* pDest, + const unsigned char* pSrc, + int32_t pixels) { + cmsDoTransform(((CLcmsCmm*)pTransform)->m_hTransform, (void*)pSrc, pDest, + pixels); +} +void* CreateProfile_Gray(double gamma) { + cmsCIExyY* D50 = (cmsCIExyY*)cmsD50_xyY(); + if (!cmsWhitePointFromTemp(D50, 6504)) { + return NULL; + } + cmsToneCurve* curve = cmsBuildGamma(NULL, gamma); + if (!curve) { + return NULL; + } + void* profile = cmsCreateGrayProfile(D50, curve); + cmsFreeToneCurve(curve); + return profile; +} +ICodec_IccModule::IccCS GetProfileCSFromHandle(void* pProfile) { + if (!pProfile) { + return ICodec_IccModule::IccCS_Unknown; + } + switch (cmsGetColorSpace(pProfile)) { + case cmsSigXYZData: + return ICodec_IccModule::IccCS_XYZ; + case cmsSigLabData: + return ICodec_IccModule::IccCS_Lab; + case cmsSigLuvData: + return ICodec_IccModule::IccCS_Luv; + case cmsSigYCbCrData: + return ICodec_IccModule::IccCS_YCbCr; + case cmsSigYxyData: + return ICodec_IccModule::IccCS_Yxy; + case cmsSigRgbData: + return ICodec_IccModule::IccCS_Rgb; + case cmsSigGrayData: + return ICodec_IccModule::IccCS_Gray; + case cmsSigHsvData: + return ICodec_IccModule::IccCS_Hsv; + case cmsSigHlsData: + return ICodec_IccModule::IccCS_Hls; + case cmsSigCmykData: + return ICodec_IccModule::IccCS_Cmyk; + case cmsSigCmyData: + return ICodec_IccModule::IccCS_Cmy; + default: + return ICodec_IccModule::IccCS_Unknown; + } +} +ICodec_IccModule::IccCS CCodec_IccModule::GetProfileCS( + const uint8_t* pProfileData, + FX_DWORD dwProfileSize) { + ICodec_IccModule::IccCS cs; + cmsHPROFILE hProfile = + cmsOpenProfileFromMem((void*)pProfileData, dwProfileSize); + if (!hProfile) { + return IccCS_Unknown; + } + cs = GetProfileCSFromHandle(hProfile); + if (hProfile) { + cmsCloseProfile(hProfile); + } + return cs; +} +ICodec_IccModule::IccCS CCodec_IccModule::GetProfileCS(IFX_FileRead* pFile) { + if (!pFile) { + return IccCS_Unknown; + } + ICodec_IccModule::IccCS cs; + FX_DWORD dwSize = (FX_DWORD)pFile->GetSize(); + uint8_t* pBuf = FX_Alloc(uint8_t, dwSize); + pFile->ReadBlock(pBuf, 0, dwSize); + cs = GetProfileCS(pBuf, dwSize); + FX_Free(pBuf); + return cs; +} +FX_DWORD TransferProfileType(void* pProfile, FX_DWORD dwFormat) { + cmsColorSpaceSignature cs = cmsGetColorSpace(pProfile); + switch (cs) { + case cmsSigXYZData: + return TYPE_XYZ_16; + case cmsSigLabData: + return TYPE_Lab_DBL; + case cmsSigLuvData: + return TYPE_YUV_8; + case cmsSigYCbCrData: + return TYPE_YCbCr_8; + case cmsSigYxyData: + return TYPE_Yxy_16; + case cmsSigRgbData: + return T_DOSWAP(dwFormat) ? TYPE_RGB_8 : TYPE_BGR_8; + case cmsSigGrayData: + return TYPE_GRAY_8; + case cmsSigHsvData: + return TYPE_HSV_8; + case cmsSigHlsData: + return TYPE_HLS_8; + case cmsSigCmykData: + return T_DOSWAP(dwFormat) ? TYPE_KYMC_8 : TYPE_CMYK_8; + case cmsSigCmyData: + return TYPE_CMY_8; + case cmsSigMCH5Data: + return T_DOSWAP(dwFormat) ? TYPE_KYMC5_8 : TYPE_CMYK5_8; + case cmsSigMCH6Data: + return TYPE_CMYK6_8; + case cmsSigMCH7Data: + return T_DOSWAP(dwFormat) ? TYPE_KYMC7_8 : TYPE_CMYK7_8; + case cmsSigMCH8Data: + return T_DOSWAP(dwFormat) ? TYPE_KYMC8_8 : TYPE_CMYK8_8; + case cmsSigMCH9Data: + return T_DOSWAP(dwFormat) ? TYPE_KYMC9_8 : TYPE_CMYK9_8; + case cmsSigMCHAData: + return T_DOSWAP(dwFormat) ? TYPE_KYMC10_8 : TYPE_CMYK10_8; + case cmsSigMCHBData: + return T_DOSWAP(dwFormat) ? TYPE_KYMC11_8 : TYPE_CMYK11_8; + case cmsSigMCHCData: + return T_DOSWAP(dwFormat) ? TYPE_KYMC12_8 : TYPE_CMYK12_8; + default: + return 0; + } +} +class CFX_IccProfileCache { + public: + CFX_IccProfileCache(); + ~CFX_IccProfileCache(); + void* m_pProfile; + FX_DWORD m_dwRate; + + protected: + void Purge(); +}; +CFX_IccProfileCache::CFX_IccProfileCache() { + m_pProfile = NULL; + m_dwRate = 1; +} +CFX_IccProfileCache::~CFX_IccProfileCache() { + if (m_pProfile) { + cmsCloseProfile(m_pProfile); + } +} +void CFX_IccProfileCache::Purge() {} +class CFX_IccTransformCache { + public: + CFX_IccTransformCache(CLcmsCmm* pCmm = NULL); + ~CFX_IccTransformCache(); + void* m_pIccTransform; + FX_DWORD m_dwRate; + CLcmsCmm* m_pCmm; + + protected: + void Purge(); +}; +CFX_IccTransformCache::CFX_IccTransformCache(CLcmsCmm* pCmm) { + m_pIccTransform = NULL; + m_dwRate = 1; + m_pCmm = pCmm; +} +CFX_IccTransformCache::~CFX_IccTransformCache() { + if (m_pIccTransform) { + cmsDeleteTransform(m_pIccTransform); + } + FX_Free(m_pCmm); +} +void CFX_IccTransformCache::Purge() {} +class CFX_ByteStringKey : public CFX_BinaryBuf { + public: + CFX_ByteStringKey() : CFX_BinaryBuf() {} + CFX_ByteStringKey& operator<<(FX_DWORD i); +}; +CFX_ByteStringKey& CFX_ByteStringKey::operator<<(FX_DWORD i) { + AppendBlock(&i, sizeof(FX_DWORD)); + return *this; +} +void* CCodec_IccModule::CreateProfile(ICodec_IccModule::IccParam* pIccParam, + Icc_CLASS ic, + CFX_BinaryBuf* pTransformKey) { + CFX_IccProfileCache* pCache = NULL; + CFX_ByteStringKey key; + CFX_ByteString text; + key << pIccParam->ColorSpace << (pIccParam->dwProfileType | ic << 8); + uint8_t ID[16]; + switch (pIccParam->dwProfileType) { + case Icc_PARAMTYPE_NONE: + return NULL; + case Icc_PARAMTYPE_BUFFER: + MD5ComputeID(pIccParam->pProfileData, pIccParam->dwProfileSize, ID); + break; + case Icc_PARAMTYPE_PARAM: + FXSYS_memset(ID, 0, 16); + switch (pIccParam->ColorSpace) { + case IccCS_Gray: + text.Format("%lf", pIccParam->Gamma); + break; + default: + break; + } + MD5ComputeID(text.GetBuffer(0), text.GetLength(), ID); + break; + default: + break; + } + key.AppendBlock(ID, 16); + CFX_ByteString ProfileKey(key.GetBuffer(), key.GetSize()); + ASSERT(pTransformKey); + pTransformKey->AppendBlock(ProfileKey.GetBuffer(0), ProfileKey.GetLength()); + auto it = m_MapProfile.find(ProfileKey); + if (it == m_MapProfile.end()) { + pCache = new CFX_IccProfileCache; + switch (pIccParam->dwProfileType) { + case Icc_PARAMTYPE_BUFFER: + pCache->m_pProfile = cmsOpenProfileFromMem(pIccParam->pProfileData, + pIccParam->dwProfileSize); + break; + case Icc_PARAMTYPE_PARAM: + switch (pIccParam->ColorSpace) { + case IccCS_Rgb: + pCache->m_pProfile = cmsCreate_sRGBProfile(); + break; + case IccCS_Gray: + pCache->m_pProfile = CreateProfile_Gray(pIccParam->Gamma); + break; + default: + break; + } + break; + default: + break; + } + m_MapProfile[ProfileKey] = pCache; + } else { + pCache = it->second; + pCache->m_dwRate++; + } + return pCache->m_pProfile; +} +void* CCodec_IccModule::CreateTransform( + ICodec_IccModule::IccParam* pInputParam, + ICodec_IccModule::IccParam* pOutputParam, + ICodec_IccModule::IccParam* pProofParam, + FX_DWORD dwIntent, + FX_DWORD dwFlag, + FX_DWORD dwPrfIntent, + FX_DWORD dwPrfFlag) { + CLcmsCmm* pCmm = NULL; + ASSERT(pInputParam && pOutputParam); + CFX_ByteStringKey key; + void* pInputProfile = CreateProfile(pInputParam, Icc_CLASS_INPUT, &key); + if (!pInputProfile) { + return NULL; + } + void* pOutputProfile = CreateProfile(pOutputParam, Icc_CLASS_OUTPUT, &key); + if (!pOutputProfile) { + return NULL; + } + FX_DWORD dwInputProfileType = + TransferProfileType(pInputProfile, pInputParam->dwFormat); + FX_DWORD dwOutputProfileType = + TransferProfileType(pOutputProfile, pOutputParam->dwFormat); + if (dwInputProfileType == 0 || dwOutputProfileType == 0) { + return NULL; + } + void* pProofProfile = NULL; + if (pProofParam) { + pProofProfile = CreateProfile(pProofParam, Icc_CLASS_PROOF, &key); + } + key << dwInputProfileType << dwOutputProfileType << dwIntent << dwFlag + << (pProofProfile != NULL) << dwPrfIntent << dwPrfFlag; + CFX_ByteStringC TransformKey(key.GetBuffer(), key.GetSize()); + CFX_IccTransformCache* pTransformCache; + auto it = m_MapTranform.find(TransformKey); + if (it == m_MapTranform.end()) { + pCmm = FX_Alloc(CLcmsCmm, 1); + pCmm->m_nSrcComponents = T_CHANNELS(dwInputProfileType); + pCmm->m_nDstComponents = T_CHANNELS(dwOutputProfileType); + pCmm->m_bLab = T_COLORSPACE(pInputParam->dwFormat) == PT_Lab; + pTransformCache = new CFX_IccTransformCache(pCmm); + if (pProofProfile) { + pTransformCache->m_pIccTransform = cmsCreateProofingTransform( + pInputProfile, dwInputProfileType, pOutputProfile, + dwOutputProfileType, pProofProfile, dwIntent, dwPrfIntent, dwPrfFlag); + } else { + pTransformCache->m_pIccTransform = + cmsCreateTransform(pInputProfile, dwInputProfileType, pOutputProfile, + dwOutputProfileType, dwIntent, dwFlag); + } + pCmm->m_hTransform = pTransformCache->m_pIccTransform; + m_MapTranform[TransformKey] = pTransformCache; + } else { + pTransformCache = it->second; + pTransformCache->m_dwRate++; + } + return pTransformCache->m_pCmm; +} +CCodec_IccModule::~CCodec_IccModule() { + for (const auto& pair : m_MapProfile) { + delete pair.second; + } + m_MapProfile.clear(); + for (const auto& pair : m_MapTranform) { + delete pair.second; + } + m_MapTranform.clear(); +} +void* CCodec_IccModule::CreateTransform_sRGB(const uint8_t* pProfileData, + FX_DWORD dwProfileSize, + FX_DWORD& nComponents, + int32_t intent, + FX_DWORD dwSrcFormat) { + return IccLib_CreateTransform_sRGB(pProfileData, dwProfileSize, nComponents, + intent, dwSrcFormat); +} + +void* CCodec_IccModule::CreateTransform_CMYK(const uint8_t* pSrcProfileData, + FX_DWORD dwSrcProfileSize, + FX_DWORD& nSrcComponents, + const uint8_t* pDstProfileData, + FX_DWORD dwDstProfileSize, + int32_t intent, + FX_DWORD dwSrcFormat, + FX_DWORD dwDstFormat) { + return IccLib_CreateTransform( + pSrcProfileData, dwSrcProfileSize, nSrcComponents, pDstProfileData, + dwDstProfileSize, 4, intent, dwSrcFormat, dwDstFormat); +} + +void CCodec_IccModule::DestroyTransform(void* pTransform) { + IccLib_DestroyTransform(pTransform); +} +void CCodec_IccModule::Translate(void* pTransform, + FX_FLOAT* pSrcValues, + FX_FLOAT* pDestValues) { + IccLib_Translate(pTransform, m_nComponents, pSrcValues, pDestValues); +} +void CCodec_IccModule::TranslateScanline(void* pTransform, + uint8_t* pDest, + const uint8_t* pSrc, + int32_t pixels) { + IccLib_TranslateImage(pTransform, pDest, pSrc, pixels); +} +const uint8_t g_CMYKSamples[81 * 81 * 3] = { + 255, 255, 255, 225, 226, 228, 199, 200, 202, 173, 174, 178, 147, 149, 152, + 123, 125, 128, 99, 99, 102, 69, 70, 71, 34, 30, 31, 255, 253, 229, + 226, 224, 203, 200, 199, 182, 173, 173, 158, 149, 148, 135, 125, 124, 113, + 99, 99, 90, 70, 69, 63, 33, 29, 24, 255, 251, 204, 228, 223, 182, + 201, 198, 163, 174, 172, 142, 150, 147, 122, 125, 123, 101, 99, 98, 80, + 70, 68, 54, 32, 28, 16, 255, 249, 179, 230, 222, 160, 203, 197, 144, + 174, 170, 124, 150, 145, 105, 125, 122, 88, 99, 97, 69, 70, 68, 46, + 31, 28, 6, 255, 247, 154, 229, 220, 138, 203, 195, 122, 176, 169, 107, + 150, 145, 91, 125, 121, 74, 100, 96, 57, 70, 67, 35, 29, 26, 0, + 255, 246, 128, 231, 217, 114, 205, 194, 101, 176, 167, 88, 150, 144, 75, + 125, 120, 60, 100, 96, 44, 70, 66, 24, 28, 26, 0, 255, 244, 96, + 231, 217, 87, 203, 192, 78, 175, 167, 66, 150, 143, 56, 125, 119, 43, + 100, 95, 29, 69, 66, 7, 26, 26, 0, 255, 243, 51, 232, 215, 51, + 204, 191, 43, 176, 165, 38, 150, 142, 28, 125, 118, 17, 99, 94, 0, + 68, 65, 0, 24, 25, 0, 255, 241, 0, 231, 215, 0, 203, 190, 0, + 176, 164, 0, 150, 141, 0, 126, 117, 0, 99, 93, 0, 68, 65, 0, + 24, 25, 0, 252, 228, 238, 222, 201, 211, 197, 180, 190, 171, 156, 166, + 147, 133, 143, 123, 111, 119, 99, 88, 94, 71, 61, 66, 34, 22, 26, + 254, 226, 213, 224, 201, 191, 199, 179, 171, 172, 155, 148, 147, 133, 128, + 123, 110, 106, 98, 87, 83, 70, 59, 57, 33, 21, 18, 254, 224, 191, + 224, 199, 172, 200, 177, 153, 173, 154, 133, 147, 132, 115, 123, 109, 94, + 98, 86, 74, 70, 59, 49, 32, 21, 9, 255, 222, 168, 227, 198, 150, + 200, 175, 135, 173, 153, 118, 148, 130, 99, 123, 109, 82, 98, 86, 64, + 69, 58, 40, 31, 19, 0, 255, 221, 145, 227, 196, 129, 201, 174, 115, + 173, 151, 99, 148, 129, 85, 124, 108, 69, 98, 85, 52, 69, 58, 30, + 30, 19, 0, 255, 219, 121, 227, 195, 109, 201, 174, 97, 174, 150, 83, + 148, 129, 70, 124, 107, 55, 98, 84, 40, 69, 58, 19, 28, 18, 0, + 255, 218, 92, 229, 194, 82, 202, 173, 75, 174, 150, 63, 149, 128, 51, + 124, 106, 39, 98, 84, 24, 68, 57, 3, 26, 18, 0, 255, 217, 54, + 228, 193, 52, 201, 172, 46, 174, 148, 36, 148, 127, 27, 123, 105, 14, + 98, 83, 0, 68, 56, 0, 25, 18, 0, 255, 216, 0, 229, 192, 2, + 202, 171, 4, 173, 148, 0, 148, 126, 0, 124, 105, 0, 98, 83, 0, + 68, 56, 0, 24, 17, 0, 249, 204, 223, 219, 181, 199, 195, 160, 178, + 170, 140, 156, 146, 119, 134, 123, 99, 112, 98, 77, 88, 70, 52, 61, + 34, 11, 20, 250, 201, 200, 221, 180, 178, 197, 159, 161, 171, 139, 139, + 147, 119, 120, 123, 98, 99, 98, 77, 78, 69, 51, 52, 34, 11, 10, + 252, 201, 180, 223, 179, 162, 197, 159, 144, 170, 138, 125, 146, 117, 107, + 122, 97, 89, 98, 76, 69, 69, 50, 44, 32, 11, 2, 252, 199, 158, + 222, 177, 143, 199, 158, 127, 171, 137, 110, 147, 117, 93, 122, 96, 76, + 97, 75, 58, 69, 50, 36, 32, 10, 0, 253, 198, 137, 223, 177, 123, + 198, 156, 110, 171, 136, 95, 146, 116, 80, 122, 96, 65, 97, 75, 47, + 69, 50, 25, 30, 10, 0, 254, 197, 115, 225, 175, 104, 198, 156, 92, + 172, 135, 79, 147, 115, 66, 123, 95, 52, 98, 74, 37, 69, 49, 15, + 29, 10, 0, 254, 196, 89, 224, 175, 80, 199, 154, 70, 172, 134, 59, + 146, 114, 48, 122, 95, 36, 97, 74, 21, 68, 49, 0, 27, 9, 0, + 255, 195, 57, 225, 173, 52, 198, 154, 44, 172, 133, 36, 147, 113, 26, + 123, 94, 14, 98, 74, 0, 68, 49, 0, 26, 10, 0, 254, 194, 15, + 225, 172, 12, 198, 153, 7, 172, 132, 3, 146, 113, 0, 123, 93, 0, + 98, 73, 0, 68, 49, 0, 26, 9, 0, 246, 178, 209, 218, 159, 186, + 194, 140, 166, 168, 122, 145, 144, 104, 125, 121, 85, 103, 97, 65, 81, + 69, 41, 55, 34, 0, 12, 248, 176, 186, 219, 157, 166, 195, 139, 149, + 168, 121, 130, 144, 103, 111, 121, 85, 91, 97, 65, 71, 69, 41, 46, + 34, 0, 4, 249, 175, 168, 220, 156, 150, 196, 139, 135, 169, 121, 116, + 144, 103, 100, 122, 84, 83, 98, 65, 63, 70, 41, 39, 33, 0, 0, + 249, 175, 148, 220, 155, 133, 196, 138, 119, 169, 120, 103, 145, 101, 87, + 121, 83, 71, 97, 65, 54, 69, 41, 31, 32, 0, 0, 249, 173, 128, + 222, 154, 115, 195, 137, 102, 170, 119, 88, 145, 101, 74, 122, 83, 59, + 97, 64, 43, 68, 40, 20, 30, 0, 0, 250, 172, 108, 221, 154, 98, + 195, 136, 86, 170, 118, 73, 145, 100, 61, 122, 82, 48, 97, 63, 32, + 69, 40, 11, 28, 0, 0, 250, 171, 85, 221, 153, 76, 196, 136, 67, + 170, 117, 56, 145, 99, 44, 121, 82, 33, 97, 63, 17, 68, 40, 0, + 28, 0, 0, 251, 171, 58, 222, 152, 50, 197, 135, 43, 169, 117, 34, + 146, 99, 25, 121, 81, 10, 96, 63, 0, 68, 40, 0, 27, 0, 0, + 250, 170, 26, 222, 151, 19, 196, 134, 13, 169, 116, 4, 145, 99, 0, + 122, 81, 0, 97, 63, 0, 67, 40, 0, 26, 0, 0, 244, 153, 194, + 215, 136, 173, 192, 121, 155, 167, 104, 135, 143, 89, 115, 121, 72, 96, + 97, 54, 75, 70, 31, 49, 34, 0, 6, 245, 153, 173, 216, 136, 155, + 192, 120, 138, 167, 104, 121, 144, 88, 103, 121, 71, 85, 97, 54, 66, + 69, 31, 42, 34, 0, 0, 246, 152, 157, 217, 135, 140, 193, 120, 126, + 167, 103, 109, 143, 88, 92, 121, 72, 76, 97, 54, 58, 69, 31, 35, + 33, 0, 0, 245, 150, 139, 218, 134, 125, 193, 119, 111, 167, 103, 96, + 144, 87, 80, 121, 71, 66, 96, 53, 49, 68, 31, 26, 32, 0, 0, + 246, 151, 122, 218, 133, 108, 194, 118, 96, 168, 102, 81, 144, 86, 69, + 120, 71, 55, 95, 53, 39, 68, 30, 17, 31, 0, 0, 248, 150, 103, + 218, 133, 91, 193, 118, 81, 168, 102, 69, 143, 86, 56, 120, 70, 43, + 96, 53, 28, 68, 31, 6, 29, 0, 0, 247, 149, 81, 218, 132, 72, + 194, 117, 62, 168, 101, 52, 144, 86, 42, 121, 70, 29, 96, 52, 13, + 68, 30, 0, 28, 0, 0, 247, 148, 55, 219, 131, 50, 194, 117, 43, + 167, 101, 32, 144, 85, 22, 120, 69, 8, 96, 52, 0, 67, 30, 0, + 27, 0, 0, 247, 147, 29, 218, 131, 24, 194, 116, 20, 168, 100, 11, + 144, 85, 0, 120, 69, 0, 96, 52, 0, 67, 30, 0, 26, 0, 0, + 242, 130, 179, 214, 114, 160, 190, 101, 143, 166, 87, 125, 143, 72, 107, + 120, 58, 88, 96, 42, 68, 69, 17, 44, 35, 0, 0, 243, 129, 161, + 215, 114, 143, 191, 101, 128, 166, 87, 113, 143, 73, 96, 120, 58, 79, + 96, 41, 60, 69, 18, 37, 33, 0, 0, 243, 129, 146, 216, 114, 130, + 192, 101, 117, 166, 87, 101, 143, 72, 86, 121, 58, 69, 96, 42, 52, + 69, 18, 29, 31, 0, 0, 243, 128, 130, 216, 114, 115, 191, 101, 102, + 165, 86, 88, 142, 72, 75, 120, 58, 60, 95, 42, 43, 68, 19, 21, + 30, 0, 0, 244, 127, 112, 217, 113, 101, 192, 99, 89, 166, 85, 75, + 142, 72, 63, 119, 57, 50, 96, 41, 35, 68, 19, 13, 30, 0, 0, + 244, 127, 96, 216, 112, 86, 191, 99, 75, 166, 86, 64, 143, 72, 52, + 120, 57, 40, 95, 41, 24, 67, 20, 1, 29, 0, 0, 245, 126, 77, + 216, 113, 68, 191, 100, 59, 166, 85, 49, 142, 71, 38, 119, 57, 26, + 95, 41, 10, 67, 20, 0, 28, 0, 0, 244, 126, 55, 216, 112, 48, + 191, 99, 40, 166, 85, 31, 143, 71, 20, 119, 57, 6, 95, 42, 0, + 67, 20, 0, 28, 0, 0, 245, 126, 33, 217, 112, 26, 192, 99, 22, + 166, 84, 11, 142, 70, 0, 119, 57, 0, 95, 41, 0, 66, 20, 0, + 27, 0, 0, 241, 102, 167, 213, 90, 149, 189, 79, 133, 165, 66, 115, + 141, 54, 98, 119, 41, 81, 96, 25, 63, 69, 0, 38, 30, 0, 0, + 241, 102, 149, 213, 90, 133, 189, 79, 119, 165, 66, 103, 142, 55, 88, + 119, 41, 71, 96, 25, 53, 69, 0, 31, 28, 0, 0, 241, 102, 135, + 214, 90, 121, 190, 79, 108, 165, 66, 92, 141, 55, 78, 119, 42, 63, + 96, 26, 46, 69, 0, 24, 28, 0, 0, 241, 101, 120, 214, 90, 107, + 189, 79, 95, 165, 67, 83, 141, 54, 68, 118, 41, 54, 95, 27, 39, + 68, 0, 16, 27, 0, 0, 241, 102, 106, 213, 90, 93, 189, 78, 82, + 164, 67, 70, 141, 55, 58, 118, 42, 45, 94, 27, 29, 67, 2, 6, + 27, 0, 0, 242, 101, 90, 214, 89, 79, 190, 79, 69, 166, 67, 59, + 141, 55, 47, 118, 41, 35, 95, 27, 19, 67, 3, 0, 26, 0, 0, + 242, 102, 72, 213, 89, 63, 191, 79, 56, 164, 67, 45, 141, 55, 34, + 118, 42, 22, 94, 28, 6, 67, 3, 0, 26, 0, 0, 242, 100, 51, + 214, 89, 45, 190, 78, 38, 164, 67, 30, 141, 55, 18, 118, 42, 3, + 95, 28, 0, 66, 4, 0, 26, 0, 0, 243, 100, 33, 214, 90, 27, + 190, 78, 22, 165, 67, 13, 141, 55, 0, 118, 43, 0, 94, 29, 0, + 66, 5, 0, 26, 0, 0, 237, 69, 153, 211, 58, 135, 187, 51, 121, + 163, 41, 105, 141, 28, 90, 118, 15, 73, 96, 0, 56, 68, 0, 33, + 25, 0, 0, 239, 67, 137, 212, 60, 123, 189, 50, 110, 163, 41, 94, + 141, 29, 79, 118, 17, 65, 95, 0, 48, 69, 0, 26, 25, 0, 0, + 240, 69, 124, 211, 60, 111, 188, 50, 98, 163, 42, 85, 141, 31, 72, + 118, 18, 57, 94, 0, 41, 68, 0, 19, 25, 0, 0, 240, 70, 112, + 212, 61, 99, 188, 52, 87, 163, 41, 74, 140, 31, 62, 118, 20, 48, + 94, 2, 32, 68, 0, 11, 24, 0, 0, 239, 70, 98, 212, 62, 86, + 188, 53, 77, 164, 42, 64, 140, 32, 52, 118, 20, 40, 94, 3, 24, + 67, 0, 3, 23, 0, 0, 239, 71, 85, 212, 61, 74, 187, 53, 65, + 163, 44, 54, 140, 34, 43, 118, 22, 30, 95, 3, 14, 67, 0, 0, + 23, 0, 0, 239, 70, 67, 212, 62, 59, 188, 53, 51, 163, 45, 42, + 141, 34, 31, 117, 22, 17, 94, 5, 2, 66, 0, 0, 23, 0, 0, + 239, 71, 50, 213, 62, 43, 188, 54, 37, 164, 45, 28, 139, 34, 16, + 117, 22, 2, 94, 7, 0, 65, 0, 0, 23, 0, 0, 240, 71, 34, + 212, 63, 29, 189, 54, 24, 163, 46, 15, 139, 36, 2, 117, 25, 0, + 94, 8, 0, 66, 0, 0, 23, 0, 0, 237, 0, 140, 209, 0, 124, + 186, 0, 112, 162, 0, 97, 141, 0, 82, 118, 0, 67, 95, 0, 49, + 68, 0, 27, 20, 0, 0, 237, 0, 126, 210, 0, 113, 187, 0, 99, + 163, 0, 86, 139, 0, 72, 118, 0, 58, 95, 0, 42, 67, 0, 20, + 20, 0, 0, 237, 1, 114, 209, 1, 102, 187, 0, 90, 163, 0, 78, + 139, 0, 64, 118, 0, 50, 95, 0, 35, 67, 0, 13, 20, 0, 0, + 236, 16, 102, 209, 7, 91, 186, 0, 80, 162, 0, 68, 139, 0, 56, + 117, 0, 43, 94, 0, 27, 67, 0, 6, 20, 0, 0, 238, 15, 89, + 209, 13, 79, 186, 6, 69, 162, 0, 58, 139, 0, 47, 117, 0, 34, + 93, 0, 20, 66, 0, 2, 20, 0, 0, 237, 20, 78, 210, 12, 68, + 187, 4, 59, 163, 0, 49, 139, 0, 38, 116, 0, 26, 94, 0, 11, + 66, 0, 0, 20, 0, 0, 237, 25, 64, 210, 18, 56, 186, 11, 48, + 162, 4, 39, 138, 0, 27, 117, 0, 14, 93, 0, 0, 66, 0, 0, + 20, 0, 0, 238, 25, 48, 210, 22, 43, 186, 15, 35, 162, 8, 26, + 140, 0, 14, 117, 0, 0, 93, 0, 0, 65, 0, 0, 20, 0, 0, + 238, 28, 35, 210, 21, 30, 187, 15, 24, 162, 8, 16, 139, 1, 2, + 117, 0, 0, 93, 0, 0, 65, 0, 0, 22, 0, 0, 219, 242, 252, + 195, 214, 225, 172, 191, 201, 148, 165, 175, 127, 142, 150, 106, 119, 126, + 84, 95, 101, 58, 66, 72, 24, 27, 32, 222, 239, 226, 196, 213, 202, + 173, 189, 180, 150, 165, 158, 129, 141, 135, 107, 118, 113, 85, 94, 90, + 58, 66, 63, 21, 26, 24, 223, 237, 203, 198, 211, 182, 175, 188, 163, + 152, 164, 141, 129, 140, 121, 107, 117, 101, 85, 93, 80, 58, 64, 54, + 21, 26, 18, 226, 236, 179, 201, 210, 160, 177, 187, 143, 153, 162, 125, + 130, 139, 106, 108, 116, 89, 85, 92, 69, 58, 64, 45, 20, 25, 8, + 227, 234, 153, 201, 208, 139, 178, 185, 124, 154, 161, 107, 131, 138, 91, + 108, 115, 75, 85, 91, 58, 58, 63, 35, 17, 25, 0, 229, 233, 130, + 203, 207, 116, 178, 184, 104, 154, 160, 90, 131, 137, 76, 109, 114, 62, + 85, 90, 46, 58, 63, 25, 16, 24, 0, 230, 231, 100, 202, 205, 90, + 179, 183, 80, 154, 159, 69, 131, 136, 57, 109, 113, 46, 86, 90, 32, + 58, 63, 10, 14, 24, 0, 230, 230, 65, 204, 204, 58, 180, 182, 52, + 155, 157, 44, 132, 135, 35, 110, 113, 24, 86, 89, 9, 57, 62, 0, + 11, 24, 0, 232, 230, 19, 204, 204, 19, 180, 181, 17, 155, 157, 10, + 131, 134, 2, 109, 112, 0, 85, 89, 0, 57, 62, 0, 10, 23, 0, + 218, 216, 236, 194, 192, 211, 172, 171, 188, 149, 149, 164, 128, 127, 141, + 106, 106, 119, 84, 84, 94, 59, 57, 66, 25, 18, 26, 221, 214, 211, + 196, 191, 190, 174, 170, 170, 150, 148, 148, 128, 126, 127, 107, 105, 106, + 85, 83, 84, 59, 56, 58, 23, 17, 18, 222, 213, 190, 197, 189, 170, + 175, 169, 153, 151, 147, 133, 129, 126, 113, 108, 105, 94, 85, 82, 74, + 59, 56, 49, 22, 17, 11, 224, 211, 168, 199, 188, 151, 175, 168, 135, + 152, 146, 117, 129, 124, 99, 107, 103, 82, 84, 82, 64, 59, 55, 41, + 21, 17, 1, 224, 210, 145, 199, 187, 130, 176, 166, 117, 152, 145, 101, + 129, 123, 86, 107, 103, 70, 85, 81, 53, 58, 55, 31, 19, 17, 0, + 227, 208, 123, 200, 186, 110, 177, 165, 98, 153, 143, 84, 130, 122, 70, + 108, 102, 57, 85, 80, 41, 58, 54, 20, 18, 16, 0, 227, 208, 97, + 202, 185, 86, 177, 164, 77, 153, 142, 65, 130, 122, 54, 108, 101, 42, + 85, 80, 27, 58, 54, 7, 16, 16, 0, 228, 206, 66, 202, 184, 58, + 178, 163, 50, 154, 141, 42, 131, 121, 33, 109, 101, 21, 86, 79, 5, + 58, 54, 0, 13, 16, 0, 228, 206, 29, 202, 183, 25, 178, 163, 20, + 154, 141, 15, 131, 121, 5, 108, 100, 0, 85, 79, 0, 58, 53, 0, + 13, 16, 0, 217, 193, 221, 193, 172, 198, 172, 153, 178, 149, 133, 154, + 128, 114, 132, 107, 94, 111, 85, 74, 89, 59, 49, 61, 25, 8, 22, + 219, 191, 198, 195, 171, 178, 173, 153, 159, 149, 132, 139, 128, 113, 119, + 107, 94, 100, 85, 73, 79, 59, 48, 52, 25, 7, 14, 221, 191, 180, + 196, 170, 160, 174, 152, 144, 150, 132, 125, 129, 113, 107, 107, 93, 89, + 85, 73, 69, 59, 48, 45, 23, 7, 4, 222, 189, 159, 197, 169, 142, + 174, 151, 127, 151, 131, 110, 129, 112, 94, 108, 93, 78, 85, 72, 60, + 58, 47, 37, 22, 7, 0, 223, 188, 138, 197, 168, 123, 175, 150, 109, + 151, 130, 95, 130, 111, 81, 108, 92, 65, 85, 72, 49, 59, 47, 27, + 21, 7, 0, 224, 187, 118, 198, 167, 105, 176, 149, 93, 152, 129, 79, + 130, 110, 68, 108, 91, 54, 85, 71, 38, 59, 47, 17, 18, 7, 0, + 224, 187, 93, 199, 166, 83, 176, 148, 73, 152, 128, 62, 129, 109, 51, + 108, 90, 39, 85, 71, 25, 58, 46, 3, 16, 8, 0, 226, 186, 64, + 200, 165, 57, 177, 147, 50, 153, 127, 40, 130, 108, 31, 108, 90, 19, + 85, 70, 3, 58, 46, 0, 16, 8, 0, 227, 185, 35, 200, 165, 30, + 176, 146, 25, 152, 127, 18, 130, 108, 7, 108, 89, 0, 85, 70, 0, + 57, 46, 0, 14, 8, 0, 216, 169, 205, 192, 150, 184, 171, 134, 164, + 149, 116, 144, 128, 99, 124, 107, 81, 103, 85, 63, 81, 60, 39, 55, + 26, 0, 15, 217, 168, 186, 193, 150, 165, 172, 134, 149, 150, 116, 130, + 128, 99, 111, 107, 81, 92, 85, 62, 72, 59, 39, 47, 25, 0, 6, + 219, 168, 168, 194, 149, 150, 173, 133, 135, 150, 116, 117, 128, 98, 99, + 107, 80, 82, 86, 62, 63, 59, 38, 39, 24, 0, 0, 219, 166, 148, + 195, 149, 133, 173, 133, 119, 150, 115, 103, 128, 98, 88, 107, 80, 72, + 85, 61, 54, 59, 38, 32, 23, 0, 0, 220, 166, 129, 196, 148, 116, + 174, 132, 103, 151, 114, 89, 129, 97, 75, 107, 79, 60, 85, 61, 44, + 59, 38, 22, 21, 0, 0, 222, 164, 110, 197, 147, 99, 175, 131, 87, + 151, 113, 75, 129, 96, 63, 107, 79, 49, 85, 61, 33, 58, 38, 12, + 19, 0, 0, 222, 164, 88, 197, 146, 79, 174, 130, 69, 151, 113, 58, + 129, 95, 47, 107, 78, 35, 85, 60, 20, 58, 38, 0, 18, 0, 0, + 223, 164, 63, 198, 145, 55, 175, 129, 48, 151, 112, 39, 129, 95, 29, + 107, 78, 16, 85, 60, 1, 58, 38, 0, 17, 0, 0, 223, 163, 36, + 198, 145, 32, 174, 129, 26, 151, 111, 17, 129, 95, 7, 107, 78, 0, + 84, 60, 0, 57, 37, 0, 15, 0, 0, 215, 147, 192, 191, 130, 172, + 170, 116, 153, 148, 100, 133, 127, 85, 115, 107, 69, 96, 85, 51, 75, + 60, 28, 50, 25, 0, 8, 217, 146, 173, 192, 130, 154, 171, 115, 138, + 149, 100, 121, 128, 84, 103, 107, 68, 85, 85, 51, 66, 60, 28, 42, + 25, 0, 0, 217, 145, 157, 193, 129, 140, 173, 115, 125, 149, 100, 109, + 128, 84, 92, 107, 68, 76, 85, 51, 58, 59, 28, 35, 23, 0, 0, + 218, 145, 140, 193, 129, 125, 172, 114, 110, 149, 99, 96, 128, 83, 81, + 107, 67, 65, 84, 51, 49, 59, 29, 27, 22, 0, 0, 219, 144, 121, + 194, 128, 108, 172, 113, 96, 149, 98, 83, 128, 83, 69, 107, 68, 55, + 85, 50, 40, 59, 28, 18, 20, 0, 0, 220, 143, 104, 195, 128, 93, + 173, 114, 82, 150, 98, 69, 127, 82, 58, 107, 67, 45, 85, 50, 30, + 59, 28, 7, 19, 0, 0, 220, 143, 84, 195, 127, 74, 173, 113, 65, + 149, 97, 55, 128, 82, 44, 106, 67, 32, 84, 50, 16, 58, 28, 0, + 18, 0, 0, 221, 142, 62, 196, 126, 53, 173, 112, 46, 150, 97, 37, + 128, 82, 26, 107, 66, 14, 84, 50, 0, 58, 28, 0, 16, 0, 0, + 222, 142, 38, 196, 126, 34, 174, 112, 27, 150, 96, 17, 128, 82, 6, + 106, 66, 0, 84, 50, 0, 57, 29, 0, 16, 0, 0, 214, 123, 179, + 191, 110, 159, 169, 98, 143, 147, 84, 124, 126, 70, 106, 107, 55, 88, + 85, 39, 69, 60, 15, 45, 23, 0, 2, 216, 123, 161, 192, 110, 144, + 170, 98, 129, 148, 84, 112, 127, 70, 95, 107, 55, 79, 85, 39, 61, + 60, 15, 37, 20, 0, 0, 217, 122, 145, 192, 110, 130, 170, 97, 116, + 149, 84, 101, 127, 70, 85, 106, 55, 70, 85, 39, 53, 59, 16, 30, + 19, 0, 0, 217, 123, 131, 192, 109, 116, 171, 96, 103, 149, 83, 89, + 127, 70, 75, 106, 55, 60, 85, 40, 45, 59, 16, 23, 17, 0, 0, + 217, 122, 114, 193, 109, 101, 172, 96, 91, 149, 82, 77, 128, 69, 64, + 106, 55, 50, 84, 39, 35, 59, 17, 14, 17, 0, 0, 218, 122, 98, + 194, 108, 87, 171, 96, 77, 149, 82, 65, 127, 69, 52, 106, 55, 40, + 84, 40, 25, 59, 18, 3, 15, 0, 0, 219, 122, 80, 193, 108, 70, + 172, 95, 61, 149, 82, 51, 127, 69, 40, 106, 55, 28, 84, 39, 12, + 58, 17, 0, 13, 0, 0, 219, 121, 59, 194, 108, 52, 172, 96, 44, + 149, 82, 35, 127, 68, 24, 106, 55, 11, 84, 40, 0, 57, 18, 0, + 13, 0, 0, 219, 121, 40, 193, 108, 33, 172, 95, 26, 149, 81, 19, + 128, 68, 6, 106, 54, 0, 84, 39, 0, 57, 18, 0, 13, 0, 0, + 213, 99, 165, 189, 87, 148, 169, 76, 132, 147, 64, 115, 126, 52, 98, + 106, 39, 81, 85, 23, 63, 60, 0, 39, 16, 0, 0, 214, 98, 149, + 191, 87, 133, 170, 76, 119, 148, 65, 103, 127, 53, 88, 106, 39, 72, + 85, 24, 55, 60, 0, 32, 15, 0, 0, 215, 99, 136, 191, 87, 121, + 170, 77, 108, 148, 65, 93, 126, 53, 79, 106, 40, 64, 85, 24, 47, + 59, 0, 25, 14, 0, 0, 215, 99, 121, 192, 87, 108, 170, 77, 96, + 148, 65, 82, 126, 53, 69, 106, 40, 55, 85, 25, 39, 59, 0, 18, + 13, 0, 0, 216, 99, 106, 191, 87, 95, 170, 76, 83, 148, 65, 71, + 126, 53, 58, 106, 41, 45, 85, 26, 30, 59, 0, 8, 11, 0, 0, + 216, 98, 91, 192, 88, 82, 170, 77, 71, 148, 65, 60, 127, 53, 48, + 105, 41, 36, 83, 26, 21, 58, 1, 2, 11, 0, 0, 217, 99, 75, + 192, 87, 66, 170, 76, 57, 148, 65, 47, 126, 53, 36, 105, 41, 24, + 83, 26, 8, 57, 2, 0, 9, 0, 0, 217, 98, 57, 192, 87, 49, + 171, 77, 41, 147, 65, 32, 126, 53, 21, 105, 41, 8, 84, 27, 0, + 57, 3, 0, 9, 0, 0, 217, 98, 40, 193, 87, 34, 171, 76, 27, + 148, 65, 19, 126, 53, 6, 105, 41, 0, 83, 27, 0, 57, 4, 0, + 9, 0, 0, 211, 67, 152, 189, 58, 136, 168, 50, 122, 147, 39, 105, + 127, 28, 89, 106, 14, 74, 85, 0, 56, 59, 0, 33, 9, 0, 0, + 213, 68, 138, 190, 59, 123, 169, 51, 109, 148, 40, 95, 126, 30, 80, + 106, 16, 65, 85, 0, 48, 59, 0, 27, 9, 0, 0, 214, 69, 125, + 190, 59, 111, 168, 51, 99, 148, 41, 86, 126, 31, 72, 106, 18, 58, + 85, 0, 41, 59, 0, 20, 7, 0, 0, 215, 70, 112, 190, 61, 100, + 169, 52, 88, 147, 42, 76, 126, 32, 63, 106, 19, 49, 84, 1, 34, + 58, 0, 13, 7, 0, 0, 214, 70, 99, 190, 62, 88, 169, 53, 77, + 147, 43, 65, 125, 32, 53, 106, 20, 40, 84, 3, 26, 58, 0, 4, + 7, 0, 0, 214, 71, 86, 190, 61, 75, 169, 53, 65, 146, 43, 54, + 126, 33, 44, 105, 21, 31, 83, 4, 17, 57, 0, 0, 7, 0, 0, + 215, 71, 71, 191, 62, 62, 169, 53, 53, 147, 44, 44, 126, 34, 33, + 105, 22, 20, 83, 5, 4, 57, 0, 0, 7, 0, 0, 215, 71, 54, + 191, 62, 47, 169, 54, 39, 147, 44, 30, 126, 35, 20, 105, 23, 6, + 83, 6, 0, 56, 0, 0, 5, 0, 0, 215, 71, 41, 191, 63, 34, + 170, 54, 27, 147, 45, 17, 126, 35, 6, 105, 23, 0, 83, 8, 0, + 56, 0, 0, 5, 0, 0, 210, 13, 140, 189, 1, 125, 167, 0, 110, + 146, 0, 96, 126, 0, 81, 106, 0, 67, 85, 0, 51, 59, 0, 28, + 4, 0, 0, 212, 18, 126, 190, 7, 113, 168, 0, 100, 146, 0, 86, + 126, 0, 73, 106, 0, 59, 84, 0, 43, 59, 0, 22, 4, 0, 0, + 212, 21, 115, 190, 13, 103, 168, 3, 91, 146, 0, 78, 125, 0, 65, + 105, 0, 52, 84, 0, 36, 58, 0, 16, 4, 0, 0, 213, 24, 103, + 189, 19, 91, 168, 9, 82, 146, 0, 69, 125, 0, 57, 105, 0, 44, + 84, 0, 29, 58, 0, 7, 4, 0, 0, 213, 27, 92, 188, 21, 81, + 168, 14, 71, 146, 1, 59, 125, 0, 48, 105, 0, 36, 84, 0, 21, + 58, 0, 4, 4, 0, 0, 213, 30, 80, 189, 22, 69, 168, 17, 61, + 146, 5, 50, 125, 0, 39, 104, 0, 27, 83, 0, 12, 57, 0, 0, + 4, 0, 0, 214, 30, 67, 189, 25, 57, 168, 20, 50, 146, 9, 40, + 125, 0, 29, 104, 0, 17, 83, 0, 2, 56, 0, 0, 4, 0, 0, + 214, 32, 53, 189, 27, 44, 169, 20, 38, 146, 13, 28, 124, 2, 17, + 104, 0, 4, 83, 0, 0, 56, 0, 0, 4, 0, 0, 214, 33, 41, + 190, 27, 33, 168, 23, 27, 146, 13, 18, 125, 3, 5, 105, 0, 0, + 83, 0, 0, 56, 0, 0, 4, 0, 0, 185, 229, 250, 164, 204, 223, + 146, 182, 199, 127, 158, 174, 108, 136, 149, 89, 113, 125, 70, 90, 100, + 46, 62, 71, 10, 25, 33, 189, 227, 225, 168, 202, 201, 148, 181, 179, + 129, 157, 156, 109, 135, 134, 90, 113, 113, 70, 89, 90, 46, 62, 62, + 8, 24, 25, 192, 226, 202, 170, 202, 182, 151, 179, 162, 130, 156, 141, + 110, 133, 121, 91, 112, 101, 71, 89, 80, 46, 61, 54, 7, 24, 19, + 194, 224, 179, 173, 200, 160, 153, 178, 144, 132, 155, 125, 112, 133, 107, + 92, 111, 89, 71, 88, 69, 46, 61, 45, 6, 23, 10, 196, 223, 155, + 174, 198, 139, 154, 176, 124, 132, 153, 107, 113, 131, 91, 92, 110, 75, + 72, 87, 58, 47, 60, 37, 4, 23, 0, 198, 221, 131, 175, 197, 117, + 155, 175, 105, 133, 152, 91, 113, 130, 76, 92, 109, 63, 72, 86, 47, + 46, 60, 26, 3, 23, 0, 200, 220, 104, 176, 196, 94, 156, 175, 84, + 134, 151, 72, 113, 129, 59, 93, 108, 47, 72, 85, 33, 46, 59, 13, + 0, 23, 0, 201, 219, 73, 179, 195, 65, 157, 173, 57, 135, 150, 48, + 114, 129, 39, 94, 108, 28, 72, 85, 15, 47, 59, 0, 0, 22, 0, + 203, 219, 42, 178, 195, 37, 157, 173, 32, 135, 150, 26, 114, 128, 16, + 94, 107, 6, 73, 85, 0, 46, 58, 0, 0, 22, 0, 186, 205, 233, + 165, 183, 209, 148, 163, 187, 128, 142, 163, 109, 121, 140, 91, 101, 118, + 71, 80, 94, 48, 54, 66, 12, 15, 27, 189, 204, 211, 169, 182, 189, + 151, 163, 169, 131, 141, 147, 111, 121, 126, 92, 101, 105, 72, 79, 84, + 48, 54, 58, 11, 15, 19, 192, 202, 190, 171, 181, 170, 152, 161, 152, + 131, 141, 133, 112, 120, 113, 93, 100, 94, 72, 79, 74, 48, 53, 50, + 10, 15, 11, 195, 201, 169, 172, 179, 151, 153, 160, 135, 132, 139, 117, + 113, 119, 100, 93, 99, 82, 72, 78, 64, 48, 53, 41, 9, 14, 3, + 195, 200, 146, 174, 179, 131, 154, 159, 117, 133, 138, 101, 113, 118, 86, + 93, 98, 70, 73, 77, 53, 48, 52, 32, 8, 15, 0, 198, 199, 125, + 175, 177, 111, 155, 158, 100, 133, 137, 85, 113, 117, 71, 93, 97, 57, + 72, 77, 42, 47, 52, 22, 5, 14, 0, 199, 198, 101, 176, 177, 89, + 155, 157, 79, 134, 136, 68, 113, 116, 56, 94, 97, 44, 73, 76, 30, + 47, 52, 10, 2, 15, 0, 200, 197, 72, 178, 176, 63, 157, 156, 56, + 135, 136, 46, 114, 116, 37, 94, 96, 26, 73, 76, 11, 47, 51, 0, + 0, 14, 0, 201, 197, 45, 177, 175, 38, 156, 155, 31, 135, 135, 25, + 114, 115, 17, 94, 96, 5, 73, 75, 0, 46, 51, 0, 0, 14, 0, + 187, 183, 218, 167, 165, 197, 149, 147, 176, 129, 127, 153, 111, 109, 132, + 92, 90, 111, 73, 70, 89, 49, 46, 62, 15, 4, 22, 190, 183, 197, + 170, 164, 177, 151, 146, 159, 130, 127, 139, 112, 109, 119, 93, 90, 99, + 72, 70, 78, 49, 45, 53, 14, 4, 15, 192, 182, 179, 171, 163, 161, + 153, 145, 144, 132, 126, 125, 113, 108, 107, 93, 89, 88, 73, 70, 69, + 49, 45, 45, 13, 5, 6, 195, 181, 159, 172, 162, 142, 152, 145, 127, + 132, 125, 111, 113, 107, 94, 93, 88, 77, 73, 69, 59, 48, 45, 37, + 11, 5, 0, 195, 180, 139, 173, 161, 124, 153, 143, 110, 133, 125, 96, + 113, 106, 81, 94, 88, 66, 73, 68, 49, 49, 44, 28, 9, 6, 0, + 196, 179, 118, 174, 160, 106, 154, 142, 94, 133, 124, 81, 113, 105, 68, + 94, 87, 54, 73, 68, 39, 48, 44, 18, 5, 5, 0, 197, 178, 96, + 176, 159, 86, 155, 141, 75, 134, 123, 64, 114, 105, 53, 94, 87, 40, + 73, 68, 26, 48, 44, 5, 2, 6, 0, 199, 178, 70, 176, 158, 62, + 156, 141, 54, 134, 122, 44, 114, 104, 35, 94, 86, 23, 73, 67, 8, + 47, 44, 0, 2, 6, 0, 199, 177, 45, 178, 158, 40, 156, 140, 32, + 135, 122, 26, 114, 104, 16, 94, 86, 4, 73, 67, 0, 47, 44, 0, + 0, 7, 0, 188, 161, 204, 168, 144, 183, 149, 129, 164, 130, 112, 144, + 112, 95, 123, 93, 78, 103, 74, 60, 81, 50, 36, 56, 16, 0, 16, + 190, 160, 185, 170, 144, 165, 151, 128, 148, 132, 111, 130, 112, 95, 110, + 93, 78, 92, 74, 59, 72, 50, 36, 48, 16, 0, 8, 192, 160, 167, + 171, 143, 150, 153, 128, 134, 132, 111, 117, 112, 94, 100, 94, 77, 82, + 74, 59, 63, 50, 36, 40, 14, 0, 0, 193, 159, 149, 172, 143, 134, + 153, 127, 119, 133, 110, 103, 113, 94, 87, 93, 77, 72, 73, 59, 54, + 50, 36, 32, 12, 0, 0, 195, 159, 131, 173, 142, 117, 153, 127, 104, + 132, 110, 90, 113, 93, 76, 93, 76, 61, 74, 59, 45, 49, 36, 23, + 9, 0, 0, 196, 158, 113, 174, 141, 101, 155, 126, 89, 133, 109, 76, + 113, 93, 64, 94, 76, 51, 74, 58, 35, 49, 36, 14, 6, 0, 0, + 197, 157, 92, 174, 141, 80, 154, 125, 71, 134, 108, 60, 114, 92, 50, + 94, 75, 37, 73, 58, 22, 48, 36, 1, 5, 0, 0, 197, 157, 68, + 175, 140, 59, 155, 124, 51, 134, 108, 41, 113, 91, 32, 94, 75, 21, + 73, 57, 5, 48, 35, 0, 5, 0, 0, 198, 156, 46, 176, 140, 40, + 155, 124, 32, 134, 107, 24, 114, 91, 14, 94, 75, 2, 73, 57, 0, + 48, 36, 0, 3, 0, 0, 189, 140, 191, 168, 126, 172, 150, 112, 154, + 131, 97, 134, 112, 82, 115, 94, 66, 96, 74, 49, 75, 51, 25, 50, + 12, 0, 10, 191, 139, 173, 170, 125, 154, 152, 111, 138, 132, 96, 121, + 113, 81, 103, 94, 66, 85, 74, 48, 66, 50, 26, 42, 12, 0, 1, + 192, 139, 157, 171, 125, 140, 152, 111, 125, 132, 96, 109, 113, 81, 92, + 94, 65, 76, 74, 48, 58, 50, 26, 35, 9, 0, 0, 193, 139, 140, + 172, 124, 125, 153, 110, 112, 133, 95, 96, 113, 80, 82, 94, 65, 66, + 74, 49, 50, 50, 26, 28, 7, 0, 0, 194, 138, 123, 172, 123, 109, + 153, 110, 97, 133, 95, 84, 113, 80, 70, 94, 65, 56, 74, 48, 40, + 50, 26, 20, 6, 0, 0, 194, 138, 105, 173, 123, 94, 153, 109, 83, + 133, 94, 70, 112, 79, 59, 94, 64, 46, 74, 48, 31, 50, 26, 9, + 4, 0, 0, 196, 138, 87, 174, 122, 77, 153, 109, 67, 133, 93, 56, + 113, 79, 46, 94, 64, 34, 73, 48, 18, 49, 27, 0, 4, 0, 0, + 196, 137, 65, 174, 122, 57, 154, 108, 49, 133, 93, 39, 113, 79, 29, + 94, 64, 18, 74, 48, 3, 49, 27, 0, 2, 0, 0, 197, 137, 47, + 175, 122, 40, 155, 108, 32, 133, 93, 23, 114, 79, 14, 94, 64, 1, + 73, 48, 0, 48, 27, 0, 2, 0, 0, 189, 119, 177, 168, 106, 159, + 150, 94, 142, 131, 81, 124, 113, 67, 107, 94, 53, 89, 74, 37, 69, + 51, 11, 45, 6, 0, 3, 191, 119, 161, 170, 106, 144, 152, 94, 129, + 132, 81, 112, 113, 67, 96, 94, 53, 79, 74, 37, 61, 51, 13, 38, + 6, 0, 0, 192, 119, 146, 170, 106, 131, 152, 94, 117, 132, 80, 101, + 112, 67, 85, 94, 53, 70, 74, 37, 53, 50, 14, 31, 4, 0, 0, + 192, 119, 131, 171, 106, 117, 153, 94, 105, 132, 80, 89, 113, 67, 75, + 94, 54, 61, 74, 38, 45, 51, 14, 23, 3, 0, 0, 193, 118, 114, + 171, 106, 102, 153, 93, 90, 132, 80, 78, 113, 67, 65, 94, 53, 52, + 74, 37, 36, 50, 15, 16, 1, 0, 0, 194, 118, 99, 172, 105, 89, + 153, 93, 78, 132, 80, 66, 113, 67, 54, 94, 53, 42, 74, 38, 27, + 50, 16, 5, 1, 0, 0, 194, 118, 82, 173, 105, 72, 153, 93, 63, + 132, 79, 53, 113, 67, 42, 94, 53, 30, 74, 38, 15, 49, 16, 0, + 0, 0, 0, 195, 117, 63, 173, 105, 55, 154, 93, 47, 133, 79, 37, + 113, 66, 27, 94, 53, 15, 73, 38, 0, 48, 16, 0, 0, 0, 0, + 195, 117, 46, 173, 104, 39, 154, 92, 32, 133, 79, 22, 113, 66, 13, + 94, 53, 0, 73, 38, 0, 48, 17, 0, 0, 0, 0, 189, 96, 166, + 168, 85, 147, 150, 74, 132, 131, 62, 115, 113, 51, 99, 94, 38, 82, + 74, 21, 63, 51, 0, 40, 1, 0, 0, 190, 96, 150, 170, 85, 133, + 152, 75, 119, 132, 63, 104, 113, 51, 88, 94, 38, 72, 75, 22, 55, + 51, 0, 33, 1, 0, 0, 192, 96, 137, 170, 85, 121, 152, 74, 108, + 132, 64, 94, 113, 52, 79, 94, 39, 64, 74, 23, 48, 50, 0, 26, + 0, 0, 0, 192, 96, 122, 171, 86, 109, 152, 75, 96, 132, 63, 83, + 113, 52, 69, 94, 39, 56, 74, 24, 41, 50, 0, 19, 0, 0, 0, + 193, 96, 107, 171, 85, 96, 152, 75, 84, 132, 64, 72, 113, 52, 60, + 94, 39, 47, 74, 24, 32, 50, 1, 10, 0, 0, 0, 193, 96, 93, + 172, 85, 82, 152, 75, 72, 133, 63, 61, 113, 51, 49, 94, 39, 37, + 73, 25, 23, 49, 2, 2, 0, 0, 0, 194, 96, 78, 172, 85, 68, + 152, 75, 59, 132, 63, 49, 113, 52, 39, 94, 40, 26, 73, 25, 11, + 48, 3, 0, 0, 0, 0, 194, 96, 60, 173, 85, 52, 153, 75, 44, + 132, 64, 35, 112, 52, 25, 94, 40, 12, 73, 26, 0, 48, 4, 0, + 0, 0, 0, 195, 96, 46, 173, 85, 38, 154, 74, 31, 133, 63, 22, + 113, 52, 11, 93, 40, 0, 73, 26, 0, 47, 5, 0, 0, 0, 0, + 188, 67, 153, 168, 58, 137, 151, 49, 122, 131, 39, 106, 113, 28, 90, + 94, 13, 75, 75, 0, 57, 51, 0, 35, 0, 0, 0, 190, 68, 138, + 170, 59, 123, 152, 50, 110, 132, 41, 96, 113, 29, 80, 94, 16, 66, + 75, 0, 49, 50, 0, 27, 0, 0, 0, 191, 69, 126, 170, 59, 112, + 151, 52, 100, 132, 42, 86, 113, 30, 73, 95, 17, 58, 75, 0, 42, + 50, 0, 21, 0, 0, 0, 192, 70, 113, 170, 61, 100, 151, 52, 89, + 132, 42, 77, 113, 31, 64, 94, 19, 50, 74, 1, 35, 50, 0, 14, + 0, 0, 0, 192, 70, 100, 170, 62, 89, 151, 53, 77, 131, 43, 66, + 112, 32, 54, 94, 20, 42, 74, 2, 27, 49, 0, 5, 0, 0, 0, + 192, 71, 87, 171, 61, 77, 152, 53, 67, 131, 44, 57, 112, 33, 45, + 94, 21, 33, 74, 4, 19, 49, 0, 1, 0, 0, 0, 193, 71, 74, + 171, 62, 64, 152, 53, 55, 132, 44, 45, 113, 34, 34, 94, 22, 23, + 73, 5, 7, 48, 0, 0, 0, 0, 0, 193, 70, 58, 172, 62, 50, + 152, 54, 42, 132, 44, 32, 112, 35, 22, 93, 23, 10, 73, 6, 0, + 47, 0, 0, 0, 0, 0, 193, 70, 45, 172, 62, 38, 153, 54, 31, + 132, 44, 21, 112, 35, 9, 94, 23, 0, 73, 7, 0, 47, 0, 0, + 0, 0, 0, 189, 26, 141, 169, 15, 126, 150, 2, 112, 131, 0, 97, + 113, 0, 82, 94, 0, 67, 75, 0, 51, 50, 0, 29, 0, 0, 0, + 190, 28, 128, 170, 18, 114, 151, 8, 101, 132, 0, 88, 113, 0, 74, + 94, 0, 60, 75, 0, 44, 50, 0, 23, 0, 0, 0, 191, 30, 117, + 170, 23, 104, 152, 11, 92, 132, 1, 79, 113, 0, 67, 95, 0, 53, + 75, 0, 37, 50, 0, 17, 0, 0, 0, 191, 33, 105, 170, 26, 93, + 151, 18, 83, 132, 6, 70, 112, 0, 58, 94, 0, 45, 75, 0, 30, + 49, 0, 8, 0, 0, 0, 191, 34, 93, 170, 27, 82, 151, 20, 72, + 131, 8, 61, 112, 0, 49, 94, 0, 38, 74, 0, 23, 49, 0, 4, + 0, 0, 0, 191, 36, 82, 170, 29, 71, 151, 22, 63, 131, 11, 52, + 112, 0, 41, 93, 0, 29, 74, 0, 14, 48, 0, 1, 0, 0, 0, + 191, 38, 69, 170, 31, 60, 151, 24, 51, 131, 14, 41, 112, 1, 31, + 93, 0, 19, 73, 0, 3, 48, 0, 0, 0, 0, 0, 192, 37, 56, + 171, 31, 47, 152, 25, 40, 131, 17, 30, 112, 4, 19, 93, 0, 7, + 73, 0, 0, 47, 0, 0, 0, 0, 0, 192, 38, 45, 171, 33, 36, + 152, 26, 30, 131, 18, 21, 111, 7, 9, 93, 0, 0, 73, 0, 0, + 47, 0, 0, 0, 0, 0, 149, 218, 248, 133, 194, 222, 119, 173, 198, + 102, 151, 173, 86, 130, 148, 70, 108, 125, 53, 85, 100, 32, 59, 71, + 0, 22, 33, 154, 216, 223, 137, 193, 200, 122, 172, 178, 106, 150, 156, + 89, 128, 133, 73, 107, 112, 54, 85, 89, 31, 59, 63, 0, 22, 26, + 159, 215, 202, 141, 192, 181, 126, 171, 161, 108, 149, 141, 90, 128, 121, + 74, 107, 100, 55, 85, 80, 32, 58, 55, 0, 22, 19, 161, 213, 179, + 144, 190, 160, 126, 170, 143, 109, 148, 125, 92, 127, 107, 74, 106, 89, + 56, 84, 69, 32, 58, 46, 0, 21, 11, 163, 211, 156, 144, 189, 139, + 129, 168, 125, 110, 147, 108, 93, 126, 92, 75, 105, 76, 57, 83, 58, + 33, 58, 37, 0, 21, 1, 167, 211, 133, 147, 188, 120, 130, 167, 105, + 110, 145, 92, 93, 125, 78, 76, 104, 64, 58, 83, 48, 33, 57, 27, + 0, 21, 0, 169, 210, 108, 149, 187, 96, 131, 166, 86, 112, 144, 74, + 94, 124, 62, 77, 103, 49, 58, 82, 35, 33, 57, 15, 0, 21, 0, + 170, 209, 80, 151, 186, 71, 133, 165, 62, 114, 143, 52, 95, 123, 42, + 77, 103, 32, 58, 81, 18, 33, 56, 0, 0, 21, 0, 173, 208, 55, + 152, 186, 49, 134, 165, 41, 114, 143, 34, 95, 122, 25, 77, 102, 14, + 58, 81, 0, 33, 56, 0, 0, 21, 0, 154, 195, 232, 137, 174, 207, + 122, 156, 185, 105, 136, 163, 89, 116, 140, 73, 97, 117, 56, 76, 94, + 35, 51, 66, 0, 13, 28, 158, 194, 209, 141, 174, 187, 125, 155, 167, + 109, 135, 146, 91, 116, 125, 75, 96, 105, 57, 75, 83, 35, 50, 57, + 0, 12, 21, 161, 193, 189, 144, 173, 169, 128, 154, 151, 110, 134, 132, + 93, 115, 113, 77, 95, 94, 58, 75, 74, 35, 50, 50, 0, 12, 13, + 164, 192, 168, 145, 171, 151, 129, 153, 134, 111, 133, 117, 94, 114, 100, + 76, 95, 82, 58, 75, 64, 36, 50, 42, 0, 12, 5, 165, 191, 147, + 147, 170, 131, 130, 152, 117, 113, 132, 102, 95, 113, 86, 77, 94, 71, + 58, 74, 54, 35, 50, 33, 0, 13, 0, 167, 189, 126, 148, 169, 113, + 132, 151, 100, 113, 131, 86, 96, 112, 73, 77, 93, 59, 59, 73, 43, + 35, 49, 23, 0, 12, 0, 170, 189, 104, 150, 168, 91, 133, 150, 81, + 114, 130, 69, 96, 111, 57, 78, 92, 46, 59, 73, 31, 35, 49, 11, + 0, 13, 0, 171, 188, 78, 152, 168, 68, 134, 149, 60, 115, 130, 50, + 96, 111, 41, 78, 92, 29, 60, 73, 15, 35, 49, 0, 0, 12, 0, + 173, 187, 55, 153, 167, 47, 134, 149, 39, 115, 129, 33, 97, 110, 24, + 79, 92, 13, 60, 72, 0, 35, 48, 0, 0, 12, 0, 157, 175, 217, + 139, 157, 196, 125, 141, 175, 109, 122, 153, 92, 104, 132, 76, 86, 110, + 59, 67, 88, 37, 43, 61, 1, 1, 23, 161, 174, 196, 144, 156, 176, + 127, 140, 158, 110, 121, 137, 94, 104, 118, 77, 85, 98, 59, 67, 78, + 37, 43, 53, 0, 2, 16, 163, 174, 178, 146, 156, 160, 130, 139, 143, + 112, 121, 124, 95, 103, 106, 78, 85, 88, 60, 66, 69, 37, 42, 46, + 0, 2, 7, 166, 173, 159, 147, 154, 142, 130, 138, 127, 113, 120, 111, + 96, 103, 95, 78, 84, 77, 60, 66, 59, 37, 43, 37, 0, 2, 0, + 166, 172, 139, 148, 154, 125, 131, 137, 112, 113, 120, 96, 96, 102, 81, + 78, 84, 66, 60, 65, 50, 37, 42, 29, 0, 3, 0, 167, 171, 120, + 149, 153, 107, 133, 137, 95, 114, 118, 81, 97, 101, 69, 79, 84, 56, + 60, 65, 40, 37, 42, 19, 0, 3, 0, 170, 170, 99, 151, 152, 87, + 134, 136, 77, 115, 118, 66, 97, 101, 55, 79, 83, 42, 61, 65, 28, + 37, 42, 7, 0, 3, 0, 172, 170, 75, 152, 151, 65, 134, 135, 57, + 115, 117, 48, 97, 100, 38, 79, 83, 27, 61, 64, 12, 36, 42, 0, + 0, 3, 0, 172, 169, 55, 154, 151, 46, 135, 134, 40, 116, 116, 32, + 97, 99, 21, 80, 82, 10, 61, 64, 0, 36, 41, 0, 0, 3, 0, + 160, 154, 203, 143, 139, 182, 127, 124, 164, 111, 107, 143, 95, 91, 122, + 78, 75, 103, 60, 57, 81, 39, 33, 56, 1, 0, 18, 163, 154, 184, + 146, 138, 165, 130, 123, 148, 113, 107, 129, 96, 90, 110, 79, 74, 92, + 61, 56, 72, 39, 34, 48, 2, 0, 9, 165, 154, 167, 147, 137, 149, + 131, 122, 134, 114, 106, 117, 96, 90, 100, 79, 74, 82, 61, 56, 64, + 39, 33, 40, 2, 0, 1, 166, 153, 150, 149, 137, 133, 132, 122, 119, + 114, 106, 104, 97, 90, 88, 79, 74, 72, 61, 56, 55, 39, 34, 33, + 0, 0, 0, 168, 152, 132, 149, 136, 117, 132, 121, 104, 114, 105, 90, + 97, 89, 76, 79, 73, 62, 61, 56, 46, 38, 34, 25, 0, 0, 0, + 169, 151, 114, 150, 135, 101, 133, 121, 90, 114, 104, 77, 97, 89, 65, + 80, 73, 51, 61, 56, 36, 38, 34, 16, 0, 0, 0, 170, 150, 94, + 151, 135, 83, 134, 120, 73, 115, 104, 62, 98, 88, 51, 80, 72, 39, + 61, 56, 24, 38, 34, 3, 0, 0, 0, 172, 150, 72, 153, 134, 63, + 135, 119, 55, 115, 103, 45, 98, 88, 36, 80, 72, 24, 61, 55, 9, + 38, 34, 0, 0, 0, 0, 172, 150, 54, 153, 134, 47, 135, 119, 38, + 116, 103, 30, 98, 87, 21, 80, 72, 8, 62, 55, 0, 37, 34, 0, + 0, 0, 0, 162, 134, 190, 145, 120, 171, 129, 108, 153, 113, 93, 134, + 97, 78, 115, 80, 63, 96, 62, 46, 75, 41, 23, 51, 0, 0, 11, + 165, 134, 173, 147, 120, 154, 131, 107, 138, 114, 92, 120, 97, 78, 103, + 80, 63, 85, 62, 46, 66, 40, 23, 43, 0, 0, 2, 166, 134, 157, + 148, 120, 140, 132, 106, 125, 114, 92, 109, 97, 77, 93, 81, 63, 77, + 62, 46, 58, 40, 24, 36, 0, 0, 0, 168, 133, 140, 149, 119, 125, + 132, 106, 112, 115, 92, 97, 98, 77, 82, 81, 62, 67, 62, 46, 50, + 40, 24, 29, 0, 0, 0, 168, 133, 123, 150, 119, 110, 133, 106, 97, + 115, 91, 84, 98, 77, 70, 81, 62, 57, 62, 46, 41, 40, 24, 20, + 0, 0, 0, 169, 132, 107, 150, 118, 94, 133, 105, 84, 115, 91, 72, + 98, 76, 60, 80, 62, 47, 62, 46, 32, 39, 25, 11, 0, 0, 0, + 171, 132, 89, 152, 118, 79, 135, 105, 69, 115, 90, 58, 98, 76, 47, + 80, 62, 36, 62, 46, 21, 39, 25, 0, 0, 0, 0, 171, 132, 69, + 153, 117, 60, 135, 104, 52, 116, 90, 42, 98, 76, 33, 81, 61, 21, + 62, 46, 6, 38, 25, 0, 0, 0, 0, 172, 132, 54, 153, 118, 45, + 135, 104, 38, 116, 90, 28, 98, 76, 18, 81, 61, 6, 62, 46, 0, + 38, 25, 0, 0, 0, 0, 164, 115, 177, 146, 103, 159, 130, 91, 143, + 114, 78, 125, 97, 65, 107, 81, 51, 89, 63, 34, 69, 41, 9, 46, + 0, 0, 4, 166, 115, 161, 148, 103, 144, 132, 91, 129, 115, 78, 112, + 98, 65, 96, 81, 51, 79, 63, 35, 61, 41, 11, 38, 0, 0, 0, + 167, 115, 146, 150, 102, 131, 132, 91, 117, 115, 78, 101, 98, 65, 86, + 81, 51, 71, 63, 35, 54, 41, 12, 32, 0, 0, 0, 168, 114, 132, + 150, 103, 118, 133, 91, 105, 116, 78, 91, 98, 64, 76, 82, 51, 61, + 63, 36, 46, 41, 13, 24, 0, 0, 0, 169, 114, 116, 150, 102, 103, + 134, 90, 91, 116, 78, 79, 98, 65, 66, 81, 51, 53, 63, 36, 37, + 40, 14, 17, 0, 0, 0, 169, 114, 101, 151, 101, 89, 134, 90, 79, + 116, 77, 67, 98, 64, 56, 81, 51, 44, 63, 36, 29, 40, 15, 7, + 0, 0, 0, 170, 114, 85, 152, 101, 75, 135, 90, 65, 116, 77, 54, + 98, 64, 44, 81, 51, 32, 63, 36, 17, 39, 15, 0, 0, 0, 0, + 172, 113, 66, 152, 101, 58, 135, 89, 49, 116, 77, 40, 99, 64, 30, + 81, 51, 18, 62, 36, 3, 38, 16, 0, 0, 0, 0, 171, 113, 51, + 153, 101, 44, 136, 89, 36, 116, 77, 28, 99, 64, 18, 81, 51, 5, + 62, 36, 0, 38, 16, 0, 0, 0, 0, 165, 94, 166, 147, 82, 147, + 132, 72, 132, 115, 61, 115, 98, 49, 99, 82, 36, 82, 64, 19, 64, + 42, 0, 41, 0, 0, 0, 167, 93, 150, 150, 83, 134, 133, 73, 120, + 116, 62, 104, 99, 49, 88, 82, 36, 72, 64, 20, 55, 41, 0, 33, + 0, 0, 0, 169, 93, 137, 150, 83, 122, 134, 73, 109, 116, 61, 94, + 99, 50, 80, 82, 37, 65, 64, 21, 49, 41, 0, 27, 0, 0, 0, + 169, 94, 123, 150, 83, 110, 133, 73, 97, 116, 61, 83, 99, 50, 70, + 82, 38, 57, 63, 23, 42, 41, 0, 20, 0, 0, 0, 169, 94, 109, + 150, 84, 97, 134, 73, 85, 116, 62, 73, 99, 51, 61, 81, 38, 48, + 63, 23, 33, 41, 1, 11, 0, 0, 0, 170, 94, 96, 150, 83, 84, + 134, 73, 74, 116, 61, 62, 99, 50, 51, 82, 38, 39, 64, 23, 24, + 40, 3, 4, 0, 0, 0, 171, 93, 79, 152, 82, 70, 135, 73, 61, + 116, 62, 51, 98, 51, 40, 81, 38, 28, 63, 24, 14, 39, 4, 0, + 0, 0, 0, 171, 94, 64, 152, 83, 55, 135, 73, 47, 116, 62, 37, + 98, 50, 27, 81, 38, 15, 63, 24, 1, 39, 4, 0, 0, 0, 0, + 172, 93, 51, 153, 82, 42, 135, 73, 35, 117, 62, 26, 99, 51, 16, + 81, 39, 3, 63, 25, 0, 38, 5, 0, 0, 0, 0, 166, 68, 153, + 148, 59, 137, 133, 49, 121, 115, 39, 106, 99, 28, 91, 82, 13, 75, + 65, 0, 58, 42, 0, 36, 0, 0, 0, 168, 68, 139, 150, 59, 124, + 134, 50, 110, 116, 40, 96, 99, 30, 81, 82, 16, 66, 64, 0, 50, + 41, 0, 29, 0, 0, 0, 169, 69, 126, 150, 59, 113, 134, 51, 101, + 117, 42, 87, 100, 30, 73, 82, 17, 59, 65, 0, 43, 41, 0, 23, + 0, 0, 0, 169, 70, 115, 150, 61, 102, 134, 52, 89, 116, 42, 77, + 99, 32, 65, 82, 19, 52, 64, 0, 36, 41, 0, 15, 0, 0, 0, + 169, 70, 101, 150, 61, 90, 134, 52, 79, 116, 43, 68, 99, 32, 55, + 82, 21, 43, 64, 2, 28, 41, 0, 6, 0, 0, 0, 170, 70, 89, + 151, 62, 79, 134, 53, 69, 116, 44, 58, 99, 33, 46, 81, 21, 34, + 64, 3, 20, 41, 0, 2, 0, 0, 0, 170, 71, 76, 152, 62, 66, + 134, 53, 57, 116, 43, 46, 99, 33, 36, 82, 22, 24, 64, 5, 10, + 40, 0, 0, 0, 0, 0, 171, 70, 61, 152, 62, 52, 135, 53, 44, + 116, 44, 35, 99, 34, 24, 82, 22, 12, 63, 6, 0, 39, 0, 0, + 0, 0, 0, 171, 71, 49, 153, 62, 41, 135, 54, 33, 117, 45, 25, + 98, 34, 13, 81, 23, 0, 63, 7, 0, 39, 0, 0, 0, 0, 0, + 167, 33, 142, 149, 24, 127, 134, 10, 113, 116, 0, 97, 100, 0, 83, + 83, 0, 68, 65, 0, 52, 40, 0, 30, 0, 0, 0, 169, 33, 129, + 150, 26, 115, 134, 17, 102, 116, 3, 89, 100, 0, 75, 83, 0, 60, + 65, 0, 45, 40, 0, 24, 0, 0, 0, 169, 36, 118, 151, 27, 104, + 134, 19, 93, 116, 7, 80, 100, 0, 67, 83, 0, 54, 65, 0, 38, + 41, 0, 17, 0, 0, 0, 169, 39, 107, 150, 30, 94, 134, 22, 84, + 116, 11, 71, 99, 0, 59, 83, 0, 46, 64, 0, 31, 40, 0, 9, + 0, 0, 0, 169, 39, 95, 151, 31, 83, 134, 24, 73, 116, 15, 62, + 100, 1, 51, 83, 0, 38, 64, 0, 24, 40, 0, 5, 0, 0, 0, + 169, 41, 83, 151, 33, 73, 134, 26, 64, 117, 17, 54, 99, 4, 42, + 82, 0, 30, 64, 0, 16, 40, 0, 1, 0, 0, 0, 170, 42, 71, + 152, 34, 62, 134, 28, 53, 117, 19, 44, 99, 6, 33, 82, 0, 21, + 63, 0, 4, 39, 0, 0, 0, 0, 0, 171, 42, 59, 152, 35, 50, + 134, 29, 42, 117, 21, 32, 99, 9, 22, 82, 0, 9, 63, 0, 0, + 38, 0, 0, 0, 0, 0, 172, 42, 48, 152, 36, 40, 135, 29, 32, + 117, 21, 23, 99, 10, 12, 82, 0, 0, 63, 0, 0, 38, 0, 0, + 0, 0, 0, 107, 207, 246, 96, 185, 220, 86, 165, 196, 73, 144, 171, + 60, 123, 147, 46, 103, 125, 32, 82, 100, 9, 56, 71, 0, 20, 33, + 115, 206, 221, 104, 184, 198, 92, 164, 178, 78, 143, 154, 64, 123, 133, + 51, 102, 111, 34, 81, 89, 10, 56, 63, 0, 20, 27, 122, 204, 200, + 108, 183, 180, 95, 163, 161, 82, 142, 140, 68, 122, 120, 54, 102, 101, + 36, 81, 79, 11, 56, 55, 0, 20, 20, 125, 203, 179, 111, 181, 160, + 97, 162, 143, 85, 141, 124, 70, 121, 107, 55, 101, 89, 38, 80, 69, + 14, 55, 46, 0, 19, 10, 128, 202, 156, 113, 180, 140, 102, 161, 125, + 87, 140, 108, 71, 120, 92, 56, 100, 76, 39, 79, 59, 14, 55, 38, + 0, 20, 3, 132, 200, 135, 117, 179, 121, 103, 159, 106, 88, 139, 93, + 73, 119, 79, 57, 100, 65, 41, 79, 49, 15, 54, 28, 0, 19, 0, + 134, 200, 111, 119, 178, 98, 105, 158, 87, 89, 138, 76, 74, 118, 64, + 58, 99, 51, 41, 78, 37, 16, 54, 17, 0, 19, 0, 137, 199, 85, + 122, 177, 75, 108, 158, 66, 91, 137, 56, 75, 118, 46, 59, 98, 35, + 42, 78, 22, 16, 54, 3, 0, 19, 0, 140, 198, 62, 125, 177, 55, + 109, 158, 47, 92, 137, 40, 76, 117, 32, 59, 98, 21, 42, 78, 6, + 16, 54, 0, 0, 18, 0, 118, 186, 231, 106, 167, 206, 93, 149, 184, + 81, 130, 161, 67, 111, 139, 54, 92, 117, 39, 72, 93, 17, 48, 66, + 0, 10, 29, 123, 185, 207, 110, 166, 186, 98, 148, 167, 85, 129, 145, + 71, 111, 125, 56, 92, 104, 40, 72, 83, 18, 48, 57, 0, 10, 22, + 128, 184, 188, 113, 165, 168, 102, 147, 151, 88, 128, 131, 73, 110, 113, + 58, 91, 94, 42, 71, 74, 19, 48, 50, 0, 9, 15, 131, 183, 168, + 116, 164, 151, 104, 146, 134, 89, 127, 117, 73, 109, 100, 58, 90, 83, + 42, 71, 65, 20, 48, 42, 0, 9, 5, 134, 182, 148, 120, 163, 131, + 105, 145, 118, 90, 126, 102, 75, 108, 86, 59, 90, 72, 43, 71, 55, + 19, 47, 34, 0, 9, 0, 136, 181, 128, 122, 162, 115, 107, 144, 102, + 92, 125, 87, 76, 107, 74, 61, 89, 60, 44, 70, 45, 20, 47, 24, + 0, 8, 0, 139, 180, 106, 124, 161, 95, 109, 144, 83, 93, 124, 71, + 77, 107, 60, 61, 89, 47, 44, 70, 33, 20, 47, 13, 0, 8, 0, + 142, 179, 82, 125, 160, 72, 111, 143, 63, 94, 124, 54, 77, 106, 44, + 61, 88, 32, 44, 69, 18, 20, 46, 0, 0, 8, 0, 143, 179, 62, + 127, 160, 54, 111, 142, 47, 94, 124, 39, 78, 106, 29, 62, 88, 18, + 45, 69, 3, 20, 46, 0, 0, 8, 0, 124, 167, 216, 112, 150, 194, + 99, 134, 174, 87, 117, 153, 73, 100, 131, 58, 82, 110, 43, 64, 88, + 23, 40, 61, 0, 0, 24, 129, 166, 195, 116, 150, 175, 103, 134, 158, + 89, 116, 137, 75, 99, 118, 60, 82, 98, 44, 63, 78, 23, 40, 53, + 0, 0, 17, 132, 166, 177, 119, 149, 160, 106, 133, 143, 90, 115, 124, + 76, 99, 107, 61, 81, 88, 45, 63, 69, 24, 40, 46, 0, 0, 9, + 136, 166, 159, 121, 148, 143, 107, 132, 126, 92, 115, 111, 77, 98, 94, + 62, 81, 78, 46, 63, 60, 23, 40, 38, 0, 0, 0, 138, 164, 140, + 122, 147, 125, 108, 131, 111, 93, 114, 97, 79, 98, 82, 63, 80, 67, + 46, 62, 50, 24, 40, 29, 0, 0, 0, 139, 163, 122, 124, 146, 109, + 110, 131, 96, 94, 114, 83, 79, 97, 70, 63, 81, 57, 46, 62, 41, + 24, 40, 21, 0, 0, 0, 141, 163, 101, 126, 145, 90, 111, 130, 79, + 95, 113, 68, 79, 96, 56, 63, 80, 44, 47, 62, 30, 23, 40, 10, + 0, 0, 0, 144, 162, 79, 127, 145, 70, 112, 129, 60, 95, 112, 51, + 79, 96, 41, 64, 79, 30, 47, 61, 15, 23, 40, 0, 0, 0, 0, + 145, 162, 60, 129, 145, 52, 113, 129, 46, 96, 112, 37, 79, 95, 27, + 64, 79, 16, 47, 61, 1, 23, 39, 0, 0, 0, 0, 131, 147, 202, + 117, 133, 181, 105, 119, 162, 91, 103, 142, 77, 87, 122, 62, 71, 102, + 47, 54, 81, 26, 31, 56, 0, 0, 18, 135, 147, 183, 120, 132, 164, + 107, 118, 147, 93, 102, 128, 78, 87, 110, 63, 71, 92, 47, 54, 72, + 26, 31, 48, 0, 0, 10, 138, 147, 166, 123, 131, 149, 108, 118, 133, + 94, 102, 116, 79, 86, 100, 64, 71, 82, 48, 54, 64, 27, 31, 41, + 0, 0, 2, 139, 146, 149, 124, 131, 134, 111, 117, 119, 94, 101, 103, + 79, 86, 88, 64, 70, 72, 48, 53, 55, 27, 31, 33, 0, 0, 0, + 141, 146, 132, 125, 131, 117, 111, 117, 104, 95, 101, 91, 80, 86, 77, + 65, 70, 62, 48, 53, 46, 26, 31, 25, 0, 0, 0, 143, 145, 115, + 126, 130, 101, 112, 116, 90, 96, 100, 78, 80, 85, 65, 65, 70, 52, + 49, 53, 37, 27, 32, 17, 0, 0, 0, 144, 144, 96, 128, 129, 85, + 112, 115, 75, 97, 100, 64, 81, 85, 52, 65, 69, 40, 49, 53, 26, + 26, 31, 5, 0, 0, 0, 146, 144, 76, 129, 129, 67, 114, 115, 58, + 97, 99, 48, 82, 84, 38, 66, 69, 27, 49, 53, 12, 26, 32, 0, + 0, 0, 0, 146, 144, 59, 130, 128, 51, 114, 114, 43, 98, 99, 35, + 82, 84, 25, 66, 69, 13, 49, 53, 0, 26, 32, 0, 0, 0, 0, + 135, 129, 189, 122, 115, 170, 107, 103, 152, 94, 89, 133, 79, 74, 114, + 64, 60, 95, 49, 43, 75, 29, 20, 51, 0, 0, 12, 138, 129, 171, + 124, 115, 153, 110, 103, 138, 95, 89, 120, 81, 74, 103, 66, 60, 86, + 50, 44, 67, 28, 21, 43, 0, 0, 3, 140, 129, 156, 125, 115, 140, + 111, 103, 125, 96, 89, 109, 81, 74, 93, 67, 60, 76, 50, 44, 59, + 29, 22, 36, 0, 0, 0, 142, 128, 140, 127, 115, 125, 112, 102, 112, + 97, 88, 97, 82, 74, 83, 67, 60, 67, 50, 44, 51, 29, 22, 29, + 0, 0, 0, 142, 128, 124, 127, 114, 111, 113, 102, 98, 98, 88, 85, + 82, 74, 71, 66, 60, 58, 50, 44, 42, 29, 22, 21, 0, 0, 0, + 144, 127, 108, 128, 114, 96, 113, 101, 85, 98, 87, 73, 82, 74, 61, + 67, 60, 48, 50, 44, 33, 28, 23, 12, 0, 0, 0, 145, 127, 91, + 129, 114, 81, 115, 101, 71, 98, 87, 60, 82, 73, 48, 67, 59, 37, + 50, 44, 22, 29, 23, 1, 0, 0, 0, 147, 127, 73, 130, 113, 63, + 115, 101, 55, 98, 87, 45, 83, 73, 35, 67, 59, 24, 50, 44, 10, + 28, 24, 0, 0, 0, 0, 147, 127, 58, 131, 113, 49, 115, 100, 42, + 99, 86, 33, 83, 73, 23, 67, 59, 10, 50, 44, 0, 27, 24, 0, + 0, 0, 0, 138, 110, 177, 124, 99, 159, 110, 88, 142, 96, 75, 125, + 82, 62, 107, 66, 48, 89, 51, 33, 70, 30, 8, 46, 0, 0, 5, + 142, 111, 160, 127, 99, 144, 113, 88, 130, 98, 75, 112, 82, 62, 96, + 68, 49, 80, 51, 33, 61, 30, 10, 39, 0, 0, 0, 143, 111, 146, + 128, 99, 131, 114, 88, 118, 98, 75, 101, 83, 62, 86, 68, 49, 71, + 52, 33, 54, 30, 11, 32, 0, 0, 0, 144, 111, 132, 128, 99, 118, + 113, 88, 106, 99, 75, 91, 83, 62, 77, 68, 49, 62, 52, 34, 46, + 30, 12, 25, 0, 0, 0, 144, 111, 117, 129, 98, 104, 114, 87, 92, + 99, 75, 80, 83, 62, 67, 68, 49, 53, 51, 34, 38, 30, 13, 18, + 0, 0, 0, 145, 111, 103, 130, 98, 91, 114, 87, 80, 99, 75, 68, + 83, 63, 57, 68, 50, 45, 51, 34, 30, 30, 14, 8, 0, 0, 0, + 146, 110, 87, 131, 98, 76, 115, 87, 67, 99, 75, 56, 83, 62, 45, + 68, 49, 33, 52, 35, 19, 30, 15, 2, 0, 0, 0, 148, 110, 70, + 131, 98, 60, 116, 86, 52, 99, 74, 43, 84, 62, 33, 69, 49, 21, + 52, 35, 6, 29, 15, 0, 0, 0, 0, 148, 110, 56, 132, 97, 48, + 117, 87, 40, 100, 75, 31, 84, 62, 22, 68, 49, 9, 51, 35, 0, + 28, 15, 0, 0, 0, 0, 142, 91, 166, 126, 80, 148, 113, 71, 132, + 98, 59, 115, 83, 47, 99, 69, 34, 82, 53, 17, 64, 32, 0, 41, + 0, 0, 0, 143, 91, 150, 128, 81, 135, 114, 71, 120, 99, 60, 104, + 85, 48, 89, 69, 35, 73, 53, 19, 56, 32, 0, 34, 0, 0, 0, + 145, 91, 137, 129, 81, 122, 115, 71, 109, 100, 60, 94, 85, 48, 81, + 69, 35, 65, 53, 19, 49, 32, 0, 28, 0, 0, 0, 146, 92, 124, + 130, 81, 110, 115, 71, 98, 100, 60, 84, 85, 49, 71, 69, 36, 57, + 53, 21, 42, 32, 0, 21, 0, 0, 0, 147, 91, 110, 130, 81, 97, + 115, 71, 86, 100, 60, 74, 84, 49, 62, 69, 36, 48, 53, 22, 34, + 32, 0, 13, 0, 0, 0, 147, 92, 97, 130, 81, 85, 116, 72, 76, + 100, 60, 63, 85, 49, 52, 69, 37, 40, 53, 22, 26, 31, 1, 5, + 0, 0, 0, 148, 92, 82, 131, 81, 71, 116, 71, 62, 100, 60, 53, + 84, 49, 42, 69, 37, 30, 52, 23, 16, 31, 2, 0, 0, 0, 0, + 148, 91, 67, 132, 81, 57, 117, 71, 49, 100, 60, 39, 84, 49, 30, + 69, 37, 18, 52, 23, 2, 30, 2, 0, 0, 0, 0, 149, 91, 54, + 132, 81, 46, 118, 71, 39, 101, 60, 29, 85, 49, 19, 69, 37, 6, + 52, 23, 0, 29, 3, 0, 0, 0, 0, 143, 68, 153, 128, 59, 137, + 115, 49, 122, 99, 39, 107, 85, 28, 91, 70, 13, 75, 54, 0, 58, + 32, 0, 36, 0, 0, 0, 146, 68, 140, 131, 59, 125, 116, 51, 111, + 100, 40, 97, 85, 29, 82, 70, 15, 67, 54, 0, 50, 32, 0, 29, + 0, 0, 0, 147, 68, 127, 131, 59, 114, 117, 51, 102, 101, 41, 88, + 86, 30, 74, 70, 17, 60, 54, 0, 44, 32, 0, 23, 0, 0, 0, + 147, 70, 115, 131, 60, 103, 116, 52, 91, 100, 42, 78, 85, 32, 65, + 70, 19, 53, 54, 1, 38, 32, 0, 17, 0, 0, 0, 147, 70, 103, + 131, 61, 91, 117, 53, 81, 101, 43, 69, 86, 32, 57, 70, 20, 44, + 54, 2, 30, 32, 0, 7, 0, 0, 0, 148, 70, 91, 132, 61, 80, + 117, 52, 70, 101, 43, 59, 85, 33, 48, 70, 21, 36, 53, 4, 22, + 32, 0, 3, 0, 0, 0, 148, 70, 78, 132, 62, 68, 117, 53, 58, + 101, 43, 48, 85, 34, 38, 70, 22, 26, 53, 6, 12, 31, 0, 0, + 0, 0, 0, 149, 71, 64, 132, 62, 54, 118, 54, 46, 101, 44, 37, + 85, 34, 27, 69, 23, 15, 53, 7, 1, 30, 0, 0, 0, 0, 0, + 150, 70, 53, 134, 61, 44, 118, 54, 36, 101, 44, 28, 85, 35, 17, + 69, 23, 4, 52, 8, 0, 30, 0, 0, 0, 0, 0, 145, 38, 143, + 130, 29, 128, 117, 18, 114, 101, 3, 98, 87, 0, 84, 72, 0, 69, + 54, 0, 53, 30, 0, 31, 0, 0, 0, 147, 38, 130, 132, 30, 116, + 117, 22, 103, 101, 8, 89, 87, 0, 76, 72, 0, 62, 54, 0, 46, + 30, 0, 24, 0, 0, 0, 148, 40, 119, 132, 31, 105, 117, 23, 94, + 101, 13, 81, 87, 0, 68, 71, 0, 55, 54, 0, 39, 30, 0, 18, + 0, 0, 0, 148, 42, 108, 132, 34, 96, 117, 25, 85, 102, 15, 73, + 86, 2, 60, 71, 0, 47, 54, 0, 33, 30, 0, 11, 0, 0, 0, + 148, 43, 96, 133, 35, 85, 117, 28, 75, 102, 18, 64, 87, 5, 52, + 71, 0, 40, 54, 0, 25, 30, 0, 5, 0, 0, 0, 149, 44, 85, + 132, 36, 75, 118, 29, 66, 101, 20, 55, 86, 8, 44, 70, 0, 32, + 53, 0, 18, 29, 0, 2, 0, 0, 0, 149, 45, 74, 133, 37, 64, + 118, 31, 55, 102, 21, 45, 85, 10, 34, 70, 0, 22, 53, 0, 6, + 28, 0, 0, 0, 0, 0, 150, 46, 61, 133, 39, 52, 118, 31, 44, + 102, 23, 34, 85, 12, 24, 70, 0, 12, 52, 0, 0, 28, 0, 0, + 0, 0, 0, 150, 46, 51, 133, 40, 42, 119, 32, 35, 102, 24, 25, + 85, 13, 14, 70, 0, 1, 52, 0, 0, 27, 0, 0, 0, 0, 0, + 53, 198, 244, 49, 177, 218, 41, 158, 195, 32, 138, 171, 22, 118, 147, + 11, 98, 124, 0, 78, 100, 0, 54, 71, 0, 18, 34, 69, 196, 220, + 64, 175, 196, 54, 157, 176, 45, 137, 154, 32, 117, 133, 19, 98, 111, + 0, 78, 89, 0, 53, 63, 0, 17, 27, 80, 195, 198, 69, 175, 179, + 60, 156, 159, 50, 136, 139, 38, 116, 120, 25, 98, 101, 4, 77, 80, + 0, 53, 55, 0, 17, 21, 84, 193, 177, 75, 173, 159, 64, 155, 142, + 55, 135, 124, 41, 116, 107, 27, 97, 89, 9, 76, 70, 0, 53, 47, + 0, 17, 11, 89, 193, 157, 79, 172, 140, 70, 154, 125, 57, 134, 109, + 44, 115, 92, 32, 96, 76, 13, 76, 59, 0, 52, 39, 0, 16, 4, + 94, 191, 135, 85, 171, 121, 72, 152, 108, 60, 133, 94, 47, 114, 80, + 32, 95, 65, 15, 76, 49, 0, 52, 29, 0, 16, 0, 98, 190, 113, + 87, 170, 100, 76, 152, 89, 62, 132, 77, 49, 113, 65, 35, 95, 52, + 18, 75, 37, 0, 52, 18, 0, 15, 0, 103, 190, 89, 90, 169, 80, + 78, 151, 70, 64, 132, 60, 51, 113, 49, 37, 94, 38, 20, 75, 25, + 0, 52, 5, 0, 15, 0, 106, 189, 69, 93, 169, 61, 80, 151, 53, + 66, 131, 45, 52, 113, 36, 37, 94, 25, 19, 74, 11, 0, 51, 0, + 0, 15, 0, 76, 178, 229, 68, 159, 205, 61, 142, 183, 50, 124, 160, + 40, 106, 138, 28, 88, 116, 12, 69, 93, 0, 45, 66, 0, 5, 29, + 86, 177, 207, 78, 158, 184, 67, 142, 166, 56, 123, 145, 45, 106, 125, + 31, 88, 105, 16, 69, 83, 0, 45, 58, 0, 6, 22, 93, 176, 187, + 81, 158, 168, 71, 141, 150, 61, 123, 131, 47, 105, 113, 35, 87, 94, + 20, 68, 74, 0, 45, 51, 0, 5, 16, 98, 175, 168, 84, 157, 150, + 75, 140, 134, 63, 122, 117, 50, 104, 100, 37, 87, 83, 21, 68, 65, + 0, 45, 42, 0, 4, 7, 100, 174, 149, 89, 155, 132, 76, 139, 117, + 65, 121, 102, 53, 104, 87, 39, 86, 72, 23, 67, 55, 0, 45, 34, + 0, 3, 0, 103, 173, 130, 92, 155, 115, 80, 138, 102, 68, 120, 88, + 53, 103, 75, 40, 86, 61, 24, 67, 45, 0, 45, 25, 0, 3, 0, + 107, 172, 108, 95, 154, 96, 82, 137, 85, 70, 119, 73, 55, 102, 61, + 42, 85, 49, 25, 67, 34, 0, 45, 14, 0, 3, 0, 110, 172, 86, + 97, 153, 76, 85, 137, 67, 70, 119, 57, 56, 102, 46, 42, 84, 35, + 26, 66, 21, 0, 44, 1, 0, 3, 0, 112, 171, 67, 98, 153, 59, + 86, 137, 52, 71, 119, 44, 58, 102, 34, 44, 85, 22, 27, 66, 7, + 0, 44, 0, 0, 3, 0, 90, 160, 215, 81, 144, 193, 70, 129, 173, + 61, 112, 151, 49, 95, 131, 37, 79, 109, 22, 61, 87, 0, 38, 61, + 0, 0, 25, 96, 160, 194, 86, 143, 174, 75, 128, 157, 65, 112, 137, + 53, 95, 117, 40, 78, 98, 25, 60, 78, 0, 38, 53, 0, 0, 17, + 100, 159, 177, 89, 143, 159, 79, 128, 143, 67, 111, 124, 55, 95, 107, + 42, 78, 89, 27, 60, 70, 2, 38, 46, 0, 0, 9, 104, 158, 159, + 92, 142, 143, 81, 127, 127, 69, 110, 110, 56, 94, 94, 43, 78, 78, + 28, 60, 60, 2, 38, 38, 0, 0, 1, 107, 157, 140, 94, 141, 125, + 82, 126, 112, 71, 110, 97, 59, 94, 82, 45, 77, 67, 29, 59, 51, + 4, 37, 30, 0, 0, 0, 110, 156, 122, 97, 140, 109, 85, 125, 97, + 72, 109, 83, 58, 93, 71, 45, 77, 57, 29, 60, 42, 5, 38, 22, + 0, 0, 0, 111, 156, 103, 99, 139, 91, 87, 125, 81, 73, 108, 69, + 60, 92, 58, 46, 77, 45, 30, 59, 31, 5, 38, 12, 0, 0, 0, + 115, 156, 82, 101, 140, 73, 88, 124, 63, 74, 108, 53, 60, 92, 44, + 46, 76, 32, 31, 59, 18, 6, 37, 0, 0, 0, 0, 116, 155, 65, + 102, 139, 58, 89, 124, 49, 75, 108, 41, 61, 92, 32, 48, 76, 21, + 31, 59, 6, 5, 37, 0, 0, 0, 0, 100, 141, 201, 88, 127, 181, + 79, 114, 162, 69, 99, 142, 57, 83, 122, 44, 68, 102, 30, 51, 81, + 7, 28, 56, 0, 0, 19, 105, 141, 182, 94, 127, 163, 83, 114, 146, + 71, 98, 128, 59, 83, 110, 46, 68, 91, 31, 51, 72, 10, 28, 48, + 0, 0, 11, 108, 141, 166, 96, 127, 149, 85, 113, 133, 73, 98, 116, + 60, 83, 99, 46, 68, 82, 32, 51, 64, 11, 29, 41, 0, 0, 2, + 111, 141, 149, 98, 126, 134, 88, 112, 119, 74, 97, 103, 61, 83, 88, + 48, 67, 72, 33, 51, 56, 11, 29, 34, 0, 0, 0, 112, 140, 132, + 100, 125, 118, 89, 112, 105, 75, 97, 91, 62, 82, 77, 49, 68, 62, + 33, 51, 47, 12, 29, 26, 0, 0, 0, 115, 140, 116, 102, 125, 103, + 90, 111, 91, 76, 96, 78, 62, 82, 65, 49, 67, 52, 34, 51, 38, + 13, 29, 18, 0, 0, 0, 117, 139, 97, 103, 124, 87, 91, 111, 77, + 78, 96, 65, 63, 81, 54, 49, 67, 41, 34, 51, 27, 12, 29, 7, + 0, 0, 0, 119, 138, 78, 105, 124, 69, 92, 110, 60, 78, 95, 50, + 65, 81, 40, 50, 67, 29, 34, 51, 15, 13, 30, 0, 0, 0, 0, + 120, 138, 64, 106, 124, 54, 93, 110, 47, 78, 95, 38, 65, 81, 29, + 50, 66, 17, 34, 50, 2, 13, 29, 0, 0, 0, 0, 107, 124, 189, + 96, 111, 169, 85, 99, 152, 73, 85, 132, 61, 71, 114, 48, 57, 95, + 34, 41, 75, 14, 18, 51, 0, 0, 13, 111, 124, 171, 100, 111, 153, + 88, 99, 137, 75, 85, 120, 63, 72, 103, 50, 58, 85, 36, 41, 66, + 15, 19, 43, 0, 0, 4, 113, 124, 156, 101, 111, 139, 90, 99, 125, + 77, 85, 109, 64, 71, 93, 51, 57, 77, 36, 42, 59, 17, 20, 37, + 0, 0, 0, 115, 124, 140, 103, 111, 125, 90, 99, 112, 78, 85, 97, + 64, 71, 82, 52, 57, 67, 36, 42, 50, 16, 20, 30, 0, 0, 0, + 117, 123, 125, 104, 110, 111, 92, 98, 99, 79, 85, 86, 65, 71, 72, + 51, 58, 59, 37, 42, 43, 17, 21, 22, 0, 0, 0, 118, 123, 110, + 105, 110, 97, 93, 98, 86, 78, 84, 74, 66, 71, 62, 52, 57, 49, + 37, 42, 34, 17, 22, 14, 0, 0, 0, 120, 123, 93, 106, 109, 82, + 94, 97, 72, 80, 84, 61, 66, 71, 50, 52, 57, 38, 37, 42, 24, + 17, 22, 2, 0, 0, 0, 121, 122, 75, 108, 109, 66, 95, 97, 58, + 80, 84, 48, 66, 71, 37, 52, 57, 26, 37, 42, 12, 16, 22, 0, + 0, 0, 0, 122, 123, 62, 108, 109, 52, 95, 97, 45, 81, 84, 36, + 67, 70, 26, 52, 57, 14, 37, 42, 0, 15, 22, 0, 0, 0, 0, + 113, 107, 177, 102, 96, 159, 89, 85, 141, 78, 72, 124, 65, 60, 107, + 52, 46, 89, 37, 30, 70, 18, 5, 46, 0, 0, 6, 116, 107, 160, + 104, 96, 144, 92, 85, 129, 80, 72, 112, 67, 60, 96, 53, 47, 80, + 38, 31, 62, 19, 7, 39, 0, 0, 0, 118, 107, 147, 105, 96, 131, + 93, 85, 118, 80, 72, 101, 67, 60, 87, 54, 47, 71, 39, 31, 54, + 19, 8, 32, 0, 0, 0, 119, 107, 132, 106, 96, 118, 94, 85, 106, + 81, 73, 91, 67, 60, 77, 54, 47, 63, 39, 32, 47, 20, 9, 25, + 0, 0, 0, 119, 107, 118, 106, 95, 105, 94, 85, 93, 81, 72, 80, + 68, 60, 68, 54, 47, 54, 39, 32, 39, 20, 11, 18, 0, 0, 0, + 121, 107, 104, 107, 96, 92, 95, 84, 80, 81, 72, 69, 68, 61, 58, + 54, 48, 46, 39, 33, 31, 20, 12, 9, 0, 0, 0, 123, 107, 88, + 108, 95, 77, 96, 84, 68, 82, 72, 57, 68, 60, 46, 54, 47, 35, + 39, 33, 20, 19, 13, 2, 0, 0, 0, 123, 106, 72, 110, 95, 63, + 96, 84, 54, 82, 72, 45, 69, 60, 35, 55, 48, 23, 39, 33, 9, + 18, 14, 0, 0, 0, 0, 125, 106, 60, 110, 94, 50, 98, 84, 42, + 83, 72, 34, 69, 60, 25, 55, 48, 12, 39, 33, 0, 17, 13, 0, + 0, 0, 0, 118, 89, 165, 105, 79, 148, 93, 69, 132, 81, 57, 115, + 68, 45, 99, 55, 32, 82, 41, 15, 64, 21, 0, 41, 0, 0, 0, + 120, 89, 150, 107, 79, 135, 96, 69, 121, 82, 58, 105, 70, 46, 89, + 56, 34, 73, 41, 17, 56, 21, 0, 34, 0, 0, 0, 121, 89, 137, + 108, 79, 123, 96, 69, 109, 82, 58, 95, 70, 47, 81, 56, 34, 66, + 41, 18, 49, 21, 0, 28, 0, 0, 0, 122, 90, 124, 109, 79, 110, + 96, 69, 99, 83, 58, 85, 70, 47, 72, 56, 35, 58, 41, 19, 42, + 21, 0, 22, 0, 0, 0, 123, 90, 111, 110, 79, 98, 97, 69, 87, + 83, 59, 75, 70, 47, 63, 56, 35, 50, 41, 20, 35, 21, 0, 14, + 0, 0, 0, 123, 90, 98, 110, 79, 87, 97, 70, 76, 84, 58, 64, + 70, 48, 53, 56, 36, 41, 40, 21, 26, 21, 0, 5, 0, 0, 0, + 125, 89, 84, 111, 79, 73, 97, 69, 64, 84, 59, 54, 70, 48, 43, + 56, 36, 31, 40, 22, 17, 20, 1, 1, 0, 0, 0, 125, 89, 69, + 112, 79, 60, 98, 70, 51, 84, 59, 42, 70, 48, 32, 56, 36, 20, + 41, 22, 5, 19, 2, 0, 0, 0, 0, 126, 89, 57, 112, 79, 49, + 99, 70, 41, 84, 59, 32, 70, 48, 22, 56, 36, 10, 40, 22, 0, + 18, 2, 0, 0, 0, 0, 121, 67, 154, 108, 58, 138, 97, 50, 124, + 84, 39, 107, 71, 28, 92, 58, 12, 76, 43, 0, 59, 20, 0, 37, + 0, 0, 0, 124, 68, 140, 111, 59, 126, 98, 50, 112, 84, 40, 98, + 71, 29, 83, 58, 15, 67, 42, 0, 51, 20, 0, 30, 0, 0, 0, + 124, 68, 129, 111, 59, 114, 99, 51, 102, 86, 41, 88, 71, 30, 75, + 58, 17, 60, 42, 0, 45, 20, 0, 24, 0, 0, 0, 125, 70, 116, + 111, 60, 103, 99, 51, 92, 85, 41, 79, 71, 31, 66, 58, 19, 53, + 42, 3, 38, 20, 0, 17, 0, 0, 0, 125, 70, 104, 111, 61, 93, + 99, 52, 81, 85, 43, 69, 72, 32, 58, 58, 20, 45, 42, 4, 31, + 20, 0, 8, 0, 0, 0, 126, 70, 92, 111, 61, 81, 99, 52, 71, + 85, 42, 60, 71, 33, 49, 57, 21, 37, 42, 6, 23, 20, 0, 3, + 0, 0, 0, 126, 70, 79, 112, 61, 70, 99, 53, 60, 85, 43, 50, + 71, 33, 39, 57, 22, 28, 41, 7, 13, 19, 0, 0, 0, 0, 0, + 127, 71, 66, 113, 62, 56, 100, 53, 48, 86, 44, 39, 71, 34, 29, + 57, 23, 18, 41, 8, 2, 18, 0, 0, 0, 0, 0, 128, 70, 55, + 114, 62, 46, 100, 54, 39, 86, 44, 30, 71, 34, 20, 57, 23, 7, + 41, 9, 0, 18, 0, 0, 0, 0, 0, 124, 41, 145, 111, 32, 128, + 99, 23, 114, 86, 10, 100, 73, 0, 85, 60, 0, 71, 43, 0, 54, + 17, 0, 32, 0, 0, 0, 126, 42, 131, 113, 33, 117, 100, 25, 104, + 86, 14, 90, 73, 0, 77, 60, 0, 63, 44, 0, 47, 18, 0, 25, + 0, 0, 0, 127, 43, 120, 113, 34, 106, 101, 26, 95, 86, 17, 82, + 73, 2, 69, 59, 0, 56, 43, 0, 41, 18, 0, 19, 0, 0, 0, + 127, 45, 109, 113, 37, 97, 101, 28, 85, 86, 19, 74, 73, 5, 61, + 59, 0, 48, 43, 0, 34, 19, 0, 11, 0, 0, 0, 127, 46, 98, + 114, 38, 86, 100, 30, 76, 87, 21, 65, 73, 9, 54, 59, 0, 41, + 43, 0, 26, 18, 0, 5, 0, 0, 0, 127, 47, 87, 113, 39, 76, + 101, 31, 67, 86, 22, 56, 72, 11, 45, 59, 0, 33, 43, 0, 19, + 18, 0, 2, 0, 0, 0, 128, 48, 75, 114, 39, 65, 101, 33, 56, + 86, 23, 46, 72, 12, 36, 58, 0, 24, 42, 0, 9, 17, 0, 0, + 0, 0, 0, 129, 48, 63, 114, 41, 54, 102, 33, 46, 87, 24, 36, + 72, 14, 26, 58, 1, 14, 42, 0, 2, 16, 0, 0, 0, 0, 0, + 128, 48, 53, 114, 41, 44, 102, 34, 37, 87, 25, 27, 72, 15, 17, + 58, 1, 3, 41, 0, 0, 15, 0, 0, 0, 0, 0, 0, 189, 242, + 0, 169, 217, 0, 151, 194, 0, 132, 170, 0, 113, 147, 0, 94, 123, + 0, 74, 99, 0, 51, 71, 0, 15, 34, 1, 187, 219, 1, 167, 195, + 0, 150, 175, 0, 131, 153, 0, 113, 132, 0, 94, 111, 0, 74, 89, + 0, 50, 63, 0, 13, 28, 1, 186, 198, 1, 167, 178, 0, 149, 158, + 0, 130, 139, 0, 111, 119, 0, 93, 100, 0, 74, 80, 0, 50, 55, + 0, 13, 22, 1, 185, 176, 1, 165, 159, 1, 148, 142, 0, 129, 123, + 0, 111, 106, 0, 93, 89, 0, 73, 70, 0, 50, 47, 0, 13, 13, + 1, 184, 157, 1, 164, 141, 1, 147, 125, 0, 128, 110, 0, 110, 93, + 0, 92, 77, 0, 73, 60, 0, 50, 39, 0, 12, 5, 25, 182, 137, + 25, 163, 122, 17, 146, 109, 0, 128, 96, 0, 110, 81, 0, 92, 66, + 0, 73, 51, 0, 50, 30, 0, 10, 0, 42, 181, 114, 35, 163, 102, + 30, 145, 91, 14, 127, 80, 0, 109, 67, 0, 91, 53, 0, 72, 39, + 0, 50, 19, 0, 10, 0, 52, 181, 92, 43, 162, 83, 32, 145, 73, + 19, 126, 63, 0, 108, 52, 0, 90, 40, 0, 72, 27, 0, 50, 7, + 0, 10, 0, 57, 181, 74, 48, 162, 66, 37, 144, 57, 24, 126, 49, + 7, 108, 40, 0, 90, 29, 0, 72, 15, 0, 49, 0, 0, 10, 0, + 1, 170, 227, 1, 152, 203, 0, 136, 182, 0, 119, 159, 0, 101, 137, + 0, 84, 115, 0, 65, 92, 0, 43, 66, 0, 1, 29, 1, 169, 206, + 1, 151, 184, 1, 136, 165, 0, 118, 144, 0, 102, 125, 0, 84, 105, + 0, 65, 83, 0, 43, 58, 0, 0, 22, 29, 168, 186, 21, 151, 167, + 14, 135, 150, 4, 118, 131, 0, 101, 112, 0, 83, 94, 0, 65, 75, + 0, 43, 51, 0, 0, 16, 41, 167, 167, 33, 150, 150, 31, 134, 134, + 19, 117, 117, 4, 100, 100, 0, 83, 83, 0, 65, 65, 0, 42, 43, + 0, 0, 8, 48, 167, 149, 41, 149, 133, 33, 133, 118, 25, 116, 103, + 13, 99, 88, 0, 83, 73, 0, 65, 56, 0, 42, 35, 0, 0, 0, + 58, 165, 130, 49, 148, 115, 42, 132, 103, 31, 115, 89, 18, 99, 75, + 0, 82, 61, 0, 64, 46, 0, 42, 26, 0, 0, 0, 62, 164, 110, + 55, 147, 97, 45, 132, 87, 35, 115, 75, 22, 98, 63, 5, 82, 50, + 0, 64, 36, 0, 42, 16, 0, 0, 0, 69, 164, 89, 60, 147, 78, + 50, 131, 70, 37, 114, 59, 26, 98, 49, 10, 81, 37, 0, 64, 24, + 0, 42, 4, 0, 0, 0, 71, 164, 71, 63, 147, 63, 53, 131, 55, + 40, 114, 47, 28, 98, 38, 13, 81, 26, 0, 64, 12, 0, 42, 0, + 0, 0, 0, 28, 153, 214, 24, 138, 193, 23, 123, 171, 16, 107, 150, + 0, 91, 130, 0, 75, 109, 0, 58, 87, 0, 35, 61, 0, 0, 25, + 48, 153, 194, 41, 138, 174, 34, 123, 156, 27, 107, 136, 16, 91, 117, + 1, 75, 98, 0, 57, 78, 0, 35, 53, 0, 0, 17, 55, 153, 177, + 47, 137, 158, 42, 122, 142, 33, 107, 124, 22, 91, 106, 6, 75, 88, + 0, 57, 70, 0, 35, 46, 0, 0, 9, 61, 152, 158, 53, 136, 143, + 45, 122, 127, 36, 106, 111, 24, 90, 94, 10, 74, 78, 0, 57, 61, + 0, 35, 39, 0, 0, 2, 67, 151, 141, 59, 135, 126, 49, 121, 112, + 39, 105, 98, 29, 90, 83, 14, 74, 68, 0, 57, 52, 0, 35, 31, + 0, 0, 0, 71, 150, 123, 62, 135, 110, 54, 120, 98, 42, 105, 84, + 31, 89, 71, 16, 74, 58, 0, 57, 43, 0, 35, 22, 0, 0, 0, + 74, 150, 105, 64, 134, 92, 55, 120, 83, 45, 104, 71, 34, 89, 59, + 20, 73, 47, 0, 57, 32, 0, 35, 13, 0, 0, 0, 78, 149, 84, + 69, 134, 75, 59, 120, 66, 47, 103, 56, 34, 88, 46, 22, 73, 34, + 1, 57, 20, 0, 35, 1, 0, 0, 0, 80, 149, 69, 70, 133, 61, + 60, 119, 53, 49, 103, 44, 36, 88, 35, 23, 73, 24, 2, 56, 10, + 0, 35, 0, 0, 0, 0, 58, 136, 200, 50, 122, 180, 45, 109, 162, + 38, 94, 141, 27, 80, 121, 15, 65, 102, 0, 48, 81, 0, 26, 56, + 0, 0, 19, 66, 136, 182, 59, 122, 163, 52, 109, 146, 42, 94, 128, + 32, 80, 109, 20, 65, 91, 2, 48, 72, 0, 26, 49, 0, 0, 11, + 70, 136, 165, 62, 122, 149, 55, 108, 133, 46, 94, 116, 35, 80, 99, + 21, 65, 82, 4, 49, 64, 0, 26, 41, 0, 0, 3, 76, 135, 149, + 66, 121, 133, 58, 108, 119, 48, 94, 103, 36, 79, 88, 23, 65, 73, + 7, 49, 56, 0, 27, 34, 0, 0, 0, 78, 135, 133, 69, 120, 118, + 60, 107, 106, 50, 93, 92, 39, 79, 77, 26, 65, 63, 8, 49, 47, + 0, 27, 26, 0, 0, 0, 82, 134, 117, 71, 120, 104, 62, 107, 92, + 51, 93, 79, 39, 78, 66, 27, 64, 53, 10, 48, 39, 0, 27, 18, + 0, 0, 0, 84, 134, 99, 73, 119, 87, 64, 106, 77, 53, 92, 66, + 42, 78, 55, 28, 64, 42, 11, 48, 29, 0, 28, 9, 0, 0, 0, + 87, 133, 81, 76, 119, 72, 66, 106, 62, 55, 92, 52, 43, 78, 42, + 29, 64, 31, 12, 48, 17, 0, 28, 0, 0, 0, 0, 88, 134, 67, + 77, 119, 58, 68, 106, 51, 56, 92, 42, 44, 78, 32, 30, 64, 20, + 12, 48, 6, 0, 28, 0, 0, 0, 0, 73, 120, 189, 64, 107, 168, + 57, 96, 151, 47, 82, 133, 38, 69, 114, 26, 55, 95, 11, 39, 75, + 0, 16, 51, 0, 0, 14, 78, 120, 171, 69, 107, 153, 62, 95, 137, + 51, 82, 119, 40, 69, 102, 29, 55, 85, 15, 39, 66, 0, 17, 44, + 0, 0, 4, 81, 120, 156, 71, 107, 140, 64, 95, 125, 53, 82, 109, + 42, 69, 93, 31, 55, 77, 16, 39, 59, 0, 18, 37, 0, 0, 0, + 85, 120, 141, 74, 107, 126, 65, 95, 112, 54, 82, 97, 43, 69, 82, + 32, 55, 67, 17, 39, 51, 0, 19, 30, 0, 0, 0, 86, 119, 126, + 76, 106, 112, 66, 95, 100, 56, 81, 85, 45, 69, 72, 33, 55, 59, + 18, 40, 43, 0, 19, 22, 0, 0, 0, 89, 119, 110, 78, 106, 98, + 69, 94, 87, 56, 81, 75, 46, 68, 62, 33, 55, 49, 18, 40, 35, + 0, 20, 15, 0, 0, 0, 89, 119, 95, 80, 106, 83, 70, 94, 73, + 58, 81, 63, 46, 68, 51, 34, 55, 39, 19, 40, 25, 0, 20, 4, + 0, 0, 0, 92, 118, 78, 82, 106, 68, 70, 93, 59, 59, 81, 49, + 47, 68, 39, 34, 55, 28, 19, 40, 14, 0, 20, 0, 0, 0, 0, + 93, 118, 65, 82, 105, 55, 72, 93, 48, 60, 81, 39, 47, 68, 29, + 34, 55, 18, 20, 40, 2, 0, 20, 0, 0, 0, 0, 83, 104, 177, + 74, 93, 159, 65, 82, 142, 56, 70, 124, 45, 57, 106, 33, 44, 89, + 20, 28, 70, 1, 4, 46, 0, 0, 8, 86, 104, 161, 78, 93, 145, + 68, 82, 128, 58, 70, 112, 48, 58, 96, 35, 45, 80, 21, 29, 62, + 1, 6, 40, 0, 0, 0, 89, 104, 147, 79, 93, 131, 69, 82, 118, + 59, 70, 102, 47, 58, 87, 36, 45, 72, 23, 29, 55, 3, 7, 33, + 0, 0, 0, 90, 104, 132, 80, 93, 119, 71, 82, 106, 60, 70, 91, + 48, 58, 77, 37, 45, 62, 23, 30, 47, 4, 7, 26, 0, 0, 0, + 92, 104, 118, 82, 93, 105, 72, 82, 93, 61, 69, 80, 50, 58, 68, + 37, 45, 55, 23, 30, 39, 4, 8, 18, 0, 0, 0, 94, 104, 105, + 82, 92, 93, 72, 82, 82, 61, 70, 70, 50, 58, 58, 38, 46, 46, + 23, 31, 31, 6, 10, 11, 0, 0, 0, 95, 104, 90, 84, 92, 79, + 74, 82, 70, 62, 70, 58, 50, 58, 48, 37, 46, 36, 23, 31, 22, + 4, 11, 3, 0, 0, 0, 96, 103, 74, 85, 92, 65, 75, 81, 56, + 63, 70, 47, 50, 58, 37, 38, 46, 25, 24, 31, 11, 3, 11, 0, + 0, 0, 0, 97, 103, 62, 86, 92, 53, 76, 81, 45, 63, 69, 36, + 51, 58, 27, 38, 46, 15, 23, 31, 0, 3, 11, 0, 0, 0, 0, + 90, 87, 165, 81, 77, 148, 72, 67, 132, 62, 55, 116, 50, 44, 99, + 39, 31, 82, 25, 14, 64, 1, 0, 42, 0, 0, 1, 93, 87, 150, + 83, 77, 135, 74, 67, 121, 63, 56, 105, 52, 45, 90, 40, 32, 74, + 25, 16, 57, 3, 0, 35, 0, 0, 0, 95, 87, 138, 85, 77, 123, + 75, 67, 109, 63, 57, 95, 53, 45, 81, 41, 33, 66, 26, 17, 50, + 4, 0, 28, 0, 0, 0, 95, 88, 124, 85, 77, 111, 75, 67, 99, + 63, 57, 86, 53, 45, 72, 41, 33, 58, 26, 18, 43, 5, 1, 22, + 0, 0, 0, 97, 88, 112, 87, 77, 100, 76, 68, 88, 64, 57, 76, + 53, 46, 63, 41, 34, 50, 26, 19, 35, 5, 2, 14, 0, 0, 0, + 99, 87, 99, 87, 78, 88, 76, 68, 77, 65, 57, 65, 53, 46, 54, + 41, 35, 42, 27, 20, 27, 6, 2, 5, 0, 0, 0, 100, 87, 85, + 88, 77, 75, 77, 68, 65, 65, 57, 54, 53, 46, 44, 41, 35, 32, + 27, 21, 19, 5, 3, 2, 0, 0, 0, 100, 88, 71, 89, 77, 61, + 78, 68, 53, 66, 57, 44, 53, 47, 33, 41, 35, 22, 27, 21, 7, + 5, 3, 0, 0, 0, 0, 101, 87, 60, 90, 77, 52, 79, 68, 44, + 66, 58, 34, 53, 47, 25, 41, 35, 13, 26, 22, 0, 5, 3, 0, + 0, 0, 0, 97, 67, 155, 86, 58, 138, 77, 50, 125, 66, 39, 108, + 55, 28, 92, 43, 12, 76, 29, 0, 59, 2, 0, 37, 0, 0, 0, + 99, 67, 141, 88, 59, 127, 78, 50, 113, 68, 40, 98, 56, 29, 83, + 44, 15, 68, 29, 0, 52, 3, 0, 30, 0, 0, 0, 100, 68, 129, + 89, 59, 115, 80, 51, 103, 68, 41, 89, 56, 30, 75, 44, 16, 61, + 30, 0, 45, 4, 0, 24, 0, 0, 0, 100, 69, 118, 90, 60, 104, + 80, 51, 92, 67, 41, 79, 56, 31, 66, 44, 18, 53, 29, 2, 38, + 4, 0, 18, 0, 0, 0, 101, 69, 104, 90, 61, 93, 79, 51, 82, + 67, 42, 70, 56, 32, 59, 44, 20, 46, 29, 4, 31, 6, 0, 9, + 0, 0, 0, 102, 69, 93, 90, 61, 83, 80, 52, 72, 68, 42, 61, + 56, 33, 50, 43, 20, 38, 29, 5, 23, 7, 0, 4, 0, 0, 0, + 102, 70, 80, 91, 61, 71, 80, 52, 61, 68, 43, 51, 56, 32, 40, + 44, 21, 29, 30, 6, 14, 7, 0, 0, 0, 0, 0, 103, 70, 68, + 92, 61, 58, 81, 53, 50, 69, 43, 41, 56, 34, 31, 43, 22, 19, + 29, 7, 3, 7, 0, 0, 0, 0, 0, 104, 70, 57, 92, 61, 48, + 82, 53, 40, 69, 43, 32, 56, 34, 22, 43, 23, 10, 29, 8, 0, + 6, 0, 0, 0, 0, 0, 101, 45, 145, 91, 35, 129, 80, 26, 116, + 69, 15, 101, 59, 0, 86, 46, 0, 71, 31, 0, 55, 0, 0, 33, + 0, 0, 0, 104, 44, 132, 92, 36, 118, 82, 28, 105, 71, 17, 91, + 58, 3, 77, 46, 0, 63, 31, 0, 48, 2, 0, 26, 0, 0, 0, + 104, 46, 121, 93, 37, 107, 82, 30, 96, 70, 20, 83, 58, 6, 70, + 46, 0, 57, 32, 0, 41, 4, 0, 20, 0, 0, 0, 104, 48, 110, + 93, 40, 98, 82, 31, 87, 70, 22, 74, 59, 9, 62, 45, 0, 49, + 31, 0, 35, 6, 0, 13, 0, 0, 0, 104, 48, 99, 92, 41, 88, + 82, 32, 77, 70, 23, 65, 58, 11, 54, 46, 0, 42, 32, 0, 27, + 7, 0, 5, 0, 0, 0, 105, 50, 88, 93, 41, 77, 82, 34, 68, + 71, 24, 57, 58, 13, 46, 45, 1, 35, 31, 0, 21, 7, 0, 2, + 0, 0, 0, 105, 50, 76, 94, 41, 66, 83, 34, 57, 71, 25, 47, + 58, 15, 37, 45, 2, 25, 32, 0, 11, 7, 0, 0, 0, 0, 0, + 106, 50, 64, 94, 42, 55, 83, 35, 47, 71, 26, 38, 58, 16, 27, + 45, 4, 17, 31, 0, 4, 7, 0, 0, 0, 0, 0, 106, 51, 54, + 95, 42, 45, 83, 35, 38, 71, 27, 30, 58, 16, 19, 45, 5, 7, + 30, 0, 0, 6, 0, 0, 0, 0, 0, 0, 181, 240, 0, 162, 216, + 0, 144, 193, 0, 126, 168, 0, 109, 146, 0, 91, 123, 0, 71, 98, + 0, 48, 71, 0, 9, 34, 0, 179, 218, 0, 161, 195, 0, 144, 174, + 0, 126, 153, 0, 108, 132, 0, 90, 110, 0, 71, 88, 0, 48, 63, + 0, 8, 29, 0, 178, 197, 0, 159, 177, 0, 143, 159, 0, 125, 139, + 0, 107, 119, 0, 90, 99, 0, 71, 79, 0, 48, 55, 0, 8, 22, + 0, 177, 177, 0, 158, 158, 0, 142, 141, 0, 124, 123, 0, 107, 106, + 0, 89, 88, 0, 71, 70, 0, 48, 47, 0, 8, 14, 0, 176, 157, + 0, 158, 141, 0, 141, 126, 0, 123, 109, 0, 106, 93, 0, 89, 78, + 0, 70, 60, 0, 47, 39, 0, 7, 5, 0, 175, 138, 0, 157, 123, + 0, 141, 110, 0, 123, 96, 0, 105, 81, 0, 88, 67, 0, 70, 51, + 0, 48, 30, 0, 6, 0, 0, 173, 115, 0, 155, 104, 0, 140, 92, + 0, 122, 80, 0, 105, 67, 0, 88, 55, 0, 69, 40, 0, 47, 20, + 0, 6, 0, 0, 173, 94, 0, 155, 85, 0, 139, 75, 0, 121, 64, + 0, 104, 53, 0, 88, 42, 0, 70, 28, 0, 47, 9, 0, 6, 0, + 0, 173, 76, 0, 155, 70, 0, 138, 61, 0, 122, 53, 0, 104, 44, + 0, 87, 32, 0, 69, 18, 0, 47, 0, 0, 6, 0, 0, 164, 226, + 0, 147, 203, 0, 131, 181, 0, 114, 158, 0, 97, 136, 0, 80, 115, + 0, 63, 92, 0, 40, 65, 0, 0, 30, 0, 162, 205, 0, 145, 184, + 0, 130, 164, 0, 114, 143, 0, 97, 124, 0, 81, 104, 0, 63, 83, + 0, 40, 58, 0, 0, 23, 0, 162, 187, 0, 145, 167, 0, 130, 150, + 0, 113, 131, 0, 96, 112, 0, 80, 93, 0, 62, 74, 0, 40, 50, + 0, 0, 16, 0, 160, 167, 0, 144, 150, 0, 129, 134, 0, 112, 116, + 0, 96, 100, 0, 80, 82, 0, 62, 65, 0, 40, 43, 0, 0, 7, + 0, 160, 148, 0, 143, 133, 0, 128, 118, 0, 111, 103, 0, 96, 88, + 0, 80, 73, 0, 62, 56, 0, 40, 35, 0, 0, 0, 0, 158, 130, + 0, 142, 117, 0, 127, 104, 0, 111, 89, 0, 95, 76, 0, 79, 62, + 0, 62, 46, 0, 40, 26, 0, 0, 0, 0, 158, 111, 0, 141, 99, + 0, 127, 88, 0, 111, 76, 0, 95, 63, 0, 79, 51, 0, 62, 37, + 0, 40, 18, 0, 0, 0, 0, 158, 91, 0, 141, 81, 0, 126, 72, + 0, 110, 62, 0, 94, 50, 0, 79, 39, 0, 62, 25, 0, 40, 5, + 0, 0, 0, 0, 157, 74, 0, 141, 66, 0, 126, 59, 0, 110, 49, + 0, 94, 40, 0, 78, 29, 0, 61, 15, 0, 40, 0, 0, 0, 0, + 0, 148, 214, 0, 133, 192, 0, 119, 171, 0, 103, 150, 0, 87, 129, + 0, 72, 108, 0, 55, 86, 0, 32, 61, 0, 0, 25, 0, 147, 193, + 0, 132, 173, 0, 118, 155, 0, 103, 136, 0, 87, 116, 0, 72, 98, + 0, 55, 78, 0, 32, 53, 0, 0, 17, 0, 147, 176, 0, 132, 158, + 0, 118, 142, 0, 102, 124, 0, 87, 106, 0, 72, 88, 0, 55, 69, + 0, 33, 46, 0, 0, 9, 0, 146, 159, 0, 131, 142, 0, 117, 127, + 0, 102, 111, 0, 87, 95, 0, 71, 79, 0, 55, 61, 0, 33, 39, + 0, 0, 2, 0, 145, 140, 0, 130, 126, 0, 117, 112, 0, 101, 98, + 0, 86, 83, 0, 71, 68, 0, 55, 52, 0, 33, 31, 0, 0, 0, + 0, 144, 124, 0, 130, 111, 0, 116, 99, 0, 101, 84, 0, 86, 72, + 0, 71, 59, 0, 55, 43, 0, 33, 23, 0, 0, 0, 0, 144, 106, + 0, 129, 94, 0, 115, 83, 0, 101, 72, 0, 85, 60, 0, 71, 48, + 0, 55, 34, 0, 33, 14, 0, 0, 0, 3, 143, 86, 0, 129, 77, + 0, 115, 68, 0, 100, 58, 0, 85, 48, 0, 70, 36, 0, 54, 22, + 0, 33, 3, 0, 0, 0, 18, 143, 72, 13, 128, 63, 0, 115, 57, + 0, 100, 47, 0, 85, 37, 0, 70, 26, 0, 54, 13, 0, 33, 0, + 0, 0, 0, 0, 132, 200, 0, 118, 179, 0, 105, 161, 0, 91, 140, + 0, 76, 121, 0, 62, 101, 0, 46, 81, 0, 24, 56, 0, 0, 19, + 0, 131, 182, 0, 118, 163, 0, 105, 146, 0, 91, 128, 0, 77, 110, + 0, 62, 91, 0, 46, 72, 0, 25, 48, 0, 0, 11, 0, 131, 165, + 0, 117, 149, 0, 104, 133, 0, 91, 116, 0, 77, 99, 0, 62, 82, + 0, 46, 64, 0, 25, 41, 0, 0, 4, 0, 131, 149, 0, 116, 134, + 0, 104, 119, 0, 91, 104, 0, 77, 89, 0, 62, 73, 0, 46, 56, + 0, 25, 34, 0, 0, 0, 10, 130, 133, 2, 116, 119, 0, 104, 106, + 0, 90, 91, 0, 76, 78, 0, 62, 64, 0, 46, 48, 0, 26, 27, + 0, 0, 0, 23, 130, 118, 20, 116, 104, 13, 103, 93, 3, 89, 79, + 0, 76, 67, 0, 62, 54, 0, 46, 39, 0, 26, 19, 0, 0, 0, + 33, 129, 101, 27, 115, 89, 19, 103, 79, 9, 89, 67, 0, 75, 56, + 0, 61, 43, 0, 46, 29, 0, 26, 10, 0, 0, 0, 41, 128, 83, + 35, 115, 73, 27, 102, 64, 15, 89, 55, 0, 76, 45, 0, 62, 33, + 0, 46, 18, 0, 26, 0, 0, 0, 0, 43, 129, 69, 38, 115, 61, + 30, 102, 54, 17, 89, 45, 2, 75, 34, 0, 61, 23, 0, 46, 9, + 0, 26, 0, 0, 0, 0, 1, 116, 188, 1, 104, 168, 0, 92, 151, + 0, 79, 132, 0, 66, 113, 0, 52, 94, 0, 36, 75, 0, 14, 52, + 0, 0, 14, 17, 116, 171, 16, 104, 153, 14, 92, 137, 8, 79, 119, + 0, 67, 102, 0, 53, 85, 0, 37, 67, 0, 16, 44, 0, 0, 4, + 31, 116, 155, 27, 104, 140, 21, 92, 125, 13, 79, 109, 3, 66, 93, + 0, 53, 77, 0, 37, 59, 0, 16, 38, 0, 0, 0, 37, 115, 141, + 30, 103, 126, 26, 92, 112, 16, 79, 98, 5, 66, 83, 0, 53, 67, + 0, 38, 51, 0, 17, 31, 0, 0, 0, 41, 115, 126, 37, 103, 112, + 31, 92, 100, 22, 79, 86, 10, 66, 72, 0, 53, 59, 0, 38, 44, + 0, 17, 23, 0, 0, 0, 48, 115, 111, 41, 102, 99, 34, 91, 88, + 24, 78, 76, 14, 66, 63, 0, 53, 50, 0, 38, 36, 0, 18, 15, + 0, 0, 0, 51, 115, 95, 46, 102, 85, 37, 91, 74, 26, 78, 63, + 16, 66, 52, 0, 53, 40, 0, 38, 26, 0, 18, 5, 0, 0, 0, + 55, 114, 80, 47, 102, 69, 40, 90, 60, 30, 78, 51, 19, 66, 41, + 3, 53, 29, 0, 38, 15, 0, 17, 0, 0, 0, 0, 56, 114, 66, + 50, 102, 58, 40, 91, 50, 32, 78, 41, 18, 66, 32, 4, 53, 21, + 0, 38, 5, 0, 17, 0, 0, 0, 0, 39, 102, 178, 37, 90, 159, + 30, 79, 142, 21, 68, 124, 14, 55, 106, 0, 42, 89, 0, 26, 70, + 0, 4, 46, 0, 0, 8, 48, 102, 161, 42, 90, 145, 35, 79, 128, + 26, 68, 112, 19, 55, 96, 3, 43, 79, 0, 27, 62, 0, 6, 40, + 0, 0, 0, 50, 102, 147, 44, 90, 132, 37, 79, 118, 30, 68, 102, + 20, 56, 87, 7, 43, 72, 0, 28, 55, 0, 6, 34, 0, 0, 0, + 53, 101, 133, 47, 90, 118, 41, 79, 106, 32, 68, 91, 21, 56, 78, + 9, 43, 63, 0, 28, 47, 0, 6, 26, 0, 0, 0, 57, 101, 119, + 50, 89, 106, 42, 79, 94, 34, 67, 81, 24, 56, 68, 9, 44, 55, + 0, 29, 40, 0, 6, 19, 0, 0, 0, 60, 100, 105, 50, 90, 94, + 45, 80, 83, 36, 68, 71, 24, 56, 59, 11, 44, 46, 0, 29, 32, + 0, 7, 12, 0, 0, 0, 63, 101, 91, 55, 90, 80, 46, 79, 70, + 37, 68, 59, 26, 56, 49, 12, 44, 37, 1, 29, 23, 0, 7, 3, + 0, 0, 0, 64, 101, 75, 56, 89, 67, 48, 79, 57, 37, 68, 48, + 27, 56, 37, 15, 44, 26, 0, 29, 12, 0, 7, 0, 0, 0, 0, + 66, 101, 64, 58, 89, 55, 49, 79, 47, 39, 68, 38, 27, 56, 29, + 14, 44, 18, 1, 30, 2, 0, 7, 0, 0, 0, 0, 57, 86, 165, + 51, 75, 148, 45, 65, 133, 38, 54, 116, 28, 43, 100, 16, 29, 83, + 0, 13, 64, 0, 0, 42, 0, 0, 3, 60, 86, 151, 55, 75, 135, + 47, 66, 121, 39, 55, 105, 30, 44, 90, 18, 31, 74, 3, 16, 57, + 0, 1, 35, 0, 0, 0, 62, 86, 139, 56, 75, 123, 49, 66, 110, + 40, 55, 95, 30, 44, 81, 19, 31, 66, 4, 17, 51, 0, 1, 29, + 0, 0, 0, 65, 86, 125, 56, 76, 112, 49, 66, 99, 39, 55, 86, + 31, 44, 72, 19, 32, 59, 5, 18, 44, 0, 1, 23, 0, 0, 0, + 67, 86, 113, 58, 75, 100, 51, 66, 88, 41, 56, 77, 31, 45, 64, + 20, 32, 51, 6, 18, 35, 0, 1, 14, 0, 0, 0, 69, 86, 99, + 61, 76, 88, 52, 66, 78, 43, 56, 66, 32, 45, 55, 20, 33, 42, + 7, 19, 27, 0, 1, 6, 0, 0, 0, 69, 86, 86, 61, 76, 75, + 53, 67, 66, 43, 56, 55, 33, 45, 45, 21, 34, 34, 8, 20, 20, + 0, 2, 2, 0, 0, 0, 71, 86, 72, 63, 75, 62, 54, 66, 55, + 45, 56, 45, 33, 45, 35, 22, 34, 23, 7, 20, 8, 0, 2, 0, + 0, 0, 0, 71, 86, 62, 64, 75, 53, 55, 66, 46, 45, 56, 36, + 33, 46, 27, 22, 34, 15, 8, 20, 0, 0, 2, 0, 0, 0, 0, + 69, 67, 156, 61, 58, 140, 53, 50, 125, 45, 39, 108, 35, 28, 93, + 25, 12, 77, 12, 0, 59, 0, 0, 37, 0, 0, 0, 71, 68, 142, + 63, 59, 126, 56, 50, 114, 47, 40, 98, 37, 28, 84, 26, 15, 68, + 12, 0, 53, 0, 0, 30, 0, 0, 0, 72, 68, 130, 63, 59, 116, + 56, 50, 104, 47, 40, 90, 38, 30, 75, 27, 16, 61, 13, 0, 46, + 0, 0, 24, 0, 0, 0, 73, 69, 118, 65, 59, 105, 57, 51, 92, + 47, 41, 80, 37, 30, 67, 26, 18, 53, 14, 1, 39, 0, 0, 18, + 0, 0, 0, 74, 69, 106, 65, 60, 93, 57, 51, 82, 48, 41, 70, + 38, 31, 59, 26, 19, 46, 13, 2, 32, 0, 0, 10, 0, 0, 0, + 76, 69, 95, 66, 61, 84, 58, 52, 73, 48, 42, 61, 37, 32, 50, + 26, 20, 38, 14, 4, 24, 0, 0, 4, 0, 0, 0, 76, 69, 81, + 68, 60, 72, 58, 52, 62, 48, 42, 51, 38, 32, 41, 27, 21, 30, + 14, 4, 16, 0, 0, 1, 0, 0, 0, 76, 69, 68, 68, 61, 60, + 60, 52, 51, 49, 43, 41, 38, 33, 32, 27, 21, 20, 14, 5, 5, + 0, 0, 0, 0, 0, 0, 78, 70, 59, 69, 61, 50, 60, 52, 42, + 49, 43, 34, 39, 33, 24, 27, 22, 13, 14, 7, 1, 0, 0, 0, + 0, 0, 0, 75, 46, 146, 68, 38, 131, 60, 30, 117, 50, 19, 102, + 41, 4, 87, 29, 0, 72, 13, 0, 55, 0, 0, 33, 0, 0, 0, + 78, 47, 132, 70, 39, 119, 61, 30, 105, 53, 20, 92, 42, 5, 78, + 30, 0, 64, 13, 0, 49, 0, 0, 27, 0, 0, 0, 79, 48, 122, + 70, 40, 108, 62, 32, 96, 52, 22, 84, 42, 9, 71, 30, 0, 58, + 14, 0, 42, 0, 0, 20, 0, 0, 0, 79, 50, 111, 70, 42, 99, + 62, 33, 88, 52, 23, 74, 41, 11, 63, 29, 0, 50, 14, 0, 36, + 0, 0, 14, 0, 0, 0, 80, 50, 99, 70, 42, 89, 61, 34, 78, + 52, 25, 67, 41, 14, 55, 30, 0, 42, 15, 0, 28, 0, 0, 6, + 0, 0, 0, 81, 51, 89, 71, 43, 78, 62, 35, 69, 52, 25, 58, + 42, 15, 47, 30, 3, 36, 15, 0, 22, 0, 0, 3, 0, 0, 0, + 81, 51, 77, 71, 44, 68, 63, 36, 59, 53, 26, 49, 41, 16, 38, + 31, 4, 27, 16, 0, 12, 0, 0, 0, 0, 0, 0, 81, 52, 65, + 72, 43, 56, 63, 36, 48, 53, 27, 39, 41, 17, 29, 30, 4, 18, + 14, 0, 3, 0, 0, 0, 0, 0, 0, 81, 52, 55, 73, 44, 47, + 64, 36, 39, 53, 28, 32, 42, 18, 21, 31, 6, 9, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 174, 239, 0, 156, 214, 0, 139, 192, + 0, 121, 168, 0, 105, 145, 0, 87, 123, 0, 68, 98, 0, 46, 70, + 0, 3, 35, 0, 172, 217, 0, 155, 194, 0, 139, 173, 0, 121, 152, + 0, 104, 130, 0, 87, 110, 0, 69, 88, 0, 46, 63, 0, 4, 28, + 0, 171, 197, 0, 153, 175, 0, 138, 158, 0, 121, 139, 0, 103, 118, + 0, 86, 100, 0, 68, 79, 0, 46, 55, 0, 4, 22, 0, 170, 177, + 0, 152, 158, 0, 136, 141, 0, 119, 124, 0, 103, 106, 0, 86, 88, + 0, 68, 70, 0, 45, 47, 0, 3, 14, 0, 169, 157, 0, 152, 141, + 0, 136, 126, 0, 119, 109, 0, 102, 94, 0, 86, 78, 0, 68, 60, + 0, 46, 39, 0, 3, 5, 0, 167, 138, 0, 150, 124, 0, 135, 111, + 0, 118, 97, 0, 102, 82, 0, 85, 68, 0, 68, 52, 0, 46, 31, + 0, 3, 0, 0, 167, 118, 0, 150, 104, 0, 135, 94, 0, 118, 81, + 0, 101, 69, 0, 84, 56, 0, 67, 41, 0, 45, 21, 0, 3, 0, + 0, 166, 97, 0, 149, 87, 0, 134, 77, 0, 117, 67, 0, 101, 56, + 0, 85, 44, 0, 67, 30, 0, 45, 10, 0, 3, 0, 0, 165, 79, + 0, 149, 73, 0, 133, 64, 0, 117, 56, 0, 101, 46, 0, 85, 34, + 0, 68, 21, 0, 46, 1, 0, 3, 0, 0, 158, 225, 0, 141, 201, + 0, 126, 180, 0, 109, 158, 0, 94, 136, 0, 78, 114, 0, 60, 91, + 0, 38, 66, 0, 0, 30, 0, 156, 203, 0, 140, 183, 0, 125, 164, + 0, 109, 143, 0, 94, 124, 0, 78, 104, 0, 61, 83, 0, 38, 57, + 0, 0, 23, 0, 156, 186, 0, 140, 166, 0, 125, 150, 0, 109, 130, + 0, 93, 111, 0, 77, 93, 0, 60, 74, 0, 38, 50, 0, 0, 17, + 0, 155, 167, 0, 138, 149, 0, 124, 134, 0, 109, 117, 0, 93, 100, + 0, 76, 83, 0, 60, 65, 0, 38, 43, 0, 0, 9, 0, 153, 147, + 0, 138, 134, 0, 124, 120, 0, 107, 103, 0, 92, 88, 0, 77, 73, + 0, 60, 56, 0, 38, 35, 0, 0, 0, 0, 153, 131, 0, 137, 118, + 0, 122, 105, 0, 107, 90, 0, 91, 76, 0, 76, 63, 0, 60, 47, + 0, 39, 28, 0, 0, 0, 0, 153, 111, 0, 136, 100, 0, 123, 90, + 0, 107, 77, 0, 92, 65, 0, 76, 52, 0, 60, 37, 0, 38, 18, + 0, 0, 0, 0, 152, 93, 0, 136, 82, 0, 122, 74, 0, 106, 63, + 0, 91, 52, 0, 76, 40, 0, 59, 26, 0, 38, 6, 0, 0, 0, + 0, 151, 78, 0, 136, 69, 0, 121, 61, 0, 106, 52, 0, 91, 43, + 0, 76, 32, 0, 59, 17, 0, 38, 0, 0, 0, 0, 0, 143, 213, + 0, 128, 191, 0, 115, 171, 0, 100, 149, 0, 84, 128, 0, 69, 108, + 0, 52, 86, 0, 30, 61, 0, 0, 25, 0, 142, 193, 0, 127, 173, + 0, 114, 154, 0, 99, 134, 0, 84, 116, 0, 69, 98, 0, 52, 77, + 0, 31, 53, 0, 0, 18, 0, 141, 176, 0, 127, 158, 0, 114, 141, + 0, 98, 122, 0, 84, 105, 0, 69, 88, 0, 53, 69, 0, 31, 46, + 0, 0, 9, 0, 141, 159, 0, 126, 142, 0, 113, 127, 0, 98, 110, + 0, 83, 95, 0, 69, 78, 0, 53, 60, 0, 32, 39, 0, 0, 2, + 0, 140, 140, 0, 126, 126, 0, 112, 112, 0, 98, 98, 0, 83, 83, + 0, 68, 69, 0, 52, 52, 0, 31, 31, 0, 0, 0, 0, 140, 124, + 0, 125, 112, 0, 112, 100, 0, 97, 86, 0, 83, 72, 0, 68, 59, + 0, 52, 44, 0, 31, 23, 0, 0, 0, 0, 139, 106, 0, 125, 96, + 0, 111, 85, 0, 97, 72, 0, 83, 62, 0, 68, 49, 0, 52, 35, + 0, 31, 15, 0, 0, 0, 0, 138, 88, 0, 124, 79, 0, 111, 70, + 0, 96, 59, 0, 82, 48, 0, 68, 38, 0, 52, 24, 0, 31, 4, + 0, 0, 0, 0, 139, 76, 0, 124, 66, 0, 111, 58, 0, 96, 50, + 0, 82, 40, 0, 68, 29, 0, 52, 15, 0, 31, 0, 0, 0, 0, + 0, 129, 200, 0, 114, 179, 0, 102, 160, 0, 87, 139, 0, 74, 120, + 0, 60, 101, 0, 44, 81, 0, 22, 56, 0, 0, 19, 0, 127, 181, + 0, 114, 163, 0, 102, 146, 0, 88, 127, 0, 74, 109, 0, 60, 91, + 0, 44, 72, 0, 23, 48, 0, 0, 11, 0, 127, 166, 0, 113, 148, + 0, 101, 133, 0, 87, 115, 0, 74, 99, 0, 60, 82, 0, 44, 64, + 0, 23, 42, 0, 0, 4, 0, 127, 150, 0, 113, 134, 0, 101, 119, + 0, 87, 104, 0, 74, 89, 0, 60, 73, 0, 44, 56, 0, 23, 35, + 0, 0, 0, 0, 125, 134, 0, 112, 118, 0, 100, 106, 0, 87, 92, + 0, 73, 78, 0, 60, 64, 0, 44, 48, 0, 23, 27, 0, 0, 0, + 0, 125, 118, 0, 112, 105, 0, 100, 94, 0, 86, 80, 0, 73, 68, + 0, 60, 54, 0, 44, 39, 0, 23, 20, 0, 0, 0, 0, 125, 101, + 0, 111, 90, 0, 99, 80, 0, 86, 69, 0, 73, 58, 0, 59, 45, + 0, 44, 30, 0, 23, 11, 0, 0, 0, 0, 124, 85, 0, 111, 75, + 0, 99, 66, 0, 86, 56, 0, 73, 45, 0, 59, 34, 0, 44, 20, + 0, 23, 1, 0, 0, 0, 0, 125, 72, 0, 111, 62, 0, 99, 56, + 0, 86, 46, 0, 73, 36, 0, 60, 26, 0, 44, 12, 0, 23, 0, + 0, 0, 0, 0, 114, 188, 0, 101, 167, 0, 89, 150, 0, 77, 131, + 0, 64, 113, 0, 50, 95, 0, 34, 75, 0, 12, 52, 0, 0, 14, + 0, 113, 170, 0, 101, 153, 0, 89, 137, 0, 77, 120, 0, 64, 102, + 0, 50, 85, 0, 35, 67, 0, 12, 44, 0, 0, 4, 0, 113, 156, + 0, 100, 139, 0, 89, 125, 0, 77, 109, 0, 64, 92, 0, 51, 77, + 0, 35, 60, 0, 12, 38, 0, 0, 0, 0, 112, 141, 0, 100, 126, + 0, 89, 113, 0, 77, 98, 0, 64, 83, 0, 51, 68, 0, 35, 51, + 0, 12, 30, 0, 0, 0, 0, 112, 127, 0, 100, 112, 0, 89, 100, + 0, 76, 87, 0, 64, 74, 0, 51, 59, 0, 35, 44, 0, 13, 24, + 0, 0, 0, 0, 112, 111, 0, 100, 100, 0, 88, 88, 0, 76, 76, + 0, 64, 64, 0, 51, 52, 0, 36, 37, 0, 13, 17, 0, 0, 0, + 0, 111, 96, 0, 99, 85, 0, 88, 76, 0, 76, 64, 0, 64, 53, + 0, 51, 41, 0, 36, 27, 0, 13, 6, 0, 0, 0, 0, 111, 81, + 0, 99, 71, 0, 88, 62, 0, 76, 52, 0, 64, 43, 0, 51, 31, + 0, 36, 17, 0, 13, 0, 0, 0, 0, 0, 111, 69, 0, 99, 60, + 0, 88, 52, 0, 75, 43, 0, 63, 34, 0, 51, 21, 0, 36, 7, + 0, 13, 0, 0, 0, 0, 0, 99, 177, 0, 88, 158, 0, 77, 141, + 0, 66, 123, 0, 53, 106, 0, 40, 89, 0, 25, 71, 0, 5, 47, + 0, 0, 8, 0, 99, 160, 0, 88, 144, 0, 77, 129, 0, 66, 112, + 0, 54, 97, 0, 41, 80, 0, 26, 62, 0, 5, 40, 0, 0, 0, + 0, 99, 147, 0, 87, 132, 0, 78, 117, 0, 66, 102, 0, 54, 87, + 0, 42, 72, 0, 26, 55, 0, 5, 34, 0, 0, 0, 0, 99, 134, + 0, 88, 119, 0, 77, 107, 0, 66, 92, 0, 54, 78, 0, 42, 64, + 0, 27, 48, 0, 5, 27, 0, 0, 0, 0, 99, 120, 0, 87, 107, + 0, 78, 94, 0, 66, 81, 0, 54, 68, 0, 42, 55, 0, 27, 40, + 0, 6, 20, 0, 0, 0, 0, 98, 105, 0, 87, 94, 0, 77, 84, + 0, 65, 71, 0, 55, 59, 0, 42, 47, 0, 28, 33, 0, 6, 12, + 0, 0, 0, 0, 98, 93, 0, 87, 81, 0, 77, 72, 0, 66, 61, + 0, 54, 49, 0, 42, 37, 0, 28, 24, 0, 6, 4, 0, 0, 0, + 0, 98, 77, 0, 87, 68, 0, 77, 59, 0, 65, 49, 0, 54, 39, + 0, 42, 27, 0, 29, 14, 0, 6, 0, 0, 0, 0, 1, 98, 65, + 7, 87, 56, 0, 77, 49, 0, 66, 41, 0, 54, 30, 0, 42, 19, + 0, 29, 3, 0, 6, 0, 0, 0, 0, 0, 84, 166, 0, 74, 149, + 0, 64, 134, 0, 53, 117, 0, 41, 100, 0, 28, 83, 0, 11, 64, + 0, 0, 42, 0, 0, 3, 0, 84, 151, 0, 74, 135, 0, 64, 121, + 0, 53, 105, 0, 42, 90, 0, 30, 75, 0, 14, 58, 0, 0, 36, + 0, 0, 0, 0, 84, 138, 0, 74, 124, 1, 64, 110, 0, 54, 95, + 0, 43, 81, 0, 30, 67, 0, 15, 51, 0, 1, 29, 0, 0, 0, + 14, 84, 126, 12, 74, 112, 2, 65, 99, 0, 54, 85, 0, 44, 73, + 0, 31, 59, 0, 16, 44, 0, 1, 23, 0, 0, 0, 16, 84, 113, + 13, 74, 100, 6, 65, 89, 0, 54, 77, 0, 44, 65, 0, 31, 51, + 0, 17, 36, 0, 1, 16, 0, 0, 0, 24, 84, 100, 18, 74, 88, + 13, 65, 78, 2, 55, 68, 0, 44, 55, 0, 32, 43, 0, 18, 28, + 0, 1, 6, 0, 0, 0, 26, 84, 87, 24, 74, 76, 17, 65, 67, + 7, 54, 57, 0, 44, 46, 0, 32, 35, 0, 19, 21, 0, 2, 3, + 0, 0, 0, 30, 84, 74, 28, 74, 64, 20, 65, 55, 12, 55, 46, + 0, 44, 35, 0, 32, 24, 0, 18, 9, 0, 1, 0, 0, 0, 0, + 32, 84, 63, 28, 74, 54, 21, 65, 47, 13, 54, 38, 0, 44, 28, + 0, 32, 16, 0, 18, 1, 0, 1, 0, 0, 0, 0, 30, 67, 155, + 20, 58, 139, 20, 49, 126, 12, 39, 110, 0, 27, 94, 0, 13, 77, + 0, 0, 60, 0, 0, 37, 0, 0, 0, 35, 67, 142, 30, 58, 126, + 23, 50, 114, 16, 40, 99, 7, 29, 85, 0, 15, 69, 0, 0, 52, + 0, 0, 30, 0, 0, 0, 35, 68, 131, 30, 59, 116, 27, 50, 104, + 18, 40, 90, 9, 29, 76, 0, 17, 62, 0, 2, 46, 0, 0, 24, + 0, 0, 0, 37, 69, 119, 33, 59, 106, 27, 51, 94, 21, 41, 80, + 9, 30, 67, 0, 18, 54, 0, 3, 39, 0, 0, 18, 0, 0, 0, + 40, 69, 107, 36, 59, 94, 28, 51, 84, 18, 41, 72, 10, 31, 60, + 0, 19, 47, 0, 4, 32, 0, 0, 10, 0, 0, 0, 42, 69, 95, + 36, 59, 84, 29, 51, 74, 19, 41, 63, 10, 31, 52, 0, 20, 39, + 0, 4, 25, 0, 0, 4, 0, 0, 0, 43, 69, 83, 38, 60, 73, + 32, 51, 62, 23, 42, 53, 11, 31, 42, 0, 20, 31, 0, 5, 17, + 0, 0, 1, 0, 0, 0, 45, 69, 70, 39, 60, 60, 33, 51, 52, + 24, 42, 43, 13, 32, 33, 0, 21, 21, 0, 5, 6, 0, 0, 0, + 0, 0, 0, 47, 69, 59, 41, 60, 51, 34, 51, 43, 24, 42, 35, + 12, 33, 26, 1, 22, 14, 0, 5, 1, 0, 0, 0, 0, 0, 0, + 46, 48, 146, 42, 40, 131, 36, 32, 118, 27, 22, 103, 17, 6, 88, + 5, 0, 73, 0, 0, 55, 0, 0, 33, 0, 0, 0, 48, 48, 133, + 44, 40, 119, 37, 32, 107, 28, 22, 93, 20, 8, 79, 7, 0, 65, + 0, 0, 49, 0, 0, 27, 0, 0, 0, 48, 50, 123, 44, 41, 109, + 37, 33, 97, 30, 23, 83, 21, 11, 71, 8, 0, 58, 0, 0, 42, + 0, 0, 21, 0, 0, 0, 49, 51, 111, 45, 42, 99, 38, 34, 87, + 29, 25, 75, 20, 13, 63, 8, 0, 51, 0, 0, 36, 0, 0, 14, + 0, 0, 0, 52, 52, 100, 44, 43, 89, 38, 35, 79, 29, 26, 68, + 19, 15, 56, 10, 1, 43, 0, 0, 28, 0, 0, 6, 0, 0, 0, + 52, 52, 90, 47, 44, 79, 39, 36, 70, 30, 27, 59, 20, 16, 47, + 9, 2, 36, 0, 0, 22, 0, 0, 2, 0, 0, 0, 52, 53, 78, + 46, 44, 68, 39, 37, 60, 32, 27, 49, 22, 17, 39, 10, 3, 28, + 0, 0, 12, 0, 0, 0, 0, 0, 0, 53, 53, 66, 47, 44, 57, + 40, 36, 48, 32, 27, 39, 22, 18, 30, 9, 4, 18, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 54, 53, 57, 48, 45, 49, 41, 37, 41, + 33, 28, 32, 22, 19, 23, 11, 6, 10, 1, 0, 0, 0, 0, 0, + 0, 0, 0, +}; +void AdobeCMYK_to_sRGB1(uint8_t c, + uint8_t m, + uint8_t y, + uint8_t k, + uint8_t& R, + uint8_t& G, + uint8_t& B) { + int fix_c = c << 8; + int fix_m = m << 8; + int fix_y = y << 8; + int fix_k = k << 8; + int c_index = (fix_c + 4096) >> 13; + int m_index = (fix_m + 4096) >> 13; + int y_index = (fix_y + 4096) >> 13; + int k_index = (fix_k + 4096) >> 13; + int pos = (c_index * 9 * 9 * 9 + m_index * 9 * 9 + y_index * 9 + k_index) * 3; + int fix_r = g_CMYKSamples[pos] << 8; + int fix_g = g_CMYKSamples[pos + 1] << 8; + int fix_b = g_CMYKSamples[pos + 2] << 8; + int c1_index = fix_c >> 13; + if (c1_index == c_index) { + c1_index = c1_index == 8 ? c1_index - 1 : c1_index + 1; + } + int m1_index = fix_m >> 13; + if (m1_index == m_index) { + m1_index = m1_index == 8 ? m1_index - 1 : m1_index + 1; + } + int y1_index = fix_y >> 13; + if (y1_index == y_index) { + y1_index = y1_index == 8 ? y1_index - 1 : y1_index + 1; + } + int k1_index = fix_k >> 13; + if (k1_index == k_index) { + k1_index = k1_index == 8 ? k1_index - 1 : k1_index + 1; + } + int c1_pos = pos + (c1_index - c_index) * 9 * 9 * 9 * 3; + int m1_pos = pos + (m1_index - m_index) * 9 * 9 * 3; + int y1_pos = pos + (y1_index - y_index) * 9 * 3; + int k1_pos = pos + (k1_index - k_index) * 3; + int c_r_delta = g_CMYKSamples[pos] - g_CMYKSamples[c1_pos]; + int c_g_delta = g_CMYKSamples[pos + 1] - g_CMYKSamples[c1_pos + 1]; + int c_b_delta = g_CMYKSamples[pos + 2] - g_CMYKSamples[c1_pos + 2]; + int m_r_delta = g_CMYKSamples[pos] - g_CMYKSamples[m1_pos]; + int m_g_delta = g_CMYKSamples[pos + 1] - g_CMYKSamples[m1_pos + 1]; + int m_b_delta = g_CMYKSamples[pos + 2] - g_CMYKSamples[m1_pos + 2]; + int y_r_delta = g_CMYKSamples[pos] - g_CMYKSamples[y1_pos]; + int y_g_delta = g_CMYKSamples[pos + 1] - g_CMYKSamples[y1_pos + 1]; + int y_b_delta = g_CMYKSamples[pos + 2] - g_CMYKSamples[y1_pos + 2]; + int k_r_delta = g_CMYKSamples[pos] - g_CMYKSamples[k1_pos]; + int k_g_delta = g_CMYKSamples[pos + 1] - g_CMYKSamples[k1_pos + 1]; + int k_b_delta = g_CMYKSamples[pos + 2] - g_CMYKSamples[k1_pos + 2]; + int c_rate = (fix_c - (c_index << 13)) * (c_index - c1_index); + fix_r += c_r_delta * c_rate / 32; + fix_g += c_g_delta * c_rate / 32; + fix_b += c_b_delta * c_rate / 32; + int m_rate = (fix_m - (m_index << 13)) * (m_index - m1_index); + fix_r += m_r_delta * m_rate / 32; + fix_g += m_g_delta * m_rate / 32; + fix_b += m_b_delta * m_rate / 32; + int y_rate = (fix_y - (y_index << 13)) * (y_index - y1_index); + fix_r += y_r_delta * y_rate / 32; + fix_g += y_g_delta * y_rate / 32; + fix_b += y_b_delta * y_rate / 32; + int k_rate = (fix_k - (k_index << 13)) * (k_index - k1_index); + fix_r += k_r_delta * k_rate / 32; + fix_g += k_g_delta * k_rate / 32; + fix_b += k_b_delta * k_rate / 32; + if (fix_r < 0) { + fix_r = 0; + } + if (fix_g < 0) { + fix_g = 0; + } + if (fix_b < 0) { + fix_b = 0; + } + R = fix_r >> 8; + G = fix_g >> 8; + B = fix_b >> 8; +} +void AdobeCMYK_to_sRGB(FX_FLOAT c, + FX_FLOAT m, + FX_FLOAT y, + FX_FLOAT k, + FX_FLOAT& R, + FX_FLOAT& G, + FX_FLOAT& B) { + uint8_t c1 = FXSYS_round(c * 255); + uint8_t m1 = FXSYS_round(m * 255); + uint8_t y1 = FXSYS_round(y * 255); + uint8_t k1 = FXSYS_round(k * 255); + uint8_t r, g, b; + AdobeCMYK_to_sRGB1(c1, m1, y1, k1, r, g, b); + R = 1.0f * r / 255; + G = 1.0f * g / 255; + B = 1.0f * b / 255; +} diff --git a/core/fxcodec/codec/fx_codec_jbig.cpp b/core/fxcodec/codec/fx_codec_jbig.cpp new file mode 100644 index 0000000000..557d246ca4 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_jbig.cpp @@ -0,0 +1,126 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include <list> + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcodec/fx_codec.h" + +// Holds per-document JBig2 related data. +class JBig2DocumentContext : public CFX_DestructObject { + public: + std::list<CJBig2_CachePair>* GetSymbolDictCache() { + return &m_SymbolDictCache; + } + + ~JBig2DocumentContext() override { + for (auto it : m_SymbolDictCache) { + delete it.second; + } + } + + private: + std::list<CJBig2_CachePair> m_SymbolDictCache; +}; + +JBig2DocumentContext* GetJBig2DocumentContext(CCodec_Jbig2Module* pModule, + CFX_PrivateData* pPrivateData) { + void* pModulePrivateData = pPrivateData->GetPrivateData(pModule); + if (pModulePrivateData) { + CFX_DestructObject* pDestructObject = + reinterpret_cast<CFX_DestructObject*>(pModulePrivateData); + return static_cast<JBig2DocumentContext*>(pDestructObject); + } + JBig2DocumentContext* pJBig2DocumentContext = new JBig2DocumentContext(); + pPrivateData->SetPrivateObj(pModule, pJBig2DocumentContext); + return pJBig2DocumentContext; +} + +CCodec_Jbig2Context::CCodec_Jbig2Context() { + FXSYS_memset(this, 0, sizeof(CCodec_Jbig2Context)); +} + +CCodec_Jbig2Module::~CCodec_Jbig2Module() {} + +void* CCodec_Jbig2Module::CreateJbig2Context() { + return new CCodec_Jbig2Context(); +} +void CCodec_Jbig2Module::DestroyJbig2Context(void* pJbig2Content) { + if (pJbig2Content) { + CJBig2_Context::DestroyContext( + ((CCodec_Jbig2Context*)pJbig2Content)->m_pContext); + delete (CCodec_Jbig2Context*)pJbig2Content; + } + pJbig2Content = NULL; +} +FXCODEC_STATUS CCodec_Jbig2Module::StartDecode(void* pJbig2Context, + CFX_PrivateData* pPrivateData, + FX_DWORD width, + FX_DWORD height, + CPDF_StreamAcc* src_stream, + CPDF_StreamAcc* global_stream, + uint8_t* dest_buf, + FX_DWORD dest_pitch, + IFX_Pause* pPause) { + if (!pJbig2Context) { + return FXCODEC_STATUS_ERR_PARAMS; + } + JBig2DocumentContext* pJBig2DocumentContext = + GetJBig2DocumentContext(this, pPrivateData); + CCodec_Jbig2Context* m_pJbig2Context = (CCodec_Jbig2Context*)pJbig2Context; + m_pJbig2Context->m_width = width; + m_pJbig2Context->m_height = height; + m_pJbig2Context->m_pSrcStream = src_stream; + m_pJbig2Context->m_pGlobalStream = global_stream; + m_pJbig2Context->m_dest_buf = dest_buf; + m_pJbig2Context->m_dest_pitch = dest_pitch; + m_pJbig2Context->m_pPause = pPause; + FXSYS_memset(dest_buf, 0, height * dest_pitch); + m_pJbig2Context->m_pContext = CJBig2_Context::CreateContext( + global_stream, src_stream, pJBig2DocumentContext->GetSymbolDictCache(), + pPause); + if (!m_pJbig2Context->m_pContext) { + return FXCODEC_STATUS_ERROR; + } + int ret = m_pJbig2Context->m_pContext->getFirstPage(dest_buf, width, height, + dest_pitch, pPause); + if (m_pJbig2Context->m_pContext->GetProcessingStatus() == + FXCODEC_STATUS_DECODE_FINISH) { + CJBig2_Context::DestroyContext(m_pJbig2Context->m_pContext); + m_pJbig2Context->m_pContext = NULL; + if (ret != JBIG2_SUCCESS) { + return FXCODEC_STATUS_ERROR; + } + int dword_size = height * dest_pitch / 4; + FX_DWORD* dword_buf = (FX_DWORD*)dest_buf; + for (int i = 0; i < dword_size; i++) { + dword_buf[i] = ~dword_buf[i]; + } + return FXCODEC_STATUS_DECODE_FINISH; + } + return m_pJbig2Context->m_pContext->GetProcessingStatus(); +} +FXCODEC_STATUS CCodec_Jbig2Module::ContinueDecode(void* pJbig2Context, + IFX_Pause* pPause) { + CCodec_Jbig2Context* m_pJbig2Context = (CCodec_Jbig2Context*)pJbig2Context; + int ret = m_pJbig2Context->m_pContext->Continue(pPause); + if (m_pJbig2Context->m_pContext->GetProcessingStatus() != + FXCODEC_STATUS_DECODE_FINISH) { + return m_pJbig2Context->m_pContext->GetProcessingStatus(); + } + CJBig2_Context::DestroyContext(m_pJbig2Context->m_pContext); + m_pJbig2Context->m_pContext = NULL; + if (ret != JBIG2_SUCCESS) { + return FXCODEC_STATUS_ERROR; + } + int dword_size = + m_pJbig2Context->m_height * m_pJbig2Context->m_dest_pitch / 4; + FX_DWORD* dword_buf = (FX_DWORD*)m_pJbig2Context->m_dest_buf; + for (int i = 0; i < dword_size; i++) { + dword_buf[i] = ~dword_buf[i]; + } + return FXCODEC_STATUS_DECODE_FINISH; +} diff --git a/core/fxcodec/codec/fx_codec_jpeg.cpp b/core/fxcodec/codec/fx_codec_jpeg.cpp new file mode 100644 index 0000000000..efbeb790e3 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_jpeg.cpp @@ -0,0 +1,692 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include <setjmp.h> + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxcrt/fx_safe_types.h" +#include "core/include/fxge/fx_dib.h" + +extern "C" { +#undef FAR +#if defined(USE_SYSTEM_LIBJPEG) +#include <jpeglib.h> +#elif defined(USE_LIBJPEG_TURBO) +#include "third_party/libjpeg_turbo/jpeglib.h" +#else +#include "third_party/libjpeg/jpeglib.h" +#endif +} + +extern "C" { +static void _JpegScanSOI(const uint8_t*& src_buf, FX_DWORD& src_size) { + if (src_size == 0) { + return; + } + FX_DWORD offset = 0; + while (offset < src_size - 1) { + if (src_buf[offset] == 0xff && src_buf[offset + 1] == 0xd8) { + src_buf += offset; + src_size -= offset; + return; + } + offset++; + } +} +}; +extern "C" { +static void _src_do_nothing(struct jpeg_decompress_struct* cinfo) {} +}; +extern "C" { +static void _error_fatal(j_common_ptr cinfo) { + longjmp(*(jmp_buf*)cinfo->client_data, -1); +} +}; +extern "C" { +static void _src_skip_data(struct jpeg_decompress_struct* cinfo, long num) { + if (num > (long)cinfo->src->bytes_in_buffer) { + _error_fatal((j_common_ptr)cinfo); + } + cinfo->src->next_input_byte += num; + cinfo->src->bytes_in_buffer -= num; +} +}; +extern "C" { +static boolean _src_fill_buffer(j_decompress_ptr cinfo) { + return 0; +} +}; +extern "C" { +static boolean _src_resync(j_decompress_ptr cinfo, int desired) { + return 0; +} +}; +extern "C" { +static void _error_do_nothing(j_common_ptr cinfo) {} +}; +extern "C" { +static void _error_do_nothing1(j_common_ptr cinfo, int) {} +}; +extern "C" { +static void _error_do_nothing2(j_common_ptr cinfo, char*) {} +}; +#define JPEG_MARKER_EXIF (JPEG_APP0 + 1) +#define JPEG_MARKER_ICC (JPEG_APP0 + 2) +#define JPEG_MARKER_AUTHORTIME (JPEG_APP0 + 3) +#define JPEG_MARKER_MAXSIZE 0xFFFF +#define JPEG_OVERHEAD_LEN 14 +static FX_BOOL _JpegEmbedIccProfile(j_compress_ptr cinfo, + const uint8_t* icc_buf_ptr, + FX_DWORD icc_length) { + if (!icc_buf_ptr || icc_length == 0) { + return FALSE; + } + FX_DWORD icc_segment_size = (JPEG_MARKER_MAXSIZE - 2 - JPEG_OVERHEAD_LEN); + FX_DWORD icc_segment_num = (icc_length / icc_segment_size) + 1; + if (icc_segment_num > 255) { + return FALSE; + } + FX_DWORD icc_data_length = + JPEG_OVERHEAD_LEN + (icc_segment_num > 1 ? icc_segment_size : icc_length); + uint8_t* icc_data = FX_Alloc(uint8_t, icc_data_length); + FXSYS_memcpy(icc_data, "\x49\x43\x43\x5f\x50\x52\x4f\x46\x49\x4c\x45\x00", + 12); + icc_data[13] = (uint8_t)icc_segment_num; + for (uint8_t i = 0; i < (icc_segment_num - 1); i++) { + icc_data[12] = i + 1; + FXSYS_memcpy(icc_data + JPEG_OVERHEAD_LEN, + icc_buf_ptr + i * icc_segment_size, icc_segment_size); + jpeg_write_marker(cinfo, JPEG_MARKER_ICC, icc_data, icc_data_length); + } + icc_data[12] = (uint8_t)icc_segment_num; + FX_DWORD icc_size = (icc_segment_num - 1) * icc_segment_size; + FXSYS_memcpy(icc_data + JPEG_OVERHEAD_LEN, icc_buf_ptr + icc_size, + icc_length - icc_size); + jpeg_write_marker(cinfo, JPEG_MARKER_ICC, icc_data, + JPEG_OVERHEAD_LEN + icc_length - icc_size); + FX_Free(icc_data); + return TRUE; +} +extern "C" { +static void _dest_do_nothing(j_compress_ptr cinfo) {} +}; +extern "C" { +static boolean _dest_empty(j_compress_ptr cinfo) { + return FALSE; +} +}; +#define JPEG_BLOCK_SIZE 1048576 +static void _JpegEncode(const CFX_DIBSource* pSource, + uint8_t*& dest_buf, + FX_STRSIZE& dest_size, + int quality, + const uint8_t* icc_buf, + FX_DWORD icc_length) { + struct jpeg_error_mgr jerr; + jerr.error_exit = _error_do_nothing; + jerr.emit_message = _error_do_nothing1; + jerr.output_message = _error_do_nothing; + jerr.format_message = _error_do_nothing2; + jerr.reset_error_mgr = _error_do_nothing; + + struct jpeg_compress_struct cinfo; + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.err = &jerr; + jpeg_create_compress(&cinfo); + int Bpp = pSource->GetBPP() / 8; + FX_DWORD nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1; + FX_DWORD pitch = pSource->GetPitch(); + FX_DWORD width = pdfium::base::checked_cast<FX_DWORD>(pSource->GetWidth()); + FX_DWORD height = pdfium::base::checked_cast<FX_DWORD>(pSource->GetHeight()); + FX_SAFE_DWORD safe_buf_len = width; + safe_buf_len *= height; + safe_buf_len *= nComponents; + safe_buf_len += 1024; + if (icc_length) { + safe_buf_len += 255 * 18; + safe_buf_len += icc_length; + } + FX_DWORD dest_buf_length = 0; + if (!safe_buf_len.IsValid()) { + dest_buf = nullptr; + } else { + dest_buf_length = safe_buf_len.ValueOrDie(); + dest_buf = FX_TryAlloc(uint8_t, dest_buf_length); + const int MIN_TRY_BUF_LEN = 1024; + while (!dest_buf && dest_buf_length > MIN_TRY_BUF_LEN) { + dest_buf_length >>= 1; + dest_buf = FX_TryAlloc(uint8_t, dest_buf_length); + } + } + if (!dest_buf) { + FX_OutOfMemoryTerminate(); + } + struct jpeg_destination_mgr dest; + dest.init_destination = _dest_do_nothing; + dest.term_destination = _dest_do_nothing; + dest.empty_output_buffer = _dest_empty; + dest.next_output_byte = dest_buf; + dest.free_in_buffer = dest_buf_length; + cinfo.dest = &dest; + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = nComponents; + if (nComponents == 1) { + cinfo.in_color_space = JCS_GRAYSCALE; + } else if (nComponents == 3) { + cinfo.in_color_space = JCS_RGB; + } else { + cinfo.in_color_space = JCS_CMYK; + } + uint8_t* line_buf = NULL; + if (nComponents > 1) { + line_buf = FX_Alloc2D(uint8_t, width, nComponents); + } + jpeg_set_defaults(&cinfo); + if (quality != 75) { + jpeg_set_quality(&cinfo, quality, TRUE); + } + jpeg_start_compress(&cinfo, TRUE); + _JpegEmbedIccProfile(&cinfo, icc_buf, icc_length); + JSAMPROW row_pointer[1]; + JDIMENSION row; + while (cinfo.next_scanline < cinfo.image_height) { + const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline); + if (nComponents > 1) { + uint8_t* dest_scan = line_buf; + if (nComponents == 3) { + for (FX_DWORD i = 0; i < width; i++) { + dest_scan[0] = src_scan[2]; + dest_scan[1] = src_scan[1]; + dest_scan[2] = src_scan[0]; + dest_scan += 3; + src_scan += Bpp; + } + } else { + for (FX_DWORD i = 0; i < pitch; i++) { + *dest_scan++ = ~*src_scan++; + } + } + row_pointer[0] = line_buf; + } else { + row_pointer[0] = (uint8_t*)src_scan; + } + row = cinfo.next_scanline; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + if (cinfo.next_scanline == row) { + dest_buf = + FX_Realloc(uint8_t, dest_buf, dest_buf_length + JPEG_BLOCK_SIZE); + dest.next_output_byte = dest_buf + dest_buf_length - dest.free_in_buffer; + dest_buf_length += JPEG_BLOCK_SIZE; + dest.free_in_buffer += JPEG_BLOCK_SIZE; + } + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + FX_Free(line_buf); + dest_size = dest_buf_length - (FX_STRSIZE)dest.free_in_buffer; +} + +#ifdef PDF_ENABLE_XFA +static void _JpegLoadAttribute(struct jpeg_decompress_struct* pInfo, + CFX_DIBAttribute* pAttribute) { + if (pInfo == NULL || pAttribute == NULL) { + return; + } + if (pAttribute) { + pAttribute->m_nXDPI = pInfo->X_density; + pAttribute->m_nYDPI = pInfo->Y_density; + pAttribute->m_wDPIUnit = pInfo->density_unit; + } +} +#endif // PDF_ENABLE_XFA + +static FX_BOOL _JpegLoadInfo(const uint8_t* src_buf, + FX_DWORD src_size, + int& width, + int& height, + int& num_components, + int& bits_per_components, + FX_BOOL& color_transform, + uint8_t** icc_buf_ptr, + FX_DWORD* icc_length) { + _JpegScanSOI(src_buf, src_size); + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + jerr.error_exit = _error_fatal; + jerr.emit_message = _error_do_nothing1; + jerr.output_message = _error_do_nothing; + jerr.format_message = _error_do_nothing2; + jerr.reset_error_mgr = _error_do_nothing; + jerr.trace_level = 0; + cinfo.err = &jerr; + jmp_buf mark; + cinfo.client_data = &mark; + if (setjmp(mark) == -1) { + return FALSE; + } + jpeg_create_decompress(&cinfo); + struct jpeg_source_mgr src; + src.init_source = _src_do_nothing; + src.term_source = _src_do_nothing; + src.skip_input_data = _src_skip_data; + src.fill_input_buffer = _src_fill_buffer; + src.resync_to_restart = _src_resync; + src.bytes_in_buffer = src_size; + src.next_input_byte = src_buf; + cinfo.src = &src; + if (setjmp(mark) == -1) { + jpeg_destroy_decompress(&cinfo); + return FALSE; + } + if (icc_buf_ptr && icc_length) { + jpeg_save_markers(&cinfo, JPEG_MARKER_ICC, JPEG_MARKER_MAXSIZE); + } + int ret = jpeg_read_header(&cinfo, TRUE); + if (ret != JPEG_HEADER_OK) { + jpeg_destroy_decompress(&cinfo); + return FALSE; + } + width = cinfo.image_width; + height = cinfo.image_height; + num_components = cinfo.num_components; + color_transform = + cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK; + bits_per_components = cinfo.data_precision; + if (icc_buf_ptr) { + *icc_buf_ptr = NULL; + } + if (icc_length) { + *icc_length = 0; + } + jpeg_destroy_decompress(&cinfo); + return TRUE; +} + +class CCodec_JpegDecoder : public CCodec_ScanlineDecoder { + public: + CCodec_JpegDecoder(); + ~CCodec_JpegDecoder() override; + + FX_BOOL Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + FX_BOOL ColorTransform); + void Destroy() { delete this; } + + // CCodec_ScanlineDecoder + void v_DownScale(int dest_width, int dest_height) override; + FX_BOOL v_Rewind() override; + uint8_t* v_GetNextLine() override; + FX_DWORD GetSrcOffset() override; + + FX_BOOL InitDecode(); + + jmp_buf m_JmpBuf; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + struct jpeg_source_mgr src; + const uint8_t* m_SrcBuf; + FX_DWORD m_SrcSize; + uint8_t* m_pScanlineBuf; + + FX_BOOL m_bInited; + FX_BOOL m_bStarted; + FX_BOOL m_bJpegTransform; + + protected: + FX_DWORD m_nDefaultScaleDenom; +}; + +CCodec_JpegDecoder::CCodec_JpegDecoder() { + m_pScanlineBuf = NULL; + m_DownScale = 1; + m_bStarted = FALSE; + m_bInited = FALSE; + FXSYS_memset(&cinfo, 0, sizeof(cinfo)); + FXSYS_memset(&jerr, 0, sizeof(jerr)); + FXSYS_memset(&src, 0, sizeof(src)); + m_nDefaultScaleDenom = 1; +} +CCodec_JpegDecoder::~CCodec_JpegDecoder() { + FX_Free(m_pScanlineBuf); + if (m_bInited) { + jpeg_destroy_decompress(&cinfo); + } +} +FX_BOOL CCodec_JpegDecoder::InitDecode() { + cinfo.err = &jerr; + cinfo.client_data = &m_JmpBuf; + if (setjmp(m_JmpBuf) == -1) { + return FALSE; + } + jpeg_create_decompress(&cinfo); + m_bInited = TRUE; + cinfo.src = &src; + src.bytes_in_buffer = m_SrcSize; + src.next_input_byte = m_SrcBuf; + if (setjmp(m_JmpBuf) == -1) { + jpeg_destroy_decompress(&cinfo); + m_bInited = FALSE; + return FALSE; + } + cinfo.image_width = m_OrigWidth; + cinfo.image_height = m_OrigHeight; + int ret = jpeg_read_header(&cinfo, TRUE); + if (ret != JPEG_HEADER_OK) { + return FALSE; + } + if (cinfo.saw_Adobe_marker) { + m_bJpegTransform = TRUE; + } + if (cinfo.num_components == 3 && !m_bJpegTransform) { + cinfo.out_color_space = cinfo.jpeg_color_space; + } + m_OrigWidth = cinfo.image_width; + m_OrigHeight = cinfo.image_height; + m_OutputWidth = m_OrigWidth; + m_OutputHeight = m_OrigHeight; + m_nDefaultScaleDenom = cinfo.scale_denom; + return TRUE; +} +FX_BOOL CCodec_JpegDecoder::Create(const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + FX_BOOL ColorTransform) { + _JpegScanSOI(src_buf, src_size); + m_SrcBuf = src_buf; + m_SrcSize = src_size; + jerr.error_exit = _error_fatal; + jerr.emit_message = _error_do_nothing1; + jerr.output_message = _error_do_nothing; + jerr.format_message = _error_do_nothing2; + jerr.reset_error_mgr = _error_do_nothing; + src.init_source = _src_do_nothing; + src.term_source = _src_do_nothing; + src.skip_input_data = _src_skip_data; + src.fill_input_buffer = _src_fill_buffer; + src.resync_to_restart = _src_resync; + m_bJpegTransform = ColorTransform; + if (src_size > 1 && + FXSYS_memcmp(src_buf + src_size - 2, "\xFF\xD9", 2) != 0) { + ((uint8_t*)src_buf)[src_size - 2] = 0xFF; + ((uint8_t*)src_buf)[src_size - 1] = 0xD9; + } + m_OutputWidth = m_OrigWidth = width; + m_OutputHeight = m_OrigHeight = height; + if (!InitDecode()) { + return FALSE; + } + if (cinfo.num_components < nComps) { + return FALSE; + } + if ((int)cinfo.image_width < width) { + return FALSE; + } + m_Pitch = + (static_cast<FX_DWORD>(cinfo.image_width) * cinfo.num_components + 3) / + 4 * 4; + m_pScanlineBuf = FX_Alloc(uint8_t, m_Pitch); + m_nComps = cinfo.num_components; + m_bpc = 8; + m_bColorTransformed = FALSE; + m_bStarted = FALSE; + return TRUE; +} +extern "C" { +int32_t FX_GetDownsampleRatio(int32_t originWidth, + int32_t originHeight, + int32_t downsampleWidth, + int32_t downsampleHeight) { + int iratio_w = originWidth / downsampleWidth; + int iratio_h = originHeight / downsampleHeight; + int ratio = (iratio_w > iratio_h) ? iratio_h : iratio_w; + if (ratio >= 8) { + return 8; + } + if (ratio >= 4) { + return 4; + } + if (ratio >= 2) { + return 2; + } + return 1; +} +} +void CCodec_JpegDecoder::v_DownScale(int dest_width, int dest_height) { + int old_scale = m_DownScale; + m_DownScale = + FX_GetDownsampleRatio(m_OrigWidth, m_OrigHeight, dest_width, dest_height); + m_OutputWidth = (m_OrigWidth + m_DownScale - 1) / m_DownScale; + m_OutputHeight = (m_OrigHeight + m_DownScale - 1) / m_DownScale; + m_Pitch = (static_cast<FX_DWORD>(m_OutputWidth) * m_nComps + 3) / 4 * 4; + if (old_scale != m_DownScale) { + m_NextLine = -1; + } +} +FX_BOOL CCodec_JpegDecoder::v_Rewind() { + if (m_bStarted) { + jpeg_destroy_decompress(&cinfo); + if (!InitDecode()) { + return FALSE; + } + } + if (setjmp(m_JmpBuf) == -1) { + return FALSE; + } + cinfo.scale_denom = m_nDefaultScaleDenom * m_DownScale; + m_OutputWidth = (m_OrigWidth + m_DownScale - 1) / m_DownScale; + m_OutputHeight = (m_OrigHeight + m_DownScale - 1) / m_DownScale; + if (!jpeg_start_decompress(&cinfo)) { + jpeg_destroy_decompress(&cinfo); + return FALSE; + } + if ((int)cinfo.output_width > m_OrigWidth) { + FXSYS_assert(FALSE); + return FALSE; + } + m_bStarted = TRUE; + return TRUE; +} +uint8_t* CCodec_JpegDecoder::v_GetNextLine() { + if (setjmp(m_JmpBuf) == -1) + return nullptr; + + int nlines = jpeg_read_scanlines(&cinfo, &m_pScanlineBuf, 1); + if (nlines < 1) { + return nullptr; + } + return m_pScanlineBuf; +} +FX_DWORD CCodec_JpegDecoder::GetSrcOffset() { + return (FX_DWORD)(m_SrcSize - src.bytes_in_buffer); +} +ICodec_ScanlineDecoder* CCodec_JpegModule::CreateDecoder( + const uint8_t* src_buf, + FX_DWORD src_size, + int width, + int height, + int nComps, + FX_BOOL ColorTransform) { + if (!src_buf || src_size == 0) { + return NULL; + } + CCodec_JpegDecoder* pDecoder = new CCodec_JpegDecoder; + if (!pDecoder->Create(src_buf, src_size, width, height, nComps, + ColorTransform)) { + delete pDecoder; + return NULL; + } + return pDecoder; +} +FX_BOOL CCodec_JpegModule::LoadInfo(const uint8_t* src_buf, + FX_DWORD src_size, + int& width, + int& height, + int& num_components, + int& bits_per_components, + FX_BOOL& color_transform, + uint8_t** icc_buf_ptr, + FX_DWORD* icc_length) { + return _JpegLoadInfo(src_buf, src_size, width, height, num_components, + bits_per_components, color_transform, icc_buf_ptr, + icc_length); +} +FX_BOOL CCodec_JpegModule::Encode(const CFX_DIBSource* pSource, + uint8_t*& dest_buf, + FX_STRSIZE& dest_size, + int quality, + const uint8_t* icc_buf, + FX_DWORD icc_length) { + if (pSource->GetBPP() < 8 || pSource->GetPalette()) + return FALSE; + + _JpegEncode(pSource, dest_buf, dest_size, quality, icc_buf, icc_length); + return TRUE; +} +struct FXJPEG_Context { + jmp_buf m_JumpMark; + jpeg_decompress_struct m_Info; + jpeg_error_mgr m_ErrMgr; + jpeg_source_mgr m_SrcMgr; + unsigned int m_SkipSize; + void* (*m_AllocFunc)(unsigned int); + void (*m_FreeFunc)(void*); +}; +extern "C" { +static void _error_fatal1(j_common_ptr cinfo) { + longjmp(((FXJPEG_Context*)cinfo->client_data)->m_JumpMark, -1); +} +}; +extern "C" { +static void _src_skip_data1(struct jpeg_decompress_struct* cinfo, long num) { + if (cinfo->src->bytes_in_buffer < (size_t)num) { + ((FXJPEG_Context*)cinfo->client_data)->m_SkipSize = + (unsigned int)(num - cinfo->src->bytes_in_buffer); + cinfo->src->bytes_in_buffer = 0; + } else { + cinfo->src->next_input_byte += num; + cinfo->src->bytes_in_buffer -= num; + } +} +}; +static void* jpeg_alloc_func(unsigned int size) { + return FX_Alloc(char, size); +} +static void jpeg_free_func(void* p) { + FX_Free(p); +} +void* CCodec_JpegModule::Start() { + FXJPEG_Context* p = FX_Alloc(FXJPEG_Context, 1); + p->m_AllocFunc = jpeg_alloc_func; + p->m_FreeFunc = jpeg_free_func; + p->m_ErrMgr.error_exit = _error_fatal1; + p->m_ErrMgr.emit_message = _error_do_nothing1; + p->m_ErrMgr.output_message = _error_do_nothing; + p->m_ErrMgr.format_message = _error_do_nothing2; + p->m_ErrMgr.reset_error_mgr = _error_do_nothing; + p->m_SrcMgr.init_source = _src_do_nothing; + p->m_SrcMgr.term_source = _src_do_nothing; + p->m_SrcMgr.skip_input_data = _src_skip_data1; + p->m_SrcMgr.fill_input_buffer = _src_fill_buffer; + p->m_SrcMgr.resync_to_restart = _src_resync; + p->m_Info.client_data = p; + p->m_Info.err = &p->m_ErrMgr; + if (setjmp(p->m_JumpMark) == -1) { + return 0; + } + jpeg_create_decompress(&p->m_Info); + p->m_Info.src = &p->m_SrcMgr; + p->m_SkipSize = 0; + return p; +} +void CCodec_JpegModule::Finish(void* pContext) { + FXJPEG_Context* p = (FXJPEG_Context*)pContext; + jpeg_destroy_decompress(&p->m_Info); + p->m_FreeFunc(p); +} +void CCodec_JpegModule::Input(void* pContext, + const unsigned char* src_buf, + FX_DWORD src_size) { + FXJPEG_Context* p = (FXJPEG_Context*)pContext; + if (p->m_SkipSize) { + if (p->m_SkipSize > src_size) { + p->m_SrcMgr.bytes_in_buffer = 0; + p->m_SkipSize -= src_size; + return; + } + src_size -= p->m_SkipSize; + src_buf += p->m_SkipSize; + p->m_SkipSize = 0; + } + p->m_SrcMgr.next_input_byte = src_buf; + p->m_SrcMgr.bytes_in_buffer = src_size; +} + +#ifdef PDF_ENABLE_XFA +int CCodec_JpegModule::ReadHeader(void* pContext, + int* width, + int* height, + int* nComps, + CFX_DIBAttribute* pAttribute) { +#else // PDF_ENABLE_XFA +int CCodec_JpegModule::ReadHeader(void* pContext, + int* width, + int* height, + int* nComps) { +#endif // PDF_ENABLE_XFA + FXJPEG_Context* p = (FXJPEG_Context*)pContext; + if (setjmp(p->m_JumpMark) == -1) { + return 1; + } + int ret = jpeg_read_header(&p->m_Info, true); + if (ret == JPEG_SUSPENDED) { + return 2; + } + if (ret != JPEG_HEADER_OK) { + return 1; + } + *width = p->m_Info.image_width; + *height = p->m_Info.image_height; + *nComps = p->m_Info.num_components; +#ifdef PDF_ENABLE_XFA + _JpegLoadAttribute(&p->m_Info, pAttribute); +#endif + return 0; +} +int CCodec_JpegModule::StartScanline(void* pContext, int down_scale) { + FXJPEG_Context* p = (FXJPEG_Context*)pContext; + if (setjmp(p->m_JumpMark) == -1) { + return 0; + } + p->m_Info.scale_denom = down_scale; + return jpeg_start_decompress(&p->m_Info); +} +FX_BOOL CCodec_JpegModule::ReadScanline(void* pContext, + unsigned char* dest_buf) { + FXJPEG_Context* p = (FXJPEG_Context*)pContext; + if (setjmp(p->m_JumpMark) == -1) { + return FALSE; + } + int nlines = jpeg_read_scanlines(&p->m_Info, &dest_buf, 1); + return nlines == 1; +} +FX_DWORD CCodec_JpegModule::GetAvailInput(void* pContext, + uint8_t** avail_buf_ptr) { + if (avail_buf_ptr) { + *avail_buf_ptr = NULL; + if (((FXJPEG_Context*)pContext)->m_SrcMgr.bytes_in_buffer > 0) { + *avail_buf_ptr = + (uint8_t*)((FXJPEG_Context*)pContext)->m_SrcMgr.next_input_byte; + } + } + return (FX_DWORD)((FXJPEG_Context*)pContext)->m_SrcMgr.bytes_in_buffer; +} diff --git a/core/fxcodec/codec/fx_codec_jpx_opj.cpp b/core/fxcodec/codec/fx_codec_jpx_opj.cpp new file mode 100644 index 0000000000..486cb929b8 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_jpx_opj.cpp @@ -0,0 +1,887 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include <algorithm> +#include <limits> +#include <vector> + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fpdfapi/fpdf_resource.h" +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxcrt/fx_safe_types.h" +#include "third_party/lcms2-2.6/include/lcms2.h" +#include "third_party/libopenjpeg20/openjpeg.h" + +static void fx_error_callback(const char* msg, void* client_data) { + (void)client_data; +} +static void fx_warning_callback(const char* msg, void* client_data) { + (void)client_data; +} +static void fx_info_callback(const char* msg, void* client_data) { + (void)client_data; +} +OPJ_SIZE_T opj_read_from_memory(void* p_buffer, + OPJ_SIZE_T nb_bytes, + void* p_user_data) { + DecodeData* srcData = static_cast<DecodeData*>(p_user_data); + if (!srcData || !srcData->src_data || srcData->src_size == 0) { + return -1; + } + // Reads at EOF return an error code. + if (srcData->offset >= srcData->src_size) { + return -1; + } + OPJ_SIZE_T bufferLength = srcData->src_size - srcData->offset; + OPJ_SIZE_T readlength = nb_bytes < bufferLength ? nb_bytes : bufferLength; + memcpy(p_buffer, &srcData->src_data[srcData->offset], readlength); + srcData->offset += readlength; + return readlength; +} +OPJ_SIZE_T opj_write_from_memory(void* p_buffer, + OPJ_SIZE_T nb_bytes, + void* p_user_data) { + DecodeData* srcData = static_cast<DecodeData*>(p_user_data); + if (!srcData || !srcData->src_data || srcData->src_size == 0) { + return -1; + } + // Writes at EOF return an error code. + if (srcData->offset >= srcData->src_size) { + return -1; + } + OPJ_SIZE_T bufferLength = srcData->src_size - srcData->offset; + OPJ_SIZE_T writeLength = nb_bytes < bufferLength ? nb_bytes : bufferLength; + memcpy(&srcData->src_data[srcData->offset], p_buffer, writeLength); + srcData->offset += writeLength; + return writeLength; +} +OPJ_OFF_T opj_skip_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data) { + DecodeData* srcData = static_cast<DecodeData*>(p_user_data); + if (!srcData || !srcData->src_data || srcData->src_size == 0) { + return -1; + } + // Offsets are signed and may indicate a negative skip. Do not support this + // because of the strange return convention where either bytes skipped or + // -1 is returned. Following that convention, a successful relative seek of + // -1 bytes would be required to to give the same result as the error case. + if (nb_bytes < 0) { + return -1; + } + // FIXME: use std::make_unsigned<OPJ_OFF_T>::type once c++11 lib is OK'd. + uint64_t unsignedNbBytes = static_cast<uint64_t>(nb_bytes); + // Additionally, the offset may take us beyond the range of a size_t (e.g. + // 32-bit platforms). If so, just clamp at EOF. + if (unsignedNbBytes > + std::numeric_limits<OPJ_SIZE_T>::max() - srcData->offset) { + srcData->offset = srcData->src_size; + } else { + OPJ_SIZE_T checkedNbBytes = static_cast<OPJ_SIZE_T>(unsignedNbBytes); + // Otherwise, mimic fseek() semantics to always succeed, even past EOF, + // clamping at EOF. We can get away with this since we don't actually + // provide negative relative skips from beyond EOF back to inside the + // data, which would be the only reason to need to know exactly how far + // beyond EOF we are. + srcData->offset = + std::min(srcData->offset + checkedNbBytes, srcData->src_size); + } + return nb_bytes; +} +OPJ_BOOL opj_seek_from_memory(OPJ_OFF_T nb_bytes, void* p_user_data) { + DecodeData* srcData = static_cast<DecodeData*>(p_user_data); + if (!srcData || !srcData->src_data || srcData->src_size == 0) { + return OPJ_FALSE; + } + // Offsets are signed and may indicate a negative position, which would + // be before the start of the file. Do not support this. + if (nb_bytes < 0) { + return OPJ_FALSE; + } + // FIXME: use std::make_unsigned<OPJ_OFF_T>::type once c++11 lib is OK'd. + uint64_t unsignedNbBytes = static_cast<uint64_t>(nb_bytes); + // Additionally, the offset may take us beyond the range of a size_t (e.g. + // 32-bit platforms). If so, just clamp at EOF. + if (unsignedNbBytes > std::numeric_limits<OPJ_SIZE_T>::max()) { + srcData->offset = srcData->src_size; + } else { + OPJ_SIZE_T checkedNbBytes = static_cast<OPJ_SIZE_T>(nb_bytes); + // Otherwise, mimic fseek() semantics to always succeed, even past EOF, + // again clamping at EOF. + srcData->offset = std::min(checkedNbBytes, srcData->src_size); + } + return OPJ_TRUE; +} +opj_stream_t* fx_opj_stream_create_memory_stream(DecodeData* data, + OPJ_SIZE_T p_size, + OPJ_BOOL p_is_read_stream) { + opj_stream_t* l_stream = 00; + if (!data || !data->src_data || data->src_size <= 0) { + return NULL; + } + l_stream = opj_stream_create(p_size, p_is_read_stream); + if (!l_stream) { + return NULL; + } + opj_stream_set_user_data(l_stream, data, NULL); + opj_stream_set_user_data_length(l_stream, data->src_size); + opj_stream_set_read_function(l_stream, opj_read_from_memory); + opj_stream_set_write_function(l_stream, opj_write_from_memory); + opj_stream_set_skip_function(l_stream, opj_skip_from_memory); + opj_stream_set_seek_function(l_stream, opj_seek_from_memory); + return l_stream; +} +static void sycc_to_rgb(int offset, + int upb, + int y, + int cb, + int cr, + int* out_r, + int* out_g, + int* out_b) { + int r, g, b; + cb -= offset; + cr -= offset; + r = y + (int)(1.402 * (float)cr); + if (r < 0) { + r = 0; + } else if (r > upb) { + r = upb; + } + *out_r = r; + g = y - (int)(0.344 * (float)cb + 0.714 * (float)cr); + if (g < 0) { + g = 0; + } else if (g > upb) { + g = upb; + } + *out_g = g; + b = y + (int)(1.772 * (float)cb); + if (b < 0) { + b = 0; + } else if (b > upb) { + b = upb; + } + *out_b = b; +} +static void sycc444_to_rgb(opj_image_t* img) { + int prec = img->comps[0].prec; + int offset = 1 << (prec - 1); + int upb = (1 << prec) - 1; + OPJ_UINT32 maxw = + std::min(std::min(img->comps[0].w, img->comps[1].w), img->comps[2].w); + OPJ_UINT32 maxh = + std::min(std::min(img->comps[0].h, img->comps[1].h), img->comps[2].h); + FX_SAFE_SIZE_T max_size = maxw; + max_size *= maxh; + if (!max_size.IsValid()) + return; + + const int* y = img->comps[0].data; + const int* cb = img->comps[1].data; + const int* cr = img->comps[2].data; + int *d0, *d1, *d2, *r, *g, *b; + d0 = r = FX_Alloc(int, max_size.ValueOrDie()); + d1 = g = FX_Alloc(int, max_size.ValueOrDie()); + d2 = b = FX_Alloc(int, max_size.ValueOrDie()); + for (size_t i = 0; i < max_size.ValueOrDie(); ++i) { + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++cb; + ++cr; + ++r; + ++g; + ++b; + } + FX_Free(img->comps[0].data); + img->comps[0].data = d0; + FX_Free(img->comps[1].data); + img->comps[1].data = d1; + FX_Free(img->comps[2].data); + img->comps[2].data = d2; +} +static void sycc422_to_rgb(opj_image_t* img) { + int prec = img->comps[0].prec; + int offset = 1 << (prec - 1); + int upb = (1 << prec) - 1; + OPJ_UINT32 maxw = + std::min(std::min(img->comps[0].w, img->comps[1].w), img->comps[2].w); + OPJ_UINT32 maxh = + std::min(std::min(img->comps[0].h, img->comps[1].h), img->comps[2].h); + FX_SAFE_SIZE_T max_size = maxw; + max_size *= maxh; + if (!max_size.IsValid()) + return; + + const int* y = img->comps[0].data; + const int* cb = img->comps[1].data; + const int* cr = img->comps[2].data; + int *d0, *d1, *d2, *r, *g, *b; + d0 = r = FX_Alloc(int, max_size.ValueOrDie()); + d1 = g = FX_Alloc(int, max_size.ValueOrDie()); + d2 = b = FX_Alloc(int, max_size.ValueOrDie()); + for (uint32_t i = 0; i < maxh; ++i) { + OPJ_UINT32 j; + for (j = 0; j < (maxw & ~static_cast<OPJ_UINT32>(1)); j += 2) { + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + ++cb; + ++cr; + } + if (j < maxw) { + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + ++cb; + ++cr; + } + } + FX_Free(img->comps[0].data); + img->comps[0].data = d0; + FX_Free(img->comps[1].data); + img->comps[1].data = d1; + FX_Free(img->comps[2].data); + img->comps[2].data = d2; + img->comps[1].w = maxw; + img->comps[1].h = maxh; + img->comps[2].w = maxw; + img->comps[2].h = maxh; + img->comps[1].dx = img->comps[0].dx; + img->comps[2].dx = img->comps[0].dx; + img->comps[1].dy = img->comps[0].dy; + img->comps[2].dy = img->comps[0].dy; +} +static bool sycc420_size_is_valid(OPJ_UINT32 y, OPJ_UINT32 cbcr) { + if (!y || !cbcr) + return false; + + return (cbcr == y / 2) || ((y & 1) && (cbcr == y / 2 + 1)); +} +static bool sycc420_must_extend_cbcr(OPJ_UINT32 y, OPJ_UINT32 cbcr) { + return (y & 1) && (cbcr == y / 2); +} +void sycc420_to_rgb(opj_image_t* img) { + OPJ_UINT32 prec = img->comps[0].prec; + if (!prec) + return; + OPJ_UINT32 offset = 1 << (prec - 1); + OPJ_UINT32 upb = (1 << prec) - 1; + OPJ_UINT32 yw = img->comps[0].w; + OPJ_UINT32 yh = img->comps[0].h; + OPJ_UINT32 cbw = img->comps[1].w; + OPJ_UINT32 cbh = img->comps[1].h; + OPJ_UINT32 crw = img->comps[2].w; + OPJ_UINT32 crh = img->comps[2].h; + if (cbw != crw || cbh != crh) + return; + if (!sycc420_size_is_valid(yw, cbw) || !sycc420_size_is_valid(yh, cbh)) + return; + bool extw = sycc420_must_extend_cbcr(yw, cbw); + bool exth = sycc420_must_extend_cbcr(yh, cbh); + FX_SAFE_DWORD safeSize = yw; + safeSize *= yh; + if (!safeSize.IsValid()) + return; + int* r = FX_Alloc(int, safeSize.ValueOrDie()); + int* g = FX_Alloc(int, safeSize.ValueOrDie()); + int* b = FX_Alloc(int, safeSize.ValueOrDie()); + int* d0 = r; + int* d1 = g; + int* d2 = b; + const int* y = img->comps[0].data; + const int* cb = img->comps[1].data; + const int* cr = img->comps[2].data; + const int* ny = nullptr; + int* nr = nullptr; + int* ng = nullptr; + int* nb = nullptr; + OPJ_UINT32 i = 0; + OPJ_UINT32 j = 0; + for (i = 0; i < (yh & ~(OPJ_UINT32)1); i += 2) { + ny = y + yw; + nr = r + yw; + ng = g + yw; + nb = b + yw; + for (j = 0; j < (yw & ~(OPJ_UINT32)1); j += 2) { + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); + ++ny; + ++nr; + ++ng; + ++nb; + sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); + ++ny; + ++nr; + ++ng; + ++nb; + ++cb; + ++cr; + } + if (j < yw) { + if (extw) { + --cb; + --cr; + } + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); + ++ny; + ++nr; + ++ng; + ++nb; + ++cb; + ++cr; + } + y += yw; + r += yw; + g += yw; + b += yw; + } + if (i < yh) { + if (exth) { + cb -= cbw; + cr -= crw; + } + for (j = 0; j < (yw & ~(OPJ_UINT32)1); j += 2) { + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + ++y; + ++r; + ++g; + ++b; + ++cb; + ++cr; + } + if (j < yw) { + if (extw) { + --cb; + --cr; + } + sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); + } + } + + FX_Free(img->comps[0].data); + img->comps[0].data = d0; + FX_Free(img->comps[1].data); + img->comps[1].data = d1; + FX_Free(img->comps[2].data); + img->comps[2].data = d2; + img->comps[1].w = yw; + img->comps[1].h = yh; + img->comps[2].w = yw; + img->comps[2].h = yh; + img->comps[1].w = yw; + img->comps[1].h = yh; + img->comps[2].w = yw; + img->comps[2].h = yh; + img->comps[1].dx = img->comps[0].dx; + img->comps[2].dx = img->comps[0].dx; + img->comps[1].dy = img->comps[0].dy; + img->comps[2].dy = img->comps[0].dy; +} +void color_sycc_to_rgb(opj_image_t* img) { + if (img->numcomps < 3) { + img->color_space = OPJ_CLRSPC_GRAY; + return; + } + if ((img->comps[0].dx == 1) && (img->comps[1].dx == 2) && + (img->comps[2].dx == 2) && (img->comps[0].dy == 1) && + (img->comps[1].dy == 2) && (img->comps[2].dy == 2)) { + sycc420_to_rgb(img); + } else if ((img->comps[0].dx == 1) && (img->comps[1].dx == 2) && + (img->comps[2].dx == 2) && (img->comps[0].dy == 1) && + (img->comps[1].dy == 1) && (img->comps[2].dy == 1)) { + sycc422_to_rgb(img); + } else if ((img->comps[0].dx == 1) && (img->comps[1].dx == 1) && + (img->comps[2].dx == 1) && (img->comps[0].dy == 1) && + (img->comps[1].dy == 1) && (img->comps[2].dy == 1)) { + sycc444_to_rgb(img); + } else { + return; + } + img->color_space = OPJ_CLRSPC_SRGB; +} +void color_apply_icc_profile(opj_image_t* image) { + cmsHPROFILE out_prof; + cmsUInt32Number in_type; + cmsUInt32Number out_type; + int* r; + int* g; + int* b; + int max; + cmsHPROFILE in_prof = + cmsOpenProfileFromMem(image->icc_profile_buf, image->icc_profile_len); + if (!in_prof) { + return; + } + cmsColorSpaceSignature out_space = cmsGetColorSpace(in_prof); + cmsUInt32Number intent = cmsGetHeaderRenderingIntent(in_prof); + int max_w = (int)image->comps[0].w; + int max_h = (int)image->comps[0].h; + int prec = (int)image->comps[0].prec; + OPJ_COLOR_SPACE oldspace = image->color_space; + if (out_space == cmsSigRgbData) { + if (prec <= 8) { + in_type = TYPE_RGB_8; + out_type = TYPE_RGB_8; + } else { + in_type = TYPE_RGB_16; + out_type = TYPE_RGB_16; + } + out_prof = cmsCreate_sRGBProfile(); + image->color_space = OPJ_CLRSPC_SRGB; + } else if (out_space == cmsSigGrayData) { + if (prec <= 8) { + in_type = TYPE_GRAY_8; + out_type = TYPE_RGB_8; + } else { + in_type = TYPE_GRAY_16; + out_type = TYPE_RGB_16; + } + out_prof = cmsCreate_sRGBProfile(); + image->color_space = OPJ_CLRSPC_SRGB; + } else if (out_space == cmsSigYCbCrData) { + in_type = TYPE_YCbCr_16; + out_type = TYPE_RGB_16; + out_prof = cmsCreate_sRGBProfile(); + image->color_space = OPJ_CLRSPC_SRGB; + } else { + return; + } + cmsHTRANSFORM transform = + cmsCreateTransform(in_prof, in_type, out_prof, out_type, intent, 0); + cmsCloseProfile(in_prof); + cmsCloseProfile(out_prof); + if (!transform) { + image->color_space = oldspace; + return; + } + if (image->numcomps > 2) { + if (prec <= 8) { + unsigned char *inbuf, *outbuf, *in, *out; + max = max_w * max_h; + cmsUInt32Number nr_samples = max * 3 * sizeof(unsigned char); + in = inbuf = FX_Alloc(unsigned char, nr_samples); + out = outbuf = FX_Alloc(unsigned char, nr_samples); + r = image->comps[0].data; + g = image->comps[1].data; + b = image->comps[2].data; + for (int i = 0; i < max; ++i) { + *in++ = (unsigned char)*r++; + *in++ = (unsigned char)*g++; + *in++ = (unsigned char)*b++; + } + cmsDoTransform(transform, inbuf, outbuf, (cmsUInt32Number)max); + r = image->comps[0].data; + g = image->comps[1].data; + b = image->comps[2].data; + for (int i = 0; i < max; ++i) { + *r++ = (int)*out++; + *g++ = (int)*out++; + *b++ = (int)*out++; + } + FX_Free(inbuf); + FX_Free(outbuf); + } else { + unsigned short *inbuf, *outbuf, *in, *out; + max = max_w * max_h; + cmsUInt32Number nr_samples = max * 3 * sizeof(unsigned short); + in = inbuf = FX_Alloc(unsigned short, nr_samples); + out = outbuf = FX_Alloc(unsigned short, nr_samples); + r = image->comps[0].data; + g = image->comps[1].data; + b = image->comps[2].data; + for (int i = 0; i < max; ++i) { + *in++ = (unsigned short)*r++; + *in++ = (unsigned short)*g++; + *in++ = (unsigned short)*b++; + } + cmsDoTransform(transform, inbuf, outbuf, (cmsUInt32Number)max); + r = image->comps[0].data; + g = image->comps[1].data; + b = image->comps[2].data; + for (int i = 0; i < max; ++i) { + *r++ = (int)*out++; + *g++ = (int)*out++; + *b++ = (int)*out++; + } + FX_Free(inbuf); + FX_Free(outbuf); + } + } else { + unsigned char *in, *inbuf, *out, *outbuf; + max = max_w * max_h; + cmsUInt32Number nr_samples = + (cmsUInt32Number)max * 3 * sizeof(unsigned char); + in = inbuf = FX_Alloc(unsigned char, nr_samples); + out = outbuf = FX_Alloc(unsigned char, nr_samples); + image->comps = (opj_image_comp_t*)realloc( + image->comps, (image->numcomps + 2) * sizeof(opj_image_comp_t)); + if (image->numcomps == 2) { + image->comps[3] = image->comps[1]; + } + image->comps[1] = image->comps[0]; + image->comps[2] = image->comps[0]; + image->comps[1].data = FX_Alloc(int, (size_t)max); + FXSYS_memset(image->comps[1].data, 0, sizeof(int) * (size_t)max); + image->comps[2].data = FX_Alloc(int, (size_t)max); + FXSYS_memset(image->comps[2].data, 0, sizeof(int) * (size_t)max); + image->numcomps += 2; + r = image->comps[0].data; + for (int i = 0; i < max; ++i) { + *in++ = (unsigned char)*r++; + } + cmsDoTransform(transform, inbuf, outbuf, (cmsUInt32Number)max); + r = image->comps[0].data; + g = image->comps[1].data; + b = image->comps[2].data; + for (int i = 0; i < max; ++i) { + *r++ = (int)*out++; + *g++ = (int)*out++; + *b++ = (int)*out++; + } + FX_Free(inbuf); + FX_Free(outbuf); + } + cmsDeleteTransform(transform); +} +void color_apply_conversion(opj_image_t* image) { + int* row; + int enumcs, numcomps; + numcomps = image->numcomps; + if (numcomps < 3) { + return; + } + row = (int*)image->icc_profile_buf; + enumcs = row[0]; + if (enumcs == 14) { + int *L, *a, *b, *red, *green, *blue, *src0, *src1, *src2; + double rl, ol, ra, oa, rb, ob, prec0, prec1, prec2; + double minL, maxL, mina, maxa, minb, maxb; + unsigned int default_type; + unsigned int i, max; + cmsHPROFILE in, out; + cmsHTRANSFORM transform; + cmsUInt16Number RGB[3]; + cmsCIELab Lab; + in = cmsCreateLab4Profile(NULL); + out = cmsCreate_sRGBProfile(); + transform = cmsCreateTransform(in, TYPE_Lab_DBL, out, TYPE_RGB_16, + INTENT_PERCEPTUAL, 0); + cmsCloseProfile(in); + cmsCloseProfile(out); + if (!transform) { + return; + } + prec0 = (double)image->comps[0].prec; + prec1 = (double)image->comps[1].prec; + prec2 = (double)image->comps[2].prec; + default_type = row[1]; + if (default_type == 0x44454600) { + rl = 100; + ra = 170; + rb = 200; + ol = 0; + oa = pow(2, prec1 - 1); + ob = pow(2, prec2 - 2) + pow(2, prec2 - 3); + } else { + rl = row[2]; + ra = row[4]; + rb = row[6]; + ol = row[3]; + oa = row[5]; + ob = row[7]; + } + L = src0 = image->comps[0].data; + a = src1 = image->comps[1].data; + b = src2 = image->comps[2].data; + max = image->comps[0].w * image->comps[0].h; + red = FX_Alloc(int, max); + image->comps[0].data = red; + green = FX_Alloc(int, max); + image->comps[1].data = green; + blue = FX_Alloc(int, max); + image->comps[2].data = blue; + minL = -(rl * ol) / (pow(2, prec0) - 1); + maxL = minL + rl; + mina = -(ra * oa) / (pow(2, prec1) - 1); + maxa = mina + ra; + minb = -(rb * ob) / (pow(2, prec2) - 1); + maxb = minb + rb; + for (i = 0; i < max; ++i) { + Lab.L = minL + (double)(*L) * (maxL - minL) / (pow(2, prec0) - 1); + ++L; + Lab.a = mina + (double)(*a) * (maxa - mina) / (pow(2, prec1) - 1); + ++a; + Lab.b = minb + (double)(*b) * (maxb - minb) / (pow(2, prec2) - 1); + ++b; + cmsDoTransform(transform, &Lab, RGB, 1); + *red++ = RGB[0]; + *green++ = RGB[1]; + *blue++ = RGB[2]; + } + cmsDeleteTransform(transform); + FX_Free(src0); + FX_Free(src1); + FX_Free(src2); + image->color_space = OPJ_CLRSPC_SRGB; + image->comps[0].prec = 16; + image->comps[1].prec = 16; + image->comps[2].prec = 16; + return; + } +} +class CJPX_Decoder { + public: + explicit CJPX_Decoder(CPDF_ColorSpace* cs); + ~CJPX_Decoder(); + FX_BOOL Init(const unsigned char* src_data, FX_DWORD src_size); + void GetInfo(FX_DWORD* width, FX_DWORD* height, FX_DWORD* components); + bool Decode(uint8_t* dest_buf, + int pitch, + const std::vector<uint8_t>& offsets); + + private: + const uint8_t* m_SrcData; + FX_DWORD m_SrcSize; + opj_image_t* image; + opj_codec_t* l_codec; + opj_stream_t* l_stream; + const CPDF_ColorSpace* const m_ColorSpace; +}; + +CJPX_Decoder::CJPX_Decoder(CPDF_ColorSpace* cs) + : image(nullptr), l_codec(nullptr), l_stream(nullptr), m_ColorSpace(cs) {} + +CJPX_Decoder::~CJPX_Decoder() { + if (l_codec) { + opj_destroy_codec(l_codec); + } + if (l_stream) { + opj_stream_destroy(l_stream); + } + if (image) { + opj_image_destroy(image); + } +} + +FX_BOOL CJPX_Decoder::Init(const unsigned char* src_data, FX_DWORD src_size) { + static const unsigned char szJP2Header[] = { + 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a}; + if (!src_data || src_size < sizeof(szJP2Header)) + return FALSE; + + image = NULL; + m_SrcData = src_data; + m_SrcSize = src_size; + DecodeData srcData(const_cast<unsigned char*>(src_data), src_size); + l_stream = fx_opj_stream_create_memory_stream(&srcData, + OPJ_J2K_STREAM_CHUNK_SIZE, 1); + if (!l_stream) { + return FALSE; + } + opj_dparameters_t parameters; + opj_set_default_decoder_parameters(¶meters); + parameters.decod_format = 0; + parameters.cod_format = 3; + if (FXSYS_memcmp(m_SrcData, szJP2Header, sizeof(szJP2Header)) == 0) { + l_codec = opj_create_decompress(OPJ_CODEC_JP2); + parameters.decod_format = 1; + } else { + l_codec = opj_create_decompress(OPJ_CODEC_J2K); + } + if (!l_codec) { + return FALSE; + } + if (m_ColorSpace && m_ColorSpace->GetFamily() == PDFCS_INDEXED) + parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; + opj_set_info_handler(l_codec, fx_info_callback, 00); + opj_set_warning_handler(l_codec, fx_warning_callback, 00); + opj_set_error_handler(l_codec, fx_error_callback, 00); + if (!opj_setup_decoder(l_codec, ¶meters)) { + return FALSE; + } + if (!opj_read_header(l_stream, l_codec, &image)) { + image = NULL; + return FALSE; + } + image->pdfium_use_colorspace = !!m_ColorSpace; + + if (!parameters.nb_tile_to_decode) { + if (!opj_set_decode_area(l_codec, image, parameters.DA_x0, parameters.DA_y0, + parameters.DA_x1, parameters.DA_y1)) { + opj_image_destroy(image); + image = NULL; + return FALSE; + } + if (!(opj_decode(l_codec, l_stream, image) && + opj_end_decompress(l_codec, l_stream))) { + opj_image_destroy(image); + image = NULL; + return FALSE; + } + } else { + if (!opj_get_decoded_tile(l_codec, l_stream, image, + parameters.tile_index)) { + return FALSE; + } + } + opj_stream_destroy(l_stream); + l_stream = NULL; + if (image->color_space != OPJ_CLRSPC_SYCC && image->numcomps == 3 && + image->comps[0].dx == image->comps[0].dy && image->comps[1].dx != 1) { + image->color_space = OPJ_CLRSPC_SYCC; + } else if (image->numcomps <= 2) { + image->color_space = OPJ_CLRSPC_GRAY; + } + if (image->color_space == OPJ_CLRSPC_SYCC) { + color_sycc_to_rgb(image); + } + if (image->icc_profile_buf) { + FX_Free(image->icc_profile_buf); + image->icc_profile_buf = NULL; + image->icc_profile_len = 0; + } + if (!image) { + return FALSE; + } + return TRUE; +} + +void CJPX_Decoder::GetInfo(FX_DWORD* width, + FX_DWORD* height, + FX_DWORD* components) { + *width = (FX_DWORD)image->x1; + *height = (FX_DWORD)image->y1; + *components = (FX_DWORD)image->numcomps; +} + +bool CJPX_Decoder::Decode(uint8_t* dest_buf, + int pitch, + const std::vector<uint8_t>& offsets) { + if (image->comps[0].w != image->x1 || image->comps[0].h != image->y1) + return false; + + if (pitch<(int)(image->comps[0].w * 8 * image->numcomps + 31)>> 5 << 2) + return false; + + FXSYS_memset(dest_buf, 0xff, image->y1 * pitch); + std::vector<uint8_t*> channel_bufs(image->numcomps); + std::vector<int> adjust_comps(image->numcomps); + for (uint32_t i = 0; i < image->numcomps; i++) { + channel_bufs[i] = dest_buf + offsets[i]; + adjust_comps[i] = image->comps[i].prec - 8; + if (i > 0) { + if (image->comps[i].dx != image->comps[i - 1].dx || + image->comps[i].dy != image->comps[i - 1].dy || + image->comps[i].prec != image->comps[i - 1].prec) { + return false; + } + } + } + int width = image->comps[0].w; + int height = image->comps[0].h; + for (uint32_t channel = 0; channel < image->numcomps; ++channel) { + uint8_t* pChannel = channel_bufs[channel]; + if (adjust_comps[channel] < 0) { + for (int row = 0; row < height; ++row) { + uint8_t* pScanline = pChannel + row * pitch; + for (int col = 0; col < width; ++col) { + uint8_t* pPixel = pScanline + col * image->numcomps; + int src = image->comps[channel].data[row * width + col]; + src += image->comps[channel].sgnd + ? 1 << (image->comps[channel].prec - 1) + : 0; + if (adjust_comps[channel] > 0) { + *pPixel = 0; + } else { + *pPixel = (uint8_t)(src << -adjust_comps[channel]); + } + } + } + } else { + for (int row = 0; row < height; ++row) { + uint8_t* pScanline = pChannel + row * pitch; + for (int col = 0; col < width; ++col) { + uint8_t* pPixel = pScanline + col * image->numcomps; + if (!image->comps[channel].data) { + continue; + } + int src = image->comps[channel].data[row * width + col]; + src += image->comps[channel].sgnd + ? 1 << (image->comps[channel].prec - 1) + : 0; + if (adjust_comps[channel] - 1 < 0) { + *pPixel = (uint8_t)((src >> adjust_comps[channel])); + } else { + int tmpPixel = (src >> adjust_comps[channel]) + + ((src >> (adjust_comps[channel] - 1)) % 2); + if (tmpPixel > 255) { + tmpPixel = 255; + } else if (tmpPixel < 0) { + tmpPixel = 0; + } + *pPixel = (uint8_t)tmpPixel; + } + } + } + } + } + return true; +} + +CCodec_JpxModule::CCodec_JpxModule() {} +CCodec_JpxModule::~CCodec_JpxModule() {} + +CJPX_Decoder* CCodec_JpxModule::CreateDecoder(const uint8_t* src_buf, + FX_DWORD src_size, + CPDF_ColorSpace* cs) { + std::unique_ptr<CJPX_Decoder> decoder(new CJPX_Decoder(cs)); + return decoder->Init(src_buf, src_size) ? decoder.release() : nullptr; +} + +void CCodec_JpxModule::GetImageInfo(CJPX_Decoder* pDecoder, + FX_DWORD* width, + FX_DWORD* height, + FX_DWORD* components) { + pDecoder->GetInfo(width, height, components); +} + +bool CCodec_JpxModule::Decode(CJPX_Decoder* pDecoder, + uint8_t* dest_data, + int pitch, + const std::vector<uint8_t>& offsets) { + return pDecoder->Decode(dest_data, pitch, offsets); +} + +void CCodec_JpxModule::DestroyDecoder(CJPX_Decoder* pDecoder) { + delete pDecoder; +} diff --git a/core/fxcodec/codec/fx_codec_jpx_unittest.cpp b/core/fxcodec/codec/fx_codec_jpx_unittest.cpp new file mode 100644 index 0000000000..d58b152a78 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_jpx_unittest.cpp @@ -0,0 +1,532 @@ +// Copyright 2014 PDFium 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 <stdint.h> + +#include <limits> + +#include "core/fxcodec/codec/codec_int.h" +#include "testing/fx_string_testhelpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const OPJ_OFF_T kSkipError = static_cast<OPJ_OFF_T>(-1); +static const OPJ_SIZE_T kReadError = static_cast<OPJ_SIZE_T>(-1); +static const OPJ_SIZE_T kWriteError = static_cast<OPJ_SIZE_T>(-1); + +static unsigned char stream_data[] = { + 0x00, 0x01, 0x02, 0x03, + 0x84, 0x85, 0x86, 0x87, // Include some hi-bytes, too. +}; + +TEST(fxcodec, DecodeDataNullDecodeData) { + unsigned char buffer[16]; + DecodeData* ptr = nullptr; + + // Error codes, not segvs, should callers pass us a NULL pointer. + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), ptr)); + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer, sizeof(buffer), ptr)); + EXPECT_EQ(kSkipError, opj_skip_from_memory(1, ptr)); + EXPECT_FALSE(opj_seek_from_memory(1, ptr)); +} + +TEST(fxcodec, DecodeDataNullStream) { + DecodeData dd(nullptr, 0); + unsigned char buffer[16]; + + // Reads of size 0 do nothing but return an error code. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // Reads of nonzero size do nothing but return an error code. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // writes of size 0 do nothing but return an error code. + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer, 0, &dd)); + + // writes of nonzero size do nothing but return an error code. + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer, sizeof(buffer), &dd)); + + // Skips of size 0 always return an error code. + EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd)); + + // Skips of nonzero size always return an error code. + EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd)); + + // Seeks to 0 offset return in error. + EXPECT_FALSE(opj_seek_from_memory(0, &dd)); + + // Seeks to non-zero offsets return in error. + EXPECT_FALSE(opj_seek_from_memory(1, &dd)); +} + +TEST(fxcodec, DecodeDataZeroSize) { + DecodeData dd(stream_data, 0); + unsigned char buffer[16]; + + // Reads of size 0 do nothing but return an error code. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // Reads of nonzero size do nothing but return an error code. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // writes of size 0 do nothing but return an error code. + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer, 0, &dd)); + + // writes of nonzero size do nothing but return an error code. + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer, sizeof(buffer), &dd)); + + // Skips of size 0 always return an error code. + EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd)); + + // Skips of nonzero size always return an error code. + EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd)); + + // Seeks to 0 offset return in error. + EXPECT_FALSE(opj_seek_from_memory(0, &dd)); + + // Seeks to non-zero offsets return in error. + EXPECT_FALSE(opj_seek_from_memory(1, &dd)); +} + +TEST(fxcodec, DecodeDataReadInBounds) { + unsigned char buffer[16]; + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Exact sized read in a single call. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer), &dd)); + EXPECT_EQ(0x00, buffer[0]); + EXPECT_EQ(0x01, buffer[1]); + EXPECT_EQ(0x02, buffer[2]); + EXPECT_EQ(0x03, buffer[3]); + EXPECT_EQ(0x84, buffer[4]); + EXPECT_EQ(0x85, buffer[5]); + EXPECT_EQ(0x86, buffer[6]); + EXPECT_EQ(0x87, buffer[7]); + EXPECT_EQ(0xbd, buffer[8]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Simple read. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(2u, opj_read_from_memory(buffer, 2, &dd)); + EXPECT_EQ(0x00, buffer[0]); + EXPECT_EQ(0x01, buffer[1]); + EXPECT_EQ(0xbd, buffer[2]); + + // Read of size 0 doesn't affect things. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(0u, opj_read_from_memory(buffer, 0, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // Read exactly up to end of data. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd)); + EXPECT_EQ(0x02, buffer[0]); + EXPECT_EQ(0x03, buffer[1]); + EXPECT_EQ(0x84, buffer[2]); + EXPECT_EQ(0x85, buffer[3]); + EXPECT_EQ(0x86, buffer[4]); + EXPECT_EQ(0x87, buffer[5]); + EXPECT_EQ(0xbd, buffer[6]); + + // Read of size 0 at EOF is still an error. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } +} + +TEST(fxcodec, DecodeDataReadBeyondBounds) { + unsigned char buffer[16]; + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Read beyond bounds in a single step. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer) + 1, &dd)); + EXPECT_EQ(0x00, buffer[0]); + EXPECT_EQ(0x01, buffer[1]); + EXPECT_EQ(0x02, buffer[2]); + EXPECT_EQ(0x03, buffer[3]); + EXPECT_EQ(0x84, buffer[4]); + EXPECT_EQ(0x85, buffer[5]); + EXPECT_EQ(0x86, buffer[6]); + EXPECT_EQ(0x87, buffer[7]); + EXPECT_EQ(0xbd, buffer[8]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Read well beyond bounds in a single step. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(8u, opj_read_from_memory( + buffer, std::numeric_limits<OPJ_SIZE_T>::max(), &dd)); + EXPECT_EQ(0x00, buffer[0]); + EXPECT_EQ(0x01, buffer[1]); + EXPECT_EQ(0x02, buffer[2]); + EXPECT_EQ(0x03, buffer[3]); + EXPECT_EQ(0x84, buffer[4]); + EXPECT_EQ(0x85, buffer[5]); + EXPECT_EQ(0x86, buffer[6]); + EXPECT_EQ(0x87, buffer[7]); + EXPECT_EQ(0xbd, buffer[8]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Read of size 6 gets first 6 bytes. + // rest of buffer intact. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd)); + EXPECT_EQ(0x00, buffer[0]); + EXPECT_EQ(0x01, buffer[1]); + EXPECT_EQ(0x02, buffer[2]); + EXPECT_EQ(0x03, buffer[3]); + EXPECT_EQ(0x84, buffer[4]); + EXPECT_EQ(0x85, buffer[5]); + EXPECT_EQ(0xbd, buffer[6]); + + // Read of size 6 gets remaining two bytes. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(2u, opj_read_from_memory(buffer, 6, &dd)); + EXPECT_EQ(0x86, buffer[0]); + EXPECT_EQ(0x87, buffer[1]); + EXPECT_EQ(0xbd, buffer[2]); + + // Read of 6 more gets nothing and leaves rest of buffer intact. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 6, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } +} + +TEST(fxcodec, DecodeDataWriteInBounds) { + unsigned char stream[16]; + static unsigned char buffer_data[] = { + 0x00, 0x01, 0x02, 0x03, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, + }; + { + // Pretend the stream can only hold 4 bytes. + DecodeData dd(stream, 4); + + memset(stream, 0xbd, sizeof(stream)); + EXPECT_EQ(4u, opj_write_from_memory(buffer_data, 4, &dd)); + EXPECT_EQ(0x00, stream[0]); + EXPECT_EQ(0x01, stream[1]); + EXPECT_EQ(0x02, stream[2]); + EXPECT_EQ(0x03, stream[3]); + EXPECT_EQ(0xbd, stream[4]); + } + { + // Pretend the stream can only hold 4 bytes. + DecodeData dd(stream, 4); + + memset(stream, 0xbd, sizeof(stream)); + EXPECT_EQ(2u, opj_write_from_memory(buffer_data, 2, &dd)); + EXPECT_EQ(2u, opj_write_from_memory(buffer_data, 2, &dd)); + EXPECT_EQ(0x00, stream[0]); + EXPECT_EQ(0x01, stream[1]); + EXPECT_EQ(0x00, stream[2]); + EXPECT_EQ(0x01, stream[3]); + EXPECT_EQ(0xbd, stream[4]); + } +} + +TEST(fxcodec, DecodeDataWriteBeyondBounds) { + unsigned char stream[16]; + static unsigned char buffer_data[] = { + 0x10, 0x11, 0x12, 0x13, 0x94, 0x95, 0x96, 0x97, + }; + { + // Pretend the stream can only hold 4 bytes. + DecodeData dd(stream, 4); + + // Write ending past EOF transfers up til EOF. + memset(stream, 0xbd, sizeof(stream)); + EXPECT_EQ(4u, opj_write_from_memory(buffer_data, 5, &dd)); + EXPECT_EQ(0x10, stream[0]); + EXPECT_EQ(0x11, stream[1]); + EXPECT_EQ(0x12, stream[2]); + EXPECT_EQ(0x13, stream[3]); + EXPECT_EQ(0xbd, stream[4]); + + // Subsequent writes fail. + memset(stream, 0xbd, sizeof(stream)); + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer_data, 5, &dd)); + EXPECT_EQ(0xbd, stream[0]); + } + { + // Pretend the stream can only hold 4 bytes. + DecodeData dd(stream, 4); + + // Write ending past EOF (two steps) transfers up til EOF. + memset(stream, 0xbd, sizeof(stream)); + EXPECT_EQ(2u, opj_write_from_memory(buffer_data, 2, &dd)); + EXPECT_EQ(2u, opj_write_from_memory(buffer_data, 4, &dd)); + EXPECT_EQ(0x10, stream[0]); + EXPECT_EQ(0x11, stream[1]); + EXPECT_EQ(0x10, stream[2]); + EXPECT_EQ(0x11, stream[3]); + EXPECT_EQ(0xbd, stream[4]); + + // Subsequent writes fail. + memset(stream, 0xbd, sizeof(stream)); + EXPECT_EQ(kWriteError, opj_write_from_memory(buffer_data, 5, &dd)); + EXPECT_EQ(0xbd, stream[0]); + } +} + +// Note: Some care needs to be taken here because the skip/seek functions +// take OPJ_OFF_T's as arguments, which are typically a signed type. +TEST(fxcodec, DecodeDataSkip) { + unsigned char buffer[16]; + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Skiping within buffer is allowed. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(1, opj_skip_from_memory(1, &dd)); + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x01, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Skiping 0 bytes changes nothing. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(0, opj_skip_from_memory(0, &dd)); + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x02, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Skiping to EOS-1 is possible. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(4, opj_skip_from_memory(4, &dd)); + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x87, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Next read fails. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Skiping directly to EOS is allowed. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(8, opj_skip_from_memory(8, &dd)); + + // Next read fails. + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Skipping beyond end of stream is allowed and returns full distance. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(9, opj_skip_from_memory(9, &dd)); + + // Next read fails. + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Skipping way beyond EOS is allowd, doesn't wrap, and returns + // full distance. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(4, opj_skip_from_memory(4, &dd)); + EXPECT_EQ(std::numeric_limits<OPJ_OFF_T>::max(), + opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd)); + + // Next read fails. If it succeeds, it may mean we wrapped. + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Negative skip within buffer not is allowed, position unchanged. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(4, opj_skip_from_memory(4, &dd)); + EXPECT_EQ(kSkipError, opj_skip_from_memory(-2, &dd)); + + // Next read succeeds as if nothing has happenned. + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x84, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Negative skip before buffer is not allowed, position unchanged. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd)); + + // Next read succeeds as if nothing has happenned. + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x85, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Negative skip way before buffer is not allowed, doesn't wrap + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(4, opj_skip_from_memory(4, &dd)); + EXPECT_EQ(kSkipError, + opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd)); + + // Next read succeeds. If it fails, it may mean we wrapped. + EXPECT_EQ(1, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x84, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + } + { + DecodeData dd(stream_data, sizeof(stream_data)); + + // Negative skip after EOS isn't alowed, still EOS. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ(8, opj_skip_from_memory(8, &dd)); + EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd)); + + // Next read fails. + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + } +} + +TEST(fxcodec, DecodeDataSeek) { + unsigned char buffer[16]; + DecodeData dd(stream_data, sizeof(stream_data)); + + // Seeking within buffer is allowed and read succeeds + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_TRUE(opj_seek_from_memory(1, &dd)); + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x01, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Seeking before start returns error leaving position unchanged. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_FALSE(opj_seek_from_memory(-1, &dd)); + EXPECT_EQ(1, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x02, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Seeking way before start returns error leaving position unchanged. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_FALSE( + opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd)); + EXPECT_EQ(1, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x03, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Seeking exactly to EOS is allowed but read fails. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_TRUE(opj_seek_from_memory(8, &dd)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // Seeking back to zero offset is allowed and read succeeds. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_TRUE(opj_seek_from_memory(0, &dd)); + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x00, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Seeking beyond end of stream is allowed but read fails. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_TRUE(opj_seek_from_memory(16, &dd)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); + + // Seeking within buffer after seek past EOF restores good state. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_TRUE(opj_seek_from_memory(4, &dd)); + EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0x84, buffer[0]); + EXPECT_EQ(0xbd, buffer[1]); + + // Seeking way beyond EOS is allowed, doesn't wrap, and read fails. + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_TRUE(opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd)); + EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd)); + EXPECT_EQ(0xbd, buffer[0]); +} + +TEST(fxcodec, YUV420ToRGB) { + opj_image_comp_t u; + memset(&u, 0, sizeof(u)); + u.dx = 1; + u.dy = 1; + u.w = 16; + u.h = 16; + u.prec = 8; + u.bpp = 8; + opj_image_comp_t v; + memset(&v, 0, sizeof(v)); + v.dx = 1; + v.dy = 1; + v.w = 16; + v.h = 16; + v.prec = 8; + v.bpp = 8; + opj_image_comp_t y; + memset(&y, 0, sizeof(y)); + y.dx = 1; + y.dy = 1; + y.prec = 8; + y.bpp = 8; + opj_image_t img; + memset(&img, 0, sizeof(img)); + img.numcomps = 3; + img.color_space = OPJ_CLRSPC_SYCC; + img.comps = FX_Alloc(opj_image_comp_t, 3); + const struct { + OPJ_UINT32 w; + bool expected; + } cases[] = {{0, false}, {1, false}, {30, false}, {31, true}, + {32, true}, {33, true}, {34, false}, {UINT_MAX, false}}; + for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); ++i) { + y.w = cases[i].w; + y.h = y.w; + img.x1 = y.w; + img.y1 = y.h; + y.data = FX_Alloc(OPJ_INT32, y.w * y.h); + memset(y.data, 1, y.w * y.h * sizeof(OPJ_INT32)); + u.data = FX_Alloc(OPJ_INT32, u.w * u.h); + memset(u.data, 0, u.w * u.h * sizeof(OPJ_INT32)); + v.data = FX_Alloc(OPJ_INT32, v.w * v.h); + memset(v.data, 0, v.w * v.h * sizeof(OPJ_INT32)); + img.comps[0] = y; + img.comps[1] = u; + img.comps[2] = v; + sycc420_to_rgb(&img); + if (cases[i].expected) { + EXPECT_EQ(img.comps[0].w, img.comps[1].w); + EXPECT_EQ(img.comps[0].h, img.comps[1].h); + EXPECT_EQ(img.comps[0].w, img.comps[2].w); + EXPECT_EQ(img.comps[0].h, img.comps[2].h); + } else { + EXPECT_NE(img.comps[0].w, img.comps[1].w); + EXPECT_NE(img.comps[0].h, img.comps[1].h); + EXPECT_NE(img.comps[0].w, img.comps[2].w); + EXPECT_NE(img.comps[0].h, img.comps[2].h); + } + FX_Free(img.comps[0].data); + FX_Free(img.comps[1].data); + FX_Free(img.comps[2].data); + } + FX_Free(img.comps); +} diff --git a/core/fxcodec/codec/fx_codec_png.cpp b/core/fxcodec/codec/fx_codec_png.cpp new file mode 100644 index 0000000000..a3882ba8fb --- /dev/null +++ b/core/fxcodec/codec/fx_codec_png.cpp @@ -0,0 +1,254 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include <algorithm> + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxge/fx_dib.h" + +extern "C" { +#undef FAR +#include "third_party/libpng16/png.h" +} + +static void _png_error_data(png_structp png_ptr, png_const_charp error_msg) { + if (png_get_error_ptr(png_ptr)) { + FXSYS_strncpy((char*)png_get_error_ptr(png_ptr), error_msg, + PNG_ERROR_SIZE - 1); + } + longjmp(png_jmpbuf(png_ptr), 1); +} +static void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {} +static void _png_load_bmp_attribute(png_structp png_ptr, + png_infop info_ptr, + CFX_DIBAttribute* pAttribute) { + if (pAttribute) { +#if defined(PNG_pHYs_SUPPORTED) + pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr); + pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr); + png_uint_32 res_x, res_y; + int unit_type; + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type); + switch (unit_type) { + case PNG_RESOLUTION_METER: + pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER; + break; + default: + pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE; + } +#endif +#if defined(PNG_iCCP_SUPPORTED) + png_charp icc_name; + png_bytep icc_profile; + png_uint_32 icc_proflen; + int compress_type; + png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile, + &icc_proflen); +#endif + int bTime = 0; +#if defined(PNG_tIME_SUPPORTED) + png_timep t = NULL; + png_get_tIME(png_ptr, info_ptr, &t); + if (t) { + FXSYS_memset(pAttribute->m_strTime, 0, sizeof(pAttribute->m_strTime)); + FXSYS_snprintf((FX_CHAR*)pAttribute->m_strTime, + sizeof(pAttribute->m_strTime), "%4d:%2d:%2d %2d:%2d:%2d", + t->year, t->month, t->day, t->hour, t->minute, t->second); + pAttribute->m_strTime[sizeof(pAttribute->m_strTime) - 1] = 0; + bTime = 1; + } +#endif +#if defined(PNG_TEXT_SUPPORTED) + int i; + FX_STRSIZE len; + const FX_CHAR* buf; + int num_text; + png_textp text = NULL; + png_get_text(png_ptr, info_ptr, &text, &num_text); + for (i = 0; i < num_text; i++) { + len = FXSYS_strlen(text[i].key); + buf = "Time"; + if (!FXSYS_memcmp(buf, text[i].key, std::min(len, FXSYS_strlen(buf)))) { + if (!bTime) { + FXSYS_memset(pAttribute->m_strTime, 0, sizeof(pAttribute->m_strTime)); + FXSYS_memcpy( + pAttribute->m_strTime, text[i].text, + std::min(sizeof(pAttribute->m_strTime) - 1, text[i].text_length)); + } + } else { + buf = "Author"; + if (!FXSYS_memcmp(buf, text[i].key, std::min(len, FXSYS_strlen(buf)))) { + pAttribute->m_strAuthor.Empty(); + pAttribute->m_strAuthor.Load((uint8_t*)text[i].text, + (FX_STRSIZE)text[i].text_length); + } + } + } +#endif + } +} +struct FXPNG_Context { + png_structp png_ptr; + png_infop info_ptr; + void* parent_ptr; + void* child_ptr; + + void* (*m_AllocFunc)(unsigned int); + void (*m_FreeFunc)(void*); +}; +extern "C" { +static void* _png_alloc_func(unsigned int size) { + return FX_Alloc(char, size); +} +static void _png_free_func(void* p) { + FX_Free(p); +} +}; +static void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) { + FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr); + if (p == NULL) { + return; + } + CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr; + if (pModule == NULL) { + return; + } + png_uint_32 width = 0, height = 0; + int bpc = 0, color_type = 0, color_type1 = 0, pass = 0; + double gamma = 1.0; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, NULL, + NULL, NULL); + color_type1 = color_type; + if (bpc > 8) { + png_set_strip_16(png_ptr); + } else if (bpc < 8) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + } + bpc = 8; + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + pass = png_set_interlace_handling(png_ptr); + if (!pModule->ReadHeaderCallback(p->child_ptr, width, height, bpc, pass, + &color_type, &gamma)) { + png_error(p->png_ptr, "Read Header Callback Error"); + } + int intent; + if (png_get_sRGB(png_ptr, info_ptr, &intent)) { + png_set_gamma(png_ptr, gamma, 0.45455); + } else { + double image_gamma; + if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) { + png_set_gamma(png_ptr, gamma, image_gamma); + } else { + png_set_gamma(png_ptr, gamma, 0.45455); + } + } + switch (color_type) { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: { + if (color_type1 & PNG_COLOR_MASK_COLOR) { + png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587); + } + } break; + case PNG_COLOR_TYPE_PALETTE: + if (color_type1 != PNG_COLOR_TYPE_PALETTE) { + png_error(p->png_ptr, "Not Support Output Palette Now"); + } + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + if (!(color_type1 & PNG_COLOR_MASK_COLOR)) { + png_set_gray_to_rgb(png_ptr); + } + png_set_bgr(png_ptr); + break; + } + if (!(color_type & PNG_COLOR_MASK_ALPHA)) { + png_set_strip_alpha(png_ptr); + } + if (color_type & PNG_COLOR_MASK_ALPHA && + !(color_type1 & PNG_COLOR_MASK_ALPHA)) { + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + } + png_read_update_info(png_ptr, info_ptr); +} +static void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {} +static void _png_get_row_func(png_structp png_ptr, + png_bytep new_row, + png_uint_32 row_num, + int pass) { + FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr); + if (p == NULL) { + return; + } + CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr; + uint8_t* src_buf = NULL; + if (!pModule->AskScanlineBufCallback(p->child_ptr, row_num, src_buf)) { + png_error(png_ptr, "Ask Scanline buffer Callback Error"); + } + if (src_buf) { + png_progressive_combine_row(png_ptr, src_buf, new_row); + } + pModule->FillScanlineBufCompletedCallback(p->child_ptr, pass, row_num); +} +void* CCodec_PngModule::Start(void* pModule) { + FXPNG_Context* p = (FXPNG_Context*)FX_Alloc(uint8_t, sizeof(FXPNG_Context)); + if (p == NULL) { + return NULL; + } + p->m_AllocFunc = _png_alloc_func; + p->m_FreeFunc = _png_free_func; + p->png_ptr = NULL; + p->info_ptr = NULL; + p->parent_ptr = (void*)this; + p->child_ptr = pModule; + p->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (p->png_ptr == NULL) { + FX_Free(p); + return NULL; + } + p->info_ptr = png_create_info_struct(p->png_ptr); + if (p->info_ptr == NULL) { + png_destroy_read_struct(&(p->png_ptr), (png_infopp)NULL, (png_infopp)NULL); + FX_Free(p); + return NULL; + } + if (setjmp(png_jmpbuf(p->png_ptr))) { + if (p) { + png_destroy_read_struct(&(p->png_ptr), &(p->info_ptr), (png_infopp)NULL); + FX_Free(p); + } + return NULL; + } + png_set_progressive_read_fn(p->png_ptr, p, _png_get_header_func, + _png_get_row_func, _png_get_end_func); + png_set_error_fn(p->png_ptr, m_szLastError, (png_error_ptr)_png_error_data, + (png_error_ptr)_png_warning_data); + return p; +} +void CCodec_PngModule::Finish(void* pContext) { + FXPNG_Context* p = (FXPNG_Context*)pContext; + if (p) { + png_destroy_read_struct(&(p->png_ptr), &(p->info_ptr), (png_infopp)NULL); + p->m_FreeFunc(p); + } +} +FX_BOOL CCodec_PngModule::Input(void* pContext, + const uint8_t* src_buf, + FX_DWORD src_size, + CFX_DIBAttribute* pAttribute) { + FXPNG_Context* p = (FXPNG_Context*)pContext; + if (setjmp(png_jmpbuf(p->png_ptr))) { + if (pAttribute && + 0 == FXSYS_strcmp(m_szLastError, "Read Header Callback Error")) { + _png_load_bmp_attribute(p->png_ptr, p->info_ptr, pAttribute); + } + return FALSE; + } + png_process_data(p->png_ptr, p->info_ptr, (uint8_t*)src_buf, src_size); + return TRUE; +} diff --git a/core/fxcodec/codec/fx_codec_progress.cpp b/core/fxcodec/codec/fx_codec_progress.cpp new file mode 100644 index 0000000000..b22d20339b --- /dev/null +++ b/core/fxcodec/codec/fx_codec_progress.cpp @@ -0,0 +1,2301 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/fx_codec_progress.h" + +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxge/fx_dib.h" + +void CFXCODEC_WeightTable::Calc(int dest_len, + int dest_min, + int dest_max, + int src_len, + int src_min, + int src_max, + FX_BOOL bInterpol) { + if (m_pWeightTables) { + FX_Free(m_pWeightTables); + } + double scale, base; + scale = (FX_FLOAT)src_len / (FX_FLOAT)dest_len; + if (dest_len < 0) { + base = (FX_FLOAT)(src_len); + } else { + base = 0.0f; + } + m_ItemSize = + (int)(sizeof(int) * 2 + + sizeof(int) * (FXSYS_ceil(FXSYS_fabs((FX_FLOAT)scale)) + 1)); + m_DestMin = dest_min; + m_pWeightTables = FX_Alloc(uint8_t, (dest_max - dest_min) * m_ItemSize + 4); + if (m_pWeightTables == NULL) { + return; + } + if (FXSYS_fabs((FX_FLOAT)scale) < 1.0f) { + for (int dest_pixel = dest_min; dest_pixel < dest_max; dest_pixel++) { + PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel); + double src_pos = dest_pixel * scale + scale / 2 + base; + if (bInterpol) { + pixel_weights.m_SrcStart = + (int)FXSYS_floor((FX_FLOAT)src_pos - 1.0f / 2); + pixel_weights.m_SrcEnd = (int)FXSYS_floor((FX_FLOAT)src_pos + 1.0f / 2); + if (pixel_weights.m_SrcStart < src_min) { + pixel_weights.m_SrcStart = src_min; + } + if (pixel_weights.m_SrcEnd >= src_max) { + pixel_weights.m_SrcEnd = src_max - 1; + } + if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) { + pixel_weights.m_Weights[0] = 65536; + } else { + pixel_weights.m_Weights[1] = FXSYS_round( + (FX_FLOAT)(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * + 65536); + pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1]; + } + } else { + pixel_weights.m_SrcStart = pixel_weights.m_SrcEnd = + (int)FXSYS_floor((FX_FLOAT)src_pos); + pixel_weights.m_Weights[0] = 65536; + } + } + return; + } + for (int dest_pixel = dest_min; dest_pixel < dest_max; dest_pixel++) { + PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel); + double src_start = dest_pixel * scale + base; + double src_end = src_start + scale; + int start_i, end_i; + if (src_start < src_end) { + start_i = (int)FXSYS_floor((FX_FLOAT)src_start); + end_i = (int)FXSYS_ceil((FX_FLOAT)src_end); + } else { + start_i = (int)FXSYS_floor((FX_FLOAT)src_end); + end_i = (int)FXSYS_ceil((FX_FLOAT)src_start); + } + if (start_i < src_min) { + start_i = src_min; + } + if (end_i >= src_max) { + end_i = src_max - 1; + } + if (start_i > end_i) { + pixel_weights.m_SrcStart = start_i; + pixel_weights.m_SrcEnd = start_i; + continue; + } + pixel_weights.m_SrcStart = start_i; + pixel_weights.m_SrcEnd = end_i; + for (int j = start_i; j <= end_i; j++) { + double dest_start = ((FX_FLOAT)j - base) / scale; + double dest_end = ((FX_FLOAT)(j + 1) - base) / scale; + if (dest_start > dest_end) { + double temp = dest_start; + dest_start = dest_end; + dest_end = temp; + } + double area_start = dest_start > (FX_FLOAT)(dest_pixel) + ? dest_start + : (FX_FLOAT)(dest_pixel); + double area_end = dest_end > (FX_FLOAT)(dest_pixel + 1) + ? (FX_FLOAT)(dest_pixel + 1) + : dest_end; + double weight = area_start >= area_end ? 0.0f : area_end - area_start; + if (weight == 0 && j == end_i) { + pixel_weights.m_SrcEnd--; + break; + } + pixel_weights.m_Weights[j - start_i] = + FXSYS_round((FX_FLOAT)(weight * 65536)); + } + } +} +void CFXCODEC_HorzTable::Calc(int dest_len, int src_len, FX_BOOL bInterpol) { + if (m_pWeightTables) { + FX_Free(m_pWeightTables); + } + double scale = (double)dest_len / (double)src_len; + m_ItemSize = sizeof(int) * 4; + int size = dest_len * m_ItemSize + 4; + m_pWeightTables = FX_Alloc(uint8_t, size); + if (m_pWeightTables == NULL) { + return; + } + FXSYS_memset(m_pWeightTables, 0, size); + if (scale > 1) { + int pre_des_col = 0; + for (int src_col = 0; src_col < src_len; src_col++) { + double des_col_f = src_col * scale; + int des_col = FXSYS_round((FX_FLOAT)des_col_f); + PixelWeight* pWeight = + (PixelWeight*)(m_pWeightTables + des_col * m_ItemSize); + pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; + pWeight->m_Weights[0] = 65536; + pWeight->m_Weights[1] = 0; + if (src_col == src_len - 1 && des_col < dest_len - 1) { + for (int des_col_index = pre_des_col + 1; des_col_index < dest_len; + des_col_index++) { + pWeight = + (PixelWeight*)(m_pWeightTables + des_col_index * m_ItemSize); + pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; + pWeight->m_Weights[0] = 65536; + pWeight->m_Weights[1] = 0; + } + return; + } + int des_col_len = des_col - pre_des_col; + for (int des_col_index = pre_des_col + 1; des_col_index < des_col; + des_col_index++) { + pWeight = (PixelWeight*)(m_pWeightTables + des_col_index * m_ItemSize); + pWeight->m_SrcStart = src_col - 1; + pWeight->m_SrcEnd = src_col; + pWeight->m_Weights[0] = + bInterpol ? FXSYS_round((FX_FLOAT)( + ((FX_FLOAT)des_col - (FX_FLOAT)des_col_index) / + (FX_FLOAT)des_col_len * 65536)) + : 65536; + pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0]; + } + pre_des_col = des_col; + } + return; + } + for (int des_col = 0; des_col < dest_len; des_col++) { + double src_col_f = des_col / scale; + int src_col = FXSYS_round((FX_FLOAT)src_col_f); + PixelWeight* pWeight = + (PixelWeight*)(m_pWeightTables + des_col * m_ItemSize); + pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; + pWeight->m_Weights[0] = 65536; + pWeight->m_Weights[1] = 0; + } +} +void CFXCODEC_VertTable::Calc(int dest_len, int src_len) { + if (m_pWeightTables) { + FX_Free(m_pWeightTables); + } + double scale = (double)dest_len / (double)src_len; + m_ItemSize = sizeof(int) * 4; + int size = dest_len * m_ItemSize + 4; + m_pWeightTables = FX_Alloc(uint8_t, size); + if (m_pWeightTables == NULL) { + return; + } + FXSYS_memset(m_pWeightTables, 0, size); + if (scale > 1) { + double step = 0.0; + int src_row = 0; + while (step < (double)dest_len) { + int start_step = (int)step; + step = scale * (++src_row); + int end_step = (int)step; + if (end_step >= dest_len) { + end_step = dest_len; + for (int des_row = start_step; des_row < end_step; des_row++) { + PixelWeight* pWeight = + (PixelWeight*)(m_pWeightTables + des_row * m_ItemSize); + pWeight->m_SrcStart = start_step; + pWeight->m_SrcEnd = start_step; + pWeight->m_Weights[0] = 65536; + pWeight->m_Weights[1] = 0; + } + return; + } + int length = end_step - start_step; + { + PixelWeight* pWeight = + (PixelWeight*)(m_pWeightTables + start_step * m_ItemSize); + pWeight->m_SrcStart = start_step; + pWeight->m_SrcEnd = start_step; + pWeight->m_Weights[0] = 65536; + pWeight->m_Weights[1] = 0; + } + for (int des_row = start_step + 1; des_row < end_step; des_row++) { + PixelWeight* pWeight = + (PixelWeight*)(m_pWeightTables + des_row * m_ItemSize); + pWeight->m_SrcStart = start_step; + pWeight->m_SrcEnd = end_step; + pWeight->m_Weights[0] = FXSYS_round((FX_FLOAT)(end_step - des_row) / + (FX_FLOAT)length * 65536); + pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0]; + } + } + } else { + for (int des_row = 0; des_row < dest_len; des_row++) { + PixelWeight* pWeight = + (PixelWeight*)(m_pWeightTables + des_row * m_ItemSize); + pWeight->m_SrcStart = des_row; + pWeight->m_SrcEnd = des_row; + pWeight->m_Weights[0] = 65536; + pWeight->m_Weights[1] = 0; + } + } +} +CCodec_ProgressiveDecoder::CCodec_ProgressiveDecoder( + CCodec_ModuleMgr* pCodecMgr) { + m_pFile = NULL; + m_pJpegContext = NULL; + m_pPngContext = NULL; + m_pGifContext = NULL; + m_pBmpContext = NULL; + m_pTiffContext = NULL; + m_pCodecMgr = NULL; + m_pSrcBuf = NULL; + m_pDecodeBuf = NULL; + m_pDeviceBitmap = NULL; + m_pSrcPalette = NULL; + m_pCodecMgr = pCodecMgr; + m_offSet = 0; + m_SrcSize = 0; + m_ScanlineSize = 0; + m_SrcWidth = m_SrcHeight = 0; + m_SrcComponents = 0; + m_SrcBPC = 0; + m_SrcPassNumber = 0; + m_clipBox = FX_RECT(0, 0, 0, 0); + m_imagType = FXCODEC_IMAGE_UNKNOWN; + m_status = FXCODEC_STATUS_DECODE_FINISH; + m_TransMethod = -1; + m_SrcRow = 0; + m_SrcFormat = FXCodec_Invalid; + m_bInterpol = TRUE; + m_FrameNumber = 0; + m_FrameCur = 0; + m_SrcPaletteNumber = 0; + m_GifPltNumber = 0; + m_GifBgIndex = 0; + m_pGifPalette = NULL; + m_GifTransIndex = -1; + m_GifFrameRect = FX_RECT(0, 0, 0, 0); + m_BmpIsTopBottom = FALSE; +} +CCodec_ProgressiveDecoder::~CCodec_ProgressiveDecoder() { + m_pFile = NULL; + if (m_pJpegContext) { + m_pCodecMgr->GetJpegModule()->Finish(m_pJpegContext); + } + if (m_pPngContext) { + m_pCodecMgr->GetPngModule()->Finish(m_pPngContext); + } + if (m_pGifContext) { + m_pCodecMgr->GetGifModule()->Finish(m_pGifContext); + } + if (m_pBmpContext) { + m_pCodecMgr->GetBmpModule()->Finish(m_pBmpContext); + } + if (m_pTiffContext) { + m_pCodecMgr->GetTiffModule()->DestroyDecoder(m_pTiffContext); + } + FX_Free(m_pSrcBuf); + FX_Free(m_pDecodeBuf); + FX_Free(m_pSrcPalette); +} +FX_BOOL CCodec_ProgressiveDecoder::JpegReadMoreData( + ICodec_JpegModule* pJpegModule, + FXCODEC_STATUS& err_status) { + FX_DWORD dwSize = (FX_DWORD)m_pFile->GetSize(); + if (dwSize <= m_offSet) { + return FALSE; + } + dwSize = dwSize - m_offSet; + FX_DWORD dwAvail = pJpegModule->GetAvailInput(m_pJpegContext, NULL); + if (dwAvail == m_SrcSize) { + if (dwSize > FXCODEC_BLOCK_SIZE) { + dwSize = FXCODEC_BLOCK_SIZE; + } + m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) / + FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE; + m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize); + if (!m_pSrcBuf) { + err_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + } else { + FX_DWORD dwConsume = m_SrcSize - dwAvail; + if (dwAvail) { + FXSYS_memcpy(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail); + } + if (dwSize > dwConsume) { + dwSize = dwConsume; + } + } + if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) { + err_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += dwSize; + pJpegModule->Input(m_pJpegContext, m_pSrcBuf, dwSize + dwAvail); + return TRUE; +} +FX_BOOL CCodec_ProgressiveDecoder::PngReadHeaderFunc(void* pModule, + int width, + int height, + int bpc, + int pass, + int* color_type, + double* gamma) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + if (pCodec->m_pDeviceBitmap == NULL) { + pCodec->m_SrcWidth = width; + pCodec->m_SrcHeight = height; + pCodec->m_SrcBPC = bpc; + pCodec->m_SrcPassNumber = pass; + pCodec->m_SrcComponents = + *color_type == 0 ? 1 : *color_type == 2 + ? 3 + : *color_type == 3 + ? 4 + : *color_type == 4 + ? 2 + : *color_type == 6 ? 4 : 0; + pCodec->m_clipBox = FX_RECT(0, 0, width, height); + return FALSE; + } + FXDIB_Format format = pCodec->m_pDeviceBitmap->GetFormat(); + switch (format) { + case FXDIB_1bppMask: + case FXDIB_1bppRgb: + ASSERT(FALSE); + return FALSE; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: + *color_type = 0; + break; + case FXDIB_Rgb: + *color_type = 2; + break; + case FXDIB_Rgb32: + case FXDIB_Argb: + *color_type = 6; + break; + default: + ASSERT(FALSE); + return FALSE; + } + *gamma = FXCODEC_PNG_GAMMA; + return TRUE; +} +FX_BOOL CCodec_ProgressiveDecoder::PngAskScanlineBufFunc(void* pModule, + int line, + uint8_t*& src_buf) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + CFX_DIBitmap* pDIBitmap = pCodec->m_pDeviceBitmap; + if (!pDIBitmap) { + ASSERT(false); + return FALSE; + } + if (line >= pCodec->m_clipBox.top && line < pCodec->m_clipBox.bottom) { + double scale_y = + (double)pCodec->m_sizeY / (double)pCodec->m_clipBox.Height(); + int32_t row = + (int32_t)((line - pCodec->m_clipBox.top) * scale_y) + pCodec->m_startY; + uint8_t* src_scan = (uint8_t*)pDIBitmap->GetScanline(row); + uint8_t* des_scan = pCodec->m_pDecodeBuf; + src_buf = pCodec->m_pDecodeBuf; + int32_t src_Bpp = pDIBitmap->GetBPP() >> 3; + int32_t des_Bpp = (pCodec->m_SrcFormat & 0xff) >> 3; + int32_t src_left = pCodec->m_startX; + int32_t des_left = pCodec->m_clipBox.left; + src_scan += src_left * src_Bpp; + des_scan += des_left * des_Bpp; + for (int32_t src_col = 0; src_col < pCodec->m_sizeX; src_col++) { + PixelWeight* pPixelWeights = + pCodec->m_WeightHorzOO.GetPixelWeight(src_col); + if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) { + continue; + } + switch (pDIBitmap->GetFormat()) { + case FXDIB_1bppMask: + case FXDIB_1bppRgb: + ASSERT(FALSE); + return FALSE; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: { + if (pDIBitmap->GetPalette()) { + return FALSE; + } + FX_DWORD des_g = 0; + des_g += pPixelWeights->m_Weights[0] * src_scan[src_col]; + des_scan[pPixelWeights->m_SrcStart] = (uint8_t)(des_g >> 16); + } break; + case FXDIB_Rgb: + case FXDIB_Rgb32: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + const uint8_t* p = src_scan + src_col * src_Bpp; + des_b += pPixelWeights->m_Weights[0] * (*p++); + des_g += pPixelWeights->m_Weights[0] * (*p++); + des_r += pPixelWeights->m_Weights[0] * (*p); + uint8_t* pDes = &des_scan[pPixelWeights->m_SrcStart * des_Bpp]; + *pDes++ = (uint8_t)((des_b) >> 16); + *pDes++ = (uint8_t)((des_g) >> 16); + *pDes = (uint8_t)((des_r) >> 16); + } break; + case FXDIB_Argb: { + FX_DWORD des_r = 0, des_g = 0, des_b = 0; + const uint8_t* p = src_scan + src_col * src_Bpp; + des_b += pPixelWeights->m_Weights[0] * (*p++); + des_g += pPixelWeights->m_Weights[0] * (*p++); + des_r += pPixelWeights->m_Weights[0] * (*p++); + uint8_t* pDes = &des_scan[pPixelWeights->m_SrcStart * des_Bpp]; + *pDes++ = (uint8_t)((des_b) >> 16); + *pDes++ = (uint8_t)((des_g) >> 16); + *pDes++ = (uint8_t)((des_r) >> 16); + *pDes = *p; + } break; + default: + return FALSE; + } + } + } + return TRUE; +} +void CCodec_ProgressiveDecoder::PngOneOneMapResampleHorz( + CFX_DIBitmap* pDeviceBitmap, + int32_t des_line, + uint8_t* src_scan, + FXCodec_Format src_format) { + uint8_t* des_scan = (uint8_t*)pDeviceBitmap->GetScanline(des_line); + int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3; + int32_t des_Bpp = pDeviceBitmap->GetBPP() >> 3; + int32_t src_left = m_clipBox.left; + int32_t des_left = m_startX; + src_scan += src_left * src_Bpp; + des_scan += des_left * des_Bpp; + for (int32_t des_col = 0; des_col < m_sizeX; des_col++) { + PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(des_col); + switch (pDeviceBitmap->GetFormat()) { + case FXDIB_1bppMask: + case FXDIB_1bppRgb: + ASSERT(FALSE); + return; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: { + if (pDeviceBitmap->GetPalette()) { + return; + } + FX_DWORD des_g = 0; + des_g += + pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart]; + des_g += + pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd]; + *des_scan++ = (uint8_t)(des_g >> 16); + } break; + case FXDIB_Rgb: + case FXDIB_Rgb32: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + const uint8_t* p = src_scan; + p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; + des_b += pPixelWeights->m_Weights[0] * (*p++); + des_g += pPixelWeights->m_Weights[0] * (*p++); + des_r += pPixelWeights->m_Weights[0] * (*p); + p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp; + des_b += pPixelWeights->m_Weights[1] * (*p++); + des_g += pPixelWeights->m_Weights[1] * (*p++); + des_r += pPixelWeights->m_Weights[1] * (*p); + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + des_scan += des_Bpp - 3; + } break; + case FXDIB_Argb: { + FX_DWORD des_a = 0, des_b = 0, des_g = 0, des_r = 0; + const uint8_t* p = src_scan; + p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; + des_b += pPixelWeights->m_Weights[0] * (*p++); + des_g += pPixelWeights->m_Weights[0] * (*p++); + des_r += pPixelWeights->m_Weights[0] * (*p++); + des_a += pPixelWeights->m_Weights[0] * (*p); + p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp; + des_b += pPixelWeights->m_Weights[1] * (*p++); + des_g += pPixelWeights->m_Weights[1] * (*p++); + des_r += pPixelWeights->m_Weights[1] * (*p++); + des_a += pPixelWeights->m_Weights[1] * (*p); + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + *des_scan++ = (uint8_t)((des_a) >> 16); + } break; + default: + return; + } + } +} +void CCodec_ProgressiveDecoder::PngFillScanlineBufCompletedFunc(void* pModule, + int pass, + int line) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + CFX_DIBitmap* pDIBitmap = pCodec->m_pDeviceBitmap; + ASSERT(pDIBitmap); + int src_top = pCodec->m_clipBox.top; + int src_bottom = pCodec->m_clipBox.bottom; + int des_top = pCodec->m_startY; + int src_hei = pCodec->m_clipBox.Height(); + int des_hei = pCodec->m_sizeY; + if (line >= src_top && line < src_bottom) { + double scale_y = (double)des_hei / (double)src_hei; + int src_row = line - src_top; + int des_row = (int)(src_row * scale_y) + des_top; + if (des_row >= des_top + des_hei) { + return; + } + pCodec->PngOneOneMapResampleHorz(pDIBitmap, des_row, pCodec->m_pDecodeBuf, + pCodec->m_SrcFormat); + if (pCodec->m_SrcPassNumber == 1 && scale_y > 1.0) { + pCodec->ResampleVert(pDIBitmap, scale_y, des_row); + return; + } + if (pass == 6 && scale_y > 1.0) { + pCodec->ResampleVert(pDIBitmap, scale_y, des_row); + } + } +} +FX_BOOL CCodec_ProgressiveDecoder::GifReadMoreData(ICodec_GifModule* pGifModule, + FXCODEC_STATUS& err_status) { + FX_DWORD dwSize = (FX_DWORD)m_pFile->GetSize(); + if (dwSize <= m_offSet) { + return FALSE; + } + dwSize = dwSize - m_offSet; + FX_DWORD dwAvail = pGifModule->GetAvailInput(m_pGifContext, NULL); + if (dwAvail == m_SrcSize) { + if (dwSize > FXCODEC_BLOCK_SIZE) { + dwSize = FXCODEC_BLOCK_SIZE; + } + m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) / + FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE; + m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize); + if (!m_pSrcBuf) { + err_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + } else { + FX_DWORD dwConsume = m_SrcSize - dwAvail; + if (dwAvail) { + FXSYS_memcpy(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail); + } + if (dwSize > dwConsume) { + dwSize = dwConsume; + } + } + if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) { + err_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += dwSize; + pGifModule->Input(m_pGifContext, m_pSrcBuf, dwSize + dwAvail); + return TRUE; +} +void CCodec_ProgressiveDecoder::GifRecordCurrentPositionCallback( + void* pModule, + FX_DWORD& cur_pos) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + FX_DWORD remain_size = + pCodec->m_pCodecMgr->GetGifModule()->GetAvailInput(pCodec->m_pGifContext); + cur_pos = pCodec->m_offSet - remain_size; +} +uint8_t* CCodec_ProgressiveDecoder::GifAskLocalPaletteBufCallback( + void* pModule, + int32_t frame_num, + int32_t pal_size) { + return FX_Alloc(uint8_t, pal_size); +} +FX_BOOL CCodec_ProgressiveDecoder::GifInputRecordPositionBufCallback( + void* pModule, + FX_DWORD rcd_pos, + const FX_RECT& img_rc, + int32_t pal_num, + void* pal_ptr, + int32_t delay_time, + FX_BOOL user_input, + int32_t trans_index, + int32_t disposal_method, + FX_BOOL interlace) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + pCodec->m_offSet = rcd_pos; + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR; + if (!pCodec->GifReadMoreData(pCodec->m_pCodecMgr->GetGifModule(), + error_status)) { + return FALSE; + } + uint8_t* pPalette = NULL; + if (pal_num != 0 && pal_ptr) { + pPalette = (uint8_t*)pal_ptr; + } else { + pal_num = pCodec->m_GifPltNumber; + pPalette = pCodec->m_pGifPalette; + } + if (pCodec->m_pSrcPalette == NULL) { + pCodec->m_pSrcPalette = FX_Alloc(FX_ARGB, pal_num); + } else if (pal_num > pCodec->m_SrcPaletteNumber) { + pCodec->m_pSrcPalette = FX_Realloc(FX_ARGB, pCodec->m_pSrcPalette, pal_num); + } + if (pCodec->m_pSrcPalette == NULL) { + return FALSE; + } + pCodec->m_SrcPaletteNumber = pal_num; + for (int i = 0; i < pal_num; i++) { + FX_DWORD j = i * 3; + pCodec->m_pSrcPalette[i] = + ArgbEncode(0xff, pPalette[j], pPalette[j + 1], pPalette[j + 2]); + } + pCodec->m_GifTransIndex = trans_index; + pCodec->m_GifFrameRect = img_rc; + pCodec->m_SrcPassNumber = interlace ? 4 : 1; + int32_t pal_index = pCodec->m_GifBgIndex; + CFX_DIBitmap* pDevice = pCodec->m_pDeviceBitmap; + if (trans_index >= pal_num) { + trans_index = -1; + } + if (trans_index != -1) { + pCodec->m_pSrcPalette[trans_index] &= 0x00ffffff; + if (pDevice->HasAlpha()) { + pal_index = trans_index; + } + } + int startX = pCodec->m_startX; + int startY = pCodec->m_startY; + int sizeX = pCodec->m_sizeX; + int sizeY = pCodec->m_sizeY; + int Bpp = pDevice->GetBPP() / 8; + FX_ARGB argb = pCodec->m_pSrcPalette[pal_index]; + for (int row = 0; row < sizeY; row++) { + uint8_t* pScanline = + (uint8_t*)pDevice->GetScanline(row + startY) + startX * Bpp; + switch (pCodec->m_TransMethod) { + case 3: { + uint8_t gray = + FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb)); + FXSYS_memset(pScanline, gray, sizeX); + break; + } + case 8: { + for (int col = 0; col < sizeX; col++) { + *pScanline++ = FXARGB_B(argb); + *pScanline++ = FXARGB_G(argb); + *pScanline++ = FXARGB_R(argb); + pScanline += Bpp - 3; + } + break; + } + case 12: { + for (int col = 0; col < sizeX; col++) { + FXARGB_SETDIB(pScanline, argb); + pScanline += 4; + } + break; + } + } + } + return TRUE; +} +void CCodec_ProgressiveDecoder::GifReadScanlineCallback(void* pModule, + int32_t row_num, + uint8_t* row_buf) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + CFX_DIBitmap* pDIBitmap = pCodec->m_pDeviceBitmap; + ASSERT(pDIBitmap); + int32_t img_width = pCodec->m_GifFrameRect.Width(); + if (!pDIBitmap->HasAlpha()) { + uint8_t* byte_ptr = row_buf; + for (int i = 0; i < img_width; i++) { + if (*byte_ptr == pCodec->m_GifTransIndex) { + *byte_ptr = pCodec->m_GifBgIndex; + } + byte_ptr++; + } + } + int32_t pal_index = pCodec->m_GifBgIndex; + if (pCodec->m_GifTransIndex != -1 && pCodec->m_pDeviceBitmap->HasAlpha()) { + pal_index = pCodec->m_GifTransIndex; + } + FXSYS_memset(pCodec->m_pDecodeBuf, pal_index, pCodec->m_SrcWidth); + bool bLastPass = (row_num % 2) == 1; + int32_t line = row_num + pCodec->m_GifFrameRect.top; + int32_t left = pCodec->m_GifFrameRect.left; + FXSYS_memcpy(pCodec->m_pDecodeBuf + left, row_buf, img_width); + int src_top = pCodec->m_clipBox.top; + int src_bottom = pCodec->m_clipBox.bottom; + int des_top = pCodec->m_startY; + int src_hei = pCodec->m_clipBox.Height(); + int des_hei = pCodec->m_sizeY; + if (line >= src_top && line < src_bottom) { + double scale_y = (double)des_hei / (double)src_hei; + int src_row = line - src_top; + int des_row = (int)(src_row * scale_y) + des_top; + if (des_row >= des_top + des_hei) { + return; + } + pCodec->ReSampleScanline(pDIBitmap, des_row, pCodec->m_pDecodeBuf, + pCodec->m_SrcFormat); + if (scale_y > 1.0 && + (!pCodec->m_bInterpol || pCodec->m_SrcPassNumber == 1)) { + pCodec->ResampleVert(pDIBitmap, scale_y, des_row); + return; + } + if (scale_y > 1.0) { + int des_bottom = des_top + pCodec->m_sizeY; + int des_Bpp = pDIBitmap->GetBPP() >> 3; + FX_DWORD des_ScanOffet = pCodec->m_startX * des_Bpp; + if (des_row + (int)scale_y >= des_bottom - 1) { + uint8_t* scan_src = + (uint8_t*)pDIBitmap->GetScanline(des_row) + des_ScanOffet; + int cur_row = des_row; + while (++cur_row < des_bottom) { + uint8_t* scan_des = + (uint8_t*)pDIBitmap->GetScanline(cur_row) + des_ScanOffet; + FX_DWORD size = pCodec->m_sizeX * des_Bpp; + FXSYS_memcpy(scan_des, scan_src, size); + } + } + if (bLastPass) { + pCodec->GifDoubleLineResampleVert(pDIBitmap, scale_y, des_row); + } + } + } +} +void CCodec_ProgressiveDecoder::GifDoubleLineResampleVert( + CFX_DIBitmap* pDeviceBitmap, + double scale_y, + int des_row) { + int des_Bpp = pDeviceBitmap->GetBPP() >> 3; + FX_DWORD des_ScanOffet = m_startX * des_Bpp; + int des_top = m_startY; + int des_row_1 = des_row - int(2 * scale_y); + if (des_row_1 < des_top) { + des_row_1 = des_top; + } + for (; des_row_1 < des_row; des_row_1++) { + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet; + PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top); + const uint8_t* scan_src1 = + pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) + + des_ScanOffet; + const uint8_t* scan_src2 = + pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet; + for (int des_col = 0; des_col < m_sizeX; des_col++) { + switch (pDeviceBitmap->GetFormat()) { + case FXDIB_Invalid: + case FXDIB_1bppMask: + case FXDIB_1bppRgb: + return; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: { + if (pDeviceBitmap->GetPalette()) { + return; + } + int des_g = 0; + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + *scan_des++ = (uint8_t)(des_g >> 16); + } break; + case FXDIB_Rgb: + case FXDIB_Rgb32: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + des_b += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_r += pWeight->m_Weights[0] * (*scan_src1++); + scan_src1 += des_Bpp - 3; + des_b += pWeight->m_Weights[1] * (*scan_src2++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + des_r += pWeight->m_Weights[1] * (*scan_src2++); + scan_src2 += des_Bpp - 3; + *scan_des++ = (uint8_t)((des_b) >> 16); + *scan_des++ = (uint8_t)((des_g) >> 16); + *scan_des++ = (uint8_t)((des_r) >> 16); + scan_des += des_Bpp - 3; + } break; + case FXDIB_Argb: { + FX_DWORD des_a = 0, des_b = 0, des_g = 0, des_r = 0; + des_b += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_r += pWeight->m_Weights[0] * (*scan_src1++); + des_a += pWeight->m_Weights[0] * (*scan_src1++); + des_b += pWeight->m_Weights[1] * (*scan_src2++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + des_r += pWeight->m_Weights[1] * (*scan_src2++); + des_a += pWeight->m_Weights[1] * (*scan_src2++); + *scan_des++ = (uint8_t)((des_b) >> 16); + *scan_des++ = (uint8_t)((des_g) >> 16); + *scan_des++ = (uint8_t)((des_r) >> 16); + *scan_des++ = (uint8_t)((des_a) >> 16); + } break; + default: + return; + } + } + } + int des_bottom = des_top + m_sizeY - 1; + if (des_row + (int)(2 * scale_y) >= des_bottom && + des_row + (int)scale_y < des_bottom) { + GifDoubleLineResampleVert(pDeviceBitmap, scale_y, des_row + (int)scale_y); + } +} +FX_BOOL CCodec_ProgressiveDecoder::BmpReadMoreData(ICodec_BmpModule* pBmpModule, + FXCODEC_STATUS& err_status) { + FX_DWORD dwSize = (FX_DWORD)m_pFile->GetSize(); + if (dwSize <= m_offSet) { + return FALSE; + } + dwSize = dwSize - m_offSet; + FX_DWORD dwAvail = pBmpModule->GetAvailInput(m_pBmpContext, NULL); + if (dwAvail == m_SrcSize) { + if (dwSize > FXCODEC_BLOCK_SIZE) { + dwSize = FXCODEC_BLOCK_SIZE; + } + m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) / + FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE; + m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize); + if (!m_pSrcBuf) { + err_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + } else { + FX_DWORD dwConsume = m_SrcSize - dwAvail; + if (dwAvail) { + FXSYS_memcpy(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail); + } + if (dwSize > dwConsume) { + dwSize = dwConsume; + } + } + if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) { + err_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += dwSize; + pBmpModule->Input(m_pBmpContext, m_pSrcBuf, dwSize + dwAvail); + return TRUE; +} +FX_BOOL CCodec_ProgressiveDecoder::BmpInputImagePositionBufCallback( + void* pModule, + FX_DWORD rcd_pos) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + pCodec->m_offSet = rcd_pos; + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR; + if (!pCodec->BmpReadMoreData(pCodec->m_pCodecMgr->GetBmpModule(), + error_status)) { + return FALSE; + } + return TRUE; +} +void CCodec_ProgressiveDecoder::BmpReadScanlineCallback(void* pModule, + int32_t row_num, + uint8_t* row_buf) { + CCodec_ProgressiveDecoder* pCodec = (CCodec_ProgressiveDecoder*)pModule; + CFX_DIBitmap* pDIBitmap = pCodec->m_pDeviceBitmap; + ASSERT(pDIBitmap); + FXSYS_memcpy(pCodec->m_pDecodeBuf, row_buf, pCodec->m_ScanlineSize); + int src_top = pCodec->m_clipBox.top; + int src_bottom = pCodec->m_clipBox.bottom; + int des_top = pCodec->m_startY; + int src_hei = pCodec->m_clipBox.Height(); + int des_hei = pCodec->m_sizeY; + if (row_num >= src_top && row_num < src_bottom) { + double scale_y = (double)des_hei / (double)src_hei; + int src_row = row_num - src_top; + int des_row = (int)(src_row * scale_y) + des_top; + if (des_row >= des_top + des_hei) { + return; + } + pCodec->ReSampleScanline(pDIBitmap, des_row, pCodec->m_pDecodeBuf, + pCodec->m_SrcFormat); + if (scale_y > 1.0) { + if (pCodec->m_BmpIsTopBottom || !pCodec->m_bInterpol) { + pCodec->ResampleVert(pDIBitmap, scale_y, des_row); + return; + } else { + pCodec->ResampleVertBT(pDIBitmap, scale_y, des_row); + } + } + } +} +void CCodec_ProgressiveDecoder::ResampleVertBT(CFX_DIBitmap* pDeviceBitmap, + double scale_y, + int des_row) { + int des_Bpp = pDeviceBitmap->GetBPP() >> 3; + FX_DWORD des_ScanOffet = m_startX * des_Bpp; + int des_top = m_startY; + int des_bottom = m_startY + m_sizeY; + int des_row_1 = des_row + int(scale_y); + if (des_row_1 >= des_bottom - 1) { + uint8_t* scan_src = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + while (++des_row < des_bottom) { + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + FX_DWORD size = m_sizeX * des_Bpp; + FXSYS_memcpy(scan_des, scan_src, size); + } + return; + } + for (; des_row_1 > des_row; des_row_1--) { + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet; + PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top); + const uint8_t* scan_src1 = + pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) + + des_ScanOffet; + const uint8_t* scan_src2 = + pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet; + for (int des_col = 0; des_col < m_sizeX; des_col++) { + switch (pDeviceBitmap->GetFormat()) { + case FXDIB_Invalid: + case FXDIB_1bppMask: + case FXDIB_1bppRgb: + return; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: { + if (pDeviceBitmap->GetPalette()) { + return; + } + int des_g = 0; + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + *scan_des++ = (uint8_t)(des_g >> 16); + } break; + case FXDIB_Rgb: + case FXDIB_Rgb32: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + des_b += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_r += pWeight->m_Weights[0] * (*scan_src1++); + scan_src1 += des_Bpp - 3; + des_b += pWeight->m_Weights[1] * (*scan_src2++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + des_r += pWeight->m_Weights[1] * (*scan_src2++); + scan_src2 += des_Bpp - 3; + *scan_des++ = (uint8_t)((des_b) >> 16); + *scan_des++ = (uint8_t)((des_g) >> 16); + *scan_des++ = (uint8_t)((des_r) >> 16); + scan_des += des_Bpp - 3; + } break; + case FXDIB_Argb: { + FX_DWORD des_a = 0, des_b = 0, des_g = 0, des_r = 0; + des_b += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_r += pWeight->m_Weights[0] * (*scan_src1++); + des_a += pWeight->m_Weights[0] * (*scan_src1++); + des_b += pWeight->m_Weights[1] * (*scan_src2++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + des_r += pWeight->m_Weights[1] * (*scan_src2++); + des_a += pWeight->m_Weights[1] * (*scan_src2++); + *scan_des++ = (uint8_t)((des_b) >> 16); + *scan_des++ = (uint8_t)((des_g) >> 16); + *scan_des++ = (uint8_t)((des_r) >> 16); + *scan_des++ = (uint8_t)((des_a) >> 16); + } break; + default: + return; + } + } + } +} +FX_BOOL CCodec_ProgressiveDecoder::DetectImageType( + FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute) { + m_offSet = 0; + FX_DWORD size = (FX_DWORD)m_pFile->GetSize(); + if (size > FXCODEC_BLOCK_SIZE) { + size = FXCODEC_BLOCK_SIZE; + } + FX_Free(m_pSrcBuf); + m_pSrcBuf = FX_Alloc(uint8_t, size); + FXSYS_memset(m_pSrcBuf, 0, size); + m_SrcSize = size; + switch (imageType) { + case FXCODEC_IMAGE_BMP: { + ICodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule(); + if (pBmpModule == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + pBmpModule->InputImagePositionBufCallback = + BmpInputImagePositionBufCallback; + pBmpModule->ReadScanlineCallback = BmpReadScanlineCallback; + m_pBmpContext = pBmpModule->Start((void*)this); + if (m_pBmpContext == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + FX_BOOL bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size); + if (!bResult) { + m_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += size; + pBmpModule->Input(m_pBmpContext, m_pSrcBuf, size); + FX_DWORD* pPalette = NULL; + int32_t readResult = pBmpModule->ReadHeader( + m_pBmpContext, &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom, + &m_SrcComponents, &m_SrcPaletteNumber, &pPalette, pAttribute); + while (readResult == 2) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT; + if (!BmpReadMoreData(pBmpModule, error_status)) { + m_status = error_status; + return FALSE; + } + readResult = pBmpModule->ReadHeader( + m_pBmpContext, &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom, + &m_SrcComponents, &m_SrcPaletteNumber, &pPalette, pAttribute); + } + if (readResult == 1) { + m_SrcBPC = 8; + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + FX_Free(m_pSrcPalette); + if (m_SrcPaletteNumber) { + m_pSrcPalette = FX_Alloc(FX_ARGB, m_SrcPaletteNumber); + FXSYS_memcpy(m_pSrcPalette, pPalette, + m_SrcPaletteNumber * sizeof(FX_DWORD)); + } else { + m_pSrcPalette = nullptr; + } + return TRUE; + } + if (m_pBmpContext) { + pBmpModule->Finish(m_pBmpContext); + m_pBmpContext = NULL; + } + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } break; + case FXCODEC_IMAGE_JPG: { + ICodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); + if (pJpegModule == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + m_pJpegContext = pJpegModule->Start(); + if (m_pJpegContext == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + FX_BOOL bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size); + if (!bResult) { + m_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += size; + pJpegModule->Input(m_pJpegContext, m_pSrcBuf, size); + int32_t readResult = + pJpegModule->ReadHeader(m_pJpegContext, &m_SrcWidth, &m_SrcHeight, + &m_SrcComponents, pAttribute); + while (readResult == 2) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT; + if (!JpegReadMoreData(pJpegModule, error_status)) { + m_status = error_status; + return FALSE; + } + readResult = + pJpegModule->ReadHeader(m_pJpegContext, &m_SrcWidth, &m_SrcHeight, + &m_SrcComponents, pAttribute); + } + if (!readResult) { + m_SrcBPC = 8; + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + return TRUE; + } + if (m_pJpegContext) { + pJpegModule->Finish(m_pJpegContext); + m_pJpegContext = NULL; + } + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } break; + case FXCODEC_IMAGE_PNG: { + ICodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule(); + if (pPngModule == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + pPngModule->ReadHeaderCallback = + CCodec_ProgressiveDecoder::PngReadHeaderFunc; + pPngModule->AskScanlineBufCallback = + CCodec_ProgressiveDecoder::PngAskScanlineBufFunc; + pPngModule->FillScanlineBufCompletedCallback = + CCodec_ProgressiveDecoder::PngFillScanlineBufCompletedFunc; + m_pPngContext = pPngModule->Start((void*)this); + if (m_pPngContext == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + FX_BOOL bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size); + if (!bResult) { + m_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += size; + bResult = pPngModule->Input(m_pPngContext, m_pSrcBuf, size, pAttribute); + while (bResult) { + FX_DWORD remain_size = (FX_DWORD)m_pFile->GetSize() - m_offSet; + FX_DWORD input_size = + remain_size > FXCODEC_BLOCK_SIZE ? FXCODEC_BLOCK_SIZE : remain_size; + if (input_size == 0) { + if (m_pPngContext) { + pPngModule->Finish(m_pPngContext); + } + m_pPngContext = NULL; + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } + if (m_pSrcBuf && input_size > m_SrcSize) { + FX_Free(m_pSrcBuf); + m_pSrcBuf = FX_Alloc(uint8_t, input_size); + FXSYS_memset(m_pSrcBuf, 0, input_size); + m_SrcSize = input_size; + } + bResult = m_pFile->ReadBlock(m_pSrcBuf, m_offSet, input_size); + if (!bResult) { + m_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += input_size; + bResult = + pPngModule->Input(m_pPngContext, m_pSrcBuf, input_size, pAttribute); + } + ASSERT(!bResult); + if (m_pPngContext) { + pPngModule->Finish(m_pPngContext); + m_pPngContext = NULL; + } + if (m_SrcPassNumber == 0) { + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } + } break; + case FXCODEC_IMAGE_GIF: { + ICodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule(); + if (pGifModule == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + pGifModule->RecordCurrentPositionCallback = + CCodec_ProgressiveDecoder::GifRecordCurrentPositionCallback; + pGifModule->AskLocalPaletteBufCallback = + CCodec_ProgressiveDecoder::GifAskLocalPaletteBufCallback; + pGifModule->InputRecordPositionBufCallback = + CCodec_ProgressiveDecoder::GifInputRecordPositionBufCallback; + pGifModule->ReadScanlineCallback = + CCodec_ProgressiveDecoder::GifReadScanlineCallback; + m_pGifContext = pGifModule->Start((void*)this); + if (m_pGifContext == NULL) { + m_status = FXCODEC_STATUS_ERR_MEMORY; + return FALSE; + } + FX_BOOL bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size); + if (!bResult) { + m_status = FXCODEC_STATUS_ERR_READ; + return FALSE; + } + m_offSet += size; + pGifModule->Input(m_pGifContext, m_pSrcBuf, size); + m_SrcComponents = 1; + int32_t readResult = pGifModule->ReadHeader( + m_pGifContext, &m_SrcWidth, &m_SrcHeight, &m_GifPltNumber, + (void**)&m_pGifPalette, &m_GifBgIndex, nullptr); + while (readResult == 2) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT; + if (!GifReadMoreData(pGifModule, error_status)) { + m_status = error_status; + return FALSE; + } + readResult = pGifModule->ReadHeader( + m_pGifContext, &m_SrcWidth, &m_SrcHeight, &m_GifPltNumber, + (void**)&m_pGifPalette, &m_GifBgIndex, nullptr); + } + if (readResult == 1) { + m_SrcBPC = 8; + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + return TRUE; + } + if (m_pGifContext) { + pGifModule->Finish(m_pGifContext); + m_pGifContext = NULL; + } + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } break; + case FXCODEC_IMAGE_TIF: { + ICodec_TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule(); + if (pTiffModule == NULL) { + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } + m_pTiffContext = pTiffModule->CreateDecoder(m_pFile); + if (m_pTiffContext == NULL) { + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } + int32_t frames = 0; + pTiffModule->GetFrames(m_pTiffContext, frames); + FX_DWORD bpc; + FX_BOOL ret = pTiffModule->LoadFrameInfo( + m_pTiffContext, 0, (FX_DWORD&)m_SrcWidth, (FX_DWORD&)m_SrcHeight, + (FX_DWORD&)m_SrcComponents, bpc, pAttribute); + m_SrcComponents = 4; + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + if (!ret) { + pTiffModule->DestroyDecoder(m_pTiffContext); + (m_pTiffContext = NULL); + (m_status = FXCODEC_STATUS_ERR_FORMAT); + return FALSE; + } + } break; + default: + m_status = FXCODEC_STATUS_ERR_FORMAT; + return FALSE; + } + return TRUE; +} +FXCODEC_STATUS CCodec_ProgressiveDecoder::LoadImageInfo( + IFX_FileRead* pFile, + FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute) { + switch (m_status) { + case FXCODEC_STATUS_FRAME_READY: + case FXCODEC_STATUS_FRAME_TOBECONTINUE: + case FXCODEC_STATUS_DECODE_READY: + case FXCODEC_STATUS_DECODE_TOBECONTINUE: + return FXCODEC_STATUS_ERROR; + default: + break; + } + if (pFile == NULL) { + m_status = FXCODEC_STATUS_ERR_PARAMS; + m_pFile = NULL; + return m_status; + } + m_pFile = pFile; + m_offSet = 0; + m_SrcWidth = m_SrcHeight = 0; + m_SrcComponents = m_SrcBPC = 0; + m_clipBox = FX_RECT(0, 0, 0, 0); + m_startX = m_startY = 0; + m_sizeX = m_sizeY = 0; + m_SrcPassNumber = 0; + if (imageType != FXCODEC_IMAGE_UNKNOWN && + DetectImageType(imageType, pAttribute)) { + m_imagType = imageType; + m_status = FXCODEC_STATUS_FRAME_READY; + return m_status; + } + for (int type = FXCODEC_IMAGE_BMP; type < FXCODEC_IMAGE_MAX; type++) { + if (DetectImageType((FXCODEC_IMAGE_TYPE)type, pAttribute)) { + m_imagType = (FXCODEC_IMAGE_TYPE)type; + m_status = FXCODEC_STATUS_FRAME_READY; + return m_status; + } + } + m_status = FXCODEC_STATUS_ERR_FORMAT; + m_pFile = NULL; + return m_status; +} +void CCodec_ProgressiveDecoder::SetClipBox(FX_RECT* clip) { + if (m_status != FXCODEC_STATUS_FRAME_READY) { + return; + } + if (clip->IsEmpty()) { + m_clipBox = FX_RECT(0, 0, 0, 0); + return; + } + if (clip->left < 0) { + clip->left = 0; + } + if (clip->right > m_SrcWidth) { + clip->right = m_SrcWidth; + } + if (clip->top < 0) { + clip->top = 0; + } + if (clip->bottom > m_SrcHeight) { + clip->bottom = m_SrcHeight; + } + if (clip->IsEmpty()) { + m_clipBox = FX_RECT(0, 0, 0, 0); + return; + } + m_clipBox = *clip; +} +void CCodec_ProgressiveDecoder::GetDownScale(int& down_scale) { + down_scale = 1; + int ratio_w = m_clipBox.Width() / m_sizeX; + int ratio_h = m_clipBox.Height() / m_sizeY; + int ratio = (ratio_w > ratio_h) ? ratio_h : ratio_w; + if (ratio >= 8) { + down_scale = 8; + } else if (ratio >= 4) { + down_scale = 4; + } else if (ratio >= 2) { + down_scale = 2; + } + m_clipBox.left /= down_scale; + m_clipBox.right /= down_scale; + m_clipBox.top /= down_scale; + m_clipBox.bottom /= down_scale; + if (m_clipBox.right == m_clipBox.left) { + m_clipBox.right = m_clipBox.left + 1; + } + if (m_clipBox.bottom == m_clipBox.top) { + m_clipBox.bottom = m_clipBox.top + 1; + } +} +void CCodec_ProgressiveDecoder::GetTransMethod(FXDIB_Format des_format, + FXCodec_Format src_format) { + switch (des_format) { + case FXDIB_1bppMask: + case FXDIB_1bppRgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 0; + break; + default: + m_TransMethod = -1; + } + } break; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 1; + break; + case FXCodec_8bppGray: + m_TransMethod = 2; + break; + case FXCodec_1bppRgb: + case FXCodec_8bppRgb: + m_TransMethod = 3; + break; + case FXCodec_Rgb: + case FXCodec_Rgb32: + case FXCodec_Argb: + m_TransMethod = 4; + break; + case FXCodec_Cmyk: + m_TransMethod = 5; + break; + default: + m_TransMethod = -1; + } + } break; + case FXDIB_Rgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 6; + break; + case FXCodec_8bppGray: + m_TransMethod = 7; + break; + case FXCodec_1bppRgb: + case FXCodec_8bppRgb: + m_TransMethod = 8; + break; + case FXCodec_Rgb: + case FXCodec_Rgb32: + case FXCodec_Argb: + m_TransMethod = 9; + break; + case FXCodec_Cmyk: + m_TransMethod = 10; + break; + default: + m_TransMethod = -1; + } + } break; + case FXDIB_Rgb32: + case FXDIB_Argb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 6; + break; + case FXCodec_8bppGray: + m_TransMethod = 7; + break; + case FXCodec_1bppRgb: + case FXCodec_8bppRgb: + if (des_format == FXDIB_Argb) { + m_TransMethod = 12; + } else { + m_TransMethod = 8; + } + break; + case FXCodec_Rgb: + case FXCodec_Rgb32: + m_TransMethod = 9; + break; + case FXCodec_Cmyk: + m_TransMethod = 10; + break; + case FXCodec_Argb: + m_TransMethod = 11; + break; + default: + m_TransMethod = -1; + } + } break; + default: + m_TransMethod = -1; + } +} +void _RGB2BGR(uint8_t* buffer, int width = 1) { + if (buffer && width > 0) { + uint8_t temp; + int i = 0; + int j = 0; + for (; i < width; i++, j += 3) { + temp = buffer[j]; + buffer[j] = buffer[j + 2]; + buffer[j + 2] = temp; + } + } +} +void CCodec_ProgressiveDecoder::ReSampleScanline(CFX_DIBitmap* pDeviceBitmap, + int des_line, + uint8_t* src_scan, + FXCodec_Format src_format) { + int src_left = m_clipBox.left; + int des_left = m_startX; + uint8_t* des_scan = + pDeviceBitmap->GetBuffer() + des_line * pDeviceBitmap->GetPitch(); + int src_bpp = src_format & 0xff; + int des_bpp = pDeviceBitmap->GetBPP(); + int src_Bpp = src_bpp >> 3; + int des_Bpp = des_bpp >> 3; + src_scan += src_left * src_Bpp; + des_scan += des_left * des_Bpp; + for (int des_col = 0; des_col < m_sizeX; des_col++) { + PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(des_col); + switch (m_TransMethod) { + case -1: + return; + case 0: + return; + case 1: + return; + case 2: { + FX_DWORD des_g = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + des_g += pixel_weight * src_scan[j]; + } + *des_scan++ = (uint8_t)(des_g >> 16); + } break; + case 3: { + int des_r = 0, des_g = 0, des_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + unsigned long argb = m_pSrcPalette[src_scan[j]]; + des_r += pixel_weight * (uint8_t)(argb >> 16); + des_g += pixel_weight * (uint8_t)(argb >> 8); + des_b += pixel_weight * (uint8_t)argb; + } + *des_scan++ = + (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16)); + } break; + case 4: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_Bpp; + des_b += pixel_weight * (*src_pixel++); + des_g += pixel_weight * (*src_pixel++); + des_r += pixel_weight * (*src_pixel); + } + *des_scan++ = + (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16)); + } break; + case 5: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_Bpp; + uint8_t src_b = 0, src_g = 0, src_r = 0; + AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1], + 255 - src_pixel[2], 255 - src_pixel[3], src_r, + src_g, src_b); + des_b += pixel_weight * src_b; + des_g += pixel_weight * src_g; + des_r += pixel_weight * src_r; + } + *des_scan++ = + (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16)); + } break; + case 6: + return; + case 7: { + FX_DWORD des_g = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + des_g += pixel_weight * src_scan[j]; + } + FXSYS_memset(des_scan, (uint8_t)(des_g >> 16), 3); + des_scan += des_Bpp; + } break; + case 8: { + int des_r = 0, des_g = 0, des_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + unsigned long argb = m_pSrcPalette[src_scan[j]]; + des_r += pixel_weight * (uint8_t)(argb >> 16); + des_g += pixel_weight * (uint8_t)(argb >> 8); + des_b += pixel_weight * (uint8_t)argb; + } + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + des_scan += des_Bpp - 3; + } break; + case 12: { + if (m_pBmpContext) { + int des_r = 0, des_g = 0, des_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + unsigned long argb = m_pSrcPalette[src_scan[j]]; + des_r += pixel_weight * (uint8_t)(argb >> 16); + des_g += pixel_weight * (uint8_t)(argb >> 8); + des_b += pixel_weight * (uint8_t)argb; + } + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + *des_scan++ = 0xFF; + } else { + int des_a = 0, des_r = 0, des_g = 0, des_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + unsigned long argb = m_pSrcPalette[src_scan[j]]; + des_a += pixel_weight * (uint8_t)(argb >> 24); + des_r += pixel_weight * (uint8_t)(argb >> 16); + des_g += pixel_weight * (uint8_t)(argb >> 8); + des_b += pixel_weight * (uint8_t)argb; + } + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + *des_scan++ = (uint8_t)((des_a) >> 16); + } + } break; + case 9: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_Bpp; + des_b += pixel_weight * (*src_pixel++); + des_g += pixel_weight * (*src_pixel++); + des_r += pixel_weight * (*src_pixel); + } + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + des_scan += des_Bpp - 3; + } break; + case 10: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_Bpp; + uint8_t src_b = 0, src_g = 0, src_r = 0; + AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1], + 255 - src_pixel[2], 255 - src_pixel[3], src_r, + src_g, src_b); + des_b += pixel_weight * src_b; + des_g += pixel_weight * src_g; + des_r += pixel_weight * src_r; + } + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + des_scan += des_Bpp - 3; + } break; + case 11: { + FX_DWORD des_alpha = 0, des_r = 0, des_g = 0, des_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { + int pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_Bpp; + pixel_weight = pixel_weight * src_pixel[3] / 255; + des_b += pixel_weight * (*src_pixel++); + des_g += pixel_weight * (*src_pixel++); + des_r += pixel_weight * (*src_pixel); + des_alpha += pixel_weight; + } + *des_scan++ = (uint8_t)((des_b) >> 16); + *des_scan++ = (uint8_t)((des_g) >> 16); + *des_scan++ = (uint8_t)((des_r) >> 16); + *des_scan++ = (uint8_t)((des_alpha * 255) >> 16); + } break; + default: + return; + } + } +} +void CCodec_ProgressiveDecoder::ResampleVert(CFX_DIBitmap* pDeviceBitmap, + double scale_y, + int des_row) { + int des_Bpp = pDeviceBitmap->GetBPP() >> 3; + FX_DWORD des_ScanOffet = m_startX * des_Bpp; + if (m_bInterpol) { + int des_top = m_startY; + int des_row_1 = des_row - int(scale_y); + if (des_row_1 < des_top) { + int des_bottom = des_top + m_sizeY; + if (des_row + (int)scale_y >= des_bottom - 1) { + uint8_t* scan_src = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + while (++des_row < des_bottom) { + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + FX_DWORD size = m_sizeX * des_Bpp; + FXSYS_memcpy(scan_des, scan_src, size); + } + } + return; + } + for (; des_row_1 < des_row; des_row_1++) { + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet; + PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top); + const uint8_t* scan_src1 = + pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) + + des_ScanOffet; + const uint8_t* scan_src2 = + pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + + des_ScanOffet; + for (int des_col = 0; des_col < m_sizeX; des_col++) { + switch (pDeviceBitmap->GetFormat()) { + case FXDIB_Invalid: + case FXDIB_1bppMask: + case FXDIB_1bppRgb: + return; + case FXDIB_8bppMask: + case FXDIB_8bppRgb: { + if (pDeviceBitmap->GetPalette()) { + return; + } + int des_g = 0; + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + *scan_des++ = (uint8_t)(des_g >> 16); + } break; + case FXDIB_Rgb: + case FXDIB_Rgb32: { + FX_DWORD des_b = 0, des_g = 0, des_r = 0; + des_b += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_r += pWeight->m_Weights[0] * (*scan_src1++); + scan_src1 += des_Bpp - 3; + des_b += pWeight->m_Weights[1] * (*scan_src2++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + des_r += pWeight->m_Weights[1] * (*scan_src2++); + scan_src2 += des_Bpp - 3; + *scan_des++ = (uint8_t)((des_b) >> 16); + *scan_des++ = (uint8_t)((des_g) >> 16); + *scan_des++ = (uint8_t)((des_r) >> 16); + scan_des += des_Bpp - 3; + } break; + case FXDIB_Argb: { + FX_DWORD des_a = 0, des_b = 0, des_g = 0, des_r = 0; + des_b += pWeight->m_Weights[0] * (*scan_src1++); + des_g += pWeight->m_Weights[0] * (*scan_src1++); + des_r += pWeight->m_Weights[0] * (*scan_src1++); + des_a += pWeight->m_Weights[0] * (*scan_src1++); + des_b += pWeight->m_Weights[1] * (*scan_src2++); + des_g += pWeight->m_Weights[1] * (*scan_src2++); + des_r += pWeight->m_Weights[1] * (*scan_src2++); + des_a += pWeight->m_Weights[1] * (*scan_src2++); + *scan_des++ = (uint8_t)((des_b) >> 16); + *scan_des++ = (uint8_t)((des_g) >> 16); + *scan_des++ = (uint8_t)((des_r) >> 16); + *scan_des++ = (uint8_t)((des_a) >> 16); + } break; + default: + return; + } + } + } + int des_bottom = des_top + m_sizeY; + if (des_row + (int)scale_y >= des_bottom - 1) { + uint8_t* scan_src = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + while (++des_row < des_bottom) { + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + FX_DWORD size = m_sizeX * des_Bpp; + FXSYS_memcpy(scan_des, scan_src, size); + } + } + return; + } + int multiple = (int)FXSYS_ceil((FX_FLOAT)scale_y - 1); + if (multiple > 0) { + uint8_t* scan_src = + (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet; + for (int i = 1; i <= multiple; i++) { + if (des_row + i >= m_startY + m_sizeY) { + return; + } + uint8_t* scan_des = + (uint8_t*)pDeviceBitmap->GetScanline(des_row + i) + des_ScanOffet; + FX_DWORD size = m_sizeX * des_Bpp; + FXSYS_memcpy(scan_des, scan_src, size); + } + } +} +void CCodec_ProgressiveDecoder::Resample(CFX_DIBitmap* pDeviceBitmap, + int32_t src_line, + uint8_t* src_scan, + FXCodec_Format src_format) { + int src_top = m_clipBox.top; + int des_top = m_startY; + int src_hei = m_clipBox.Height(); + int des_hei = m_sizeY; + if (src_line >= src_top) { + double scale_y = (double)des_hei / (double)src_hei; + int src_row = src_line - src_top; + int des_row = (int)(src_row * scale_y) + des_top; + if (des_row >= des_top + des_hei) { + return; + } + ReSampleScanline(pDeviceBitmap, des_row, m_pDecodeBuf, src_format); + if (scale_y > 1.0) { + ResampleVert(pDeviceBitmap, scale_y, des_row); + } + } +} +FXCODEC_STATUS CCodec_ProgressiveDecoder::GetFrames(int32_t& frames, + IFX_Pause* pPause) { + if (!(m_status == FXCODEC_STATUS_FRAME_READY || + m_status == FXCODEC_STATUS_FRAME_TOBECONTINUE)) { + return FXCODEC_STATUS_ERROR; + } + switch (m_imagType) { + case FXCODEC_IMAGE_BMP: + case FXCODEC_IMAGE_JPG: + case FXCODEC_IMAGE_PNG: + case FXCODEC_IMAGE_TIF: + frames = m_FrameNumber = 1; + return m_status = FXCODEC_STATUS_DECODE_READY; + case FXCODEC_IMAGE_GIF: { + ICodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule(); + while (TRUE) { + int32_t readResult = + pGifModule->LoadFrameInfo(m_pGifContext, &m_FrameNumber); + while (readResult == 2) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_READ; + if (!GifReadMoreData(pGifModule, error_status)) { + return error_status; + } + if (pPause && pPause->NeedToPauseNow()) { + return m_status = FXCODEC_STATUS_FRAME_TOBECONTINUE; + } + readResult = pGifModule->LoadFrameInfo(m_pGifContext, &m_FrameNumber); + } + if (readResult == 1) { + frames = m_FrameNumber; + return m_status = FXCODEC_STATUS_DECODE_READY; + } + if (m_pGifContext) { + pGifModule->Finish(m_pGifContext); + m_pGifContext = NULL; + } + return m_status = FXCODEC_STATUS_ERROR; + } + } break; + default: + break; + } + return FXCODEC_STATUS_ERROR; +} +FXCODEC_STATUS CCodec_ProgressiveDecoder::StartDecode(CFX_DIBitmap* pDIBitmap, + int start_x, + int start_y, + int size_x, + int size_y, + int32_t frames, + FX_BOOL bInterpol) { + if (m_status != FXCODEC_STATUS_DECODE_READY) { + return FXCODEC_STATUS_ERROR; + } + if (pDIBitmap == NULL || pDIBitmap->GetBPP() < 8 || frames < 0 || + frames >= m_FrameNumber) { + return FXCODEC_STATUS_ERR_PARAMS; + } + m_pDeviceBitmap = pDIBitmap; + if (m_clipBox.IsEmpty()) { + return FXCODEC_STATUS_ERR_PARAMS; + } + if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535) { + return FXCODEC_STATUS_ERR_PARAMS; + } + FX_RECT device_rc = + FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y); + int32_t out_range_x = device_rc.right - pDIBitmap->GetWidth(); + int32_t out_range_y = device_rc.bottom - pDIBitmap->GetHeight(); + device_rc.Intersect( + FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight())); + if (device_rc.IsEmpty()) { + return FXCODEC_STATUS_ERR_PARAMS; + } + m_startX = device_rc.left; + m_startY = device_rc.top; + m_sizeX = device_rc.Width(); + m_sizeY = device_rc.Height(); + m_bInterpol = bInterpol; + m_FrameCur = 0; + if (start_x < 0 || out_range_x > 0) { + FX_FLOAT scaleX = (FX_FLOAT)m_clipBox.Width() / (FX_FLOAT)size_x; + if (start_x < 0) { + m_clipBox.left -= (int32_t)FXSYS_ceil((FX_FLOAT)start_x * scaleX); + } + if (out_range_x > 0) { + m_clipBox.right -= (int32_t)FXSYS_floor((FX_FLOAT)out_range_x * scaleX); + } + } + if (start_y < 0 || out_range_y > 0) { + FX_FLOAT scaleY = (FX_FLOAT)m_clipBox.Height() / (FX_FLOAT)size_y; + if (start_y < 0) { + m_clipBox.top -= (int32_t)FXSYS_ceil((FX_FLOAT)start_y * scaleY); + } + if (out_range_y > 0) { + m_clipBox.bottom -= (int32_t)FXSYS_floor((FX_FLOAT)out_range_y * scaleY); + } + } + if (m_clipBox.IsEmpty()) { + return FXCODEC_STATUS_ERR_PARAMS; + } + switch (m_imagType) { + case FXCODEC_IMAGE_JPG: { + ICodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); + int down_scale = 1; + GetDownScale(down_scale); + FX_BOOL bStart = pJpegModule->StartScanline(m_pJpegContext, down_scale); + while (!bStart) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR; + if (!JpegReadMoreData(pJpegModule, error_status)) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = error_status; + } + bStart = pJpegModule->StartScanline(m_pJpegContext, down_scale); + } + int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale; + scanline_size = (scanline_size * m_SrcComponents + 3) / 4 * 4; + FX_Free(m_pDecodeBuf); + m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size); + FXSYS_memset(m_pDecodeBuf, 0, scanline_size); + m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, + m_clipBox.Width(), m_bInterpol); + m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); + switch (m_SrcComponents) { + case 1: + m_SrcFormat = FXCodec_8bppGray; + break; + case 3: + m_SrcFormat = FXCodec_Rgb; + break; + case 4: + m_SrcFormat = FXCodec_Cmyk; + break; + } + GetTransMethod(pDIBitmap->GetFormat(), m_SrcFormat); + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } break; + case FXCODEC_IMAGE_PNG: { + ICodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule(); + if (pPngModule == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + if (m_pPngContext) { + pPngModule->Finish(m_pPngContext); + m_pPngContext = NULL; + } + m_pPngContext = pPngModule->Start((void*)this); + if (m_pPngContext == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + m_offSet = 0; + switch (m_pDeviceBitmap->GetFormat()) { + case FXDIB_8bppMask: + case FXDIB_8bppRgb: + m_SrcComponents = 1; + m_SrcFormat = FXCodec_8bppGray; + break; + case FXDIB_Rgb: + m_SrcComponents = 3; + m_SrcFormat = FXCodec_Rgb; + break; + case FXDIB_Rgb32: + case FXDIB_Argb: + m_SrcComponents = 4; + m_SrcFormat = FXCodec_Argb; + break; + default: { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_PARAMS; + } + } + GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); + int scanline_size = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4; + FX_Free(m_pDecodeBuf); + m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size); + FXSYS_memset(m_pDecodeBuf, 0, scanline_size); + m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width(), m_bInterpol); + m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } break; + case FXCODEC_IMAGE_GIF: { + ICodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule(); + if (pGifModule == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + m_SrcFormat = FXCodec_8bppRgb; + GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); + int scanline_size = (m_SrcWidth + 3) / 4 * 4; + FX_Free(m_pDecodeBuf); + m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size); + FXSYS_memset(m_pDecodeBuf, 0, scanline_size); + m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, + m_clipBox.Width(), m_bInterpol); + m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); + m_FrameCur = frames; + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } break; + case FXCODEC_IMAGE_BMP: { + ICodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule(); + if (pBmpModule == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + switch (m_SrcComponents) { + case 1: + m_SrcFormat = FXCodec_8bppRgb; + break; + case 3: + m_SrcFormat = FXCodec_Rgb; + break; + case 4: + m_SrcFormat = FXCodec_Rgb32; + break; + } + GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); + m_ScanlineSize = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4; + FX_Free(m_pDecodeBuf); + m_pDecodeBuf = FX_Alloc(uint8_t, m_ScanlineSize); + FXSYS_memset(m_pDecodeBuf, 0, m_ScanlineSize); + m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, + m_clipBox.Width(), m_bInterpol); + m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } break; + case FXCODEC_IMAGE_TIF: + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + default: + break; + } + return FXCODEC_STATUS_ERROR; +} +FXCODEC_STATUS CCodec_ProgressiveDecoder::ContinueDecode(IFX_Pause* pPause) { + if (m_status != FXCODEC_STATUS_DECODE_TOBECONTINUE) { + return FXCODEC_STATUS_ERROR; + } + switch (m_imagType) { + case FXCODEC_IMAGE_JPG: { + ICodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); + while (TRUE) { + FX_BOOL readRes = + pJpegModule->ReadScanline(m_pJpegContext, m_pDecodeBuf); + while (!readRes) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH; + if (!JpegReadMoreData(pJpegModule, error_status)) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = error_status; + } + readRes = pJpegModule->ReadScanline(m_pJpegContext, m_pDecodeBuf); + } + if (m_SrcFormat == FXCodec_Rgb) { + int src_Bpp = (m_SrcFormat & 0xff) >> 3; + _RGB2BGR(m_pDecodeBuf + m_clipBox.left * src_Bpp, m_clipBox.Width()); + } + if (m_SrcRow >= m_clipBox.bottom) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_DECODE_FINISH; + } + Resample(m_pDeviceBitmap, m_SrcRow, m_pDecodeBuf, m_SrcFormat); + m_SrcRow++; + if (pPause && pPause->NeedToPauseNow()) { + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + } break; + case FXCODEC_IMAGE_PNG: { + ICodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule(); + while (TRUE) { + FX_DWORD remain_size = (FX_DWORD)m_pFile->GetSize() - m_offSet; + FX_DWORD input_size = + remain_size > FXCODEC_BLOCK_SIZE ? FXCODEC_BLOCK_SIZE : remain_size; + if (input_size == 0) { + if (m_pPngContext) { + pPngModule->Finish(m_pPngContext); + } + m_pPngContext = NULL; + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_DECODE_FINISH; + } + if (m_pSrcBuf && input_size > m_SrcSize) { + FX_Free(m_pSrcBuf); + m_pSrcBuf = FX_Alloc(uint8_t, input_size); + FXSYS_memset(m_pSrcBuf, 0, input_size); + m_SrcSize = input_size; + } + FX_BOOL bResult = m_pFile->ReadBlock(m_pSrcBuf, m_offSet, input_size); + if (!bResult) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_READ; + } + m_offSet += input_size; + bResult = + pPngModule->Input(m_pPngContext, m_pSrcBuf, input_size, nullptr); + if (!bResult) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERROR; + } + if (pPause && pPause->NeedToPauseNow()) { + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + } break; + case FXCODEC_IMAGE_GIF: { + ICodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule(); + while (TRUE) { + int32_t readRes = + pGifModule->LoadFrame(m_pGifContext, m_FrameCur, nullptr); + while (readRes == 2) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH; + if (!GifReadMoreData(pGifModule, error_status)) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = error_status; + } + if (pPause && pPause->NeedToPauseNow()) { + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + readRes = pGifModule->LoadFrame(m_pGifContext, m_FrameCur, nullptr); + } + if (readRes == 1) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_DECODE_FINISH; + } + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERROR; + } + } break; + case FXCODEC_IMAGE_BMP: { + ICodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule(); + while (TRUE) { + int32_t readRes = pBmpModule->LoadImage(m_pBmpContext); + while (readRes == 2) { + FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH; + if (!BmpReadMoreData(pBmpModule, error_status)) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = error_status; + } + if (pPause && pPause->NeedToPauseNow()) { + return m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + readRes = pBmpModule->LoadImage(m_pBmpContext); + } + if (readRes == 1) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_DECODE_FINISH; + } + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERROR; + } + } break; + case FXCODEC_IMAGE_TIF: { + ICodec_TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule(); + FX_BOOL ret = FALSE; + if (m_pDeviceBitmap->GetBPP() == 32 && + m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX && + m_pDeviceBitmap->GetHeight() == m_SrcHeight && + m_SrcHeight == m_sizeY && m_startX == 0 && m_startY == 0 && + m_clipBox.left == 0 && m_clipBox.top == 0 && + m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight) { + ret = pTiffModule->Decode(m_pTiffContext, m_pDeviceBitmap); + m_pDeviceBitmap = NULL; + m_pFile = NULL; + if (!ret) { + return m_status = FXCODEC_STATUS_ERROR; + } + return m_status = FXCODEC_STATUS_DECODE_FINISH; + } else { + CFX_DIBitmap* pDIBitmap = new CFX_DIBitmap; + pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Argb); + if (pDIBitmap->GetBuffer() == NULL) { + delete pDIBitmap; + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + ret = pTiffModule->Decode(m_pTiffContext, pDIBitmap); + if (!ret) { + delete pDIBitmap; + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERROR; + } + CFX_DIBitmap* pClipBitmap = + (m_clipBox.left == 0 && m_clipBox.top == 0 && + m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight) + ? pDIBitmap + : pDIBitmap->Clone(&m_clipBox); + if (pDIBitmap != pClipBitmap) { + delete pDIBitmap; + } + if (pClipBitmap == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + CFX_DIBitmap* pFormatBitmap = NULL; + switch (m_pDeviceBitmap->GetFormat()) { + case FXDIB_8bppRgb: + pFormatBitmap = new CFX_DIBitmap; + pFormatBitmap->Create(pClipBitmap->GetWidth(), + pClipBitmap->GetHeight(), FXDIB_8bppRgb); + break; + case FXDIB_8bppMask: + pFormatBitmap = new CFX_DIBitmap; + pFormatBitmap->Create(pClipBitmap->GetWidth(), + pClipBitmap->GetHeight(), FXDIB_8bppMask); + break; + case FXDIB_Rgb: + pFormatBitmap = new CFX_DIBitmap; + pFormatBitmap->Create(pClipBitmap->GetWidth(), + pClipBitmap->GetHeight(), FXDIB_Rgb); + break; + case FXDIB_Rgb32: + pFormatBitmap = new CFX_DIBitmap; + pFormatBitmap->Create(pClipBitmap->GetWidth(), + pClipBitmap->GetHeight(), FXDIB_Rgb32); + break; + case FXDIB_Argb: + pFormatBitmap = pClipBitmap; + break; + default: + break; + } + switch (m_pDeviceBitmap->GetFormat()) { + case FXDIB_8bppRgb: + case FXDIB_8bppMask: { + for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) { + uint8_t* src_line = (uint8_t*)pClipBitmap->GetScanline(row); + uint8_t* des_line = (uint8_t*)pFormatBitmap->GetScanline(row); + for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) { + uint8_t _a = 255 - src_line[3]; + uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255; + uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255; + uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255; + *des_line++ = FXRGB2GRAY(r, g, b); + src_line += 4; + } + } + } break; + case FXDIB_Rgb: + case FXDIB_Rgb32: { + int32_t desBpp = + (m_pDeviceBitmap->GetFormat() == FXDIB_Rgb) ? 3 : 4; + for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) { + uint8_t* src_line = (uint8_t*)pClipBitmap->GetScanline(row); + uint8_t* des_line = (uint8_t*)pFormatBitmap->GetScanline(row); + for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) { + uint8_t _a = 255 - src_line[3]; + uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255; + uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255; + uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255; + *des_line++ = b; + *des_line++ = g; + *des_line++ = r; + des_line += desBpp - 3; + src_line += 4; + } + } + } break; + default: + break; + } + if (pClipBitmap != pFormatBitmap) { + delete pClipBitmap; + } + if (pFormatBitmap == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + CFX_DIBitmap* pStrechBitmap = pFormatBitmap->StretchTo( + m_sizeX, m_sizeY, m_bInterpol ? FXDIB_INTERPOL : FXDIB_DOWNSAMPLE); + delete pFormatBitmap; + pFormatBitmap = NULL; + if (pStrechBitmap == NULL) { + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_ERR_MEMORY; + } + m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY, + pStrechBitmap, 0, 0); + delete pStrechBitmap; + pStrechBitmap = NULL; + m_pDeviceBitmap = NULL; + m_pFile = NULL; + return m_status = FXCODEC_STATUS_DECODE_FINISH; + } + } break; + default: + break; + } + return FXCODEC_STATUS_ERROR; +} +ICodec_ProgressiveDecoder* CCodec_ModuleMgr::CreateProgressiveDecoder() { + return new CCodec_ProgressiveDecoder(this); +} diff --git a/core/fxcodec/codec/fx_codec_progress.h b/core/fxcodec/codec/fx_codec_progress.h new file mode 100644 index 0000000000..c9ee389e02 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_progress.h @@ -0,0 +1,221 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_CODEC_FX_CODEC_PROGRESS_H_ +#define CORE_FXCODEC_CODEC_FX_CODEC_PROGRESS_H_ + +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxcrt/fx_memory.h" +#include "core/include/fxcrt/fx_system.h" +#include "core/include/fxge/fx_dib.h" + +#define FXCODEC_BLOCK_SIZE 4096 +#define FXCODEC_PNG_GAMMA 2.2 + +#if _FX_OS_ == _FX_MACOSX_ || _FX_OS_ == _FX_IOS_ +#undef FXCODEC_PNG_GAMMA +#define FXCODEC_PNG_GAMMA 1.7 +#endif + +struct PixelWeight { + int m_SrcStart; + int m_SrcEnd; + int m_Weights[1]; +}; + +class CFXCODEC_WeightTable { + public: + CFXCODEC_WeightTable() { m_pWeightTables = NULL; } + ~CFXCODEC_WeightTable() { FX_Free(m_pWeightTables); } + + void Calc(int dest_len, + int dest_min, + int dest_max, + int src_len, + int src_min, + int src_max, + FX_BOOL bInterpol); + PixelWeight* GetPixelWeight(int pixel) { + return (PixelWeight*)(m_pWeightTables + (pixel - m_DestMin) * m_ItemSize); + } + + int m_DestMin, m_ItemSize; + uint8_t* m_pWeightTables; +}; +class CFXCODEC_HorzTable { + public: + CFXCODEC_HorzTable() { m_pWeightTables = NULL; } + ~CFXCODEC_HorzTable() { FX_Free(m_pWeightTables); } + + void Calc(int dest_len, int src_len, FX_BOOL bInterpol); + PixelWeight* GetPixelWeight(int pixel) { + return (PixelWeight*)(m_pWeightTables + pixel * m_ItemSize); + } + + int m_ItemSize; + uint8_t* m_pWeightTables; +}; +class CFXCODEC_VertTable { + public: + CFXCODEC_VertTable() { m_pWeightTables = NULL; } + ~CFXCODEC_VertTable() { FX_Free(m_pWeightTables); } + void Calc(int dest_len, int src_len); + PixelWeight* GetPixelWeight(int pixel) { + return (PixelWeight*)(m_pWeightTables + pixel * m_ItemSize); + } + int m_ItemSize; + uint8_t* m_pWeightTables; +}; +enum FXCodec_Format { + FXCodec_Invalid = 0, + FXCodec_1bppGray = 0x101, + FXCodec_1bppRgb = 0x001, + FXCodec_8bppGray = 0x108, + FXCodec_8bppRgb = 0x008, + FXCodec_Rgb = 0x018, + FXCodec_Rgb32 = 0x020, + FXCodec_Argb = 0x220, + FXCodec_Cmyk = 0x120 +}; +class CCodec_ProgressiveDecoder : public ICodec_ProgressiveDecoder { + public: + CCodec_ProgressiveDecoder(CCodec_ModuleMgr* pCodecMgr); + ~CCodec_ProgressiveDecoder() override; + + FXCODEC_STATUS LoadImageInfo(IFX_FileRead* pFile, + FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute) override; + + FXCODEC_IMAGE_TYPE GetType() const override { return m_imagType; } + int32_t GetWidth() const override { return m_SrcWidth; } + int32_t GetHeight() const override { return m_SrcHeight; } + int32_t GetNumComponents() const override { return m_SrcComponents; } + int32_t GetBPC() const override { return m_SrcBPC; } + void SetClipBox(FX_RECT* clip) override; + + FXCODEC_STATUS GetFrames(int32_t& frames, IFX_Pause* pPause) override; + FXCODEC_STATUS StartDecode(CFX_DIBitmap* pDIBitmap, + int start_x, + int start_y, + int size_x, + int size_y, + int32_t frames, + FX_BOOL bInterpol) override; + + FXCODEC_STATUS ContinueDecode(IFX_Pause* pPause) override; + + protected: + static FX_BOOL PngReadHeaderFunc(void* pModule, + int width, + int height, + int bpc, + int pass, + int* color_type, + double* gamma); + static FX_BOOL PngAskScanlineBufFunc(void* pModule, + int line, + uint8_t*& src_buf); + static void PngFillScanlineBufCompletedFunc(void* pModule, + int pass, + int line); + static void GifRecordCurrentPositionCallback(void* pModule, + FX_DWORD& cur_pos); + static uint8_t* GifAskLocalPaletteBufCallback(void* pModule, + int32_t frame_num, + int32_t pal_size); + static FX_BOOL GifInputRecordPositionBufCallback(void* pModule, + FX_DWORD rcd_pos, + const FX_RECT& img_rc, + int32_t pal_num, + void* pal_ptr, + int32_t delay_time, + FX_BOOL user_input, + int32_t trans_index, + int32_t disposal_method, + FX_BOOL interlace); + static void GifReadScanlineCallback(void* pModule, + int32_t row_num, + uint8_t* row_buf); + static FX_BOOL BmpInputImagePositionBufCallback(void* pModule, + FX_DWORD rcd_pos); + static void BmpReadScanlineCallback(void* pModule, + int32_t row_num, + uint8_t* row_buf); + + FX_BOOL DetectImageType(FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute); + void GetDownScale(int& down_scale); + void GetTransMethod(FXDIB_Format des_format, FXCodec_Format src_format); + void ReSampleScanline(CFX_DIBitmap* pDeviceBitmap, + int32_t des_line, + uint8_t* src_scan, + FXCodec_Format src_format); + void Resample(CFX_DIBitmap* pDeviceBitmap, + int32_t src_line, + uint8_t* src_scan, + FXCodec_Format src_format); + void ResampleVert(CFX_DIBitmap* pDeviceBitmap, double scale_y, int des_row); + FX_BOOL JpegReadMoreData(ICodec_JpegModule* pJpegModule, + FXCODEC_STATUS& err_status); + void PngOneOneMapResampleHorz(CFX_DIBitmap* pDeviceBitmap, + int32_t des_line, + uint8_t* src_scan, + FXCodec_Format src_format); + FX_BOOL GifReadMoreData(ICodec_GifModule* pGifModule, + FXCODEC_STATUS& err_status); + void GifDoubleLineResampleVert(CFX_DIBitmap* pDeviceBitmap, + double scale_y, + int des_row); + FX_BOOL BmpReadMoreData(ICodec_BmpModule* pBmpModule, + FXCODEC_STATUS& err_status); + void ResampleVertBT(CFX_DIBitmap* pDeviceBitmap, double scale_y, int des_row); + + public: + IFX_FileRead* m_pFile; + CCodec_ModuleMgr* m_pCodecMgr; + void* m_pJpegContext; + void* m_pPngContext; + void* m_pGifContext; + void* m_pBmpContext; + void* m_pTiffContext; + FXCODEC_IMAGE_TYPE m_imagType; + FX_DWORD m_offSet; + uint8_t* m_pSrcBuf; + FX_DWORD m_SrcSize; + uint8_t* m_pDecodeBuf; + int m_ScanlineSize; + CFX_DIBitmap* m_pDeviceBitmap; + FX_BOOL m_bInterpol; + CFXCODEC_WeightTable m_WeightHorz; + CFXCODEC_VertTable m_WeightVert; + CFXCODEC_HorzTable m_WeightHorzOO; + int m_SrcWidth; + int m_SrcHeight; + int m_SrcComponents; + int m_SrcBPC; + FX_RECT m_clipBox; + int m_startX; + int m_startY; + int m_sizeX; + int m_sizeY; + int m_TransMethod; + FX_ARGB* m_pSrcPalette; + int m_SrcPaletteNumber; + int m_SrcRow; + FXCodec_Format m_SrcFormat; + int m_SrcPassNumber; + int m_FrameNumber; + int m_FrameCur; + int m_GifBgIndex; + uint8_t* m_pGifPalette; + int32_t m_GifPltNumber; + int m_GifTransIndex; + FX_RECT m_GifFrameRect; + FX_BOOL m_BmpIsTopBottom; + FXCODEC_STATUS m_status; +}; + +#endif // CORE_FXCODEC_CODEC_FX_CODEC_PROGRESS_H_ diff --git a/core/fxcodec/codec/fx_codec_tiff.cpp b/core/fxcodec/codec/fx_codec_tiff.cpp new file mode 100644 index 0000000000..2af92f2f12 --- /dev/null +++ b/core/fxcodec/codec/fx_codec_tiff.cpp @@ -0,0 +1,544 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/codec/codec_int.h" +#include "core/include/fxcodec/fx_codec.h" +#include "core/include/fxge/fx_dib.h" + +extern "C" { +#include "third_party/libtiff/tiffiop.h" +} + +void* IccLib_CreateTransform_sRGB(const unsigned char* pProfileData, + unsigned int dwProfileSize, + int nComponents, + int intent, + FX_DWORD dwSrcFormat = Icc_FORMAT_DEFAULT); +void IccLib_TranslateImage(void* pTransform, + unsigned char* pDest, + const unsigned char* pSrc, + int pixels); +void IccLib_DestroyTransform(void* pTransform); +class CCodec_TiffContext { + public: + CCodec_TiffContext(); + ~CCodec_TiffContext(); + + FX_BOOL InitDecoder(IFX_FileRead* file_ptr); + void GetFrames(int32_t& frames); + FX_BOOL LoadFrameInfo(int32_t frame, + FX_DWORD& width, + FX_DWORD& height, + FX_DWORD& comps, + FX_DWORD& bpc, + CFX_DIBAttribute* pAttribute); + FX_BOOL Decode(CFX_DIBitmap* pDIBitmap); + + union { + IFX_FileRead* in; + IFX_FileStream* out; + } io; + + FX_DWORD offset; + + TIFF* tif_ctx; + void* icc_ctx; + int32_t frame_num; + int32_t frame_cur; + FX_BOOL isDecoder; + + private: + FX_BOOL isSupport(CFX_DIBitmap* pDIBitmap); + void SetPalette(CFX_DIBitmap* pDIBitmap, uint16_t bps); + FX_BOOL Decode1bppRGB(CFX_DIBitmap* pDIBitmap, + int32_t height, + int32_t width, + uint16_t bps, + uint16_t spp); + FX_BOOL Decode8bppRGB(CFX_DIBitmap* pDIBitmap, + int32_t height, + int32_t width, + uint16_t bps, + uint16_t spp); + FX_BOOL Decode24bppRGB(CFX_DIBitmap* pDIBitmap, + int32_t height, + int32_t width, + uint16_t bps, + uint16_t spp); +}; +CCodec_TiffContext::CCodec_TiffContext() { + offset = 0; + frame_num = 0; + frame_cur = 0; + io.in = NULL; + tif_ctx = NULL; + icc_ctx = NULL; + isDecoder = TRUE; +} +CCodec_TiffContext::~CCodec_TiffContext() { + if (icc_ctx) { + IccLib_DestroyTransform(icc_ctx); + icc_ctx = NULL; + } + if (tif_ctx) { + TIFFClose(tif_ctx); + } +} +static tsize_t _tiff_read(thandle_t context, tdata_t buf, tsize_t length) { + CCodec_TiffContext* pTiffContext = (CCodec_TiffContext*)context; + FX_BOOL ret = FALSE; + if (pTiffContext->isDecoder) { + ret = pTiffContext->io.in->ReadBlock(buf, pTiffContext->offset, length); + } else { + ret = pTiffContext->io.out->ReadBlock(buf, pTiffContext->offset, length); + } + if (!ret) { + return 0; + } + pTiffContext->offset += (FX_DWORD)length; + return length; +} +static tsize_t _tiff_write(thandle_t context, tdata_t buf, tsize_t length) { + CCodec_TiffContext* pTiffContext = (CCodec_TiffContext*)context; + ASSERT(!pTiffContext->isDecoder); + if (!pTiffContext->io.out->WriteBlock(buf, pTiffContext->offset, length)) { + return 0; + } + pTiffContext->offset += (FX_DWORD)length; + return length; +} +static toff_t _tiff_seek(thandle_t context, toff_t offset, int whence) { + CCodec_TiffContext* pTiffContext = (CCodec_TiffContext*)context; + switch (whence) { + case 0: + pTiffContext->offset = (FX_DWORD)offset; + break; + case 1: + pTiffContext->offset += (FX_DWORD)offset; + break; + case 2: + if (pTiffContext->isDecoder) { + if (pTiffContext->io.in->GetSize() < (FX_FILESIZE)offset) { + return -1; + } + pTiffContext->offset = + (FX_DWORD)(pTiffContext->io.in->GetSize() - offset); + } else { + if (pTiffContext->io.out->GetSize() < (FX_FILESIZE)offset) { + return -1; + } + pTiffContext->offset = + (FX_DWORD)(pTiffContext->io.out->GetSize() - offset); + } + break; + default: + return -1; + } + ASSERT(pTiffContext->isDecoder ? (pTiffContext->offset <= + (FX_DWORD)pTiffContext->io.in->GetSize()) + : TRUE); + return pTiffContext->offset; +} +static int _tiff_close(thandle_t context) { + return 0; +} +static toff_t _tiff_get_size(thandle_t context) { + CCodec_TiffContext* pTiffContext = (CCodec_TiffContext*)context; + return pTiffContext->isDecoder ? (toff_t)pTiffContext->io.in->GetSize() + : (toff_t)pTiffContext->io.out->GetSize(); +} +static int _tiff_map(thandle_t context, tdata_t*, toff_t*) { + return 0; +} +static void _tiff_unmap(thandle_t context, tdata_t, toff_t) {} +TIFF* _tiff_open(void* context, const char* mode) { + TIFF* tif = TIFFClientOpen("Tiff Image", mode, (thandle_t)context, _tiff_read, + _tiff_write, _tiff_seek, _tiff_close, + _tiff_get_size, _tiff_map, _tiff_unmap); + if (tif) { + tif->tif_fd = (int)(intptr_t)context; + } + return tif; +} +void* _TIFFmalloc(tmsize_t size) { + return FXMEM_DefaultAlloc(size, 0); +} +void _TIFFfree(void* ptr) { + FXMEM_DefaultFree(ptr, 0); +} +void* _TIFFrealloc(void* ptr, tmsize_t size) { + return FXMEM_DefaultRealloc(ptr, size, 0); +} +void _TIFFmemset(void* ptr, int val, tmsize_t size) { + FXSYS_memset(ptr, val, (size_t)size); +} +void _TIFFmemcpy(void* des, const void* src, tmsize_t size) { + FXSYS_memcpy(des, src, (size_t)size); +} +int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) { + return FXSYS_memcmp(ptr1, ptr2, (size_t)size); +} + +TIFFErrorHandler _TIFFwarningHandler = nullptr; +TIFFErrorHandler _TIFFerrorHandler = nullptr; + +int TIFFCmyk2Rgb(thandle_t context, + uint8 c, + uint8 m, + uint8 y, + uint8 k, + uint8* r, + uint8* g, + uint8* b) { + if (context == NULL) { + return 0; + } + CCodec_TiffContext* p = (CCodec_TiffContext*)context; + if (p->icc_ctx) { + unsigned char cmyk[4], bgr[3]; + cmyk[0] = c, cmyk[1] = m, cmyk[2] = y, cmyk[3] = k; + IccLib_TranslateImage(p->icc_ctx, bgr, cmyk, 1); + *r = bgr[2], *g = bgr[1], *b = bgr[0]; + } else { + AdobeCMYK_to_sRGB1(c, m, y, k, *r, *g, *b); + } + return 1; +} +FX_BOOL CCodec_TiffContext::InitDecoder(IFX_FileRead* file_ptr) { + io.in = file_ptr; + tif_ctx = _tiff_open(this, "r"); + if (tif_ctx == NULL) { + return FALSE; + } + return TRUE; +} +void CCodec_TiffContext::GetFrames(int32_t& frames) { + frames = frame_num = TIFFNumberOfDirectories(tif_ctx); +} +#define TIFF_EXIF_GETINFO(key, T, tag) \ + { \ + T val = (T)0; \ + TIFFGetField(tif_ctx, tag, &val); \ + if (val) { \ + (key) = FX_Alloc(uint8_t, sizeof(T)); \ + if ((key)) { \ + T* ptr = (T*)(key); \ + *ptr = val; \ + pExif->m_TagVal.SetAt(tag, (key)); \ + } \ + } \ + } \ + (key) = NULL; +#define TIFF_EXIF_GETSTRINGINFO(key, tag) \ + { \ + FX_DWORD size = 0; \ + uint8_t* buf = NULL; \ + TIFFGetField(tif_ctx, tag, &size, &buf); \ + if (size && buf) { \ + (key) = FX_Alloc(uint8_t, size); \ + if ((key)) { \ + FXSYS_memcpy((key), buf, size); \ + pExif->m_TagVal.SetAt(tag, (key)); \ + } \ + } \ + } \ + (key) = NULL; + +namespace { + +template <class T> +FX_BOOL Tiff_Exif_GetInfo(TIFF* tif_ctx, ttag_t tag, CFX_DIBAttribute* pAttr) { + T val = 0; + TIFFGetField(tif_ctx, tag, &val); + if (!val) + return FALSE; + T* ptr = FX_Alloc(T, 1); + *ptr = val; + pAttr->m_Exif[tag] = (void*)ptr; + return TRUE; +} +void Tiff_Exif_GetStringInfo(TIFF* tif_ctx, + ttag_t tag, + CFX_DIBAttribute* pAttr) { + FX_CHAR* buf = nullptr; + TIFFGetField(tif_ctx, tag, &buf); + if (!buf) + return; + FX_STRSIZE size = FXSYS_strlen(buf); + uint8_t* ptr = FX_Alloc(uint8_t, size + 1); + FXSYS_memcpy(ptr, buf, size); + ptr[size] = 0; + pAttr->m_Exif[tag] = ptr; +} + +} // namespace + +FX_BOOL CCodec_TiffContext::LoadFrameInfo(int32_t frame, + FX_DWORD& width, + FX_DWORD& height, + FX_DWORD& comps, + FX_DWORD& bpc, + CFX_DIBAttribute* pAttribute) { + if (!TIFFSetDirectory(tif_ctx, (uint16)frame)) { + return FALSE; + } + FX_WORD tif_cs; + FX_DWORD tif_icc_size = 0; + uint8_t* tif_icc_buf = NULL; + FX_WORD tif_bpc = 0; + FX_WORD tif_cps; + FX_DWORD tif_rps; + width = height = comps = 0; + TIFFGetField(tif_ctx, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tif_ctx, TIFFTAG_IMAGELENGTH, &height); + TIFFGetField(tif_ctx, TIFFTAG_SAMPLESPERPIXEL, &comps); + TIFFGetField(tif_ctx, TIFFTAG_BITSPERSAMPLE, &tif_bpc); + TIFFGetField(tif_ctx, TIFFTAG_PHOTOMETRIC, &tif_cs); + TIFFGetField(tif_ctx, TIFFTAG_COMPRESSION, &tif_cps); + TIFFGetField(tif_ctx, TIFFTAG_ROWSPERSTRIP, &tif_rps); + TIFFGetField(tif_ctx, TIFFTAG_ICCPROFILE, &tif_icc_size, &tif_icc_buf); + if (pAttribute) { + pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_INCH; + if (TIFFGetField(tif_ctx, TIFFTAG_RESOLUTIONUNIT, + &pAttribute->m_wDPIUnit)) { + pAttribute->m_wDPIUnit -= 1; + } + Tiff_Exif_GetInfo<FX_WORD>(tif_ctx, TIFFTAG_ORIENTATION, pAttribute); + if (Tiff_Exif_GetInfo<FX_FLOAT>(tif_ctx, TIFFTAG_XRESOLUTION, pAttribute)) { + void* val = pAttribute->m_Exif[TIFFTAG_XRESOLUTION]; + FX_FLOAT fDpi = val ? *reinterpret_cast<FX_FLOAT*>(val) : 0; + pAttribute->m_nXDPI = (int32_t)(fDpi + 0.5f); + } + if (Tiff_Exif_GetInfo<FX_FLOAT>(tif_ctx, TIFFTAG_YRESOLUTION, pAttribute)) { + void* val = pAttribute->m_Exif[TIFFTAG_YRESOLUTION]; + FX_FLOAT fDpi = val ? *reinterpret_cast<FX_FLOAT*>(val) : 0; + pAttribute->m_nYDPI = (int32_t)(fDpi + 0.5f); + } + Tiff_Exif_GetStringInfo(tif_ctx, TIFFTAG_IMAGEDESCRIPTION, pAttribute); + Tiff_Exif_GetStringInfo(tif_ctx, TIFFTAG_MAKE, pAttribute); + Tiff_Exif_GetStringInfo(tif_ctx, TIFFTAG_MODEL, pAttribute); + } + bpc = tif_bpc; + if (tif_rps > height) { + TIFFSetField(tif_ctx, TIFFTAG_ROWSPERSTRIP, tif_rps = height); + } + return TRUE; +} +void _TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) { + for (int32_t n = 0; n < pixel; n++) { + uint8_t tmp = pBuf[0]; + pBuf[0] = pBuf[2]; + pBuf[2] = tmp; + pBuf += spp; + } +} +FX_BOOL CCodec_TiffContext::isSupport(CFX_DIBitmap* pDIBitmap) { + if (TIFFIsTiled(tif_ctx)) { + return FALSE; + } + uint16_t photometric; + if (!TIFFGetField(tif_ctx, TIFFTAG_PHOTOMETRIC, &photometric)) { + return FALSE; + } + switch (pDIBitmap->GetBPP()) { + case 1: + case 8: + if (photometric != PHOTOMETRIC_PALETTE) { + return FALSE; + } + break; + case 24: + if (photometric != PHOTOMETRIC_RGB) { + return FALSE; + } + break; + default: + return FALSE; + } + uint16_t planarconfig; + if (!TIFFGetFieldDefaulted(tif_ctx, TIFFTAG_PLANARCONFIG, &planarconfig)) { + return FALSE; + } + if (planarconfig == PLANARCONFIG_SEPARATE) { + return FALSE; + } + return TRUE; +} +void CCodec_TiffContext::SetPalette(CFX_DIBitmap* pDIBitmap, uint16_t bps) { + uint16_t *red_orig, *green_orig, *blue_orig; + TIFFGetField(tif_ctx, TIFFTAG_COLORMAP, &red_orig, &green_orig, &blue_orig); + for (int32_t i = (1L << bps) - 1; i >= 0; i--) { +#define CVT(x) ((uint16_t)((x) >> 8)) + red_orig[i] = CVT(red_orig[i]); + green_orig[i] = CVT(green_orig[i]); + blue_orig[i] = CVT(blue_orig[i]); +#undef CVT + } + int32_t len = 1 << bps; + for (int32_t index = 0; index < len; index++) { + FX_DWORD r = red_orig[index] & 0xFF; + FX_DWORD g = green_orig[index] & 0xFF; + FX_DWORD b = blue_orig[index] & 0xFF; + FX_DWORD color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) | + (((uint32)0xffL) << 24); + pDIBitmap->SetPaletteEntry(index, color); + } +} +FX_BOOL CCodec_TiffContext::Decode1bppRGB(CFX_DIBitmap* pDIBitmap, + int32_t height, + int32_t width, + uint16_t bps, + uint16_t spp) { + if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 || + !isSupport(pDIBitmap)) { + return FALSE; + } + SetPalette(pDIBitmap, bps); + int32_t size = (int32_t)TIFFScanlineSize(tif_ctx); + uint8_t* buf = (uint8_t*)_TIFFmalloc(size); + if (buf == NULL) { + TIFFError(TIFFFileName(tif_ctx), "No space for scanline buffer"); + return FALSE; + } + uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer(); + FX_DWORD pitch = pDIBitmap->GetPitch(); + for (int32_t row = 0; row < height; row++) { + TIFFReadScanline(tif_ctx, buf, row, 0); + for (int32_t j = 0; j < size; j++) { + bitMapbuffer[row * pitch + j] = buf[j]; + } + } + _TIFFfree(buf); + return TRUE; +} +FX_BOOL CCodec_TiffContext::Decode8bppRGB(CFX_DIBitmap* pDIBitmap, + int32_t height, + int32_t width, + uint16_t bps, + uint16_t spp) { + if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) || + !isSupport(pDIBitmap)) { + return FALSE; + } + SetPalette(pDIBitmap, bps); + int32_t size = (int32_t)TIFFScanlineSize(tif_ctx); + uint8_t* buf = (uint8_t*)_TIFFmalloc(size); + if (buf == NULL) { + TIFFError(TIFFFileName(tif_ctx), "No space for scanline buffer"); + return FALSE; + } + uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer(); + FX_DWORD pitch = pDIBitmap->GetPitch(); + for (int32_t row = 0; row < height; row++) { + TIFFReadScanline(tif_ctx, buf, row, 0); + for (int32_t j = 0; j < size; j++) { + switch (bps) { + case 4: + bitMapbuffer[row * pitch + 2 * j + 0] = (buf[j] & 0xF0) >> 4; + bitMapbuffer[row * pitch + 2 * j + 1] = (buf[j] & 0x0F) >> 0; + break; + case 8: + bitMapbuffer[row * pitch + j] = buf[j]; + break; + } + } + } + _TIFFfree(buf); + return TRUE; +} +FX_BOOL CCodec_TiffContext::Decode24bppRGB(CFX_DIBitmap* pDIBitmap, + int32_t height, + int32_t width, + uint16_t bps, + uint16_t spp) { + if (pDIBitmap->GetBPP() != 24 || !isSupport(pDIBitmap)) { + return FALSE; + } + int32_t size = (int32_t)TIFFScanlineSize(tif_ctx); + uint8_t* buf = (uint8_t*)_TIFFmalloc(size); + if (buf == NULL) { + TIFFError(TIFFFileName(tif_ctx), "No space for scanline buffer"); + return FALSE; + } + uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer(); + FX_DWORD pitch = pDIBitmap->GetPitch(); + for (int32_t row = 0; row < height; row++) { + TIFFReadScanline(tif_ctx, buf, row, 0); + for (int32_t j = 0; j < size - 2; j += 3) { + bitMapbuffer[row * pitch + j + 0] = buf[j + 2]; + bitMapbuffer[row * pitch + j + 1] = buf[j + 1]; + bitMapbuffer[row * pitch + j + 2] = buf[j + 0]; + } + } + _TIFFfree(buf); + return TRUE; +} +FX_BOOL CCodec_TiffContext::Decode(CFX_DIBitmap* pDIBitmap) { + FX_DWORD img_wid = pDIBitmap->GetWidth(); + FX_DWORD img_hei = pDIBitmap->GetHeight(); + FX_DWORD width = 0; + FX_DWORD height = 0; + TIFFGetField(tif_ctx, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tif_ctx, TIFFTAG_IMAGELENGTH, &height); + if (img_wid != width || img_hei != height) { + return FALSE; + } + if (pDIBitmap->GetBPP() == 32) { + FX_WORD rotation = ORIENTATION_TOPLEFT; + TIFFGetField(tif_ctx, TIFFTAG_ORIENTATION, &rotation); + if (TIFFReadRGBAImageOriented(tif_ctx, img_wid, img_hei, + (uint32*)pDIBitmap->GetBuffer(), rotation, + 1)) { + for (FX_DWORD row = 0; row < img_hei; row++) { + uint8_t* row_buf = (uint8_t*)pDIBitmap->GetScanline(row); + _TiffBGRA2RGBA(row_buf, img_wid, 4); + } + return TRUE; + } + } + uint16_t spp, bps; + TIFFGetField(tif_ctx, TIFFTAG_SAMPLESPERPIXEL, &spp); + TIFFGetField(tif_ctx, TIFFTAG_BITSPERSAMPLE, &bps); + FX_DWORD bpp = bps * spp; + if (bpp == 1) { + return Decode1bppRGB(pDIBitmap, height, width, bps, spp); + } else if (bpp <= 8) { + return Decode8bppRGB(pDIBitmap, height, width, bps, spp); + } else if (bpp <= 24) { + return Decode24bppRGB(pDIBitmap, height, width, bps, spp); + } + return FALSE; +} +void* CCodec_TiffModule::CreateDecoder(IFX_FileRead* file_ptr) { + CCodec_TiffContext* pDecoder = new CCodec_TiffContext; + if (!pDecoder->InitDecoder(file_ptr)) { + delete pDecoder; + return NULL; + } + return pDecoder; +} +void CCodec_TiffModule::GetFrames(void* ctx, int32_t& frames) { + CCodec_TiffContext* pDecoder = (CCodec_TiffContext*)ctx; + pDecoder->GetFrames(frames); +} +FX_BOOL CCodec_TiffModule::LoadFrameInfo(void* ctx, + int32_t frame, + FX_DWORD& width, + FX_DWORD& height, + FX_DWORD& comps, + FX_DWORD& bpc, + CFX_DIBAttribute* pAttribute) { + CCodec_TiffContext* pDecoder = (CCodec_TiffContext*)ctx; + return pDecoder->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute); +} +FX_BOOL CCodec_TiffModule::Decode(void* ctx, class CFX_DIBitmap* pDIBitmap) { + CCodec_TiffContext* pDecoder = (CCodec_TiffContext*)ctx; + return pDecoder->Decode(pDIBitmap); +} +void CCodec_TiffModule::DestroyDecoder(void* ctx) { + CCodec_TiffContext* pDecoder = (CCodec_TiffContext*)ctx; + delete pDecoder; +} diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp new file mode 100644 index 0000000000..1673110c7e --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp @@ -0,0 +1,120 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" + +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/include/fxcrt/fx_basic.h" + +namespace { + +struct JBig2ArithQe { + unsigned int Qe; + unsigned int NMPS; + unsigned int NLPS; + unsigned int nSwitch; +}; + +const JBig2ArithQe kQeTable[] = { + // Stupid hack to keep clang-format from reformatting this badly. + {0x5601, 1, 1, 1}, {0x3401, 2, 6, 0}, {0x1801, 3, 9, 0}, + {0x0AC1, 4, 12, 0}, {0x0521, 5, 29, 0}, {0x0221, 38, 33, 0}, + {0x5601, 7, 6, 1}, {0x5401, 8, 14, 0}, {0x4801, 9, 14, 0}, + {0x3801, 10, 14, 0}, {0x3001, 11, 17, 0}, {0x2401, 12, 18, 0}, + {0x1C01, 13, 20, 0}, {0x1601, 29, 21, 0}, {0x5601, 15, 14, 1}, + {0x5401, 16, 14, 0}, {0x5101, 17, 15, 0}, {0x4801, 18, 16, 0}, + {0x3801, 19, 17, 0}, {0x3401, 20, 18, 0}, {0x3001, 21, 19, 0}, + {0x2801, 22, 19, 0}, {0x2401, 23, 20, 0}, {0x2201, 24, 21, 0}, + {0x1C01, 25, 22, 0}, {0x1801, 26, 23, 0}, {0x1601, 27, 24, 0}, + {0x1401, 28, 25, 0}, {0x1201, 29, 26, 0}, {0x1101, 30, 27, 0}, + {0x0AC1, 31, 28, 0}, {0x09C1, 32, 29, 0}, {0x08A1, 33, 30, 0}, + {0x0521, 34, 31, 0}, {0x0441, 35, 32, 0}, {0x02A1, 36, 33, 0}, + {0x0221, 37, 34, 0}, {0x0141, 38, 35, 0}, {0x0111, 39, 36, 0}, + {0x0085, 40, 37, 0}, {0x0049, 41, 38, 0}, {0x0025, 42, 39, 0}, + {0x0015, 43, 40, 0}, {0x0009, 44, 41, 0}, {0x0005, 45, 42, 0}, + {0x0001, 45, 43, 0}, {0x5601, 46, 46, 0}}; + +const unsigned int kDefaultAValue = 0x8000; + +int DecodeNMPS(JBig2ArithCtx* pCX, const JBig2ArithQe& qe) { + pCX->I = qe.NMPS; + return pCX->MPS; +} + +int DecodeNLPS(JBig2ArithCtx* pCX, const JBig2ArithQe& qe) { + // TODO(thestig): |D|, |MPS| and friends probably should be booleans. + int D = 1 - pCX->MPS; + if (qe.nSwitch == 1) + pCX->MPS = 1 - pCX->MPS; + pCX->I = qe.NLPS; + return D; +} + +} // namespace + +CJBig2_ArithDecoder::CJBig2_ArithDecoder(CJBig2_BitStream* pStream) + : m_pStream(pStream) { + m_B = m_pStream->getCurByte_arith(); + m_C = (m_B ^ 0xff) << 16; + BYTEIN(); + m_C = m_C << 7; + m_CT = m_CT - 7; + m_A = kDefaultAValue; +} + +CJBig2_ArithDecoder::~CJBig2_ArithDecoder() {} + +int CJBig2_ArithDecoder::DECODE(JBig2ArithCtx* pCX) { + if (!pCX || pCX->I >= FX_ArraySize(kQeTable)) + return 0; + + const JBig2ArithQe& qe = kQeTable[pCX->I]; + m_A -= qe.Qe; + if ((m_C >> 16) < m_A) { + if (m_A & kDefaultAValue) + return pCX->MPS; + + const int D = m_A < qe.Qe ? DecodeNLPS(pCX, qe) : DecodeNMPS(pCX, qe); + ReadValueA(); + return D; + } + + m_C -= m_A << 16; + const int D = m_A < qe.Qe ? DecodeNMPS(pCX, qe) : DecodeNLPS(pCX, qe); + m_A = qe.Qe; + ReadValueA(); + return D; +} + +void CJBig2_ArithDecoder::BYTEIN() { + unsigned char B1; + if (m_B == 0xff) { + B1 = m_pStream->getNextByte_arith(); + if (B1 > 0x8f) { + m_CT = 8; + } else { + m_pStream->incByteIdx(); + m_B = B1; + m_C = m_C + 0xfe00 - (m_B << 9); + m_CT = 7; + } + } else { + m_pStream->incByteIdx(); + m_B = m_pStream->getCurByte_arith(); + m_C = m_C + 0xff00 - (m_B << 8); + m_CT = 8; + } +} + +void CJBig2_ArithDecoder::ReadValueA() { + do { + if (m_CT == 0) + BYTEIN(); + m_A <<= 1; + m_C <<= 1; + --m_CT; + } while ((m_A & kDefaultAValue) == 0); +} diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.h b/core/fxcodec/jbig2/JBig2_ArithDecoder.h new file mode 100644 index 0000000000..a8ab5dd730 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.h @@ -0,0 +1,38 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_ARITHDECODER_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_ARITHDECODER_H_ + +class CJBig2_BitStream; + +struct JBig2ArithCtx { + JBig2ArithCtx() : MPS(0), I(0) {} + + unsigned int MPS; + unsigned int I; +}; + +class CJBig2_ArithDecoder { + public: + explicit CJBig2_ArithDecoder(CJBig2_BitStream* pStream); + + ~CJBig2_ArithDecoder(); + + int DECODE(JBig2ArithCtx* pCX); + + private: + void BYTEIN(); + void ReadValueA(); + + unsigned char m_B; + unsigned int m_C; + unsigned int m_A; + unsigned int m_CT; + CJBig2_BitStream* const m_pStream; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_ARITHDECODER_H_ diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp new file mode 100644 index 0000000000..63594672c7 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp @@ -0,0 +1,92 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_ArithIntDecoder.h" + +#include <vector> + +#include "core/include/fxcrt/fx_basic.h" + +namespace { + +int ShiftOr(int val, int bitwise_or_val) { + return (val << 1) | bitwise_or_val; +} + +const struct ArithIntDecodeData { + int nNeedBits; + int nValue; +} g_ArithIntDecodeData[] = { + {2, 0}, {4, 4}, {6, 20}, {8, 84}, {12, 340}, {32, 4436}, +}; + +size_t RecursiveDecode(CJBig2_ArithDecoder* decoder, + std::vector<JBig2ArithCtx>* context, + int* prev, + size_t depth) { + static const size_t kDepthEnd = FX_ArraySize(g_ArithIntDecodeData) - 1; + if (depth == kDepthEnd) + return kDepthEnd; + + JBig2ArithCtx* pCX = &(*context)[*prev]; + int D = decoder->DECODE(pCX); + *prev = ShiftOr(*prev, D); + if (!D) + return depth; + return RecursiveDecode(decoder, context, prev, depth + 1); +} + +} // namespace + +CJBig2_ArithIntDecoder::CJBig2_ArithIntDecoder() { + m_IAx.resize(512); +} + +CJBig2_ArithIntDecoder::~CJBig2_ArithIntDecoder() {} + +bool CJBig2_ArithIntDecoder::decode(CJBig2_ArithDecoder* pArithDecoder, + int* nResult) { + int PREV = 1; + const int S = pArithDecoder->DECODE(&m_IAx[PREV]); + PREV = ShiftOr(PREV, S); + + const size_t nDecodeDataIndex = + RecursiveDecode(pArithDecoder, &m_IAx, &PREV, 0); + + int nTemp = 0; + for (int i = 0; i < g_ArithIntDecodeData[nDecodeDataIndex].nNeedBits; ++i) { + int D = pArithDecoder->DECODE(&m_IAx[PREV]); + PREV = ShiftOr(PREV, D); + if (PREV >= 256) + PREV = (PREV & 511) | 256; + nTemp = ShiftOr(nTemp, D); + } + int nValue = g_ArithIntDecodeData[nDecodeDataIndex].nValue; + nValue += nTemp; + if (S == 1 && nValue > 0) + nValue = -nValue; + + *nResult = nValue; + return S != 1 || nValue != 0; +} + +CJBig2_ArithIaidDecoder::CJBig2_ArithIaidDecoder(unsigned char SBSYMCODELENA) + : SBSYMCODELEN(SBSYMCODELENA) { + m_IAID.resize(1 << SBSYMCODELEN); +} + +CJBig2_ArithIaidDecoder::~CJBig2_ArithIaidDecoder() {} + +void CJBig2_ArithIaidDecoder::decode(CJBig2_ArithDecoder* pArithDecoder, + FX_DWORD* nResult) { + int PREV = 1; + for (unsigned char i = 0; i < SBSYMCODELEN; ++i) { + JBig2ArithCtx* pCX = &m_IAID[PREV]; + int D = pArithDecoder->DECODE(pCX); + PREV = ShiftOr(PREV, D); + } + *nResult = PREV - (1 << SBSYMCODELEN); +} diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h new file mode 100644 index 0000000000..9a04504ca7 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h @@ -0,0 +1,41 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_ + +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithIntDecoder { + public: + CJBig2_ArithIntDecoder(); + ~CJBig2_ArithIntDecoder(); + + // Returns true on success, and false when an OOB condition occurs. Many + // callers can tolerate OOB and do not check the return value. + bool decode(CJBig2_ArithDecoder* pArithDecoder, int* nResult); + + private: + std::vector<JBig2ArithCtx> m_IAx; +}; + +class CJBig2_ArithIaidDecoder { + public: + explicit CJBig2_ArithIaidDecoder(unsigned char SBSYMCODELENA); + ~CJBig2_ArithIaidDecoder(); + + void decode(CJBig2_ArithDecoder* pArithDecoder, FX_DWORD* nResult); + + private: + std::vector<JBig2ArithCtx> m_IAID; + + const unsigned char SBSYMCODELEN; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_ diff --git a/core/fxcodec/jbig2/JBig2_BitStream.cpp b/core/fxcodec/jbig2/JBig2_BitStream.cpp new file mode 100644 index 0000000000..87451c2d72 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_BitStream.cpp @@ -0,0 +1,188 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_BitStream.h" + +#include <algorithm> + +#include "core/include/fpdfapi/cpdf_stream.h" + +CJBig2_BitStream::CJBig2_BitStream(CPDF_StreamAcc* pSrcStream) + : m_pBuf(pSrcStream->GetData()), + m_dwLength(pSrcStream->GetSize()), + m_dwByteIdx(0), + m_dwBitIdx(0), + m_dwObjNum(pSrcStream->GetStream() ? pSrcStream->GetStream()->GetObjNum() + : 0) { + if (m_dwLength > 256 * 1024 * 1024) { + m_dwLength = 0; + m_pBuf = nullptr; + } +} + +CJBig2_BitStream::~CJBig2_BitStream() {} + +int32_t CJBig2_BitStream::readNBits(FX_DWORD dwBits, FX_DWORD* dwResult) { + FX_DWORD dwBitPos = getBitPos(); + if (dwBitPos > LengthInBits()) + return -1; + + *dwResult = 0; + if (dwBitPos + dwBits <= LengthInBits()) + dwBitPos = dwBits; + else + dwBitPos = LengthInBits() - dwBitPos; + + for (; dwBitPos > 0; --dwBitPos) { + *dwResult = + (*dwResult << 1) | ((m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01); + AdvanceBit(); + } + return 0; +} + +int32_t CJBig2_BitStream::readNBits(FX_DWORD dwBits, int32_t* nResult) { + FX_DWORD dwBitPos = getBitPos(); + if (dwBitPos > LengthInBits()) + return -1; + + *nResult = 0; + if (dwBitPos + dwBits <= LengthInBits()) + dwBitPos = dwBits; + else + dwBitPos = LengthInBits() - dwBitPos; + + for (; dwBitPos > 0; --dwBitPos) { + *nResult = + (*nResult << 1) | ((m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01); + AdvanceBit(); + } + return 0; +} + +int32_t CJBig2_BitStream::read1Bit(FX_DWORD* dwResult) { + if (!IsInBound()) + return -1; + + *dwResult = (m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01; + AdvanceBit(); + return 0; +} + +int32_t CJBig2_BitStream::read1Bit(FX_BOOL* bResult) { + if (!IsInBound()) + return -1; + + *bResult = (m_pBuf[m_dwByteIdx] >> (7 - m_dwBitIdx)) & 0x01; + AdvanceBit(); + return 0; +} + +int32_t CJBig2_BitStream::read1Byte(uint8_t* cResult) { + if (!IsInBound()) + return -1; + + *cResult = m_pBuf[m_dwByteIdx]; + ++m_dwByteIdx; + return 0; +} + +int32_t CJBig2_BitStream::readInteger(FX_DWORD* dwResult) { + if (m_dwByteIdx + 3 >= m_dwLength) + return -1; + + *dwResult = (m_pBuf[m_dwByteIdx] << 24) | (m_pBuf[m_dwByteIdx + 1] << 16) | + (m_pBuf[m_dwByteIdx + 2] << 8) | m_pBuf[m_dwByteIdx + 3]; + m_dwByteIdx += 4; + return 0; +} + +int32_t CJBig2_BitStream::readShortInteger(FX_WORD* dwResult) { + if (m_dwByteIdx + 1 >= m_dwLength) + return -1; + + *dwResult = (m_pBuf[m_dwByteIdx] << 8) | m_pBuf[m_dwByteIdx + 1]; + m_dwByteIdx += 2; + return 0; +} + +void CJBig2_BitStream::alignByte() { + if (m_dwBitIdx != 0) { + ++m_dwByteIdx; + m_dwBitIdx = 0; + } +} + +uint8_t CJBig2_BitStream::getCurByte() const { + return IsInBound() ? m_pBuf[m_dwByteIdx] : 0; +} + +void CJBig2_BitStream::incByteIdx() { + if (IsInBound()) + ++m_dwByteIdx; +} + +uint8_t CJBig2_BitStream::getCurByte_arith() const { + return IsInBound() ? m_pBuf[m_dwByteIdx] : 0xFF; +} + +uint8_t CJBig2_BitStream::getNextByte_arith() const { + return m_dwByteIdx + 1 < m_dwLength ? m_pBuf[m_dwByteIdx + 1] : 0xFF; +} + +FX_DWORD CJBig2_BitStream::getOffset() const { + return m_dwByteIdx; +} + +void CJBig2_BitStream::setOffset(FX_DWORD dwOffset) { + m_dwByteIdx = std::min(dwOffset, m_dwLength); +} + +FX_DWORD CJBig2_BitStream::getBitPos() const { + return (m_dwByteIdx << 3) + m_dwBitIdx; +} + +void CJBig2_BitStream::setBitPos(FX_DWORD dwBitPos) { + m_dwByteIdx = dwBitPos >> 3; + m_dwBitIdx = dwBitPos & 7; +} + +const uint8_t* CJBig2_BitStream::getBuf() const { + return m_pBuf; +} + +const uint8_t* CJBig2_BitStream::getPointer() const { + return m_pBuf + m_dwByteIdx; +} + +void CJBig2_BitStream::offset(FX_DWORD dwOffset) { + m_dwByteIdx += dwOffset; +} + +FX_DWORD CJBig2_BitStream::getByteLeft() const { + return m_dwLength - m_dwByteIdx; +} + +void CJBig2_BitStream::AdvanceBit() { + if (m_dwBitIdx == 7) { + ++m_dwByteIdx; + m_dwBitIdx = 0; + } else { + ++m_dwBitIdx; + } +} + +bool CJBig2_BitStream::IsInBound() const { + return m_dwByteIdx < m_dwLength; +} + +FX_DWORD CJBig2_BitStream::LengthInBits() const { + return m_dwLength << 3; +} + +FX_DWORD CJBig2_BitStream::getObjNum() const { + return m_dwObjNum; +} diff --git a/core/fxcodec/jbig2/JBig2_BitStream.h b/core/fxcodec/jbig2/JBig2_BitStream.h new file mode 100644 index 0000000000..c24fedfba1 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_BitStream.h @@ -0,0 +1,58 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_ + +#include "core/include/fxcrt/fx_basic.h" + +class CPDF_StreamAcc; + +class CJBig2_BitStream { + public: + explicit CJBig2_BitStream(CPDF_StreamAcc* pSrcStream); + ~CJBig2_BitStream(); + + // TODO(thestig): readFoo() should return bool. + int32_t readNBits(FX_DWORD nBits, FX_DWORD* dwResult); + int32_t readNBits(FX_DWORD nBits, int32_t* nResult); + int32_t read1Bit(FX_DWORD* dwResult); + int32_t read1Bit(FX_BOOL* bResult); + int32_t read1Byte(uint8_t* cResult); + int32_t readInteger(FX_DWORD* dwResult); + int32_t readShortInteger(FX_WORD* wResult); + void alignByte(); + uint8_t getCurByte() const; + void incByteIdx(); + uint8_t getCurByte_arith() const; + uint8_t getNextByte_arith() const; + FX_DWORD getOffset() const; + void setOffset(FX_DWORD dwOffset); + FX_DWORD getBitPos() const; + void setBitPos(FX_DWORD dwBitPos); + const uint8_t* getBuf() const; + FX_DWORD getLength() const { return m_dwLength; } + const uint8_t* getPointer() const; + void offset(FX_DWORD dwOffset); + FX_DWORD getByteLeft() const; + FX_DWORD getObjNum() const; + + private: + void AdvanceBit(); + bool IsInBound() const; + FX_DWORD LengthInBits() const; + + const uint8_t* m_pBuf; + FX_DWORD m_dwLength; + FX_DWORD m_dwByteIdx; + FX_DWORD m_dwBitIdx; + const FX_DWORD m_dwObjNum; + + CJBig2_BitStream(const CJBig2_BitStream&) = delete; + void operator=(const CJBig2_BitStream&) = delete; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_ diff --git a/core/fxcodec/jbig2/JBig2_Context.cpp b/core/fxcodec/jbig2/JBig2_Context.cpp new file mode 100644 index 0000000000..0a17fff328 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Context.cpp @@ -0,0 +1,1439 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_Context.h" + +#include <algorithm> +#include <list> +#include <utility> +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/fxcodec/jbig2/JBig2_GrdProc.h" +#include "core/fxcodec/jbig2/JBig2_GrrdProc.h" +#include "core/fxcodec/jbig2/JBig2_HtrdProc.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h" +#include "core/fxcodec/jbig2/JBig2_PddProc.h" +#include "core/fxcodec/jbig2/JBig2_SddProc.h" +#include "core/fxcodec/jbig2/JBig2_TrdProc.h" +#include "core/include/fpdfapi/cpdf_stream.h" +#include "third_party/base/stl_util.h" + +namespace { + +size_t GetHuffContextSize(uint8_t val) { + return val == 0 ? 65536 : val == 1 ? 8192 : 1024; +} + +size_t GetRefAggContextSize(FX_BOOL val) { + return val ? 1024 : 8192; +} + +} // namespace + +// Implement a very small least recently used (LRU) cache. It is very +// common for a JBIG2 dictionary to span multiple pages in a PDF file, +// and we do not want to decode the same dictionary over and over +// again. We key off of the memory location of the dictionary. The +// list keeps track of the freshness of entries, with freshest ones +// at the front. Even a tiny cache size like 2 makes a dramatic +// difference for typical JBIG2 documents. +static const int kSymbolDictCacheMaxSize = 2; + +CJBig2_Context* CJBig2_Context::CreateContext( + CPDF_StreamAcc* pGlobalStream, + CPDF_StreamAcc* pSrcStream, + std::list<CJBig2_CachePair>* pSymbolDictCache, + IFX_Pause* pPause) { + return new CJBig2_Context(pGlobalStream, pSrcStream, pSymbolDictCache, pPause, + false); +} + +void CJBig2_Context::DestroyContext(CJBig2_Context* pContext) { + delete pContext; +} + +CJBig2_Context::CJBig2_Context(CPDF_StreamAcc* pGlobalStream, + CPDF_StreamAcc* pSrcStream, + std::list<CJBig2_CachePair>* pSymbolDictCache, + IFX_Pause* pPause, + bool bIsGlobal) + : m_nSegmentDecoded(0), + m_bInPage(false), + m_bBufSpecified(false), + m_PauseStep(10), + m_pPause(pPause), + m_ProcessingStatus(FXCODEC_STATUS_FRAME_READY), + m_gbContext(NULL), + m_dwOffset(0), + m_pSymbolDictCache(pSymbolDictCache), + m_bIsGlobal(bIsGlobal) { + if (pGlobalStream && (pGlobalStream->GetSize() > 0)) { + m_pGlobalContext = new CJBig2_Context(nullptr, pGlobalStream, + pSymbolDictCache, pPause, true); + } else { + m_pGlobalContext = nullptr; + } + + m_pStream.reset(new CJBig2_BitStream(pSrcStream)); +} + +CJBig2_Context::~CJBig2_Context() { + FX_Free(m_gbContext); + m_gbContext = NULL; + delete m_pGlobalContext; + m_pGlobalContext = NULL; +} + +int32_t CJBig2_Context::decode_SquentialOrgnazation(IFX_Pause* pPause) { + int32_t nRet; + if (m_pStream->getByteLeft() <= 0) + return JBIG2_END_OF_FILE; + + while (m_pStream->getByteLeft() >= JBIG2_MIN_SEGMENT_SIZE) { + if (!m_pSegment) { + m_pSegment.reset(new CJBig2_Segment); + nRet = parseSegmentHeader(m_pSegment.get()); + if (nRet != JBIG2_SUCCESS) { + m_pSegment.reset(); + return nRet; + } + m_dwOffset = m_pStream->getOffset(); + } + nRet = parseSegmentData(m_pSegment.get(), pPause); + if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + m_PauseStep = 2; + return JBIG2_SUCCESS; + } + if (nRet == JBIG2_END_OF_PAGE || nRet == JBIG2_END_OF_FILE) { + m_pSegment.reset(); + return JBIG2_SUCCESS; + } + if (nRet != JBIG2_SUCCESS) { + m_pSegment.reset(); + return nRet; + } + if (m_pSegment->m_dwData_length != 0xffffffff) { + m_dwOffset += m_pSegment->m_dwData_length; + m_pStream->setOffset(m_dwOffset); + } else { + m_pStream->offset(4); + } + m_SegmentList.push_back(m_pSegment.release()); + if (m_pStream->getByteLeft() > 0 && m_pPage && pPause && + pPause->NeedToPauseNow()) { + m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + m_PauseStep = 2; + return JBIG2_SUCCESS; + } + } + return JBIG2_SUCCESS; +} +int32_t CJBig2_Context::decode_EmbedOrgnazation(IFX_Pause* pPause) { + return decode_SquentialOrgnazation(pPause); +} +int32_t CJBig2_Context::decode_RandomOrgnazation_FirstPage(IFX_Pause* pPause) { + int32_t nRet; + while (m_pStream->getByteLeft() > JBIG2_MIN_SEGMENT_SIZE) { + std::unique_ptr<CJBig2_Segment> pSegment(new CJBig2_Segment); + nRet = parseSegmentHeader(pSegment.get()); + if (nRet != JBIG2_SUCCESS) { + return nRet; + } else if (pSegment->m_cFlags.s.type == 51) { + break; + } + m_SegmentList.push_back(pSegment.release()); + if (pPause && m_pPause && pPause->NeedToPauseNow()) { + m_PauseStep = 3; + m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return JBIG2_SUCCESS; + } + } + m_nSegmentDecoded = 0; + return decode_RandomOrgnazation(pPause); +} +int32_t CJBig2_Context::decode_RandomOrgnazation(IFX_Pause* pPause) { + for (; m_nSegmentDecoded < m_SegmentList.size(); ++m_nSegmentDecoded) { + int32_t nRet = + parseSegmentData(m_SegmentList.get(m_nSegmentDecoded), pPause); + if (nRet == JBIG2_END_OF_PAGE || nRet == JBIG2_END_OF_FILE) + return JBIG2_SUCCESS; + + if (nRet != JBIG2_SUCCESS) + return nRet; + + if (m_pPage && pPause && pPause->NeedToPauseNow()) { + m_PauseStep = 4; + m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return JBIG2_SUCCESS; + } + } + return JBIG2_SUCCESS; +} +int32_t CJBig2_Context::getFirstPage(uint8_t* pBuf, + int32_t width, + int32_t height, + int32_t stride, + IFX_Pause* pPause) { + int32_t nRet = 0; + if (m_pGlobalContext) { + nRet = m_pGlobalContext->decode_EmbedOrgnazation(pPause); + if (nRet != JBIG2_SUCCESS) { + m_ProcessingStatus = FXCODEC_STATUS_ERROR; + return nRet; + } + } + m_PauseStep = 0; + m_pPage.reset(new CJBig2_Image(width, height, stride, pBuf)); + m_bBufSpecified = true; + if (pPause && pPause->NeedToPauseNow()) { + m_PauseStep = 1; + m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return nRet; + } + return Continue(pPause); +} +int32_t CJBig2_Context::Continue(IFX_Pause* pPause) { + m_ProcessingStatus = FXCODEC_STATUS_DECODE_READY; + int32_t nRet; + if (m_PauseStep <= 1) { + nRet = decode_EmbedOrgnazation(pPause); + } else if (m_PauseStep == 2) { + nRet = decode_SquentialOrgnazation(pPause); + } else if (m_PauseStep == 3) { + nRet = decode_RandomOrgnazation_FirstPage(pPause); + } else if (m_PauseStep == 4) { + nRet = decode_RandomOrgnazation(pPause); + } else if (m_PauseStep == 5) { + m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; + return JBIG2_SUCCESS; + } + if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + return nRet; + } + m_PauseStep = 5; + if (!m_bBufSpecified && nRet == JBIG2_SUCCESS) { + m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; + return JBIG2_SUCCESS; + } + if (nRet == JBIG2_SUCCESS) { + m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; + } else { + m_ProcessingStatus = FXCODEC_STATUS_ERROR; + } + return nRet; +} + +CJBig2_Segment* CJBig2_Context::findSegmentByNumber(FX_DWORD dwNumber) { + if (m_pGlobalContext) { + CJBig2_Segment* pSeg = m_pGlobalContext->findSegmentByNumber(dwNumber); + if (pSeg) { + return pSeg; + } + } + for (size_t i = 0; i < m_SegmentList.size(); ++i) { + CJBig2_Segment* pSeg = m_SegmentList.get(i); + if (pSeg->m_dwNumber == dwNumber) { + return pSeg; + } + } + return nullptr; +} +CJBig2_Segment* CJBig2_Context::findReferredSegmentByTypeAndIndex( + CJBig2_Segment* pSegment, + uint8_t cType, + int32_t nIndex) { + int32_t count = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i]); + if (pSeg && pSeg->m_cFlags.s.type == cType) { + if (count == nIndex) + return pSeg; + ++count; + } + } + return NULL; +} +int32_t CJBig2_Context::parseSegmentHeader(CJBig2_Segment* pSegment) { + if (m_pStream->readInteger(&pSegment->m_dwNumber) != 0 || + m_pStream->read1Byte(&pSegment->m_cFlags.c) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + + FX_DWORD dwTemp; + uint8_t cTemp = m_pStream->getCurByte(); + if ((cTemp >> 5) == 7) { + if (m_pStream->readInteger( + (FX_DWORD*)&pSegment->m_nReferred_to_segment_count) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + pSegment->m_nReferred_to_segment_count &= 0x1fffffff; + if (pSegment->m_nReferred_to_segment_count > + JBIG2_MAX_REFERRED_SEGMENT_COUNT) { + return JBIG2_ERROR_LIMIT; + } + dwTemp = 5 + 4 + (pSegment->m_nReferred_to_segment_count + 1) / 8; + } else { + if (m_pStream->read1Byte(&cTemp) != 0) + return JBIG2_ERROR_TOO_SHORT; + + pSegment->m_nReferred_to_segment_count = cTemp >> 5; + dwTemp = 5 + 1; + } + uint8_t cSSize = + pSegment->m_dwNumber > 65536 ? 4 : pSegment->m_dwNumber > 256 ? 2 : 1; + uint8_t cPSize = pSegment->m_cFlags.s.page_association_size ? 4 : 1; + if (pSegment->m_nReferred_to_segment_count) { + pSegment->m_pReferred_to_segment_numbers = + FX_Alloc(FX_DWORD, pSegment->m_nReferred_to_segment_count); + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + switch (cSSize) { + case 1: + if (m_pStream->read1Byte(&cTemp) != 0) + return JBIG2_ERROR_TOO_SHORT; + + pSegment->m_pReferred_to_segment_numbers[i] = cTemp; + break; + case 2: + FX_WORD wTemp; + if (m_pStream->readShortInteger(&wTemp) != 0) + return JBIG2_ERROR_TOO_SHORT; + + pSegment->m_pReferred_to_segment_numbers[i] = wTemp; + break; + case 4: + if (m_pStream->readInteger(&dwTemp) != 0) + return JBIG2_ERROR_TOO_SHORT; + + pSegment->m_pReferred_to_segment_numbers[i] = dwTemp; + break; + } + if (pSegment->m_pReferred_to_segment_numbers[i] >= pSegment->m_dwNumber) + return JBIG2_ERROR_TOO_SHORT; + } + } + if (cPSize == 1) { + if (m_pStream->read1Byte(&cTemp) != 0) + return JBIG2_ERROR_TOO_SHORT; + pSegment->m_dwPage_association = cTemp; + } else { + if (m_pStream->readInteger(&pSegment->m_dwPage_association) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + } + if (m_pStream->readInteger(&pSegment->m_dwData_length) != 0) + return JBIG2_ERROR_TOO_SHORT; + + pSegment->m_dwObjNum = m_pStream->getObjNum(); + pSegment->m_dwDataOffset = m_pStream->getOffset(); + pSegment->m_State = JBIG2_SEGMENT_DATA_UNPARSED; + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseSegmentData(CJBig2_Segment* pSegment, + IFX_Pause* pPause) { + int32_t ret = ProcessingParseSegmentData(pSegment, pPause); + while (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE && + m_pStream->getByteLeft() > 0) { + ret = ProcessingParseSegmentData(pSegment, pPause); + } + return ret; +} + +int32_t CJBig2_Context::ProcessingParseSegmentData(CJBig2_Segment* pSegment, + IFX_Pause* pPause) { + switch (pSegment->m_cFlags.s.type) { + case 0: + return parseSymbolDict(pSegment, pPause); + case 4: + case 6: + case 7: + if (!m_bInPage) + return JBIG2_ERROR_FATAL; + return parseTextRegion(pSegment); + case 16: + return parsePatternDict(pSegment, pPause); + case 20: + case 22: + case 23: + if (!m_bInPage) + return JBIG2_ERROR_FATAL; + return parseHalftoneRegion(pSegment, pPause); + case 36: + case 38: + case 39: + if (!m_bInPage) + return JBIG2_ERROR_FATAL; + return parseGenericRegion(pSegment, pPause); + case 40: + case 42: + case 43: + if (!m_bInPage) + return JBIG2_ERROR_FATAL; + return parseGenericRefinementRegion(pSegment); + case 48: { + FX_WORD wTemp; + std::unique_ptr<JBig2PageInfo> pPageInfo(new JBig2PageInfo); + if (m_pStream->readInteger(&pPageInfo->m_dwWidth) != 0 || + m_pStream->readInteger(&pPageInfo->m_dwHeight) != 0 || + m_pStream->readInteger(&pPageInfo->m_dwResolutionX) != 0 || + m_pStream->readInteger(&pPageInfo->m_dwResolutionY) != 0 || + m_pStream->read1Byte(&pPageInfo->m_cFlags) != 0 || + m_pStream->readShortInteger(&wTemp) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + pPageInfo->m_bIsStriped = !!(wTemp & 0x8000); + pPageInfo->m_wMaxStripeSize = wTemp & 0x7fff; + bool bMaxHeight = (pPageInfo->m_dwHeight == 0xffffffff); + if (bMaxHeight && pPageInfo->m_bIsStriped != TRUE) + pPageInfo->m_bIsStriped = TRUE; + + if (!m_bBufSpecified) { + FX_DWORD height = + bMaxHeight ? pPageInfo->m_wMaxStripeSize : pPageInfo->m_dwHeight; + m_pPage.reset(new CJBig2_Image(pPageInfo->m_dwWidth, height)); + } + + if (!m_pPage->m_pData) { + m_ProcessingStatus = FXCODEC_STATUS_ERROR; + return JBIG2_ERROR_TOO_SHORT; + } + + m_pPage->fill((pPageInfo->m_cFlags & 4) ? 1 : 0); + m_PageInfoList.push_back(pPageInfo.release()); + m_bInPage = true; + } break; + case 49: + m_bInPage = false; + return JBIG2_END_OF_PAGE; + break; + case 50: + m_pStream->offset(pSegment->m_dwData_length); + break; + case 51: + return JBIG2_END_OF_FILE; + case 52: + m_pStream->offset(pSegment->m_dwData_length); + break; + case 53: + return parseTable(pSegment); + case 62: + m_pStream->offset(pSegment->m_dwData_length); + break; + default: + break; + } + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseSymbolDict(CJBig2_Segment* pSegment, + IFX_Pause* pPause) { + FX_WORD wFlags; + if (m_pStream->readShortInteger(&wFlags) != 0) + return JBIG2_ERROR_TOO_SHORT; + + std::unique_ptr<CJBig2_SDDProc> pSymbolDictDecoder(new CJBig2_SDDProc); + pSymbolDictDecoder->SDHUFF = wFlags & 0x0001; + pSymbolDictDecoder->SDREFAGG = (wFlags >> 1) & 0x0001; + pSymbolDictDecoder->SDTEMPLATE = (wFlags >> 10) & 0x0003; + pSymbolDictDecoder->SDRTEMPLATE = (wFlags >> 12) & 0x0003; + uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003; + uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003; + uint8_t cSDHUFFBMSIZE = (wFlags >> 6) & 0x0001; + uint8_t cSDHUFFAGGINST = (wFlags >> 7) & 0x0001; + if (pSymbolDictDecoder->SDHUFF == 0) { + const FX_DWORD dwTemp = (pSymbolDictDecoder->SDTEMPLATE == 0) ? 8 : 2; + for (FX_DWORD i = 0; i < dwTemp; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDAT[i]) != 0) + return JBIG2_ERROR_TOO_SHORT; + } + } + if (pSymbolDictDecoder->SDREFAGG == 1 && + pSymbolDictDecoder->SDRTEMPLATE == 0) { + for (int32_t i = 0; i < 4; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDRAT[i]) != 0) + return JBIG2_ERROR_TOO_SHORT; + } + } + if (m_pStream->readInteger(&pSymbolDictDecoder->SDNUMEXSYMS) != 0 || + m_pStream->readInteger(&pSymbolDictDecoder->SDNUMNEWSYMS) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + if (pSymbolDictDecoder->SDNUMEXSYMS > JBIG2_MAX_EXPORT_SYSMBOLS || + pSymbolDictDecoder->SDNUMNEWSYMS > JBIG2_MAX_NEW_SYSMBOLS) { + return JBIG2_ERROR_LIMIT; + } + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + if (!findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i])) + return JBIG2_ERROR_FATAL; + } + CJBig2_Segment* pLRSeg = nullptr; + pSymbolDictDecoder->SDNUMINSYMS = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { + pSymbolDictDecoder->SDNUMINSYMS += pSeg->m_Result.sd->NumImages(); + pLRSeg = pSeg; + } + } + + std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SDINSYMS; + if (pSymbolDictDecoder->SDNUMINSYMS != 0) { + SDINSYMS.reset(FX_Alloc(CJBig2_Image*, pSymbolDictDecoder->SDNUMINSYMS)); + FX_DWORD dwTemp = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { + const CJBig2_SymbolDict& dict = *pSeg->m_Result.sd; + for (size_t j = 0; j < dict.NumImages(); ++j) + SDINSYMS.get()[dwTemp + j] = dict.GetImage(j); + dwTemp += dict.NumImages(); + } + } + } + pSymbolDictDecoder->SDINSYMS = SDINSYMS.get(); + + std::unique_ptr<CJBig2_HuffmanTable> Table_B1; + std::unique_ptr<CJBig2_HuffmanTable> Table_B2; + std::unique_ptr<CJBig2_HuffmanTable> Table_B3; + std::unique_ptr<CJBig2_HuffmanTable> Table_B4; + std::unique_ptr<CJBig2_HuffmanTable> Table_B5; + if (pSymbolDictDecoder->SDHUFF == 1) { + if (cSDHUFFDH == 2 || cSDHUFFDW == 2) + return JBIG2_ERROR_FATAL; + + int32_t nIndex = 0; + if (cSDHUFFDH == 0) { + Table_B4.reset(new CJBig2_HuffmanTable(HuffmanTable_B4, + FX_ArraySize(HuffmanTable_B4), + HuffmanTable_HTOOB_B4)); + pSymbolDictDecoder->SDHUFFDH = Table_B4.get(); + } else if (cSDHUFFDH == 1) { + Table_B5.reset(new CJBig2_HuffmanTable(HuffmanTable_B5, + FX_ArraySize(HuffmanTable_B5), + HuffmanTable_HTOOB_B5)); + pSymbolDictDecoder->SDHUFFDH = Table_B5.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pSymbolDictDecoder->SDHUFFDH = pSeg->m_Result.ht; + } + if (cSDHUFFDW == 0) { + Table_B2.reset(new CJBig2_HuffmanTable(HuffmanTable_B2, + FX_ArraySize(HuffmanTable_B2), + HuffmanTable_HTOOB_B2)); + pSymbolDictDecoder->SDHUFFDW = Table_B2.get(); + } else if (cSDHUFFDW == 1) { + Table_B3.reset(new CJBig2_HuffmanTable(HuffmanTable_B3, + FX_ArraySize(HuffmanTable_B3), + HuffmanTable_HTOOB_B3)); + pSymbolDictDecoder->SDHUFFDW = Table_B3.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pSymbolDictDecoder->SDHUFFDW = pSeg->m_Result.ht; + } + if (cSDHUFFBMSIZE == 0) { + Table_B1.reset(new CJBig2_HuffmanTable(HuffmanTable_B1, + FX_ArraySize(HuffmanTable_B1), + HuffmanTable_HTOOB_B1)); + pSymbolDictDecoder->SDHUFFBMSIZE = Table_B1.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pSymbolDictDecoder->SDHUFFBMSIZE = pSeg->m_Result.ht; + } + if (pSymbolDictDecoder->SDREFAGG == 1) { + if (cSDHUFFAGGINST == 0) { + if (!Table_B1) { + Table_B1.reset(new CJBig2_HuffmanTable(HuffmanTable_B1, + FX_ArraySize(HuffmanTable_B1), + HuffmanTable_HTOOB_B1)); + } + pSymbolDictDecoder->SDHUFFAGGINST = Table_B1.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pSymbolDictDecoder->SDHUFFAGGINST = pSeg->m_Result.ht; + } + } + } + + const bool bUseGbContext = (pSymbolDictDecoder->SDHUFF == 0); + const bool bUseGrContext = (pSymbolDictDecoder->SDREFAGG == 1); + const size_t gbContextSize = + GetHuffContextSize(pSymbolDictDecoder->SDTEMPLATE); + const size_t grContextSize = + GetRefAggContextSize(pSymbolDictDecoder->SDRTEMPLATE); + std::vector<JBig2ArithCtx> gbContext; + std::vector<JBig2ArithCtx> grContext; + if ((wFlags & 0x0100) && pLRSeg) { + if (bUseGbContext) { + gbContext = pLRSeg->m_Result.sd->GbContext(); + if (gbContext.size() != gbContextSize) + return JBIG2_ERROR_FATAL; + } + if (bUseGrContext) { + grContext = pLRSeg->m_Result.sd->GrContext(); + if (grContext.size() != grContextSize) + return JBIG2_ERROR_FATAL; + } + } else { + if (bUseGbContext) + gbContext.resize(gbContextSize); + if (bUseGrContext) + grContext.resize(grContextSize); + } + + CJBig2_CacheKey key = + CJBig2_CacheKey(pSegment->m_dwObjNum, pSegment->m_dwDataOffset); + FX_BOOL cache_hit = false; + pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER; + if (m_bIsGlobal && key.first != 0) { + for (auto it = m_pSymbolDictCache->begin(); it != m_pSymbolDictCache->end(); + ++it) { + if (it->first == key) { + std::unique_ptr<CJBig2_SymbolDict> copy(it->second->DeepCopy()); + pSegment->m_Result.sd = copy.release(); + m_pSymbolDictCache->push_front(*it); + m_pSymbolDictCache->erase(it); + cache_hit = true; + break; + } + } + } + if (!cache_hit) { + if (bUseGbContext) { + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(m_pStream.get())); + pSegment->m_Result.sd = pSymbolDictDecoder->decode_Arith( + pArithDecoder.get(), &gbContext, &grContext); + if (!pSegment->m_Result.sd) + return JBIG2_ERROR_FATAL; + + m_pStream->alignByte(); + m_pStream->offset(2); + } else { + pSegment->m_Result.sd = pSymbolDictDecoder->decode_Huffman( + m_pStream.get(), &gbContext, &grContext, pPause); + if (!pSegment->m_Result.sd) + return JBIG2_ERROR_FATAL; + m_pStream->alignByte(); + } + if (m_bIsGlobal && kSymbolDictCacheMaxSize > 0) { + std::unique_ptr<CJBig2_SymbolDict> value = + pSegment->m_Result.sd->DeepCopy(); + int size = pdfium::CollectionSize<int>(*m_pSymbolDictCache); + while (size >= kSymbolDictCacheMaxSize) { + delete m_pSymbolDictCache->back().second; + m_pSymbolDictCache->pop_back(); + --size; + } + m_pSymbolDictCache->push_front(CJBig2_CachePair(key, value.release())); + } + } + if (wFlags & 0x0200) { + if (bUseGbContext) + pSegment->m_Result.sd->SetGbContext(gbContext); + if (bUseGrContext) + pSegment->m_Result.sd->SetGrContext(grContext); + } + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseTextRegion(CJBig2_Segment* pSegment) { + FX_WORD wFlags; + JBig2RegionInfo ri; + if (parseRegionInfo(&ri) != JBIG2_SUCCESS || + m_pStream->readShortInteger(&wFlags) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + + std::unique_ptr<CJBig2_TRDProc> pTRD(new CJBig2_TRDProc); + pTRD->SBW = ri.width; + pTRD->SBH = ri.height; + pTRD->SBHUFF = wFlags & 0x0001; + pTRD->SBREFINE = (wFlags >> 1) & 0x0001; + FX_DWORD dwTemp = (wFlags >> 2) & 0x0003; + pTRD->SBSTRIPS = 1 << dwTemp; + pTRD->REFCORNER = (JBig2Corner)((wFlags >> 4) & 0x0003); + pTRD->TRANSPOSED = (wFlags >> 6) & 0x0001; + pTRD->SBCOMBOP = (JBig2ComposeOp)((wFlags >> 7) & 0x0003); + pTRD->SBDEFPIXEL = (wFlags >> 9) & 0x0001; + pTRD->SBDSOFFSET = (wFlags >> 10) & 0x001f; + if (pTRD->SBDSOFFSET >= 0x0010) { + pTRD->SBDSOFFSET = pTRD->SBDSOFFSET - 0x0020; + } + pTRD->SBRTEMPLATE = (wFlags >> 15) & 0x0001; + + uint8_t cSBHUFFFS; + uint8_t cSBHUFFDS; + uint8_t cSBHUFFDT; + uint8_t cSBHUFFRDW; + uint8_t cSBHUFFRDH; + uint8_t cSBHUFFRDX; + uint8_t cSBHUFFRDY; + uint8_t cSBHUFFRSIZE; + if (pTRD->SBHUFF == 1) { + if (m_pStream->readShortInteger(&wFlags) != 0) + return JBIG2_ERROR_TOO_SHORT; + + cSBHUFFFS = wFlags & 0x0003; + cSBHUFFDS = (wFlags >> 2) & 0x0003; + cSBHUFFDT = (wFlags >> 4) & 0x0003; + cSBHUFFRDW = (wFlags >> 6) & 0x0003; + cSBHUFFRDH = (wFlags >> 8) & 0x0003; + cSBHUFFRDX = (wFlags >> 10) & 0x0003; + cSBHUFFRDY = (wFlags >> 12) & 0x0003; + cSBHUFFRSIZE = (wFlags >> 14) & 0x0001; + } + if (pTRD->SBREFINE == 1 && pTRD->SBRTEMPLATE == 0) { + for (int32_t i = 0; i < 4; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pTRD->SBRAT[i]) != 0) + return JBIG2_ERROR_TOO_SHORT; + } + } + if (m_pStream->readInteger(&pTRD->SBNUMINSTANCES) != 0) + return JBIG2_ERROR_TOO_SHORT; + + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + if (!findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i])) + return JBIG2_ERROR_FATAL; + } + + pTRD->SBNUMSYMS = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { + pTRD->SBNUMSYMS += pSeg->m_Result.sd->NumImages(); + } + } + + std::unique_ptr<CJBig2_Image*, FxFreeDeleter> SBSYMS; + if (pTRD->SBNUMSYMS > 0) { + SBSYMS.reset(FX_Alloc(CJBig2_Image*, pTRD->SBNUMSYMS)); + dwTemp = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { + const CJBig2_SymbolDict& dict = *pSeg->m_Result.sd; + for (size_t j = 0; j < dict.NumImages(); ++j) + SBSYMS.get()[dwTemp + j] = dict.GetImage(j); + dwTemp += dict.NumImages(); + } + } + pTRD->SBSYMS = SBSYMS.get(); + } else { + pTRD->SBSYMS = NULL; + } + + std::unique_ptr<JBig2HuffmanCode, FxFreeDeleter> SBSYMCODES; + if (pTRD->SBHUFF == 1) { + SBSYMCODES.reset( + decodeSymbolIDHuffmanTable(m_pStream.get(), pTRD->SBNUMSYMS)); + if (!SBSYMCODES) + return JBIG2_ERROR_FATAL; + + m_pStream->alignByte(); + pTRD->SBSYMCODES = SBSYMCODES.get(); + } else { + dwTemp = 0; + while ((FX_DWORD)(1 << dwTemp) < pTRD->SBNUMSYMS) { + ++dwTemp; + } + pTRD->SBSYMCODELEN = (uint8_t)dwTemp; + } + + std::unique_ptr<CJBig2_HuffmanTable> Table_B1; + std::unique_ptr<CJBig2_HuffmanTable> Table_B6; + std::unique_ptr<CJBig2_HuffmanTable> Table_B7; + std::unique_ptr<CJBig2_HuffmanTable> Table_B8; + std::unique_ptr<CJBig2_HuffmanTable> Table_B9; + std::unique_ptr<CJBig2_HuffmanTable> Table_B10; + std::unique_ptr<CJBig2_HuffmanTable> Table_B11; + std::unique_ptr<CJBig2_HuffmanTable> Table_B12; + std::unique_ptr<CJBig2_HuffmanTable> Table_B13; + std::unique_ptr<CJBig2_HuffmanTable> Table_B14; + std::unique_ptr<CJBig2_HuffmanTable> Table_B15; + if (pTRD->SBHUFF == 1) { + if (cSBHUFFFS == 2 || cSBHUFFRDW == 2 || cSBHUFFRDH == 2 || + cSBHUFFRDX == 2 || cSBHUFFRDY == 2) { + return JBIG2_ERROR_FATAL; + } + int32_t nIndex = 0; + if (cSBHUFFFS == 0) { + Table_B6.reset(new CJBig2_HuffmanTable(HuffmanTable_B6, + FX_ArraySize(HuffmanTable_B6), + HuffmanTable_HTOOB_B6)); + pTRD->SBHUFFFS = Table_B6.get(); + } else if (cSBHUFFFS == 1) { + Table_B7.reset(new CJBig2_HuffmanTable(HuffmanTable_B7, + FX_ArraySize(HuffmanTable_B7), + HuffmanTable_HTOOB_B7)); + pTRD->SBHUFFFS = Table_B7.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFFS = pSeg->m_Result.ht; + } + if (cSBHUFFDS == 0) { + Table_B8.reset(new CJBig2_HuffmanTable(HuffmanTable_B8, + FX_ArraySize(HuffmanTable_B8), + HuffmanTable_HTOOB_B8)); + pTRD->SBHUFFDS = Table_B8.get(); + } else if (cSBHUFFDS == 1) { + Table_B9.reset(new CJBig2_HuffmanTable(HuffmanTable_B9, + FX_ArraySize(HuffmanTable_B9), + HuffmanTable_HTOOB_B9)); + pTRD->SBHUFFDS = Table_B9.get(); + } else if (cSBHUFFDS == 2) { + Table_B10.reset(new CJBig2_HuffmanTable(HuffmanTable_B10, + FX_ArraySize(HuffmanTable_B10), + HuffmanTable_HTOOB_B10)); + pTRD->SBHUFFDS = Table_B10.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFDS = pSeg->m_Result.ht; + } + if (cSBHUFFDT == 0) { + Table_B11.reset(new CJBig2_HuffmanTable(HuffmanTable_B11, + FX_ArraySize(HuffmanTable_B11), + HuffmanTable_HTOOB_B11)); + pTRD->SBHUFFDT = Table_B11.get(); + } else if (cSBHUFFDT == 1) { + Table_B12.reset(new CJBig2_HuffmanTable(HuffmanTable_B12, + FX_ArraySize(HuffmanTable_B12), + HuffmanTable_HTOOB_B12)); + pTRD->SBHUFFDT = Table_B12.get(); + } else if (cSBHUFFDT == 2) { + Table_B13.reset(new CJBig2_HuffmanTable(HuffmanTable_B13, + FX_ArraySize(HuffmanTable_B13), + HuffmanTable_HTOOB_B13)); + pTRD->SBHUFFDT = Table_B13.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFDT = pSeg->m_Result.ht; + } + if (cSBHUFFRDW == 0) { + Table_B14.reset(new CJBig2_HuffmanTable(HuffmanTable_B14, + FX_ArraySize(HuffmanTable_B14), + HuffmanTable_HTOOB_B14)); + pTRD->SBHUFFRDW = Table_B14.get(); + } else if (cSBHUFFRDW == 1) { + Table_B15.reset(new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + pTRD->SBHUFFRDW = Table_B15.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFRDW = pSeg->m_Result.ht; + } + if (cSBHUFFRDH == 0) { + if (!Table_B14) { + Table_B14.reset(new CJBig2_HuffmanTable(HuffmanTable_B14, + FX_ArraySize(HuffmanTable_B14), + HuffmanTable_HTOOB_B14)); + } + pTRD->SBHUFFRDH = Table_B14.get(); + } else if (cSBHUFFRDH == 1) { + if (!Table_B15) { + Table_B15.reset(new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + } + pTRD->SBHUFFRDH = Table_B15.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFRDH = pSeg->m_Result.ht; + } + if (cSBHUFFRDX == 0) { + if (!Table_B14) { + Table_B14.reset(new CJBig2_HuffmanTable(HuffmanTable_B14, + FX_ArraySize(HuffmanTable_B14), + HuffmanTable_HTOOB_B14)); + } + pTRD->SBHUFFRDX = Table_B14.get(); + } else if (cSBHUFFRDX == 1) { + if (!Table_B15) { + Table_B15.reset(new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + } + pTRD->SBHUFFRDX = Table_B15.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFRDX = pSeg->m_Result.ht; + } + if (cSBHUFFRDY == 0) { + if (!Table_B14) { + Table_B14.reset(new CJBig2_HuffmanTable(HuffmanTable_B14, + FX_ArraySize(HuffmanTable_B14), + HuffmanTable_HTOOB_B14)); + } + pTRD->SBHUFFRDY = Table_B14.get(); + } else if (cSBHUFFRDY == 1) { + if (!Table_B15) { + Table_B15.reset(new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + } + pTRD->SBHUFFRDY = Table_B15.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFRDY = pSeg->m_Result.ht; + } + if (cSBHUFFRSIZE == 0) { + Table_B1.reset(new CJBig2_HuffmanTable(HuffmanTable_B1, + FX_ArraySize(HuffmanTable_B1), + HuffmanTable_HTOOB_B1)); + pTRD->SBHUFFRSIZE = Table_B1.get(); + } else { + CJBig2_Segment* pSeg = + findReferredSegmentByTypeAndIndex(pSegment, 53, nIndex++); + if (!pSeg) + return JBIG2_ERROR_FATAL; + pTRD->SBHUFFRSIZE = pSeg->m_Result.ht; + } + } + std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext; + if (pTRD->SBREFINE == 1) { + const size_t size = GetRefAggContextSize(pTRD->SBRTEMPLATE); + grContext.reset(FX_Alloc(JBig2ArithCtx, size)); + JBIG2_memset(grContext.get(), 0, sizeof(JBig2ArithCtx) * size); + } + if (pTRD->SBHUFF == 0) { + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(m_pStream.get())); + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; + pSegment->m_Result.im = + pTRD->decode_Arith(pArithDecoder.get(), grContext.get(), nullptr); + if (!pSegment->m_Result.im) + return JBIG2_ERROR_FATAL; + m_pStream->alignByte(); + m_pStream->offset(2); + } else { + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; + pSegment->m_Result.im = + pTRD->decode_Huffman(m_pStream.get(), grContext.get()); + if (!pSegment->m_Result.im) + return JBIG2_ERROR_FATAL; + m_pStream->alignByte(); + } + if (pSegment->m_cFlags.s.type != 4) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back(); + if ((pPageInfo->m_bIsStriped == 1) && + (ri.y + ri.height > m_pPage->m_nHeight)) { + m_pPage->expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); + } + } + m_pPage->composeFrom(ri.x, ri.y, pSegment->m_Result.im, + (JBig2ComposeOp)(ri.flags & 0x03)); + delete pSegment->m_Result.im; + pSegment->m_Result.im = NULL; + } + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parsePatternDict(CJBig2_Segment* pSegment, + IFX_Pause* pPause) { + uint8_t cFlags; + std::unique_ptr<CJBig2_PDDProc> pPDD(new CJBig2_PDDProc); + if (m_pStream->read1Byte(&cFlags) != 0 || + m_pStream->read1Byte(&pPDD->HDPW) != 0 || + m_pStream->read1Byte(&pPDD->HDPH) != 0 || + m_pStream->readInteger(&pPDD->GRAYMAX) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + if (pPDD->GRAYMAX > JBIG2_MAX_PATTERN_INDEX) + return JBIG2_ERROR_LIMIT; + + pPDD->HDMMR = cFlags & 0x01; + pPDD->HDTEMPLATE = (cFlags >> 1) & 0x03; + pSegment->m_nResultType = JBIG2_PATTERN_DICT_POINTER; + if (pPDD->HDMMR == 0) { + const size_t size = GetHuffContextSize(pPDD->HDTEMPLATE); + std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext( + FX_Alloc(JBig2ArithCtx, size)); + JBIG2_memset(gbContext.get(), 0, sizeof(JBig2ArithCtx) * size); + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(m_pStream.get())); + pSegment->m_Result.pd = + pPDD->decode_Arith(pArithDecoder.get(), gbContext.get(), pPause); + if (!pSegment->m_Result.pd) + return JBIG2_ERROR_FATAL; + + m_pStream->alignByte(); + m_pStream->offset(2); + } else { + pSegment->m_Result.pd = pPDD->decode_MMR(m_pStream.get(), pPause); + if (!pSegment->m_Result.pd) + return JBIG2_ERROR_FATAL; + m_pStream->alignByte(); + } + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseHalftoneRegion(CJBig2_Segment* pSegment, + IFX_Pause* pPause) { + uint8_t cFlags; + JBig2RegionInfo ri; + std::unique_ptr<CJBig2_HTRDProc> pHRD(new CJBig2_HTRDProc); + if (parseRegionInfo(&ri) != JBIG2_SUCCESS || + m_pStream->read1Byte(&cFlags) != 0 || + m_pStream->readInteger(&pHRD->HGW) != 0 || + m_pStream->readInteger(&pHRD->HGH) != 0 || + m_pStream->readInteger((FX_DWORD*)&pHRD->HGX) != 0 || + m_pStream->readInteger((FX_DWORD*)&pHRD->HGY) != 0 || + m_pStream->readShortInteger(&pHRD->HRX) != 0 || + m_pStream->readShortInteger(&pHRD->HRY) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + + if (pHRD->HGW == 0 || pHRD->HGH == 0) + return JBIG2_ERROR_FATAL; + + pHRD->HBW = ri.width; + pHRD->HBH = ri.height; + pHRD->HMMR = cFlags & 0x01; + pHRD->HTEMPLATE = (cFlags >> 1) & 0x03; + pHRD->HENABLESKIP = (cFlags >> 3) & 0x01; + pHRD->HCOMBOP = (JBig2ComposeOp)((cFlags >> 4) & 0x07); + pHRD->HDEFPIXEL = (cFlags >> 7) & 0x01; + if (pSegment->m_nReferred_to_segment_count != 1) + return JBIG2_ERROR_FATAL; + + CJBig2_Segment* pSeg = + findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[0]); + if (!pSeg || (pSeg->m_cFlags.s.type != 16)) + return JBIG2_ERROR_FATAL; + + CJBig2_PatternDict* pPatternDict = pSeg->m_Result.pd; + if (!pPatternDict || (pPatternDict->NUMPATS == 0)) + return JBIG2_ERROR_FATAL; + + pHRD->HNUMPATS = pPatternDict->NUMPATS; + pHRD->HPATS = pPatternDict->HDPATS; + pHRD->HPW = pPatternDict->HDPATS[0]->m_nWidth; + pHRD->HPH = pPatternDict->HDPATS[0]->m_nHeight; + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; + if (pHRD->HMMR == 0) { + const size_t size = GetHuffContextSize(pHRD->HTEMPLATE); + std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> gbContext( + FX_Alloc(JBig2ArithCtx, size)); + JBIG2_memset(gbContext.get(), 0, sizeof(JBig2ArithCtx) * size); + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(m_pStream.get())); + pSegment->m_Result.im = + pHRD->decode_Arith(pArithDecoder.get(), gbContext.get(), pPause); + if (!pSegment->m_Result.im) + return JBIG2_ERROR_FATAL; + + m_pStream->alignByte(); + m_pStream->offset(2); + } else { + pSegment->m_Result.im = pHRD->decode_MMR(m_pStream.get(), pPause); + if (!pSegment->m_Result.im) + return JBIG2_ERROR_FATAL; + m_pStream->alignByte(); + } + if (pSegment->m_cFlags.s.type != 20) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back(); + if (pPageInfo->m_bIsStriped == 1 && + ri.y + ri.height > m_pPage->m_nHeight) { + m_pPage->expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); + } + } + m_pPage->composeFrom(ri.x, ri.y, pSegment->m_Result.im, + (JBig2ComposeOp)(ri.flags & 0x03)); + delete pSegment->m_Result.im; + pSegment->m_Result.im = NULL; + } + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseGenericRegion(CJBig2_Segment* pSegment, + IFX_Pause* pPause) { + if (!m_pGRD) { + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc); + uint8_t cFlags; + if (parseRegionInfo(&m_ri) != JBIG2_SUCCESS || + m_pStream->read1Byte(&cFlags) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + if (m_ri.height < 0 || m_ri.width < 0) + return JBIG2_FAILED; + + pGRD->GBW = m_ri.width; + pGRD->GBH = m_ri.height; + pGRD->MMR = cFlags & 0x01; + pGRD->GBTEMPLATE = (cFlags >> 1) & 0x03; + pGRD->TPGDON = (cFlags >> 3) & 0x01; + if (pGRD->MMR == 0) { + if (pGRD->GBTEMPLATE == 0) { + for (int32_t i = 0; i < 8; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + } + } else { + for (int32_t i = 0; i < 2; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + } + } + } + pGRD->USESKIP = 0; + m_pGRD = std::move(pGRD); + } + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; + if (m_pGRD->MMR == 0) { + if (!m_gbContext) { + const size_t size = GetHuffContextSize(m_pGRD->GBTEMPLATE); + m_gbContext = FX_Alloc(JBig2ArithCtx, size); + JBIG2_memset(m_gbContext, 0, sizeof(JBig2ArithCtx) * size); + } + if (!m_pArithDecoder) { + m_pArithDecoder.reset(new CJBig2_ArithDecoder(m_pStream.get())); + m_ProcessingStatus = m_pGRD->Start_decode_Arith( + &pSegment->m_Result.im, m_pArithDecoder.get(), m_gbContext, pPause); + } else { + m_ProcessingStatus = m_pGRD->Continue_decode(pPause); + } + if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + if (pSegment->m_cFlags.s.type != 36) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back(); + if ((pPageInfo->m_bIsStriped == 1) && + (m_ri.y + m_ri.height > m_pPage->m_nHeight)) { + m_pPage->expand(m_ri.y + m_ri.height, + (pPageInfo->m_cFlags & 4) ? 1 : 0); + } + } + FX_RECT Rect = m_pGRD->GetReplaceRect(); + m_pPage->composeFrom(m_ri.x + Rect.left, m_ri.y + Rect.top, + pSegment->m_Result.im, + (JBig2ComposeOp)(m_ri.flags & 0x03), &Rect); + } + return JBIG2_SUCCESS; + } else { + m_pArithDecoder.reset(); + FX_Free(m_gbContext); + m_gbContext = NULL; + if (!pSegment->m_Result.im) { + m_ProcessingStatus = FXCODEC_STATUS_ERROR; + m_pGRD.reset(); + return JBIG2_ERROR_FATAL; + } + m_pStream->alignByte(); + m_pStream->offset(2); + } + } else { + FXCODEC_STATUS status = m_pGRD->Start_decode_MMR(&pSegment->m_Result.im, + m_pStream.get(), pPause); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + m_pGRD->Continue_decode(pPause); + } + if (!pSegment->m_Result.im) { + m_pGRD.reset(); + return JBIG2_ERROR_FATAL; + } + m_pStream->alignByte(); + } + if (pSegment->m_cFlags.s.type != 36) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back(); + if ((pPageInfo->m_bIsStriped == 1) && + (m_ri.y + m_ri.height > m_pPage->m_nHeight)) { + m_pPage->expand(m_ri.y + m_ri.height, + (pPageInfo->m_cFlags & 4) ? 1 : 0); + } + } + FX_RECT Rect = m_pGRD->GetReplaceRect(); + m_pPage->composeFrom(m_ri.x + Rect.left, m_ri.y + Rect.top, + pSegment->m_Result.im, + (JBig2ComposeOp)(m_ri.flags & 0x03), &Rect); + delete pSegment->m_Result.im; + pSegment->m_Result.im = NULL; + } + m_pGRD.reset(); + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseGenericRefinementRegion(CJBig2_Segment* pSegment) { + JBig2RegionInfo ri; + uint8_t cFlags; + if (parseRegionInfo(&ri) != JBIG2_SUCCESS || + m_pStream->read1Byte(&cFlags) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + std::unique_ptr<CJBig2_GRRDProc> pGRRD(new CJBig2_GRRDProc); + pGRRD->GRW = ri.width; + pGRRD->GRH = ri.height; + pGRRD->GRTEMPLATE = cFlags & 0x01; + pGRRD->TPGRON = (cFlags >> 1) & 0x01; + if (pGRRD->GRTEMPLATE == 0) { + for (int32_t i = 0; i < 4; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pGRRD->GRAT[i]) != 0) + return JBIG2_ERROR_TOO_SHORT; + } + } + CJBig2_Segment* pSeg = nullptr; + if (pSegment->m_nReferred_to_segment_count > 0) { + int32_t i; + for (i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + pSeg = findSegmentByNumber(pSegment->m_pReferred_to_segment_numbers[0]); + if (!pSeg) + return JBIG2_ERROR_FATAL; + + if (pSeg->m_cFlags.s.type == 4 || pSeg->m_cFlags.s.type == 20 || + pSeg->m_cFlags.s.type == 36 || pSeg->m_cFlags.s.type == 40) { + break; + } + } + if (i >= pSegment->m_nReferred_to_segment_count) + return JBIG2_ERROR_FATAL; + + pGRRD->GRREFERENCE = pSeg->m_Result.im; + } else { + pGRRD->GRREFERENCE = m_pPage.get(); + } + pGRRD->GRREFERENCEDX = 0; + pGRRD->GRREFERENCEDY = 0; + const size_t size = GetRefAggContextSize(pGRRD->GRTEMPLATE); + std::unique_ptr<JBig2ArithCtx, FxFreeDeleter> grContext( + FX_Alloc(JBig2ArithCtx, size)); + JBIG2_memset(grContext.get(), 0, sizeof(JBig2ArithCtx) * size); + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(m_pStream.get())); + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; + pSegment->m_Result.im = pGRRD->decode(pArithDecoder.get(), grContext.get()); + if (!pSegment->m_Result.im) + return JBIG2_ERROR_FATAL; + + m_pStream->alignByte(); + m_pStream->offset(2); + if (pSegment->m_cFlags.s.type != 40) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back(); + if ((pPageInfo->m_bIsStriped == 1) && + (ri.y + ri.height > m_pPage->m_nHeight)) { + m_pPage->expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); + } + } + m_pPage->composeFrom(ri.x, ri.y, pSegment->m_Result.im, + (JBig2ComposeOp)(ri.flags & 0x03)); + delete pSegment->m_Result.im; + pSegment->m_Result.im = NULL; + } + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseTable(CJBig2_Segment* pSegment) { + pSegment->m_nResultType = JBIG2_HUFFMAN_TABLE_POINTER; + pSegment->m_Result.ht = nullptr; + std::unique_ptr<CJBig2_HuffmanTable> pHuff( + new CJBig2_HuffmanTable(m_pStream.get())); + if (!pHuff->IsOK()) + return JBIG2_ERROR_FATAL; + + pSegment->m_Result.ht = pHuff.release(); + m_pStream->alignByte(); + return JBIG2_SUCCESS; +} + +int32_t CJBig2_Context::parseRegionInfo(JBig2RegionInfo* pRI) { + if (m_pStream->readInteger((FX_DWORD*)&pRI->width) != 0 || + m_pStream->readInteger((FX_DWORD*)&pRI->height) != 0 || + m_pStream->readInteger((FX_DWORD*)&pRI->x) != 0 || + m_pStream->readInteger((FX_DWORD*)&pRI->y) != 0 || + m_pStream->read1Byte(&pRI->flags) != 0) { + return JBIG2_ERROR_TOO_SHORT; + } + return JBIG2_SUCCESS; +} + +JBig2HuffmanCode* CJBig2_Context::decodeSymbolIDHuffmanTable( + CJBig2_BitStream* pStream, + FX_DWORD SBNUMSYMS) { + const size_t kRunCodesSize = 35; + int32_t runcodes[kRunCodesSize]; + int32_t runcodes_len[kRunCodesSize]; + for (int32_t i = 0; i < kRunCodesSize; ++i) { + if (pStream->readNBits(4, &runcodes_len[i]) != 0) + return nullptr; + } + huffman_assign_code(runcodes, runcodes_len, kRunCodesSize); + + std::unique_ptr<JBig2HuffmanCode, FxFreeDeleter> SBSYMCODES( + FX_Alloc(JBig2HuffmanCode, SBNUMSYMS)); + int32_t run; + int32_t i = 0; + while (i < (int)SBNUMSYMS) { + int32_t j; + int32_t nVal = 0; + int32_t nBits = 0; + FX_DWORD nTemp; + while (true) { + if (pStream->read1Bit(&nTemp) != 0) + return nullptr; + + nVal = (nVal << 1) | nTemp; + ++nBits; + for (j = 0; j < kRunCodesSize; ++j) { + if (nBits == runcodes_len[j] && nVal == runcodes[j]) { + break; + } + } + if (j < kRunCodesSize) { + break; + } + } + int32_t runcode = j; + if (runcode < 32) { + SBSYMCODES.get()[i].codelen = runcode; + run = 0; + } else if (runcode == 32) { + if (pStream->readNBits(2, &nTemp) != 0) + return nullptr; + run = nTemp + 3; + } else if (runcode == 33) { + if (pStream->readNBits(3, &nTemp) != 0) + return nullptr; + run = nTemp + 3; + } else if (runcode == 34) { + if (pStream->readNBits(7, &nTemp) != 0) + return nullptr; + run = nTemp + 11; + } + if (run > 0) { + if (i + run > (int)SBNUMSYMS) + return nullptr; + for (j = 0; j < run; ++j) { + if (runcode == 32 && i > 0) { + SBSYMCODES.get()[i + j].codelen = SBSYMCODES.get()[i - 1].codelen; + } else { + SBSYMCODES.get()[i + j].codelen = 0; + } + } + i += run; + } else { + ++i; + } + } + huffman_assign_code(SBSYMCODES.get(), SBNUMSYMS); + return SBSYMCODES.release(); +} + +void CJBig2_Context::huffman_assign_code(int* CODES, int* PREFLEN, int NTEMP) { + // TODO(thestig) CJBig2_HuffmanTable::parseFromCodedBuffer() has similar code. + int CURLEN, LENMAX, CURCODE, CURTEMP, i; + int* LENCOUNT; + int* FIRSTCODE; + LENMAX = 0; + for (i = 0; i < NTEMP; ++i) { + if (PREFLEN[i] > LENMAX) { + LENMAX = PREFLEN[i]; + } + } + LENCOUNT = FX_Alloc(int, LENMAX + 1); + JBIG2_memset(LENCOUNT, 0, sizeof(int) * (LENMAX + 1)); + FIRSTCODE = FX_Alloc(int, LENMAX + 1); + for (i = 0; i < NTEMP; ++i) { + ++LENCOUNT[PREFLEN[i]]; + } + CURLEN = 1; + FIRSTCODE[0] = 0; + LENCOUNT[0] = 0; + while (CURLEN <= LENMAX) { + FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN - 1] + LENCOUNT[CURLEN - 1]) << 1; + CURCODE = FIRSTCODE[CURLEN]; + CURTEMP = 0; + while (CURTEMP < NTEMP) { + if (PREFLEN[CURTEMP] == CURLEN) { + CODES[CURTEMP] = CURCODE; + CURCODE = CURCODE + 1; + } + CURTEMP = CURTEMP + 1; + } + CURLEN = CURLEN + 1; + } + FX_Free(LENCOUNT); + FX_Free(FIRSTCODE); +} +void CJBig2_Context::huffman_assign_code(JBig2HuffmanCode* SBSYMCODES, + int NTEMP) { + int CURLEN, LENMAX, CURCODE, CURTEMP, i; + int* LENCOUNT; + int* FIRSTCODE; + LENMAX = 0; + for (i = 0; i < NTEMP; ++i) { + if (SBSYMCODES[i].codelen > LENMAX) { + LENMAX = SBSYMCODES[i].codelen; + } + } + LENCOUNT = FX_Alloc(int, (LENMAX + 1)); + JBIG2_memset(LENCOUNT, 0, sizeof(int) * (LENMAX + 1)); + FIRSTCODE = FX_Alloc(int, (LENMAX + 1)); + for (i = 0; i < NTEMP; ++i) { + ++LENCOUNT[SBSYMCODES[i].codelen]; + } + CURLEN = 1; + FIRSTCODE[0] = 0; + LENCOUNT[0] = 0; + while (CURLEN <= LENMAX) { + FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN - 1] + LENCOUNT[CURLEN - 1]) << 1; + CURCODE = FIRSTCODE[CURLEN]; + CURTEMP = 0; + while (CURTEMP < NTEMP) { + if (SBSYMCODES[CURTEMP].codelen == CURLEN) { + SBSYMCODES[CURTEMP].code = CURCODE; + CURCODE = CURCODE + 1; + } + CURTEMP = CURTEMP + 1; + } + CURLEN = CURLEN + 1; + } + FX_Free(LENCOUNT); + FX_Free(FIRSTCODE); +} diff --git a/core/fxcodec/jbig2/JBig2_Context.h b/core/fxcodec/jbig2/JBig2_Context.h new file mode 100644 index 0000000000..f09b8fc1ab --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Context.h @@ -0,0 +1,134 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_CONTEXT_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_CONTEXT_H_ + +#include <list> +#include <memory> +#include <utility> + +#include "core/fxcodec/jbig2/JBig2_List.h" +#include "core/fxcodec/jbig2/JBig2_Page.h" +#include "core/fxcodec/jbig2/JBig2_Segment.h" +#include "core/include/fpdfapi/cpdf_object.h" +#include "core/include/fxcodec/fx_codec_def.h" + +class CJBig2_ArithDecoder; +class CJBig2_GRDProc; +class CPDF_StreamAcc; +class IFX_Pause; + +// Cache is keyed by the ObjNum of a stream and an index within the stream. +using CJBig2_CacheKey = std::pair<FX_DWORD, FX_DWORD>; +// NB: CJBig2_SymbolDict* is owned. +using CJBig2_CachePair = std::pair<CJBig2_CacheKey, CJBig2_SymbolDict*>; + +#define JBIG2_SUCCESS 0 +#define JBIG2_FAILED -1 +#define JBIG2_ERROR_TOO_SHORT -2 +#define JBIG2_ERROR_FATAL -3 +#define JBIG2_END_OF_PAGE 2 +#define JBIG2_END_OF_FILE 3 +#define JBIG2_ERROR_FILE_FORMAT -4 +#define JBIG2_ERROR_STREAM_TYPE -5 +#define JBIG2_ERROR_LIMIT -6 +#define JBIG2_MIN_SEGMENT_SIZE 11 + +class CJBig2_Context { + public: + static CJBig2_Context* CreateContext( + CPDF_StreamAcc* pGlobalStream, + CPDF_StreamAcc* pSrcStream, + std::list<CJBig2_CachePair>* pSymbolDictCache, + IFX_Pause* pPause = NULL); + + static void DestroyContext(CJBig2_Context* pContext); + + int32_t getFirstPage(uint8_t* pBuf, + int32_t width, + int32_t height, + int32_t stride, + IFX_Pause* pPause); + + int32_t Continue(IFX_Pause* pPause); + FXCODEC_STATUS GetProcessingStatus() { return m_ProcessingStatus; } + + private: + CJBig2_Context(CPDF_StreamAcc* pGlobalStream, + CPDF_StreamAcc* pSrcStream, + std::list<CJBig2_CachePair>* pSymbolDictCache, + IFX_Pause* pPause, + bool bIsGlobal); + + ~CJBig2_Context(); + + int32_t decode_SquentialOrgnazation(IFX_Pause* pPause); + + int32_t decode_EmbedOrgnazation(IFX_Pause* pPause); + + int32_t decode_RandomOrgnazation_FirstPage(IFX_Pause* pPause); + + int32_t decode_RandomOrgnazation(IFX_Pause* pPause); + + CJBig2_Segment* findSegmentByNumber(FX_DWORD dwNumber); + + CJBig2_Segment* findReferredSegmentByTypeAndIndex(CJBig2_Segment* pSegment, + uint8_t cType, + int32_t nIndex); + + int32_t parseSegmentHeader(CJBig2_Segment* pSegment); + + int32_t parseSegmentData(CJBig2_Segment* pSegment, IFX_Pause* pPause); + int32_t ProcessingParseSegmentData(CJBig2_Segment* pSegment, + IFX_Pause* pPause); + + int32_t parseSymbolDict(CJBig2_Segment* pSegment, IFX_Pause* pPause); + + int32_t parseTextRegion(CJBig2_Segment* pSegment); + + int32_t parsePatternDict(CJBig2_Segment* pSegment, IFX_Pause* pPause); + + int32_t parseHalftoneRegion(CJBig2_Segment* pSegment, IFX_Pause* pPause); + + int32_t parseGenericRegion(CJBig2_Segment* pSegment, IFX_Pause* pPause); + + int32_t parseGenericRefinementRegion(CJBig2_Segment* pSegment); + + int32_t parseTable(CJBig2_Segment* pSegment); + + int32_t parseRegionInfo(JBig2RegionInfo* pRI); + + JBig2HuffmanCode* decodeSymbolIDHuffmanTable(CJBig2_BitStream* pStream, + FX_DWORD SBNUMSYMS); + + void huffman_assign_code(int* CODES, int* PREFLEN, int NTEMP); + + void huffman_assign_code(JBig2HuffmanCode* SBSYMCODES, int NTEMP); + + private: + CJBig2_Context* m_pGlobalContext; + std::unique_ptr<CJBig2_BitStream> m_pStream; + CJBig2_List<CJBig2_Segment> m_SegmentList; + CJBig2_List<JBig2PageInfo> m_PageInfoList; + std::unique_ptr<CJBig2_Image> m_pPage; + size_t m_nSegmentDecoded; + bool m_bInPage; + bool m_bBufSpecified; + int32_t m_PauseStep; + IFX_Pause* m_pPause; + FXCODEC_STATUS m_ProcessingStatus; + std::unique_ptr<CJBig2_ArithDecoder> m_pArithDecoder; + std::unique_ptr<CJBig2_GRDProc> m_pGRD; + JBig2ArithCtx* m_gbContext; + std::unique_ptr<CJBig2_Segment> m_pSegment; + FX_DWORD m_dwOffset; + JBig2RegionInfo m_ri; + std::list<CJBig2_CachePair>* const m_pSymbolDictCache; + bool m_bIsGlobal; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_CONTEXT_H_ diff --git a/core/fxcodec/jbig2/JBig2_Define.h b/core/fxcodec/jbig2/JBig2_Define.h new file mode 100644 index 0000000000..5149c19ef2 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Define.h @@ -0,0 +1,36 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_ + +#include "core/include/fxcrt/fx_system.h" + +#define JBIG2_memset FXSYS_memset +#define JBIG2_memcmp FXSYS_memcmp +#define JBIG2_memcpy FXSYS_memcpy +#define JBIG2_OOB 1 + +struct JBig2RegionInfo { + int32_t width; + int32_t height; + int32_t x; + int32_t y; + uint8_t flags; +}; + +struct JBig2HuffmanCode { + int32_t codelen; + int32_t code; +}; + +#define JBIG2_MAX_REFERRED_SEGMENT_COUNT 64 +#define JBIG2_MAX_EXPORT_SYSMBOLS 65535 +#define JBIG2_MAX_NEW_SYSMBOLS 65535 +#define JBIG2_MAX_PATTERN_INDEX 65535 +#define JBIG2_MAX_IMAGE_SIZE 65535 + +#endif // CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_ diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrdProc.cpp new file mode 100644 index 0000000000..a3fc33c2be --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_GrdProc.cpp @@ -0,0 +1,1259 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_GrdProc.h" + +#include <memory> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/include/fxcodec/fx_codec.h" + +CJBig2_GRDProc::CJBig2_GRDProc() + : m_loopIndex(0), + m_pLine(nullptr), + m_pPause(nullptr), + m_DecodeType(0), + LTP(0) { + m_ReplaceRect.left = 0; + m_ReplaceRect.bottom = 0; + m_ReplaceRect.top = 0; + m_ReplaceRect.right = 0; +} + +bool CJBig2_GRDProc::UseTemplate0Opt3() const { + return (GBAT[0] == 3) && (GBAT[1] == -1) && (GBAT[2] == -3) && + (GBAT[3] == -1) && (GBAT[4] == 2) && (GBAT[5] == -2) && + (GBAT[6] == -2) && (GBAT[7] == -2); +} + +bool CJBig2_GRDProc::UseTemplate1Opt3() const { + return (GBAT[0] == 3) && (GBAT[1] == -1); +} + +bool CJBig2_GRDProc::UseTemplate23Opt3() const { + return (GBAT[0] == 2) && (GBAT[1] == -1); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + if (GBW == 0 || GBH == 0) + return new CJBig2_Image(GBW, GBH); + + if (GBTEMPLATE == 0) { + if (UseTemplate0Opt3()) + return decode_Arith_Template0_opt3(pArithDecoder, gbContext); + return decode_Arith_Template0_unopt(pArithDecoder, gbContext); + } else if (GBTEMPLATE == 1) { + if (UseTemplate1Opt3()) + return decode_Arith_Template1_opt3(pArithDecoder, gbContext); + return decode_Arith_Template1_unopt(pArithDecoder, gbContext); + } else if (GBTEMPLATE == 2) { + if (UseTemplate23Opt3()) + return decode_Arith_Template2_opt3(pArithDecoder, gbContext); + return decode_Arith_Template2_unopt(pArithDecoder, gbContext); + } else { + if (UseTemplate23Opt3()) + return decode_Arith_Template3_opt3(pArithDecoder, gbContext); + return decode_Arith_Template3_unopt(pArithDecoder, gbContext); + } +} +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template0_opt3( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + uint8_t *pLine, *pLine1, *pLine2, cVal; + int32_t nStride, nStride2, k; + int32_t nLineBytes, nBitsLeft, cc; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + if (!GBREG->m_pData) + return nullptr; + + pLine = GBREG->m_pData; + nStride = GBREG->m_nStride; + nStride2 = nStride << 1; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + FX_DWORD height = GBH & 0x7fffffff; + for (FX_DWORD h = 0; h < height; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x9b25]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + if (h > 1) { + pLine1 = pLine - nStride2; + pLine2 = pLine - nStride; + line1 = (*pLine1++) << 6; + line2 = *pLine2++; + CONTEXT = ((line1 & 0xf800) | (line2 & 0x07f0)); + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | ((*pLine1++) << 6); + line2 = (line2 << 8) | (*pLine2++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal | + ((line1 >> k) & 0x0800) | ((line2 >> k) & 0x0010)); + } + pLine[cc] = cVal; + } + line1 <<= 8; + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = + (((CONTEXT & 0x7bf7) << 1) | bVal | + ((line1 >> (7 - k)) & 0x0800) | ((line2 >> (7 - k)) & 0x0010)); + } + pLine[nLineBytes] = cVal; + } else { + pLine2 = pLine - nStride; + line2 = (h & 1) ? (*pLine2++) : 0; + CONTEXT = (line2 & 0x07f0); + for (cc = 0; cc < nLineBytes; cc++) { + if (h & 1) { + line2 = (line2 << 8) | (*pLine2++); + } + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = + (((CONTEXT & 0x7bf7) << 1) | bVal | ((line2 >> k) & 0x0010)); + } + pLine[cc] = cVal; + } + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal | + (((line2 >> (7 - k))) & 0x0010)); + } + pLine[nLineBytes] = cVal; + } + } + pLine += nStride; + } + return GBREG.release(); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template0_unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + GBREG->fill(0); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x9b25]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + line1 = GBREG->getPixel(1, h - 2); + line1 |= GBREG->getPixel(0, h - 2) << 1; + line2 = GBREG->getPixel(2, h - 1); + line2 |= GBREG->getPixel(1, h - 1) << 1; + line2 |= GBREG->getPixel(0, h - 1) << 2; + line3 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, h)) { + bVal = 0; + } else { + CONTEXT = line3; + CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 4; + CONTEXT |= line2 << 5; + CONTEXT |= GBREG->getPixel(w + GBAT[2], h + GBAT[3]) << 10; + CONTEXT |= GBREG->getPixel(w + GBAT[4], h + GBAT[5]) << 11; + CONTEXT |= line1 << 12; + CONTEXT |= GBREG->getPixel(w + GBAT[6], h + GBAT[7]) << 15; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + GBREG->setPixel(w, h, bVal); + } + line1 = ((line1 << 1) | GBREG->getPixel(w + 2, h - 2)) & 0x07; + line2 = ((line2 << 1) | GBREG->getPixel(w + 3, h - 1)) & 0x1f; + line3 = ((line3 << 1) | bVal) & 0x0f; + } + } + } + return GBREG.release(); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template1_opt3( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + uint8_t *pLine, *pLine1, *pLine2, cVal; + int32_t nStride, nStride2, k; + int32_t nLineBytes, nBitsLeft, cc; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + if (!GBREG->m_pData) + return nullptr; + + pLine = GBREG->m_pData; + nStride = GBREG->m_nStride; + nStride2 = nStride << 1; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0795]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + if (h > 1) { + pLine1 = pLine - nStride2; + pLine2 = pLine - nStride; + line1 = (*pLine1++) << 4; + line2 = *pLine2++; + CONTEXT = (line1 & 0x1e00) | ((line2 >> 1) & 0x01f8); + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | ((*pLine1++) << 4); + line2 = (line2 << 8) | (*pLine2++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal | + ((line1 >> k) & 0x0200) | ((line2 >> (k + 1)) & 0x0008); + } + pLine[cc] = cVal; + } + line1 <<= 8; + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal | + ((line1 >> (7 - k)) & 0x0200) | + ((line2 >> (8 - k)) & 0x0008); + } + pLine[nLineBytes] = cVal; + } else { + pLine2 = pLine - nStride; + line2 = (h & 1) ? (*pLine2++) : 0; + CONTEXT = (line2 >> 1) & 0x01f8; + for (cc = 0; cc < nLineBytes; cc++) { + if (h & 1) { + line2 = (line2 << 8) | (*pLine2++); + } + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal | + ((line2 >> (k + 1)) & 0x0008); + } + pLine[cc] = cVal; + } + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = + ((CONTEXT & 0x0efb) << 1) | bVal | ((line2 >> (8 - k)) & 0x0008); + } + pLine[nLineBytes] = cVal; + } + } + pLine += nStride; + } + return GBREG.release(); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template1_unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + GBREG->fill(0); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0795]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + line1 = GBREG->getPixel(2, h - 2); + line1 |= GBREG->getPixel(1, h - 2) << 1; + line1 |= GBREG->getPixel(0, h - 2) << 2; + line2 = GBREG->getPixel(2, h - 1); + line2 |= GBREG->getPixel(1, h - 1) << 1; + line2 |= GBREG->getPixel(0, h - 1) << 2; + line3 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, h)) { + bVal = 0; + } else { + CONTEXT = line3; + CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 3; + CONTEXT |= line2 << 4; + CONTEXT |= line1 << 9; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + GBREG->setPixel(w, h, bVal); + } + line1 = ((line1 << 1) | GBREG->getPixel(w + 3, h - 2)) & 0x0f; + line2 = ((line2 << 1) | GBREG->getPixel(w + 3, h - 1)) & 0x1f; + line3 = ((line3 << 1) | bVal) & 0x07; + } + } + } + return GBREG.release(); +} +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template2_opt3( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + uint8_t *pLine, *pLine1, *pLine2, cVal; + int32_t nStride, nStride2, k; + int32_t nLineBytes, nBitsLeft, cc; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + if (!GBREG->m_pData) + return nullptr; + + pLine = GBREG->m_pData; + nStride = GBREG->m_nStride; + nStride2 = nStride << 1; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x00e5]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + if (h > 1) { + pLine1 = pLine - nStride2; + pLine2 = pLine - nStride; + line1 = (*pLine1++) << 1; + line2 = *pLine2++; + CONTEXT = (line1 & 0x0380) | ((line2 >> 3) & 0x007c); + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | ((*pLine1++) << 1); + line2 = (line2 << 8) | (*pLine2++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + ((line1 >> k) & 0x0080) | ((line2 >> (k + 3)) & 0x0004); + } + pLine[cc] = cVal; + } + line1 <<= 8; + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + ((line1 >> (7 - k)) & 0x0080) | + ((line2 >> (10 - k)) & 0x0004); + } + pLine[nLineBytes] = cVal; + } else { + pLine2 = pLine - nStride; + line2 = (h & 1) ? (*pLine2++) : 0; + CONTEXT = (line2 >> 3) & 0x007c; + for (cc = 0; cc < nLineBytes; cc++) { + if (h & 1) { + line2 = (line2 << 8) | (*pLine2++); + } + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + ((line2 >> (k + 3)) & 0x0004); + } + pLine[cc] = cVal; + } + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + (((line2 >> (10 - k))) & 0x0004); + } + pLine[nLineBytes] = cVal; + } + } + pLine += nStride; + } + return GBREG.release(); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template2_unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + GBREG->fill(0); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x00e5]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + line1 = GBREG->getPixel(1, h - 2); + line1 |= GBREG->getPixel(0, h - 2) << 1; + line2 = GBREG->getPixel(1, h - 1); + line2 |= GBREG->getPixel(0, h - 1) << 1; + line3 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, h)) { + bVal = 0; + } else { + CONTEXT = line3; + CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 2; + CONTEXT |= line2 << 3; + CONTEXT |= line1 << 7; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + GBREG->setPixel(w, h, bVal); + } + line1 = ((line1 << 1) | GBREG->getPixel(w + 2, h - 2)) & 0x07; + line2 = ((line2 << 1) | GBREG->getPixel(w + 2, h - 1)) & 0x0f; + line3 = ((line3 << 1) | bVal) & 0x03; + } + } + } + return GBREG.release(); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template3_opt3( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1; + uint8_t *pLine, *pLine1, cVal; + int32_t nStride, k; + int32_t nLineBytes, nBitsLeft, cc; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + if (!GBREG->m_pData) + return nullptr; + + pLine = GBREG->m_pData; + nStride = GBREG->m_nStride; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0195]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + if (h > 0) { + pLine1 = pLine - nStride; + line1 = *pLine1++; + CONTEXT = (line1 >> 1) & 0x03f0; + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | (*pLine1++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal | + ((line1 >> (k + 1)) & 0x0010); + } + pLine[cc] = cVal; + } + line1 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = + ((CONTEXT & 0x01f7) << 1) | bVal | ((line1 >> (8 - k)) & 0x0010); + } + pLine[nLineBytes] = cVal; + } else { + CONTEXT = 0; + for (cc = 0; cc < nLineBytes; cc++) { + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal; + } + pLine[cc] = cVal; + } + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal; + } + pLine[nLineBytes] = cVal; + } + } + pLine += nStride; + } + return GBREG.release(); +} + +CJBig2_Image* CJBig2_GRDProc::decode_Arith_Template3_unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + LTP = 0; + std::unique_ptr<CJBig2_Image> GBREG(new CJBig2_Image(GBW, GBH)); + GBREG->fill(0); + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0195]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + GBREG->copyLine(h, h - 1); + } else { + line1 = GBREG->getPixel(1, h - 1); + line1 |= GBREG->getPixel(0, h - 1) << 1; + line2 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, h)) { + bVal = 0; + } else { + CONTEXT = line2; + CONTEXT |= GBREG->getPixel(w + GBAT[0], h + GBAT[1]) << 4; + CONTEXT |= line1 << 5; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + GBREG->setPixel(w, h, bVal); + } + line1 = ((line1 << 1) | GBREG->getPixel(w + 2, h - 1)) & 0x1f; + line2 = ((line2 << 1) | bVal) & 0x0f; + } + } + } + return GBREG.release(); +} + +FXCODEC_STATUS CJBig2_GRDProc::Start_decode_Arith( + CJBig2_Image** pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + if (GBW == 0 || GBH == 0) { + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_READY; + m_pPause = pPause; + if (!*pImage) + *pImage = new CJBig2_Image(GBW, GBH); + if (!(*pImage)->m_pData) { + delete *pImage; + *pImage = nullptr; + m_ProssiveStatus = FXCODEC_STATUS_ERROR; + return FXCODEC_STATUS_ERROR; + } + m_DecodeType = 1; + m_pImage = pImage; + (*m_pImage)->fill(0); + m_pArithDecoder = pArithDecoder; + m_gbContext = gbContext; + LTP = 0; + m_pLine = nullptr; + m_loopIndex = 0; + return decode_Arith(pPause); +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith(IFX_Pause* pPause) { + int iline = m_loopIndex; + CJBig2_Image* pImage = *m_pImage; + if (GBTEMPLATE == 0) { + if (UseTemplate0Opt3()) { + m_ProssiveStatus = decode_Arith_Template0_opt3(pImage, m_pArithDecoder, + m_gbContext, pPause); + } else { + m_ProssiveStatus = decode_Arith_Template0_unopt(pImage, m_pArithDecoder, + m_gbContext, pPause); + } + } else if (GBTEMPLATE == 1) { + if (UseTemplate1Opt3()) { + m_ProssiveStatus = decode_Arith_Template1_opt3(pImage, m_pArithDecoder, + m_gbContext, pPause); + } else { + m_ProssiveStatus = decode_Arith_Template1_unopt(pImage, m_pArithDecoder, + m_gbContext, pPause); + } + } else if (GBTEMPLATE == 2) { + if (UseTemplate23Opt3()) { + m_ProssiveStatus = decode_Arith_Template2_opt3(pImage, m_pArithDecoder, + m_gbContext, pPause); + } else { + m_ProssiveStatus = decode_Arith_Template2_unopt(pImage, m_pArithDecoder, + m_gbContext, pPause); + } + } else { + if (UseTemplate23Opt3()) { + m_ProssiveStatus = decode_Arith_Template3_opt3(pImage, m_pArithDecoder, + m_gbContext, pPause); + } else { + m_ProssiveStatus = decode_Arith_Template3_unopt(pImage, m_pArithDecoder, + m_gbContext, pPause); + } + } + m_ReplaceRect.left = 0; + m_ReplaceRect.right = pImage->m_nWidth; + m_ReplaceRect.top = iline; + m_ReplaceRect.bottom = m_loopIndex; + if (m_ProssiveStatus == FXCODEC_STATUS_DECODE_FINISH) { + m_loopIndex = 0; + } + return m_ProssiveStatus; +} + +FXCODEC_STATUS CJBig2_GRDProc::Start_decode_MMR(CJBig2_Image** pImage, + CJBig2_BitStream* pStream, + IFX_Pause* pPause) { + int bitpos, i; + *pImage = new CJBig2_Image(GBW, GBH); + if (!(*pImage)->m_pData) { + delete (*pImage); + (*pImage) = nullptr; + m_ProssiveStatus = FXCODEC_STATUS_ERROR; + return m_ProssiveStatus; + } + bitpos = (int)pStream->getBitPos(); + FaxG4Decode(pStream->getBuf(), pStream->getLength(), &bitpos, + (*pImage)->m_pData, GBW, GBH, (*pImage)->m_nStride); + pStream->setBitPos(bitpos); + for (i = 0; (FX_DWORD)i < (*pImage)->m_nStride * GBH; i++) { + (*pImage)->m_pData[i] = ~(*pImage)->m_pData[i]; + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return m_ProssiveStatus; +} + +FXCODEC_STATUS CJBig2_GRDProc::Continue_decode(IFX_Pause* pPause) { + if (m_ProssiveStatus != FXCODEC_STATUS_DECODE_TOBECONTINUE) + return m_ProssiveStatus; + + if (m_DecodeType != 1) { + m_ProssiveStatus = FXCODEC_STATUS_ERROR; + return m_ProssiveStatus; + } + + return decode_Arith(pPause); +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template0_opt3( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + uint8_t *pLine1, *pLine2, cVal; + int32_t nStride, nStride2, k; + int32_t nLineBytes, nBitsLeft, cc; + if (!m_pLine) { + m_pLine = pImage->m_pData; + } + nStride = pImage->m_nStride; + nStride2 = nStride << 1; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + FX_DWORD height = GBH & 0x7fffffff; + for (; m_loopIndex < height; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x9b25]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + if (m_loopIndex > 1) { + pLine1 = m_pLine - nStride2; + pLine2 = m_pLine - nStride; + line1 = (*pLine1++) << 6; + line2 = *pLine2++; + CONTEXT = ((line1 & 0xf800) | (line2 & 0x07f0)); + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | ((*pLine1++) << 6); + line2 = (line2 << 8) | (*pLine2++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal | + ((line1 >> k) & 0x0800) | ((line2 >> k) & 0x0010)); + } + m_pLine[cc] = cVal; + } + line1 <<= 8; + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = + (((CONTEXT & 0x7bf7) << 1) | bVal | + ((line1 >> (7 - k)) & 0x0800) | ((line2 >> (7 - k)) & 0x0010)); + } + m_pLine[nLineBytes] = cVal; + } else { + pLine2 = m_pLine - nStride; + line2 = (m_loopIndex & 1) ? (*pLine2++) : 0; + CONTEXT = (line2 & 0x07f0); + for (cc = 0; cc < nLineBytes; cc++) { + if (m_loopIndex & 1) { + line2 = (line2 << 8) | (*pLine2++); + } + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = + (((CONTEXT & 0x7bf7) << 1) | bVal | ((line2 >> k) & 0x0010)); + } + m_pLine[cc] = cVal; + } + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = (((CONTEXT & 0x7bf7) << 1) | bVal | + ((line2 >> (7 - k)) & 0x0010)); + } + m_pLine[nLineBytes] = cVal; + } + } + m_pLine += nStride; + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template0_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3; + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x9b25]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + line1 = pImage->getPixel(1, m_loopIndex - 2); + line1 |= pImage->getPixel(0, m_loopIndex - 2) << 1; + line2 = pImage->getPixel(2, m_loopIndex - 1); + line2 |= pImage->getPixel(1, m_loopIndex - 1) << 1; + line2 |= pImage->getPixel(0, m_loopIndex - 1) << 2; + line3 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, m_loopIndex)) { + bVal = 0; + } else { + CONTEXT = line3; + CONTEXT |= pImage->getPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4; + CONTEXT |= line2 << 5; + CONTEXT |= pImage->getPixel(w + GBAT[2], m_loopIndex + GBAT[3]) << 10; + CONTEXT |= pImage->getPixel(w + GBAT[4], m_loopIndex + GBAT[5]) << 11; + CONTEXT |= line1 << 12; + CONTEXT |= pImage->getPixel(w + GBAT[6], m_loopIndex + GBAT[7]) << 15; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + pImage->setPixel(w, m_loopIndex, bVal); + } + line1 = + ((line1 << 1) | pImage->getPixel(w + 2, m_loopIndex - 2)) & 0x07; + line2 = + ((line2 << 1) | pImage->getPixel(w + 3, m_loopIndex - 1)) & 0x1f; + line3 = ((line3 << 1) | bVal) & 0x0f; + } + } + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template1_opt3( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + uint8_t *pLine1, *pLine2, cVal; + int32_t nStride, nStride2, k; + int32_t nLineBytes, nBitsLeft, cc; + if (!m_pLine) { + m_pLine = pImage->m_pData; + } + nStride = pImage->m_nStride; + nStride2 = nStride << 1; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0795]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + if (m_loopIndex > 1) { + pLine1 = m_pLine - nStride2; + pLine2 = m_pLine - nStride; + line1 = (*pLine1++) << 4; + line2 = *pLine2++; + CONTEXT = (line1 & 0x1e00) | ((line2 >> 1) & 0x01f8); + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | ((*pLine1++) << 4); + line2 = (line2 << 8) | (*pLine2++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal | + ((line1 >> k) & 0x0200) | ((line2 >> (k + 1)) & 0x0008); + } + m_pLine[cc] = cVal; + } + line1 <<= 8; + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal | + ((line1 >> (7 - k)) & 0x0200) | + ((line2 >> (8 - k)) & 0x0008); + } + m_pLine[nLineBytes] = cVal; + } else { + pLine2 = m_pLine - nStride; + line2 = (m_loopIndex & 1) ? (*pLine2++) : 0; + CONTEXT = (line2 >> 1) & 0x01f8; + for (cc = 0; cc < nLineBytes; cc++) { + if (m_loopIndex & 1) { + line2 = (line2 << 8) | (*pLine2++); + } + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x0efb) << 1) | bVal | + ((line2 >> (k + 1)) & 0x0008); + } + m_pLine[cc] = cVal; + } + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = + ((CONTEXT & 0x0efb) << 1) | bVal | ((line2 >> (8 - k)) & 0x0008); + } + m_pLine[nLineBytes] = cVal; + } + } + m_pLine += nStride; + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template1_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3; + for (FX_DWORD h = 0; h < GBH; h++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0795]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(h, h - 1); + } else { + line1 = pImage->getPixel(2, h - 2); + line1 |= pImage->getPixel(1, h - 2) << 1; + line1 |= pImage->getPixel(0, h - 2) << 2; + line2 = pImage->getPixel(2, h - 1); + line2 |= pImage->getPixel(1, h - 1) << 1; + line2 |= pImage->getPixel(0, h - 1) << 2; + line3 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, h)) { + bVal = 0; + } else { + CONTEXT = line3; + CONTEXT |= pImage->getPixel(w + GBAT[0], h + GBAT[1]) << 3; + CONTEXT |= line2 << 4; + CONTEXT |= line1 << 9; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + pImage->setPixel(w, h, bVal); + } + line1 = ((line1 << 1) | pImage->getPixel(w + 3, h - 2)) & 0x0f; + line2 = ((line2 << 1) | pImage->getPixel(w + 3, h - 1)) & 0x1f; + line3 = ((line3 << 1) | bVal) & 0x07; + } + } + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template2_opt3( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + uint8_t *pLine1, *pLine2, cVal; + int32_t nStride, nStride2, k; + int32_t nLineBytes, nBitsLeft, cc; + if (!m_pLine) { + m_pLine = pImage->m_pData; + } + nStride = pImage->m_nStride; + nStride2 = nStride << 1; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x00e5]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + if (m_loopIndex > 1) { + pLine1 = m_pLine - nStride2; + pLine2 = m_pLine - nStride; + line1 = (*pLine1++) << 1; + line2 = *pLine2++; + CONTEXT = (line1 & 0x0380) | ((line2 >> 3) & 0x007c); + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | ((*pLine1++) << 1); + line2 = (line2 << 8) | (*pLine2++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + ((line1 >> k) & 0x0080) | ((line2 >> (k + 3)) & 0x0004); + } + m_pLine[cc] = cVal; + } + line1 <<= 8; + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + ((line1 >> (7 - k)) & 0x0080) | + ((line2 >> (10 - k)) & 0x0004); + } + m_pLine[nLineBytes] = cVal; + } else { + pLine2 = m_pLine - nStride; + line2 = (m_loopIndex & 1) ? (*pLine2++) : 0; + CONTEXT = (line2 >> 3) & 0x007c; + for (cc = 0; cc < nLineBytes; cc++) { + if (m_loopIndex & 1) { + line2 = (line2 << 8) | (*pLine2++); + } + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + ((line2 >> (k + 3)) & 0x0004); + } + m_pLine[cc] = cVal; + } + line2 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x01bd) << 1) | bVal | + (((line2 >> (10 - k))) & 0x0004); + } + m_pLine[nLineBytes] = cVal; + } + } + m_pLine += nStride; + if (pPause && m_loopIndex % 50 == 0 && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template2_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3; + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x00e5]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + line1 = pImage->getPixel(1, m_loopIndex - 2); + line1 |= pImage->getPixel(0, m_loopIndex - 2) << 1; + line2 = pImage->getPixel(1, m_loopIndex - 1); + line2 |= pImage->getPixel(0, m_loopIndex - 1) << 1; + line3 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, m_loopIndex)) { + bVal = 0; + } else { + CONTEXT = line3; + CONTEXT |= pImage->getPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 2; + CONTEXT |= line2 << 3; + CONTEXT |= line1 << 7; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + pImage->setPixel(w, m_loopIndex, bVal); + } + line1 = + ((line1 << 1) | pImage->getPixel(w + 2, m_loopIndex - 2)) & 0x07; + line2 = + ((line2 << 1) | pImage->getPixel(w + 2, m_loopIndex - 1)) & 0x0f; + line3 = ((line3 << 1) | bVal) & 0x03; + } + } + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template3_opt3( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1; + uint8_t *pLine1, cVal; + int32_t nStride, k; + int32_t nLineBytes, nBitsLeft, cc; + if (!m_pLine) { + m_pLine = pImage->m_pData; + } + nStride = pImage->m_nStride; + nLineBytes = ((GBW + 7) >> 3) - 1; + nBitsLeft = GBW - (nLineBytes << 3); + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0195]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + if (m_loopIndex > 0) { + pLine1 = m_pLine - nStride; + line1 = *pLine1++; + CONTEXT = (line1 >> 1) & 0x03f0; + for (cc = 0; cc < nLineBytes; cc++) { + line1 = (line1 << 8) | (*pLine1++); + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal | + ((line1 >> (k + 1)) & 0x0010); + } + m_pLine[cc] = cVal; + } + line1 <<= 8; + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = + ((CONTEXT & 0x01f7) << 1) | bVal | ((line1 >> (8 - k)) & 0x0010); + } + m_pLine[nLineBytes] = cVal; + } else { + CONTEXT = 0; + for (cc = 0; cc < nLineBytes; cc++) { + cVal = 0; + for (k = 7; k >= 0; k--) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << k; + CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal; + } + m_pLine[cc] = cVal; + } + cVal = 0; + for (k = 0; k < nBitsLeft; k++) { + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x01f7) << 1) | bVal; + } + m_pLine[nLineBytes] = cVal; + } + } + m_pLine += nStride; + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} + +FXCODEC_STATUS CJBig2_GRDProc::decode_Arith_Template3_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_BOOL SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2; + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + SLTP = pArithDecoder->DECODE(&gbContext[0x0195]); + LTP = LTP ^ SLTP; + } + if (LTP == 1) { + pImage->copyLine(m_loopIndex, m_loopIndex - 1); + } else { + line1 = pImage->getPixel(1, m_loopIndex - 1); + line1 |= pImage->getPixel(0, m_loopIndex - 1) << 1; + line2 = 0; + for (FX_DWORD w = 0; w < GBW; w++) { + if (USESKIP && SKIP->getPixel(w, m_loopIndex)) { + bVal = 0; + } else { + CONTEXT = line2; + CONTEXT |= pImage->getPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4; + CONTEXT |= line1 << 5; + bVal = pArithDecoder->DECODE(&gbContext[CONTEXT]); + } + if (bVal) { + pImage->setPixel(w, m_loopIndex, bVal); + } + line1 = + ((line1 << 1) | pImage->getPixel(w + 2, m_loopIndex - 1)) & 0x1f; + line2 = ((line2 << 1) | bVal) & 0x0f; + } + } + if (pPause && pPause->NeedToPauseNow()) { + m_loopIndex++; + m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; + return FXCODEC_STATUS_DECODE_TOBECONTINUE; + } + } + m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; + return FXCODEC_STATUS_DECODE_FINISH; +} diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.h b/core/fxcodec/jbig2/JBig2_GrdProc.h new file mode 100644 index 0000000000..2a181b1822 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_GrdProc.h @@ -0,0 +1,124 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_ + +#include "core/include/fxcodec/fx_codec_def.h" +#include "core/include/fxcrt/fx_coordinates.h" +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithDecoder; +class CJBig2_BitStream; +class CJBig2_Image; +class IFX_Pause; +struct JBig2ArithCtx; + +class CJBig2_GRDProc { + public: + CJBig2_GRDProc(); + + CJBig2_Image* decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + FXCODEC_STATUS Start_decode_Arith(CJBig2_Image** pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS Start_decode_MMR(CJBig2_Image** pImage, + CJBig2_BitStream* pStream, + IFX_Pause* pPause); + FXCODEC_STATUS Continue_decode(IFX_Pause* pPause); + FX_RECT GetReplaceRect() const { return m_ReplaceRect; } + + FX_BOOL MMR; + FX_DWORD GBW; + FX_DWORD GBH; + uint8_t GBTEMPLATE; + FX_BOOL TPGDON; + FX_BOOL USESKIP; + CJBig2_Image* SKIP; + int8_t GBAT[8]; + + private: + bool UseTemplate0Opt3() const; + bool UseTemplate1Opt3() const; + bool UseTemplate23Opt3() const; + + FXCODEC_STATUS decode_Arith(IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template0_opt3(CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template0_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template1_opt3(CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template1_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template2_opt3(CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template2_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template3_opt3(CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + FXCODEC_STATUS decode_Arith_Template3_unopt( + CJBig2_Image* pImage, + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + CJBig2_Image* decode_Arith_Template0_opt3(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template0_unopt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template1_opt3(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template1_unopt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template2_opt3(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template2_unopt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template3_opt3(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + CJBig2_Image* decode_Arith_Template3_unopt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext); + + FX_DWORD m_loopIndex; + uint8_t* m_pLine; + IFX_Pause* m_pPause; + FXCODEC_STATUS m_ProssiveStatus; + CJBig2_Image** m_pImage; + CJBig2_ArithDecoder* m_pArithDecoder; + JBig2ArithCtx* m_gbContext; + FX_WORD m_DecodeType; + FX_BOOL LTP; + FX_RECT m_ReplaceRect; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_ diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp new file mode 100644 index 0000000000..c6ff3dd647 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp @@ -0,0 +1,516 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_GrrdProc.h" + +#include <memory> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/fxcodec/jbig2/JBig2_Image.h" + +CJBig2_Image* CJBig2_GRRDProc::decode(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { + if (GRW == 0 || GRH == 0) + return new CJBig2_Image(GRW, GRH); + + if (GRTEMPLATE == 0) { + if ((GRAT[0] == -1) && (GRAT[1] == -1) && (GRAT[2] == -1) && + (GRAT[3] == -1) && (GRREFERENCEDX == 0) && + (GRW == (FX_DWORD)GRREFERENCE->m_nWidth)) { + return decode_Template0_opt(pArithDecoder, grContext); + } + return decode_Template0_unopt(pArithDecoder, grContext); + } + + if ((GRREFERENCEDX == 0) && (GRW == (FX_DWORD)GRREFERENCE->m_nWidth)) + return decode_Template1_opt(pArithDecoder, grContext); + return decode_Template1_unopt(pArithDecoder, grContext); +} + +CJBig2_Image* CJBig2_GRRDProc::decode_Template0_unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3, line4, line5; + LTP = 0; + std::unique_ptr<CJBig2_Image> GRREG(new CJBig2_Image(GRW, GRH)); + GRREG->fill(0); + for (FX_DWORD h = 0; h < GRH; h++) { + if (TPGRON) { + SLTP = pArithDecoder->DECODE(&grContext[0x0010]); + LTP = LTP ^ SLTP; + } + if (LTP == 0) { + line1 = GRREG->getPixel(1, h - 1); + line1 |= GRREG->getPixel(0, h - 1) << 1; + line2 = 0; + line3 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY - 1); + line3 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1) + << 1; + line4 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY); + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1; + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY) + << 2; + line5 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1); + line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1) + << 1; + line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY + 1) + << 2; + for (FX_DWORD w = 0; w < GRW; w++) { + CONTEXT = line5; + CONTEXT |= line4 << 3; + CONTEXT |= line3 << 6; + CONTEXT |= GRREFERENCE->getPixel(w - GRREFERENCEDX + GRAT[2], + h - GRREFERENCEDY + GRAT[3]) + << 8; + CONTEXT |= line2 << 9; + CONTEXT |= line1 << 10; + CONTEXT |= GRREG->getPixel(w + GRAT[0], h + GRAT[1]) << 12; + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + GRREG->setPixel(w, h, bVal); + line1 = ((line1 << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x03; + line2 = ((line2 << 1) | bVal) & 0x01; + line3 = ((line3 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, + h - GRREFERENCEDY - 1)) & + 0x03; + line4 = + ((line4 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) & + 0x07; + line5 = ((line5 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, + h - GRREFERENCEDY + 1)) & + 0x07; + } + } else { + line1 = GRREG->getPixel(1, h - 1); + line1 |= GRREG->getPixel(0, h - 1) << 1; + line2 = 0; + line3 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY - 1); + line3 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1) + << 1; + line4 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY); + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1; + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY) + << 2; + line5 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1); + line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1) + << 1; + line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY + 1) + << 2; + for (FX_DWORD w = 0; w < GRW; w++) { + bVal = GRREFERENCE->getPixel(w, h); + if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w - 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w - 1, h)) && + (bVal == GRREFERENCE->getPixel(w + 1, h)) && + (bVal == GRREFERENCE->getPixel(w - 1, h + 1)) && + (bVal == GRREFERENCE->getPixel(w, h + 1)) && + (bVal == GRREFERENCE->getPixel(w + 1, h + 1)))) { + CONTEXT = line5; + CONTEXT |= line4 << 3; + CONTEXT |= line3 << 6; + CONTEXT |= GRREFERENCE->getPixel(w - GRREFERENCEDX + GRAT[2], + h - GRREFERENCEDY + GRAT[3]) + << 8; + CONTEXT |= line2 << 9; + CONTEXT |= line1 << 10; + CONTEXT |= GRREG->getPixel(w + GRAT[0], h + GRAT[1]) << 12; + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + } + GRREG->setPixel(w, h, bVal); + line1 = ((line1 << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x03; + line2 = ((line2 << 1) | bVal) & 0x01; + line3 = ((line3 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, + h - GRREFERENCEDY - 1)) & + 0x03; + line4 = + ((line4 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) & + 0x07; + line5 = ((line5 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, + h - GRREFERENCEDY + 1)) & + 0x07; + } + } + } + return GRREG.release(); +} + +CJBig2_Image* CJBig2_GRRDProc::decode_Template0_opt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { + if (!GRREFERENCE->m_pData) + return nullptr; + + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line1_r, line2_r, line3_r; + uint8_t *pLine, *pLineR, cVal; + intptr_t nStride, nStrideR, nOffset; + int32_t k, nBits; + int32_t GRWR, GRHR; + int32_t GRW, GRH; + GRW = (int32_t)CJBig2_GRRDProc::GRW; + GRH = (int32_t)CJBig2_GRRDProc::GRH; + LTP = 0; + std::unique_ptr<CJBig2_Image> GRREG(new CJBig2_Image(GRW, GRH)); + if (!GRREG->m_pData) + return nullptr; + + pLine = GRREG->m_pData; + pLineR = GRREFERENCE->m_pData; + nStride = GRREG->m_nStride; + nStrideR = GRREFERENCE->m_nStride; + GRWR = (int32_t)GRREFERENCE->m_nWidth; + GRHR = (int32_t)GRREFERENCE->m_nHeight; + if (GRREFERENCEDY < -GRHR + 1 || GRREFERENCEDY > GRHR - 1) { + GRREFERENCEDY = 0; + } + nOffset = -GRREFERENCEDY * nStrideR; + for (int32_t h = 0; h < GRH; h++) { + if (TPGRON) { + SLTP = pArithDecoder->DECODE(&grContext[0x0010]); + LTP = LTP ^ SLTP; + } + line1 = (h > 0) ? pLine[-nStride] << 4 : 0; + int32_t reference_h = h - GRREFERENCEDY; + FX_BOOL line1_r_ok = (reference_h > 0 && reference_h < GRHR + 1); + FX_BOOL line2_r_ok = (reference_h > -1 && reference_h < GRHR); + FX_BOOL line3_r_ok = (reference_h > -2 && reference_h < GRHR - 1); + line1_r = line1_r_ok ? pLineR[nOffset - nStrideR] : 0; + line2_r = line2_r_ok ? pLineR[nOffset] : 0; + line3_r = line3_r_ok ? pLineR[nOffset + nStrideR] : 0; + if (LTP == 0) { + CONTEXT = (line1 & 0x1c00) | (line1_r & 0x01c0) | + ((line2_r >> 3) & 0x0038) | ((line3_r >> 6) & 0x0007); + for (int32_t w = 0; w < GRW; w += 8) { + nBits = GRW - w > 8 ? 8 : GRW - w; + if (h > 0) + line1 = (line1 << 8) | + (w + 8 < GRW ? pLine[-nStride + (w >> 3) + 1] << 4 : 0); + if (h > GRHR + GRREFERENCEDY + 1) { + line1_r = 0; + line2_r = 0; + line3_r = 0; + } else { + if (line1_r_ok) + line1_r = + (line1_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset - nStrideR + (w >> 3) + 1] : 0); + if (line2_r_ok) + line2_r = (line2_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + (w >> 3) + 1] : 0); + if (line3_r_ok) { + line3_r = + (line3_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + nStrideR + (w >> 3) + 1] : 0); + } else { + line3_r = 0; + } + } + cVal = 0; + for (k = 0; k < nBits; k++) { + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x0cdb) << 1) | (bVal << 9) | + ((line1 >> (7 - k)) & 0x0400) | + ((line1_r >> (7 - k)) & 0x0040) | + ((line2_r >> (10 - k)) & 0x0008) | + ((line3_r >> (13 - k)) & 0x0001); + } + pLine[w >> 3] = cVal; + } + } else { + CONTEXT = (line1 & 0x1c00) | (line1_r & 0x01c0) | + ((line2_r >> 3) & 0x0038) | ((line3_r >> 6) & 0x0007); + for (int32_t w = 0; w < GRW; w += 8) { + nBits = GRW - w > 8 ? 8 : GRW - w; + if (h > 0) + line1 = (line1 << 8) | + (w + 8 < GRW ? pLine[-nStride + (w >> 3) + 1] << 4 : 0); + if (line1_r_ok) + line1_r = + (line1_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset - nStrideR + (w >> 3) + 1] : 0); + if (line2_r_ok) + line2_r = (line2_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + (w >> 3) + 1] : 0); + if (line3_r_ok) { + line3_r = + (line3_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + nStrideR + (w >> 3) + 1] : 0); + } else { + line3_r = 0; + } + cVal = 0; + for (k = 0; k < nBits; k++) { + bVal = GRREFERENCE->getPixel(w + k, h); + if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w + k - 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + k, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + k + 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + k - 1, h)) && + (bVal == GRREFERENCE->getPixel(w + k + 1, h)) && + (bVal == GRREFERENCE->getPixel(w + k - 1, h + 1)) && + (bVal == GRREFERENCE->getPixel(w + k, h + 1)) && + (bVal == GRREFERENCE->getPixel(w + k + 1, h + 1)))) { + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + } + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x0cdb) << 1) | (bVal << 9) | + ((line1 >> (7 - k)) & 0x0400) | + ((line1_r >> (7 - k)) & 0x0040) | + ((line2_r >> (10 - k)) & 0x0008) | + ((line3_r >> (13 - k)) & 0x0001); + } + pLine[w >> 3] = cVal; + } + } + pLine += nStride; + if (h < GRHR + GRREFERENCEDY) { + pLineR += nStrideR; + } + } + return GRREG.release(); +} + +CJBig2_Image* CJBig2_GRRDProc::decode_Template1_unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line2, line3, line4, line5; + LTP = 0; + std::unique_ptr<CJBig2_Image> GRREG(new CJBig2_Image(GRW, GRH)); + GRREG->fill(0); + for (FX_DWORD h = 0; h < GRH; h++) { + if (TPGRON) { + SLTP = pArithDecoder->DECODE(&grContext[0x0008]); + LTP = LTP ^ SLTP; + } + if (LTP == 0) { + line1 = GRREG->getPixel(1, h - 1); + line1 |= GRREG->getPixel(0, h - 1) << 1; + line1 |= GRREG->getPixel(-1, h - 1) << 2; + line2 = 0; + line3 = GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1); + line4 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY); + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1; + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY) + << 2; + line5 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1); + line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1) + << 1; + for (FX_DWORD w = 0; w < GRW; w++) { + CONTEXT = line5; + CONTEXT |= line4 << 2; + CONTEXT |= line3 << 5; + CONTEXT |= line2 << 6; + CONTEXT |= line1 << 7; + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + GRREG->setPixel(w, h, bVal); + line1 = ((line1 << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x07; + line2 = ((line2 << 1) | bVal) & 0x01; + line3 = ((line3 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 1, + h - GRREFERENCEDY - 1)) & + 0x01; + line4 = + ((line4 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) & + 0x07; + line5 = ((line5 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, + h - GRREFERENCEDY + 1)) & + 0x03; + } + } else { + line1 = GRREG->getPixel(1, h - 1); + line1 |= GRREG->getPixel(0, h - 1) << 1; + line1 |= GRREG->getPixel(-1, h - 1) << 2; + line2 = 0; + line3 = GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY - 1); + line4 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY); + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY) << 1; + line4 |= GRREFERENCE->getPixel(-GRREFERENCEDX - 1, h - GRREFERENCEDY) + << 2; + line5 = GRREFERENCE->getPixel(-GRREFERENCEDX + 1, h - GRREFERENCEDY + 1); + line5 |= GRREFERENCE->getPixel(-GRREFERENCEDX, h - GRREFERENCEDY + 1) + << 1; + for (FX_DWORD w = 0; w < GRW; w++) { + bVal = GRREFERENCE->getPixel(w, h); + if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w - 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w - 1, h)) && + (bVal == GRREFERENCE->getPixel(w + 1, h)) && + (bVal == GRREFERENCE->getPixel(w - 1, h + 1)) && + (bVal == GRREFERENCE->getPixel(w, h + 1)) && + (bVal == GRREFERENCE->getPixel(w + 1, h + 1)))) { + CONTEXT = line5; + CONTEXT |= line4 << 2; + CONTEXT |= line3 << 5; + CONTEXT |= line2 << 6; + CONTEXT |= line1 << 7; + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + } + GRREG->setPixel(w, h, bVal); + line1 = ((line1 << 1) | GRREG->getPixel(w + 2, h - 1)) & 0x07; + line2 = ((line2 << 1) | bVal) & 0x01; + line3 = ((line3 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 1, + h - GRREFERENCEDY - 1)) & + 0x01; + line4 = + ((line4 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, h - GRREFERENCEDY)) & + 0x07; + line5 = ((line5 << 1) | + GRREFERENCE->getPixel(w - GRREFERENCEDX + 2, + h - GRREFERENCEDY + 1)) & + 0x03; + } + } + } + return GRREG.release(); +} + +CJBig2_Image* CJBig2_GRRDProc::decode_Template1_opt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { + if (!GRREFERENCE->m_pData) + return nullptr; + + FX_BOOL LTP, SLTP, bVal; + FX_DWORD CONTEXT; + FX_DWORD line1, line1_r, line2_r, line3_r; + uint8_t *pLine, *pLineR, cVal; + intptr_t nStride, nStrideR, nOffset; + int32_t k, nBits; + int32_t GRWR, GRHR; + int32_t GRW, GRH; + GRW = (int32_t)CJBig2_GRRDProc::GRW; + GRH = (int32_t)CJBig2_GRRDProc::GRH; + LTP = 0; + std::unique_ptr<CJBig2_Image> GRREG(new CJBig2_Image(GRW, GRH)); + if (!GRREG->m_pData) + return nullptr; + + pLine = GRREG->m_pData; + pLineR = GRREFERENCE->m_pData; + nStride = GRREG->m_nStride; + nStrideR = GRREFERENCE->m_nStride; + GRWR = (int32_t)GRREFERENCE->m_nWidth; + GRHR = (int32_t)GRREFERENCE->m_nHeight; + if (GRREFERENCEDY < -GRHR + 1 || GRREFERENCEDY > GRHR - 1) { + GRREFERENCEDY = 0; + } + nOffset = -GRREFERENCEDY * nStrideR; + for (int32_t h = 0; h < GRH; h++) { + if (TPGRON) { + SLTP = pArithDecoder->DECODE(&grContext[0x0008]); + LTP = LTP ^ SLTP; + } + line1 = (h > 0) ? pLine[-nStride] << 1 : 0; + int32_t reference_h = h - GRREFERENCEDY; + FX_BOOL line1_r_ok = (reference_h > 0 && reference_h < GRHR + 1); + FX_BOOL line2_r_ok = (reference_h > -1 && reference_h < GRHR); + FX_BOOL line3_r_ok = (reference_h > -2 && reference_h < GRHR - 1); + line1_r = line1_r_ok ? pLineR[nOffset - nStrideR] : 0; + line2_r = line2_r_ok ? pLineR[nOffset] : 0; + line3_r = line3_r_ok ? pLineR[nOffset + nStrideR] : 0; + if (LTP == 0) { + CONTEXT = (line1 & 0x0380) | ((line1_r >> 2) & 0x0020) | + ((line2_r >> 4) & 0x001c) | ((line3_r >> 6) & 0x0003); + for (int32_t w = 0; w < GRW; w += 8) { + nBits = GRW - w > 8 ? 8 : GRW - w; + if (h > 0) + line1 = (line1 << 8) | + (w + 8 < GRW ? pLine[-nStride + (w >> 3) + 1] << 1 : 0); + if (line1_r_ok) + line1_r = + (line1_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset - nStrideR + (w >> 3) + 1] : 0); + if (line2_r_ok) + line2_r = (line2_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + (w >> 3) + 1] : 0); + if (line3_r_ok) { + line3_r = + (line3_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + nStrideR + (w >> 3) + 1] : 0); + } else { + line3_r = 0; + } + cVal = 0; + for (k = 0; k < nBits; k++) { + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x018d) << 1) | (bVal << 6) | + ((line1 >> (7 - k)) & 0x0080) | + ((line1_r >> (9 - k)) & 0x0020) | + ((line2_r >> (11 - k)) & 0x0004) | + ((line3_r >> (13 - k)) & 0x0001); + } + pLine[w >> 3] = cVal; + } + } else { + CONTEXT = (line1 & 0x0380) | ((line1_r >> 2) & 0x0020) | + ((line2_r >> 4) & 0x001c) | ((line3_r >> 6) & 0x0003); + for (int32_t w = 0; w < GRW; w += 8) { + nBits = GRW - w > 8 ? 8 : GRW - w; + if (h > 0) + line1 = (line1 << 8) | + (w + 8 < GRW ? pLine[-nStride + (w >> 3) + 1] << 1 : 0); + if (line1_r_ok) + line1_r = + (line1_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset - nStrideR + (w >> 3) + 1] : 0); + if (line2_r_ok) + line2_r = (line2_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + (w >> 3) + 1] : 0); + if (line3_r_ok) { + line3_r = + (line3_r << 8) | + (w + 8 < GRWR ? pLineR[nOffset + nStrideR + (w >> 3) + 1] : 0); + } else { + line3_r = 0; + } + cVal = 0; + for (k = 0; k < nBits; k++) { + bVal = GRREFERENCE->getPixel(w + k, h); + if (!(TPGRON && (bVal == GRREFERENCE->getPixel(w + k - 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + k, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + k + 1, h - 1)) && + (bVal == GRREFERENCE->getPixel(w + k - 1, h)) && + (bVal == GRREFERENCE->getPixel(w + k + 1, h)) && + (bVal == GRREFERENCE->getPixel(w + k - 1, h + 1)) && + (bVal == GRREFERENCE->getPixel(w + k, h + 1)) && + (bVal == GRREFERENCE->getPixel(w + k + 1, h + 1)))) { + bVal = pArithDecoder->DECODE(&grContext[CONTEXT]); + } + cVal |= bVal << (7 - k); + CONTEXT = ((CONTEXT & 0x018d) << 1) | (bVal << 6) | + ((line1 >> (7 - k)) & 0x0080) | + ((line1_r >> (9 - k)) & 0x0020) | + ((line2_r >> (11 - k)) & 0x0004) | + ((line3_r >> (13 - k)) & 0x0001); + } + pLine[w >> 3] = cVal; + } + } + pLine += nStride; + if (h < GRHR + GRREFERENCEDY) { + pLineR += nStrideR; + } + } + return GRREG.release(); +} diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.h b/core/fxcodec/jbig2/JBig2_GrrdProc.h new file mode 100644 index 0000000000..36cfdcae3c --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_GrrdProc.h @@ -0,0 +1,43 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_ + +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithDecoder; +class CJBig2_Image; +struct JBig2ArithCtx; + +class CJBig2_GRRDProc { + public: + CJBig2_Image* decode(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext); + + CJBig2_Image* decode_Template0_unopt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext); + + CJBig2_Image* decode_Template0_opt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext); + + CJBig2_Image* decode_Template1_unopt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext); + + CJBig2_Image* decode_Template1_opt(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext); + + FX_DWORD GRW; + FX_DWORD GRH; + FX_BOOL GRTEMPLATE; + CJBig2_Image* GRREFERENCE; + int32_t GRREFERENCEDX; + int32_t GRREFERENCEDY; + FX_BOOL TPGRON; + int8_t GRAT[4]; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_ diff --git a/core/fxcodec/jbig2/JBig2_GsidProc.cpp b/core/fxcodec/jbig2/JBig2_GsidProc.cpp new file mode 100644 index 0000000000..92532d7238 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_GsidProc.cpp @@ -0,0 +1,123 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_GsidProc.h" + +#include <memory> + +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/fxcodec/jbig2/JBig2_GrdProc.h" +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/fxcodec/jbig2/JBig2_List.h" +#include "core/include/fxcrt/fx_basic.h" + +FX_DWORD* CJBig2_GSIDProc::decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc()); + pGRD->MMR = GSMMR; + pGRD->GBW = GSW; + pGRD->GBH = GSH; + pGRD->GBTEMPLATE = GSTEMPLATE; + pGRD->TPGDON = 0; + pGRD->USESKIP = GSUSESKIP; + pGRD->SKIP = GSKIP; + if (GSTEMPLATE <= 1) { + pGRD->GBAT[0] = 3; + } else { + pGRD->GBAT[0] = 2; + } + pGRD->GBAT[1] = -1; + if (pGRD->GBTEMPLATE == 0) { + pGRD->GBAT[2] = -3; + pGRD->GBAT[3] = -1; + pGRD->GBAT[4] = 2; + pGRD->GBAT[5] = -2; + pGRD->GBAT[6] = -2; + pGRD->GBAT[7] = -2; + } + + CJBig2_List<CJBig2_Image> GSPLANES(GSBPP); + for (int32_t i = GSBPP - 1; i >= 0; --i) { + CJBig2_Image* pImage = nullptr; + FXCODEC_STATUS status = + pGRD->Start_decode_Arith(&pImage, pArithDecoder, gbContext, nullptr); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) + pGRD->Continue_decode(pPause); + + if (!pImage) + return nullptr; + + GSPLANES.set(i, pImage); + + if (i < GSBPP - 1) + pImage->composeFrom(0, 0, GSPLANES.get(i + 1), JBIG2_COMPOSE_XOR); + } + std::unique_ptr<FX_DWORD, FxFreeDeleter> GSVALS( + FX_Alloc2D(FX_DWORD, GSW, GSH)); + JBIG2_memset(GSVALS.get(), 0, sizeof(FX_DWORD) * GSW * GSH); + for (FX_DWORD y = 0; y < GSH; ++y) { + for (FX_DWORD x = 0; x < GSW; ++x) { + for (int32_t i = 0; i < GSBPP; ++i) { + GSVALS.get()[y * GSW + x] |= GSPLANES.get(i)->getPixel(x, y) << i; + } + } + } + return GSVALS.release(); +} + +FX_DWORD* CJBig2_GSIDProc::decode_MMR(CJBig2_BitStream* pStream, + IFX_Pause* pPause) { + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc()); + pGRD->MMR = GSMMR; + pGRD->GBW = GSW; + pGRD->GBH = GSH; + + std::unique_ptr<CJBig2_Image*> GSPLANES(FX_Alloc(CJBig2_Image*, GSBPP)); + JBIG2_memset(GSPLANES.get(), 0, sizeof(CJBig2_Image*) * GSBPP); + FXCODEC_STATUS status = + pGRD->Start_decode_MMR(&GSPLANES.get()[GSBPP - 1], pStream, nullptr); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + pGRD->Continue_decode(pPause); + } + if (!GSPLANES.get()[GSBPP - 1]) + return nullptr; + + pStream->alignByte(); + pStream->offset(3); + int32_t J = GSBPP - 2; + while (J >= 0) { + FXCODEC_STATUS status = + pGRD->Start_decode_MMR(&GSPLANES.get()[J], pStream, nullptr); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + pGRD->Continue_decode(pPause); + } + if (!GSPLANES.get()[J]) { + for (int32_t K = GSBPP - 1; K > J; --K) { + delete GSPLANES.get()[K]; + return nullptr; + } + } + pStream->alignByte(); + pStream->offset(3); + GSPLANES.get()[J]->composeFrom(0, 0, GSPLANES.get()[J + 1], + JBIG2_COMPOSE_XOR); + J = J - 1; + } + std::unique_ptr<FX_DWORD> GSVALS(FX_Alloc2D(FX_DWORD, GSW, GSH)); + JBIG2_memset(GSVALS.get(), 0, sizeof(FX_DWORD) * GSW * GSH); + for (FX_DWORD y = 0; y < GSH; ++y) { + for (FX_DWORD x = 0; x < GSW; ++x) { + for (J = 0; J < GSBPP; ++J) { + GSVALS.get()[y * GSW + x] |= GSPLANES.get()[J]->getPixel(x, y) << J; + } + } + } + for (J = 0; J < GSBPP; ++J) { + delete GSPLANES.get()[J]; + } + return GSVALS.release(); +} diff --git a/core/fxcodec/jbig2/JBig2_GsidProc.h b/core/fxcodec/jbig2/JBig2_GsidProc.h new file mode 100644 index 0000000000..50c24eaa60 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_GsidProc.h @@ -0,0 +1,36 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_GSIDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_GSIDPROC_H_ + +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithDecoder; +class CJBig2_BitStream; +class CJBig2_Image; +class IFX_Pause; +struct JBig2ArithCtx; + +class CJBig2_GSIDProc { + public: + FX_DWORD* decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + + FX_DWORD* decode_MMR(CJBig2_BitStream* pStream, IFX_Pause* pPause); + + public: + FX_BOOL GSMMR; + FX_BOOL GSUSESKIP; + uint8_t GSBPP; + FX_DWORD GSW; + FX_DWORD GSH; + uint8_t GSTEMPLATE; + CJBig2_Image* GSKIP; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_GSIDPROC_H_ diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp new file mode 100644 index 0000000000..daee067d74 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp @@ -0,0 +1,104 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_HtrdProc.h" + +#include <memory> + +#include "core/fxcodec/jbig2/JBig2_GsidProc.h" +#include "core/include/fxcrt/fx_basic.h" + +CJBig2_Image* CJBig2_HTRDProc::decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_DWORD ng, mg; + int32_t x, y; + FX_DWORD HBPP; + FX_DWORD* GI; + std::unique_ptr<CJBig2_Image> HSKIP; + std::unique_ptr<CJBig2_Image> HTREG(new CJBig2_Image(HBW, HBH)); + HTREG->fill(HDEFPIXEL); + if (HENABLESKIP == 1) { + HSKIP.reset(new CJBig2_Image(HGW, HGH)); + for (mg = 0; mg < HGH; mg++) { + for (ng = 0; ng < HGW; ng++) { + x = (HGX + mg * HRY + ng * HRX) >> 8; + y = (HGY + mg * HRX - ng * HRY) >> 8; + if ((x + HPW <= 0) | (x >= (int32_t)HBW) | (y + HPH <= 0) | + (y >= (int32_t)HPH)) { + HSKIP->setPixel(ng, mg, 1); + } else { + HSKIP->setPixel(ng, mg, 0); + } + } + } + } + HBPP = 1; + while ((FX_DWORD)(1 << HBPP) < HNUMPATS) { + HBPP++; + } + std::unique_ptr<CJBig2_GSIDProc> pGID(new CJBig2_GSIDProc()); + pGID->GSMMR = HMMR; + pGID->GSW = HGW; + pGID->GSH = HGH; + pGID->GSBPP = (uint8_t)HBPP; + pGID->GSUSESKIP = HENABLESKIP; + pGID->GSKIP = HSKIP.get(); + pGID->GSTEMPLATE = HTEMPLATE; + GI = pGID->decode_Arith(pArithDecoder, gbContext, pPause); + if (!GI) + return nullptr; + + for (mg = 0; mg < HGH; mg++) { + for (ng = 0; ng < HGW; ng++) { + x = (HGX + mg * HRY + ng * HRX) >> 8; + y = (HGY + mg * HRX - ng * HRY) >> 8; + FX_DWORD pat_index = GI[mg * HGW + ng]; + if (pat_index >= HNUMPATS) { + pat_index = HNUMPATS - 1; + } + HTREG->composeFrom(x, y, HPATS[pat_index], HCOMBOP); + } + } + FX_Free(GI); + return HTREG.release(); +} + +CJBig2_Image* CJBig2_HTRDProc::decode_MMR(CJBig2_BitStream* pStream, + IFX_Pause* pPause) { + FX_DWORD ng, mg; + int32_t x, y; + FX_DWORD* GI; + std::unique_ptr<CJBig2_Image> HTREG(new CJBig2_Image(HBW, HBH)); + HTREG->fill(HDEFPIXEL); + FX_DWORD HBPP = 1; + while ((FX_DWORD)(1 << HBPP) < HNUMPATS) { + HBPP++; + } + std::unique_ptr<CJBig2_GSIDProc> pGID(new CJBig2_GSIDProc()); + pGID->GSMMR = HMMR; + pGID->GSW = HGW; + pGID->GSH = HGH; + pGID->GSBPP = (uint8_t)HBPP; + pGID->GSUSESKIP = 0; + GI = pGID->decode_MMR(pStream, pPause); + if (!GI) + return nullptr; + + for (mg = 0; mg < HGH; mg++) { + for (ng = 0; ng < HGW; ng++) { + x = (HGX + mg * HRY + ng * HRX) >> 8; + y = (HGY + mg * HRX - ng * HRY) >> 8; + FX_DWORD pat_index = GI[mg * HGW + ng]; + if (pat_index >= HNUMPATS) { + pat_index = HNUMPATS - 1; + } + HTREG->composeFrom(x, y, HPATS[pat_index], HCOMBOP); + } + } + FX_Free(GI); + return HTREG.release(); +} diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.h b/core/fxcodec/jbig2/JBig2_HtrdProc.h new file mode 100644 index 0000000000..583145b5ca --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HtrdProc.h @@ -0,0 +1,46 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_ + +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithDecoder; +class CJBig2_BitStream; +class IFX_Pause; +struct JBig2ArithCtx; + +class CJBig2_HTRDProc { + public: + CJBig2_Image* decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + + CJBig2_Image* decode_MMR(CJBig2_BitStream* pStream, IFX_Pause* pPause); + + public: + FX_DWORD HBW; + FX_DWORD HBH; + FX_BOOL HMMR; + uint8_t HTEMPLATE; + FX_DWORD HNUMPATS; + CJBig2_Image** HPATS; + FX_BOOL HDEFPIXEL; + JBig2ComposeOp HCOMBOP; + FX_BOOL HENABLESKIP; + FX_DWORD HGW; + FX_DWORD HGH; + int32_t HGX; + int32_t HGY; + FX_WORD HRX; + FX_WORD HRY; + uint8_t HPW; + uint8_t HPH; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_ diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp new file mode 100644 index 0000000000..afcc17a55e --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp @@ -0,0 +1,45 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h" + +#include "core/fxcodec/jbig2/JBig2_Define.h" + +CJBig2_HuffmanDecoder::CJBig2_HuffmanDecoder(CJBig2_BitStream* pStream) + : m_pStream(pStream) {} + +CJBig2_HuffmanDecoder::~CJBig2_HuffmanDecoder() {} + +int CJBig2_HuffmanDecoder::decodeAValue(CJBig2_HuffmanTable* pTable, + int* nResult) { + int nVal = 0; + int nBits = 0; + while (1) { + FX_DWORD nTmp; + if (m_pStream->read1Bit(&nTmp) == -1) + break; + + nVal = (nVal << 1) | nTmp; + ++nBits; + for (FX_DWORD i = 0; i < pTable->Size(); ++i) { + if (pTable->GetPREFLEN()[i] == nBits && pTable->GetCODES()[i] == nVal) { + if (pTable->IsHTOOB() && i == pTable->Size() - 1) + return JBIG2_OOB; + + if (m_pStream->readNBits(pTable->GetRANGELEN()[i], &nTmp) == -1) + return -1; + + FX_DWORD offset = pTable->IsHTOOB() ? 3 : 2; + if (i == pTable->Size() - offset) + *nResult = pTable->GetRANGELOW()[i] - nTmp; + else + *nResult = pTable->GetRANGELOW()[i] + nTmp; + return 0; + } + } + } + return -1; +} diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h new file mode 100644 index 0000000000..c72346ac18 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h @@ -0,0 +1,24 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_HUFFMANDECODER_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_HUFFMANDECODER_H_ + +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanTable.h" +class CJBig2_HuffmanDecoder { + public: + explicit CJBig2_HuffmanDecoder(CJBig2_BitStream* pStream); + + ~CJBig2_HuffmanDecoder(); + + int decodeAValue(CJBig2_HuffmanTable* pTable, int* nResult); + + private: + CJBig2_BitStream* const m_pStream; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_HUFFMANDECODER_H_ diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp new file mode 100644 index 0000000000..f4a838eb24 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp @@ -0,0 +1,136 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_HuffmanTable.h" + +#include <algorithm> +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_BitStream.h" +#include "core/fxcodec/jbig2/JBig2_Define.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h" +#include "core/include/fxcrt/fx_memory.h" + +CJBig2_HuffmanTable::CJBig2_HuffmanTable(const JBig2TableLine* pTable, + FX_DWORD nLines, + bool bHTOOB) + : m_bOK(true), HTOOB(bHTOOB), NTEMP(nLines) { + ParseFromStandardTable(pTable); +} + +CJBig2_HuffmanTable::CJBig2_HuffmanTable(CJBig2_BitStream* pStream) + : HTOOB(false), NTEMP(0) { + m_bOK = ParseFromCodedBuffer(pStream); +} + +CJBig2_HuffmanTable::~CJBig2_HuffmanTable() {} + +void CJBig2_HuffmanTable::ParseFromStandardTable(const JBig2TableLine* pTable) { + PREFLEN.resize(NTEMP); + RANGELEN.resize(NTEMP); + RANGELOW.resize(NTEMP); + for (FX_DWORD i = 0; i < NTEMP; ++i) { + PREFLEN[i] = pTable[i].PREFLEN; + RANGELEN[i] = pTable[i].RANDELEN; + RANGELOW[i] = pTable[i].RANGELOW; + } + InitCodes(); +} + +bool CJBig2_HuffmanTable::ParseFromCodedBuffer(CJBig2_BitStream* pStream) { + unsigned char cTemp; + if (pStream->read1Byte(&cTemp) == -1) + return false; + + HTOOB = !!(cTemp & 0x01); + unsigned char HTPS = ((cTemp >> 1) & 0x07) + 1; + unsigned char HTRS = ((cTemp >> 4) & 0x07) + 1; + FX_DWORD HTLOW; + FX_DWORD HTHIGH; + if (pStream->readInteger(&HTLOW) == -1 || + pStream->readInteger(&HTHIGH) == -1) { + return false; + } + + const int low = static_cast<int>(HTLOW); + const int high = static_cast<int>(HTHIGH); + if (low > high) + return false; + + ExtendBuffers(false); + int cur_low = low; + do { + if ((pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) || + (pStream->readNBits(HTRS, &RANGELEN[NTEMP]) == -1)) { + return false; + } + RANGELOW[NTEMP] = cur_low; + cur_low += (1 << RANGELEN[NTEMP]); + ExtendBuffers(true); + } while (cur_low < high); + + if (pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) + return false; + + RANGELEN[NTEMP] = 32; + RANGELOW[NTEMP] = low - 1; + ExtendBuffers(true); + + if (pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) + return false; + + RANGELEN[NTEMP] = 32; + RANGELOW[NTEMP] = high; + ExtendBuffers(true); + + if (HTOOB) { + if (pStream->readNBits(HTPS, &PREFLEN[NTEMP]) == -1) + return false; + + ++NTEMP; + } + + InitCodes(); + return true; +} + +void CJBig2_HuffmanTable::InitCodes() { + int lenmax = 0; + for (FX_DWORD i = 0; i < NTEMP; ++i) + lenmax = std::max(PREFLEN[i], lenmax); + + CODES.resize(NTEMP); + std::vector<int> LENCOUNT(lenmax + 1); + std::vector<int> FIRSTCODE(lenmax + 1); + for (int len : PREFLEN) + ++LENCOUNT[len]; + + FIRSTCODE[0] = 0; + LENCOUNT[0] = 0; + for (int i = 1; i <= lenmax; ++i) { + FIRSTCODE[i] = (FIRSTCODE[i - 1] + LENCOUNT[i - 1]) << 1; + int CURCODE = FIRSTCODE[i]; + for (FX_DWORD j = 0; j < NTEMP; ++j) { + if (PREFLEN[j] == i) + CODES[j] = CURCODE++; + } + } +} + +void CJBig2_HuffmanTable::ExtendBuffers(bool increment) { + if (increment) + ++NTEMP; + + size_t size = PREFLEN.size(); + if (NTEMP < size) + return; + + size += 16; + ASSERT(NTEMP < size); + PREFLEN.resize(size); + RANGELEN.resize(size); + RANGELOW.resize(size); +} diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.h b/core/fxcodec/jbig2/JBig2_HuffmanTable.h new file mode 100644 index 0000000000..892c19e9d6 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.h @@ -0,0 +1,50 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_ + +#include <vector> + +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_BitStream; +struct JBig2TableLine; + +class CJBig2_HuffmanTable { + public: + CJBig2_HuffmanTable(const JBig2TableLine* pTable, + FX_DWORD nLines, + bool bHTOOB); + + explicit CJBig2_HuffmanTable(CJBig2_BitStream* pStream); + + ~CJBig2_HuffmanTable(); + + bool IsHTOOB() const { return HTOOB; } + FX_DWORD Size() const { return NTEMP; } + const std::vector<int>& GetCODES() const { return CODES; } + const std::vector<int>& GetPREFLEN() const { return PREFLEN; } + const std::vector<int>& GetRANGELEN() const { return RANGELEN; } + const std::vector<int>& GetRANGELOW() const { return RANGELOW; } + bool IsOK() const { return m_bOK; } + + private: + void ParseFromStandardTable(const JBig2TableLine* pTable); + bool ParseFromCodedBuffer(CJBig2_BitStream* pStream); + void InitCodes(); + void ExtendBuffers(bool increment); + + bool m_bOK; + bool HTOOB; + FX_DWORD NTEMP; + std::vector<int> CODES; + std::vector<int> PREFLEN; + std::vector<int> RANGELEN; + std::vector<int> RANGELOW; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_ diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h b/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h new file mode 100644 index 0000000000..0b768654f5 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h @@ -0,0 +1,98 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_STANDARD_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_STANDARD_H_ + +#include "core/include/fxcrt/fx_system.h" + +struct JBig2TableLine { + int PREFLEN; + int RANDELEN; + int RANGELOW; +}; + +const bool HuffmanTable_HTOOB_B1 = false; +const JBig2TableLine HuffmanTable_B1[] = {{1, 4, 0}, + {2, 8, 16}, + {3, 16, 272}, + {0, 32, -1}, + {3, 32, 65808}}; +const bool HuffmanTable_HTOOB_B2 = true; +const JBig2TableLine HuffmanTable_B2[] = {{1, 0, 0}, {2, 0, 1}, {3, 0, 2}, + {4, 3, 3}, {5, 6, 11}, {0, 32, -1}, + {6, 32, 75}, {6, 0, 0}}; +const bool HuffmanTable_HTOOB_B3 = true; +const JBig2TableLine HuffmanTable_B3[] = { + {8, 8, -256}, {1, 0, 0}, {2, 0, 1}, {3, 0, 2}, {4, 3, 3}, + {5, 6, 11}, {8, 32, -257}, {7, 32, 75}, {6, 0, 0}}; +const bool HuffmanTable_HTOOB_B4 = false; +const JBig2TableLine HuffmanTable_B4[] = { + {1, 0, 1}, {2, 0, 2}, {3, 0, 3}, {4, 3, 4}, + {5, 6, 12}, {0, 32, -1}, {5, 32, 76}, +}; +const bool HuffmanTable_HTOOB_B5 = false; +const JBig2TableLine HuffmanTable_B5[] = {{7, 8, -255}, {1, 0, 1}, {2, 0, 2}, + {3, 0, 3}, {4, 3, 4}, {5, 6, 12}, + {7, 32, -256}, {6, 32, 76}}; +const bool HuffmanTable_HTOOB_B6 = false; +const JBig2TableLine HuffmanTable_B6[] = { + {5, 10, -2048}, {4, 9, -1024}, {4, 8, -512}, {4, 7, -256}, {5, 6, -128}, + {5, 5, -64}, {4, 5, -32}, {2, 7, 0}, {3, 7, 128}, {3, 8, 256}, + {4, 9, 512}, {4, 10, 1024}, {6, 32, -2049}, {6, 32, 2048}}; +const bool HuffmanTable_HTOOB_B7 = false; +const JBig2TableLine HuffmanTable_B7[] = { + {4, 9, -1024}, {3, 8, -512}, {4, 7, -256}, {5, 6, -128}, {5, 5, -64}, + {4, 5, -32}, {4, 5, 0}, {5, 5, 32}, {5, 6, 64}, {4, 7, 128}, + {3, 8, 256}, {3, 9, 512}, {3, 10, 1024}, {5, 32, -1025}, {5, 32, 2048}, +}; +const bool HuffmanTable_HTOOB_B8 = true; +const JBig2TableLine HuffmanTable_B8[] = { + {8, 3, -15}, {9, 1, -7}, {8, 1, -5}, {9, 0, -3}, {7, 0, -2}, + {4, 0, -1}, {2, 1, 0}, {5, 0, 2}, {6, 0, 3}, {3, 4, 4}, + {6, 1, 20}, {4, 4, 22}, {4, 5, 38}, {5, 6, 70}, {5, 7, 134}, + {6, 7, 262}, {7, 8, 390}, {6, 10, 646}, {9, 32, -16}, {9, 32, 1670}, + {2, 0, 0}}; +const bool HuffmanTable_HTOOB_B9 = true; +const JBig2TableLine HuffmanTable_B9[] = { + {8, 4, -31}, {9, 2, -15}, {8, 2, -11}, {9, 1, -7}, {7, 1, -5}, + {4, 1, -3}, {3, 1, -1}, {3, 1, 1}, {5, 1, 3}, {6, 1, 5}, + {3, 5, 7}, {6, 2, 39}, {4, 5, 43}, {4, 6, 75}, {5, 7, 139}, + {5, 8, 267}, {6, 8, 523}, {7, 9, 779}, {6, 11, 1291}, {9, 32, -32}, + {9, 32, 3339}, {2, 0, 0}}; +const bool HuffmanTable_HTOOB_B10 = true; +const JBig2TableLine HuffmanTable_B10[] = { + {7, 4, -21}, {8, 0, -5}, {7, 0, -4}, {5, 0, -3}, {2, 2, -2}, + {5, 0, 2}, {6, 0, 3}, {7, 0, 4}, {8, 0, 5}, {2, 6, 6}, + {5, 5, 70}, {6, 5, 102}, {6, 6, 134}, {6, 7, 198}, {6, 8, 326}, + {6, 9, 582}, {6, 10, 1094}, {7, 11, 2118}, {8, 32, -22}, {8, 32, 4166}, + {2, 0, 0}}; +const bool HuffmanTable_HTOOB_B11 = false; +const JBig2TableLine HuffmanTable_B11[] = { + {1, 0, 1}, {2, 1, 2}, {4, 0, 4}, {4, 1, 5}, {5, 1, 7}, + {5, 2, 9}, {6, 2, 13}, {7, 2, 17}, {7, 3, 21}, {7, 4, 29}, + {7, 5, 45}, {7, 6, 77}, {0, 32, 0}, {7, 32, 141}}; +const bool HuffmanTable_HTOOB_B12 = false; +const JBig2TableLine HuffmanTable_B12[] = { + {1, 0, 1}, {2, 0, 2}, {3, 1, 3}, {5, 0, 5}, {5, 1, 6}, + {6, 1, 8}, {7, 0, 10}, {7, 1, 11}, {7, 2, 13}, {7, 3, 17}, + {7, 4, 25}, {8, 5, 41}, {0, 32, 0}, {8, 32, 73}}; +const bool HuffmanTable_HTOOB_B13 = false; +const JBig2TableLine HuffmanTable_B13[] = { + {1, 0, 1}, {3, 0, 2}, {4, 0, 3}, {5, 0, 4}, {4, 1, 5}, + {3, 3, 7}, {6, 1, 15}, {6, 2, 17}, {6, 3, 21}, {6, 4, 29}, + {6, 5, 45}, {7, 6, 77}, {0, 32, 0}, {7, 32, 141}}; +const bool HuffmanTable_HTOOB_B14 = false; +const JBig2TableLine HuffmanTable_B14[] = {{3, 0, -2}, {3, 0, -1}, {1, 0, 0}, + {3, 0, 1}, {3, 0, 2}, {0, 32, -3}, + {0, 32, 3}}; +const bool HuffmanTable_HTOOB_B15 = false; +const JBig2TableLine HuffmanTable_B15[] = { + {7, 4, -24}, {6, 2, -8}, {5, 1, -4}, {4, 0, -2}, {3, 0, -1}, + {1, 0, 0}, {3, 0, 1}, {4, 0, 2}, {5, 1, 3}, {6, 2, 5}, + {7, 4, 9}, {7, 32, -25}, {7, 32, 25}}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_STANDARD_H_ diff --git a/core/fxcodec/jbig2/JBig2_Image.cpp b/core/fxcodec/jbig2/JBig2_Image.cpp new file mode 100644 index 0000000000..311c38270e --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Image.cpp @@ -0,0 +1,1099 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include <limits.h> + +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/include/fxcrt/fx_coordinates.h" +#include "core/include/fxcrt/fx_safe_types.h" + +CJBig2_Image::CJBig2_Image(int32_t w, int32_t h) { + m_nWidth = w; + m_nHeight = h; + if (m_nWidth <= 0 || m_nHeight <= 0 || m_nWidth > INT_MAX - 31) { + m_pData = NULL; + m_bNeedFree = FALSE; + return; + } + m_nStride = ((w + 31) >> 5) << 2; + if (m_nStride * m_nHeight > 0 && 104857600 / (int)m_nStride > m_nHeight) { + m_pData = FX_Alloc2D(uint8_t, m_nStride, m_nHeight); + } else { + m_pData = NULL; + } + m_bNeedFree = TRUE; +} +CJBig2_Image::CJBig2_Image(int32_t w, + int32_t h, + int32_t stride, + uint8_t* pBuf) { + m_nWidth = w; + m_nHeight = h; + m_nStride = stride; + m_pData = pBuf; + m_bNeedFree = FALSE; +} +CJBig2_Image::CJBig2_Image(const CJBig2_Image& im) { + m_nWidth = im.m_nWidth; + m_nHeight = im.m_nHeight; + m_nStride = im.m_nStride; + if (im.m_pData) { + m_pData = FX_Alloc2D(uint8_t, m_nStride, m_nHeight); + JBIG2_memcpy(m_pData, im.m_pData, m_nStride * m_nHeight); + } else { + m_pData = NULL; + } + m_bNeedFree = TRUE; +} +CJBig2_Image::~CJBig2_Image() { + if (m_bNeedFree) { + FX_Free(m_pData); + } +} +FX_BOOL CJBig2_Image::getPixel(int32_t x, int32_t y) { + if (!m_pData) { + return 0; + } + int32_t m, n; + if (x < 0 || x >= m_nWidth) { + return 0; + } + if (y < 0 || y >= m_nHeight) { + return 0; + } + m = y * m_nStride + (x >> 3); + n = x & 7; + return ((m_pData[m] >> (7 - n)) & 1); +} + +int32_t CJBig2_Image::setPixel(int32_t x, int32_t y, FX_BOOL v) { + if (!m_pData) { + return 0; + } + int32_t m, n; + if (x < 0 || x >= m_nWidth) { + return 0; + } + if (y < 0 || y >= m_nHeight) { + return 0; + } + m = y * m_nStride + (x >> 3); + n = x & 7; + if (v) { + m_pData[m] |= 1 << (7 - n); + } else { + m_pData[m] &= ~(1 << (7 - n)); + } + return 1; +} +void CJBig2_Image::copyLine(int32_t hTo, int32_t hFrom) { + if (!m_pData) { + return; + } + if (hFrom < 0 || hFrom >= m_nHeight) { + JBIG2_memset(m_pData + hTo * m_nStride, 0, m_nStride); + } else { + JBIG2_memcpy(m_pData + hTo * m_nStride, m_pData + hFrom * m_nStride, + m_nStride); + } +} +void CJBig2_Image::fill(FX_BOOL v) { + if (!m_pData) { + return; + } + JBIG2_memset(m_pData, v ? 0xff : 0, m_nStride * m_nHeight); +} +FX_BOOL CJBig2_Image::composeTo(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op) { + if (!m_pData) { + return FALSE; + } + return composeTo_opt2(pDst, x, y, op); +} +FX_BOOL CJBig2_Image::composeTo(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op, + const FX_RECT* pSrcRect) { + if (!m_pData) { + return FALSE; + } + if (NULL == pSrcRect || *pSrcRect == FX_RECT(0, 0, m_nWidth, m_nHeight)) { + return composeTo_opt2(pDst, x, y, op); + } + return composeTo_opt2(pDst, x, y, op, pSrcRect); +} + +FX_BOOL CJBig2_Image::composeFrom(int32_t x, + int32_t y, + CJBig2_Image* pSrc, + JBig2ComposeOp op) { + if (!m_pData) { + return FALSE; + } + return pSrc->composeTo(this, x, y, op); +} +FX_BOOL CJBig2_Image::composeFrom(int32_t x, + int32_t y, + CJBig2_Image* pSrc, + JBig2ComposeOp op, + const FX_RECT* pSrcRect) { + if (!m_pData) { + return FALSE; + } + return pSrc->composeTo(this, x, y, op, pSrcRect); +} +#define JBIG2_GETDWORD(buf) \ + ((FX_DWORD)(((buf)[0] << 24) | ((buf)[1] << 16) | ((buf)[2] << 8) | (buf)[3])) +CJBig2_Image* CJBig2_Image::subImage(int32_t x, + int32_t y, + int32_t w, + int32_t h) { + int32_t m, n, j; + uint8_t *pLineSrc, *pLineDst; + FX_DWORD wTmp; + uint8_t *pSrc, *pSrcEnd, *pDst, *pDstEnd; + if (w == 0 || h == 0) { + return NULL; + } + CJBig2_Image* pImage = new CJBig2_Image(w, h); + if (!m_pData) { + pImage->fill(0); + return pImage; + } + if (!pImage->m_pData) { + return pImage; + } + pLineSrc = m_pData + m_nStride * y; + pLineDst = pImage->m_pData; + m = (x >> 5) << 2; + n = x & 31; + if (n == 0) { + for (j = 0; j < h; j++) { + pSrc = pLineSrc + m; + pSrcEnd = pLineSrc + m_nStride; + pDst = pLineDst; + pDstEnd = pLineDst + pImage->m_nStride; + for (; pDst < pDstEnd; pSrc += 4, pDst += 4) { + *((FX_DWORD*)pDst) = *((FX_DWORD*)pSrc); + } + pLineSrc += m_nStride; + pLineDst += pImage->m_nStride; + } + } else { + for (j = 0; j < h; j++) { + pSrc = pLineSrc + m; + pSrcEnd = pLineSrc + m_nStride; + pDst = pLineDst; + pDstEnd = pLineDst + pImage->m_nStride; + for (; pDst < pDstEnd; pSrc += 4, pDst += 4) { + if (pSrc + 4 < pSrcEnd) { + wTmp = (JBIG2_GETDWORD(pSrc) << n) | + (JBIG2_GETDWORD(pSrc + 4) >> (32 - n)); + } else { + wTmp = JBIG2_GETDWORD(pSrc) << n; + } + pDst[0] = (uint8_t)(wTmp >> 24); + pDst[1] = (uint8_t)(wTmp >> 16); + pDst[2] = (uint8_t)(wTmp >> 8); + pDst[3] = (uint8_t)wTmp; + } + pLineSrc += m_nStride; + pLineDst += pImage->m_nStride; + } + } + return pImage; +} +void CJBig2_Image::expand(int32_t h, FX_BOOL v) { + if (!m_pData || h <= m_nHeight) { + return; + } + FX_DWORD dwH = pdfium::base::checked_cast<FX_DWORD>(h); + FX_DWORD dwStride = pdfium::base::checked_cast<FX_DWORD>(m_nStride); + FX_DWORD dwHeight = pdfium::base::checked_cast<FX_DWORD>(m_nHeight); + FX_SAFE_DWORD safeMemSize = dwH; + safeMemSize *= dwStride; + if (!safeMemSize.IsValid()) { + return; + } + // The guaranteed reallocated memory is to be < 4GB (unsigned int). + m_pData = FX_Realloc(uint8_t, m_pData, safeMemSize.ValueOrDie()); + + // The result of dwHeight * dwStride doesn't overflow after the + // checking of safeMemSize. + // The same as the result of (dwH - dwHeight) * dwStride) because + // dwH - dwHeight is always less than dwH(h) which is checked in + // the calculation of dwH * dwStride. + JBIG2_memset(m_pData + dwHeight * dwStride, v ? 0xff : 0, + (dwH - dwHeight) * dwStride); + m_nHeight = h; +} +FX_BOOL CJBig2_Image::composeTo_opt2(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op) { + int32_t xs0 = 0, ys0 = 0, xs1 = 0, ys1 = 0, xd0 = 0, yd0 = 0, xd1 = 0, + yd1 = 0, xx = 0, yy = 0, w = 0, h = 0, middleDwords = 0, lineLeft = 0; + + FX_DWORD s1 = 0, d1 = 0, d2 = 0, shift = 0, shift1 = 0, shift2 = 0, tmp = 0, + tmp1 = 0, tmp2 = 0, maskL = 0, maskR = 0, maskM = 0; + + uint8_t *lineSrc = NULL, *lineDst = NULL, *sp = NULL, *dp = NULL; + + if (!m_pData) { + return FALSE; + } + if (x < -1048576 || x > 1048576 || y < -1048576 || y > 1048576) { + return FALSE; + } + if (y < 0) { + ys0 = -y; + } + if (y + m_nHeight > pDst->m_nHeight) { + ys1 = pDst->m_nHeight - y; + } else { + ys1 = m_nHeight; + } + if (x < 0) { + xs0 = -x; + } + if (x + m_nWidth > pDst->m_nWidth) { + xs1 = pDst->m_nWidth - x; + } else { + xs1 = m_nWidth; + } + if ((ys0 >= ys1) || (xs0 >= xs1)) { + return 0; + } + w = xs1 - xs0; + h = ys1 - ys0; + if (y >= 0) { + yd0 = y; + } + if (x >= 0) { + xd0 = x; + } + xd1 = xd0 + w; + yd1 = yd0 + h; + d1 = xd0 & 31; + d2 = xd1 & 31; + s1 = xs0 & 31; + maskL = 0xffffffff >> d1; + maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32); + maskM = maskL & maskR; + lineSrc = m_pData + ys0 * m_nStride + ((xs0 >> 5) << 2); + lineLeft = m_nStride - ((xs0 >> 5) << 2); + lineDst = pDst->m_pData + yd0 * pDst->m_nStride + ((xd0 >> 5) << 2); + if ((xd0 & ~31) == ((xd1 - 1) & ~31)) { + if ((xs0 & ~31) == ((xs1 - 1) & ~31)) { + if (s1 > d1) { + shift = s1 - d1; + for (yy = yd0; yy < yd1; yy++) { + tmp1 = JBIG2_GETDWORD(lineSrc) << shift; + tmp2 = JBIG2_GETDWORD(lineDst); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskM) | (tmp1 & maskM); + break; + } + lineDst[0] = (uint8_t)(tmp >> 24); + lineDst[1] = (uint8_t)(tmp >> 16); + lineDst[2] = (uint8_t)(tmp >> 8); + lineDst[3] = (uint8_t)tmp; + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } else { + shift = d1 - s1; + for (yy = yd0; yy < yd1; yy++) { + tmp1 = JBIG2_GETDWORD(lineSrc) >> shift; + tmp2 = JBIG2_GETDWORD(lineDst); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskM) | (tmp1 & maskM); + break; + } + lineDst[0] = (uint8_t)(tmp >> 24); + lineDst[1] = (uint8_t)(tmp >> 16); + lineDst[2] = (uint8_t)(tmp >> 8); + lineDst[3] = (uint8_t)tmp; + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } + } else { + shift1 = s1 - d1; + shift2 = 32 - shift1; + for (yy = yd0; yy < yd1; yy++) { + tmp1 = (JBIG2_GETDWORD(lineSrc) << shift1) | + (JBIG2_GETDWORD(lineSrc + 4) >> shift2); + tmp2 = JBIG2_GETDWORD(lineDst); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskM) | (tmp1 & maskM); + break; + } + lineDst[0] = (uint8_t)(tmp >> 24); + lineDst[1] = (uint8_t)(tmp >> 16); + lineDst[2] = (uint8_t)(tmp >> 8); + lineDst[3] = (uint8_t)tmp; + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } + } else { + if (s1 > d1) { + shift1 = s1 - d1; + shift2 = 32 - shift1; + middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5); + for (yy = yd0; yy < yd1; yy++) { + sp = lineSrc; + dp = lineDst; + if (d1 != 0) { + tmp1 = (JBIG2_GETDWORD(sp) << shift1) | + (JBIG2_GETDWORD(sp + 4) >> shift2); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskL) | (tmp1 & maskL); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + for (xx = 0; xx < middleDwords; xx++) { + tmp1 = (JBIG2_GETDWORD(sp) << shift1) | + (JBIG2_GETDWORD(sp + 4) >> shift2); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = tmp1 | tmp2; + break; + case JBIG2_COMPOSE_AND: + tmp = tmp1 & tmp2; + break; + case JBIG2_COMPOSE_XOR: + tmp = tmp1 ^ tmp2; + break; + case JBIG2_COMPOSE_XNOR: + tmp = ~(tmp1 ^ tmp2); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = tmp1; + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + if (d2 != 0) { + tmp1 = + (JBIG2_GETDWORD(sp) << shift1) | + (((sp + 4) < lineSrc + lineLeft ? JBIG2_GETDWORD(sp + 4) : 0) >> + shift2); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskR) | (tmp1 & maskR); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + } + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } else if (s1 == d1) { + middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5); + for (yy = yd0; yy < yd1; yy++) { + sp = lineSrc; + dp = lineDst; + if (d1 != 0) { + tmp1 = JBIG2_GETDWORD(sp); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskL) | (tmp1 & maskL); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + for (xx = 0; xx < middleDwords; xx++) { + tmp1 = JBIG2_GETDWORD(sp); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = tmp1 | tmp2; + break; + case JBIG2_COMPOSE_AND: + tmp = tmp1 & tmp2; + break; + case JBIG2_COMPOSE_XOR: + tmp = tmp1 ^ tmp2; + break; + case JBIG2_COMPOSE_XNOR: + tmp = ~(tmp1 ^ tmp2); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = tmp1; + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + if (d2 != 0) { + tmp1 = JBIG2_GETDWORD(sp); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskR) | (tmp1 & maskR); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + } + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } else { + shift1 = d1 - s1; + shift2 = 32 - shift1; + middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5); + for (yy = yd0; yy < yd1; yy++) { + sp = lineSrc; + dp = lineDst; + if (d1 != 0) { + tmp1 = JBIG2_GETDWORD(sp) >> shift1; + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskL) | (tmp1 & maskL); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + dp += 4; + } + for (xx = 0; xx < middleDwords; xx++) { + tmp1 = (JBIG2_GETDWORD(sp) << shift2) | + ((JBIG2_GETDWORD(sp + 4)) >> shift1); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = tmp1 | tmp2; + break; + case JBIG2_COMPOSE_AND: + tmp = tmp1 & tmp2; + break; + case JBIG2_COMPOSE_XOR: + tmp = tmp1 ^ tmp2; + break; + case JBIG2_COMPOSE_XNOR: + tmp = ~(tmp1 ^ tmp2); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = tmp1; + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + if (d2 != 0) { + tmp1 = + (JBIG2_GETDWORD(sp) << shift2) | + (((sp + 4) < lineSrc + lineLeft ? JBIG2_GETDWORD(sp + 4) : 0) >> + shift1); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskR) | (tmp1 & maskR); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + } + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } + } + return 1; +} +FX_BOOL CJBig2_Image::composeTo_opt2(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op, + const FX_RECT* pSrcRect) { + int32_t xs0, ys0, xs1, ys1, xd0, yd0, xd1, yd1, xx, yy, w, h, middleDwords, + lineLeft; + FX_DWORD s1, d1, d2, shift, shift1, shift2, tmp, tmp1, tmp2, maskL, maskR, + maskM; + uint8_t *lineSrc, *lineDst, *sp, *dp; + int32_t sw, sh; + if (!m_pData) { + return FALSE; + } + if (x < -1048576 || x > 1048576 || y < -1048576 || y > 1048576) { + return FALSE; + } + sw = pSrcRect->Width(); + sh = pSrcRect->Height(); + if (y < 0) { + ys0 = -y; + } else { + ys0 = 0; + } + if (y + sh > pDst->m_nHeight) { + ys1 = pDst->m_nHeight - y; + } else { + ys1 = sh; + } + if (x < 0) { + xs0 = -x; + } else { + xs0 = 0; + } + if (x + sw > pDst->m_nWidth) { + xs1 = pDst->m_nWidth - x; + } else { + xs1 = sw; + } + if ((ys0 >= ys1) || (xs0 >= xs1)) { + return 0; + } + w = xs1 - xs0; + h = ys1 - ys0; + if (y < 0) { + yd0 = 0; + } else { + yd0 = y; + } + if (x < 0) { + xd0 = 0; + } else { + xd0 = x; + } + xd1 = xd0 + w; + yd1 = yd0 + h; + d1 = xd0 & 31; + d2 = xd1 & 31; + s1 = xs0 & 31; + maskL = 0xffffffff >> d1; + maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32); + maskM = maskL & maskR; + lineSrc = m_pData + (pSrcRect->top + ys0) * m_nStride + + (((xs0 + pSrcRect->left) >> 5) << 2); + lineLeft = m_nStride - ((xs0 >> 5) << 2); + lineDst = pDst->m_pData + yd0 * pDst->m_nStride + ((xd0 >> 5) << 2); + if ((xd0 & ~31) == ((xd1 - 1) & ~31)) { + if ((xs0 & ~31) == ((xs1 - 1) & ~31)) { + if (s1 > d1) { + shift = s1 - d1; + for (yy = yd0; yy < yd1; yy++) { + tmp1 = JBIG2_GETDWORD(lineSrc) << shift; + tmp2 = JBIG2_GETDWORD(lineDst); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskM) | (tmp1 & maskM); + break; + } + lineDst[0] = (uint8_t)(tmp >> 24); + lineDst[1] = (uint8_t)(tmp >> 16); + lineDst[2] = (uint8_t)(tmp >> 8); + lineDst[3] = (uint8_t)tmp; + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } else { + shift = d1 - s1; + for (yy = yd0; yy < yd1; yy++) { + tmp1 = JBIG2_GETDWORD(lineSrc) >> shift; + tmp2 = JBIG2_GETDWORD(lineDst); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskM) | (tmp1 & maskM); + break; + } + lineDst[0] = (uint8_t)(tmp >> 24); + lineDst[1] = (uint8_t)(tmp >> 16); + lineDst[2] = (uint8_t)(tmp >> 8); + lineDst[3] = (uint8_t)tmp; + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } + } else { + shift1 = s1 - d1; + shift2 = 32 - shift1; + for (yy = yd0; yy < yd1; yy++) { + tmp1 = (JBIG2_GETDWORD(lineSrc) << shift1) | + (JBIG2_GETDWORD(lineSrc + 4) >> shift2); + tmp2 = JBIG2_GETDWORD(lineDst); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskM) | ((tmp1 | tmp2) & maskM); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskM) | ((tmp1 & tmp2) & maskM); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskM) | ((tmp1 ^ tmp2) & maskM); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskM) | ((~(tmp1 ^ tmp2)) & maskM); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskM) | (tmp1 & maskM); + break; + } + lineDst[0] = (uint8_t)(tmp >> 24); + lineDst[1] = (uint8_t)(tmp >> 16); + lineDst[2] = (uint8_t)(tmp >> 8); + lineDst[3] = (uint8_t)tmp; + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } + } else { + if (s1 > d1) { + shift1 = s1 - d1; + shift2 = 32 - shift1; + middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5); + for (yy = yd0; yy < yd1; yy++) { + sp = lineSrc; + dp = lineDst; + if (d1 != 0) { + tmp1 = (JBIG2_GETDWORD(sp) << shift1) | + (JBIG2_GETDWORD(sp + 4) >> shift2); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskL) | (tmp1 & maskL); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + for (xx = 0; xx < middleDwords; xx++) { + tmp1 = (JBIG2_GETDWORD(sp) << shift1) | + (JBIG2_GETDWORD(sp + 4) >> shift2); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = tmp1 | tmp2; + break; + case JBIG2_COMPOSE_AND: + tmp = tmp1 & tmp2; + break; + case JBIG2_COMPOSE_XOR: + tmp = tmp1 ^ tmp2; + break; + case JBIG2_COMPOSE_XNOR: + tmp = ~(tmp1 ^ tmp2); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = tmp1; + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + if (d2 != 0) { + tmp1 = + (JBIG2_GETDWORD(sp) << shift1) | + (((sp + 4) < lineSrc + lineLeft ? JBIG2_GETDWORD(sp + 4) : 0) >> + shift2); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskR) | (tmp1 & maskR); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + } + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } else if (s1 == d1) { + middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5); + for (yy = yd0; yy < yd1; yy++) { + sp = lineSrc; + dp = lineDst; + if (d1 != 0) { + tmp1 = JBIG2_GETDWORD(sp); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskL) | (tmp1 & maskL); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + for (xx = 0; xx < middleDwords; xx++) { + tmp1 = JBIG2_GETDWORD(sp); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = tmp1 | tmp2; + break; + case JBIG2_COMPOSE_AND: + tmp = tmp1 & tmp2; + break; + case JBIG2_COMPOSE_XOR: + tmp = tmp1 ^ tmp2; + break; + case JBIG2_COMPOSE_XNOR: + tmp = ~(tmp1 ^ tmp2); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = tmp1; + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + if (d2 != 0) { + tmp1 = JBIG2_GETDWORD(sp); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskR) | (tmp1 & maskR); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + } + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } else { + shift1 = d1 - s1; + shift2 = 32 - shift1; + middleDwords = (xd1 >> 5) - ((xd0 + 31) >> 5); + for (yy = yd0; yy < yd1; yy++) { + sp = lineSrc; + dp = lineDst; + if (d1 != 0) { + tmp1 = JBIG2_GETDWORD(sp) >> shift1; + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskL) | ((tmp1 | tmp2) & maskL); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskL) | ((tmp1 & tmp2) & maskL); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskL) | ((tmp1 ^ tmp2) & maskL); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskL) | ((~(tmp1 ^ tmp2)) & maskL); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskL) | (tmp1 & maskL); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + dp += 4; + } + for (xx = 0; xx < middleDwords; xx++) { + tmp1 = (JBIG2_GETDWORD(sp) << shift2) | + ((JBIG2_GETDWORD(sp + 4)) >> shift1); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = tmp1 | tmp2; + break; + case JBIG2_COMPOSE_AND: + tmp = tmp1 & tmp2; + break; + case JBIG2_COMPOSE_XOR: + tmp = tmp1 ^ tmp2; + break; + case JBIG2_COMPOSE_XNOR: + tmp = ~(tmp1 ^ tmp2); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = tmp1; + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + sp += 4; + dp += 4; + } + if (d2 != 0) { + tmp1 = + (JBIG2_GETDWORD(sp) << shift2) | + (((sp + 4) < lineSrc + lineLeft ? JBIG2_GETDWORD(sp + 4) : 0) >> + shift1); + tmp2 = JBIG2_GETDWORD(dp); + switch (op) { + case JBIG2_COMPOSE_OR: + tmp = (tmp2 & ~maskR) | ((tmp1 | tmp2) & maskR); + break; + case JBIG2_COMPOSE_AND: + tmp = (tmp2 & ~maskR) | ((tmp1 & tmp2) & maskR); + break; + case JBIG2_COMPOSE_XOR: + tmp = (tmp2 & ~maskR) | ((tmp1 ^ tmp2) & maskR); + break; + case JBIG2_COMPOSE_XNOR: + tmp = (tmp2 & ~maskR) | ((~(tmp1 ^ tmp2)) & maskR); + break; + case JBIG2_COMPOSE_REPLACE: + tmp = (tmp2 & ~maskR) | (tmp1 & maskR); + break; + } + dp[0] = (uint8_t)(tmp >> 24); + dp[1] = (uint8_t)(tmp >> 16); + dp[2] = (uint8_t)(tmp >> 8); + dp[3] = (uint8_t)tmp; + } + lineSrc += m_nStride; + lineDst += pDst->m_nStride; + } + } + } + return 1; +} diff --git a/core/fxcodec/jbig2/JBig2_Image.h b/core/fxcodec/jbig2/JBig2_Image.h new file mode 100644 index 0000000000..a18a95d596 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Image.h @@ -0,0 +1,85 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_IMAGE_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_IMAGE_H_ + +#include "core/fxcodec/jbig2/JBig2_Define.h" + +enum JBig2ComposeOp { + JBIG2_COMPOSE_OR = 0, + JBIG2_COMPOSE_AND = 1, + JBIG2_COMPOSE_XOR = 2, + JBIG2_COMPOSE_XNOR = 3, + JBIG2_COMPOSE_REPLACE = 4 +}; + +struct FX_RECT; +class CJBig2_Image { + public: + CJBig2_Image(int32_t w, int32_t h); + + CJBig2_Image(int32_t w, int32_t h, int32_t stride, uint8_t* pBuf); + + CJBig2_Image(const CJBig2_Image& im); + + ~CJBig2_Image(); + + FX_BOOL getPixel(int32_t x, int32_t y); + + int32_t setPixel(int32_t x, int32_t y, FX_BOOL v); + + void copyLine(int32_t hTo, int32_t hFrom); + + void fill(FX_BOOL v); + + FX_BOOL composeTo(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op); + FX_BOOL composeTo(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op, + const FX_RECT* pSrcRect); + + FX_BOOL composeTo_opt2(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op); + FX_BOOL composeTo_opt2(CJBig2_Image* pDst, + int32_t x, + int32_t y, + JBig2ComposeOp op, + const FX_RECT* pSrcRect); + + FX_BOOL composeFrom(int32_t x, + int32_t y, + CJBig2_Image* pSrc, + JBig2ComposeOp op); + FX_BOOL composeFrom(int32_t x, + int32_t y, + CJBig2_Image* pSrc, + JBig2ComposeOp op, + const FX_RECT* pSrcRect); + + CJBig2_Image* subImage(int32_t x, int32_t y, int32_t w, int32_t h); + + void expand(int32_t h, FX_BOOL v); + + public: + int32_t m_nWidth; + + int32_t m_nHeight; + + int32_t m_nStride; + + uint8_t* m_pData; + + FX_BOOL m_bNeedFree; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_IMAGE_H_ diff --git a/core/fxcodec/jbig2/JBig2_List.h b/core/fxcodec/jbig2/JBig2_List.h new file mode 100644 index 0000000000..b021ac3375 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_List.h @@ -0,0 +1,54 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_LIST_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_LIST_H_ + +#include <stdlib.h> + +#include <vector> + +// A poor man's ScopedVector for pointers of TYPE. +// Owns all the pointers contained within and deletes them on destruction. +template <class TYPE> +class CJBig2_List { + public: + CJBig2_List() {} + explicit CJBig2_List(size_t count) { resize(count); } + + ~CJBig2_List() { clear(); } + + TYPE* get(size_t index) const { return m_vector[index]; } + TYPE* back() const { return m_vector.back(); } + size_t size() const { return m_vector.size(); } + + // Deletes all the pointers contained within. + void clear() { + for (size_t i = 0; i < m_vector.size(); ++i) + delete m_vector[i]; + m_vector.clear(); + } + + // Takes ownership of |pItem|. + void push_back(TYPE* pItem) { m_vector.push_back(pItem); } + + // Takes ownership of |pItem|. + void set(size_t index, TYPE* pItem) { + delete m_vector[index]; + m_vector[index] = pItem; + } + + void resize(size_t count) { + for (size_t i = count; i < size(); ++i) + delete m_vector[i]; + m_vector.resize(count); + } + + private: + std::vector<TYPE*> m_vector; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_LIST_H_ diff --git a/core/fxcodec/jbig2/JBig2_Page.h b/core/fxcodec/jbig2/JBig2_Page.h new file mode 100644 index 0000000000..b3e09d8ae0 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Page.h @@ -0,0 +1,22 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_ + +#include "core/include/fxcrt/fx_system.h" + +struct JBig2PageInfo { + FX_DWORD m_dwWidth; + FX_DWORD m_dwHeight; + FX_DWORD m_dwResolutionX; + FX_DWORD m_dwResolutionY; + uint8_t m_cFlags; + FX_BOOL m_bIsStriped; + FX_WORD m_wMaxStripeSize; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_ diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.cpp b/core/fxcodec/jbig2/JBig2_PatternDict.cpp new file mode 100644 index 0000000000..58157ef13a --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_PatternDict.cpp @@ -0,0 +1,23 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_PatternDict.h" + +#include "core/include/fxcrt/fx_memory.h" + +CJBig2_PatternDict::CJBig2_PatternDict() { + NUMPATS = 0; + HDPATS = NULL; +} + +CJBig2_PatternDict::~CJBig2_PatternDict() { + if (HDPATS) { + for (FX_DWORD i = 0; i < NUMPATS; i++) { + delete HDPATS[i]; + } + FX_Free(HDPATS); + } +} diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.h b/core/fxcodec/jbig2/JBig2_PatternDict.h new file mode 100644 index 0000000000..3196fcaaf3 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_PatternDict.h @@ -0,0 +1,23 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_PATTERNDICT_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_PATTERNDICT_H_ + +#include "core/fxcodec/jbig2/JBig2_Define.h" +#include "core/fxcodec/jbig2/JBig2_Image.h" + +class CJBig2_PatternDict { + public: + CJBig2_PatternDict(); + + ~CJBig2_PatternDict(); + + FX_DWORD NUMPATS; + CJBig2_Image** HDPATS; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_PATTERNDICT_H_ diff --git a/core/fxcodec/jbig2/JBig2_PddProc.cpp b/core/fxcodec/jbig2/JBig2_PddProc.cpp new file mode 100644 index 0000000000..5b67aadb57 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_PddProc.cpp @@ -0,0 +1,87 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_PddProc.h" + +#include <memory> + +#include "core/fxcodec/jbig2/JBig2_GrdProc.h" +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/fxcodec/jbig2/JBig2_PatternDict.h" + +CJBig2_PatternDict* CJBig2_PDDProc::decode_Arith( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause) { + FX_DWORD GRAY; + CJBig2_Image* BHDC = nullptr; + std::unique_ptr<CJBig2_PatternDict> pDict(new CJBig2_PatternDict()); + pDict->NUMPATS = GRAYMAX + 1; + pDict->HDPATS = FX_Alloc(CJBig2_Image*, pDict->NUMPATS); + JBIG2_memset(pDict->HDPATS, 0, sizeof(CJBig2_Image*) * pDict->NUMPATS); + + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc()); + pGRD->MMR = HDMMR; + pGRD->GBW = (GRAYMAX + 1) * HDPW; + pGRD->GBH = HDPH; + pGRD->GBTEMPLATE = HDTEMPLATE; + pGRD->TPGDON = 0; + pGRD->USESKIP = 0; + pGRD->GBAT[0] = -(int32_t)HDPW; + pGRD->GBAT[1] = 0; + if (pGRD->GBTEMPLATE == 0) { + pGRD->GBAT[2] = -3; + pGRD->GBAT[3] = -1; + pGRD->GBAT[4] = 2; + pGRD->GBAT[5] = -2; + pGRD->GBAT[6] = -2; + pGRD->GBAT[7] = -2; + } + FXCODEC_STATUS status = + pGRD->Start_decode_Arith(&BHDC, pArithDecoder, gbContext, nullptr); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + pGRD->Continue_decode(pPause); + } + if (!BHDC) + return nullptr; + + GRAY = 0; + while (GRAY <= GRAYMAX) { + pDict->HDPATS[GRAY] = BHDC->subImage(HDPW * GRAY, 0, HDPW, HDPH); + GRAY = GRAY + 1; + } + delete BHDC; + return pDict.release(); +} + +CJBig2_PatternDict* CJBig2_PDDProc::decode_MMR(CJBig2_BitStream* pStream, + IFX_Pause* pPause) { + FX_DWORD GRAY; + CJBig2_Image* BHDC = nullptr; + std::unique_ptr<CJBig2_PatternDict> pDict(new CJBig2_PatternDict()); + pDict->NUMPATS = GRAYMAX + 1; + pDict->HDPATS = FX_Alloc(CJBig2_Image*, pDict->NUMPATS); + JBIG2_memset(pDict->HDPATS, 0, sizeof(CJBig2_Image*) * pDict->NUMPATS); + + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc()); + pGRD->MMR = HDMMR; + pGRD->GBW = (GRAYMAX + 1) * HDPW; + pGRD->GBH = HDPH; + FXCODEC_STATUS status = pGRD->Start_decode_MMR(&BHDC, pStream, nullptr); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + pGRD->Continue_decode(pPause); + } + if (!BHDC) + return nullptr; + + GRAY = 0; + while (GRAY <= GRAYMAX) { + pDict->HDPATS[GRAY] = BHDC->subImage(HDPW * GRAY, 0, HDPW, HDPH); + GRAY = GRAY + 1; + } + delete BHDC; + return pDict.release(); +} diff --git a/core/fxcodec/jbig2/JBig2_PddProc.h b/core/fxcodec/jbig2/JBig2_PddProc.h new file mode 100644 index 0000000000..25f941235f --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_PddProc.h @@ -0,0 +1,34 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_ + +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithDecoder; +class CJBig2_BitStream; +class CJBig2_PatternDict; +class IFX_Pause; +struct JBig2ArithCtx; + +class CJBig2_PDDProc { + public: + CJBig2_PatternDict* decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + IFX_Pause* pPause); + + CJBig2_PatternDict* decode_MMR(CJBig2_BitStream* pStream, IFX_Pause* pPause); + + public: + FX_BOOL HDMMR; + uint8_t HDPW; + uint8_t HDPH; + FX_DWORD GRAYMAX; + uint8_t HDTEMPLATE; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_ diff --git a/core/fxcodec/jbig2/JBig2_SddProc.cpp b/core/fxcodec/jbig2/JBig2_SddProc.cpp new file mode 100644 index 0000000000..80079b93a5 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_SddProc.cpp @@ -0,0 +1,624 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_SddProc.h" + +#include <memory> +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_ArithIntDecoder.h" +#include "core/fxcodec/jbig2/JBig2_GrdProc.h" +#include "core/fxcodec/jbig2/JBig2_GrrdProc.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanTable.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanTable_Standard.h" +#include "core/fxcodec/jbig2/JBig2_SymbolDict.h" +#include "core/fxcodec/jbig2/JBig2_TrdProc.h" +#include "core/include/fxcrt/fx_basic.h" + +CJBig2_SymbolDict* CJBig2_SDDProc::decode_Arith( + CJBig2_ArithDecoder* pArithDecoder, + std::vector<JBig2ArithCtx>* gbContext, + std::vector<JBig2ArithCtx>* grContext) { + CJBig2_Image** SDNEWSYMS; + FX_DWORD HCHEIGHT, NSYMSDECODED; + int32_t HCDH; + FX_DWORD SYMWIDTH, TOTWIDTH; + int32_t DW; + CJBig2_Image* BS; + FX_DWORD I, J, REFAGGNINST; + FX_BOOL* EXFLAGS; + FX_DWORD EXINDEX; + FX_BOOL CUREXFLAG; + FX_DWORD EXRUNLENGTH; + FX_DWORD nTmp; + FX_DWORD SBNUMSYMS; + uint8_t SBSYMCODELEN; + int32_t RDXI, RDYI; + CJBig2_Image** SBSYMS; + std::unique_ptr<CJBig2_ArithIaidDecoder> IAID; + std::unique_ptr<CJBig2_SymbolDict> pDict; + std::unique_ptr<CJBig2_ArithIntDecoder> IADH(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IADW(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IAAI(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IARDX(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IARDY(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IAEX(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IADT(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IAFS(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IADS(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IAIT(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IARI(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IARDW(new CJBig2_ArithIntDecoder); + std::unique_ptr<CJBig2_ArithIntDecoder> IARDH(new CJBig2_ArithIntDecoder); + nTmp = 0; + while ((FX_DWORD)(1 << nTmp) < (SDNUMINSYMS + SDNUMNEWSYMS)) { + nTmp++; + } + IAID.reset(new CJBig2_ArithIaidDecoder((uint8_t)nTmp)); + SDNEWSYMS = FX_Alloc(CJBig2_Image*, SDNUMNEWSYMS); + FXSYS_memset(SDNEWSYMS, 0, SDNUMNEWSYMS * sizeof(CJBig2_Image*)); + + HCHEIGHT = 0; + NSYMSDECODED = 0; + while (NSYMSDECODED < SDNUMNEWSYMS) { + BS = nullptr; + IADH->decode(pArithDecoder, &HCDH); + HCHEIGHT = HCHEIGHT + HCDH; + if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE) { + goto failed; + } + SYMWIDTH = 0; + TOTWIDTH = 0; + for (;;) { + if (!IADW->decode(pArithDecoder, &DW)) + break; + + if (NSYMSDECODED >= SDNUMNEWSYMS) + goto failed; + + SYMWIDTH = SYMWIDTH + DW; + if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > JBIG2_MAX_IMAGE_SIZE) + goto failed; + + if (HCHEIGHT == 0 || SYMWIDTH == 0) { + TOTWIDTH = TOTWIDTH + SYMWIDTH; + SDNEWSYMS[NSYMSDECODED] = nullptr; + NSYMSDECODED = NSYMSDECODED + 1; + continue; + } + TOTWIDTH = TOTWIDTH + SYMWIDTH; + if (SDREFAGG == 0) { + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc()); + pGRD->MMR = 0; + pGRD->GBW = SYMWIDTH; + pGRD->GBH = HCHEIGHT; + pGRD->GBTEMPLATE = SDTEMPLATE; + pGRD->TPGDON = 0; + pGRD->USESKIP = 0; + pGRD->GBAT[0] = SDAT[0]; + pGRD->GBAT[1] = SDAT[1]; + pGRD->GBAT[2] = SDAT[2]; + pGRD->GBAT[3] = SDAT[3]; + pGRD->GBAT[4] = SDAT[4]; + pGRD->GBAT[5] = SDAT[5]; + pGRD->GBAT[6] = SDAT[6]; + pGRD->GBAT[7] = SDAT[7]; + BS = pGRD->decode_Arith(pArithDecoder, gbContext->data()); + if (!BS) { + goto failed; + } + } else { + IAAI->decode(pArithDecoder, (int*)&REFAGGNINST); + if (REFAGGNINST > 1) { + std::unique_ptr<CJBig2_TRDProc> pDecoder(new CJBig2_TRDProc()); + pDecoder->SBHUFF = SDHUFF; + pDecoder->SBREFINE = 1; + pDecoder->SBW = SYMWIDTH; + pDecoder->SBH = HCHEIGHT; + pDecoder->SBNUMINSTANCES = REFAGGNINST; + pDecoder->SBSTRIPS = 1; + pDecoder->SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; + SBNUMSYMS = pDecoder->SBNUMSYMS; + nTmp = 0; + while ((FX_DWORD)(1 << nTmp) < SBNUMSYMS) { + nTmp++; + } + SBSYMCODELEN = (uint8_t)nTmp; + pDecoder->SBSYMCODELEN = SBSYMCODELEN; + SBSYMS = FX_Alloc(CJBig2_Image*, SBNUMSYMS); + JBIG2_memcpy(SBSYMS, SDINSYMS, SDNUMINSYMS * sizeof(CJBig2_Image*)); + JBIG2_memcpy(SBSYMS + SDNUMINSYMS, SDNEWSYMS, + NSYMSDECODED * sizeof(CJBig2_Image*)); + pDecoder->SBSYMS = SBSYMS; + pDecoder->SBDEFPIXEL = 0; + pDecoder->SBCOMBOP = JBIG2_COMPOSE_OR; + pDecoder->TRANSPOSED = 0; + pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT; + pDecoder->SBDSOFFSET = 0; + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFFS(new CJBig2_HuffmanTable( + HuffmanTable_B6, FX_ArraySize(HuffmanTable_B6), + HuffmanTable_HTOOB_B6)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFDS(new CJBig2_HuffmanTable( + HuffmanTable_B8, FX_ArraySize(HuffmanTable_B8), + HuffmanTable_HTOOB_B8)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFDT(new CJBig2_HuffmanTable( + HuffmanTable_B11, FX_ArraySize(HuffmanTable_B11), + HuffmanTable_HTOOB_B11)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDW( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDH( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDX( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDY( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRSIZE( + new CJBig2_HuffmanTable(HuffmanTable_B1, + FX_ArraySize(HuffmanTable_B1), + HuffmanTable_HTOOB_B1)); + pDecoder->SBHUFFFS = SBHUFFFS.get(); + pDecoder->SBHUFFDS = SBHUFFDS.get(); + pDecoder->SBHUFFDT = SBHUFFDT.get(); + pDecoder->SBHUFFRDW = SBHUFFRDW.get(); + pDecoder->SBHUFFRDH = SBHUFFRDH.get(); + pDecoder->SBHUFFRDX = SBHUFFRDX.get(); + pDecoder->SBHUFFRDY = SBHUFFRDY.get(); + pDecoder->SBHUFFRSIZE = SBHUFFRSIZE.get(); + pDecoder->SBRTEMPLATE = SDRTEMPLATE; + pDecoder->SBRAT[0] = SDRAT[0]; + pDecoder->SBRAT[1] = SDRAT[1]; + pDecoder->SBRAT[2] = SDRAT[2]; + pDecoder->SBRAT[3] = SDRAT[3]; + JBig2IntDecoderState ids; + ids.IADT = IADT.get(); + ids.IAFS = IAFS.get(); + ids.IADS = IADS.get(); + ids.IAIT = IAIT.get(); + ids.IARI = IARI.get(); + ids.IARDW = IARDW.get(); + ids.IARDH = IARDH.get(); + ids.IARDX = IARDX.get(); + ids.IARDY = IARDY.get(); + ids.IAID = IAID.get(); + BS = pDecoder->decode_Arith(pArithDecoder, grContext->data(), &ids); + if (!BS) { + FX_Free(SBSYMS); + goto failed; + } + FX_Free(SBSYMS); + } else if (REFAGGNINST == 1) { + SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; + FX_DWORD IDI; + IAID->decode(pArithDecoder, &IDI); + IARDX->decode(pArithDecoder, &RDXI); + IARDY->decode(pArithDecoder, &RDYI); + if (IDI >= SBNUMSYMS) { + goto failed; + } + SBSYMS = FX_Alloc(CJBig2_Image*, SBNUMSYMS); + JBIG2_memcpy(SBSYMS, SDINSYMS, SDNUMINSYMS * sizeof(CJBig2_Image*)); + JBIG2_memcpy(SBSYMS + SDNUMINSYMS, SDNEWSYMS, + NSYMSDECODED * sizeof(CJBig2_Image*)); + if (!SBSYMS[IDI]) { + FX_Free(SBSYMS); + goto failed; + } + std::unique_ptr<CJBig2_GRRDProc> pGRRD(new CJBig2_GRRDProc()); + pGRRD->GRW = SYMWIDTH; + pGRRD->GRH = HCHEIGHT; + pGRRD->GRTEMPLATE = SDRTEMPLATE; + pGRRD->GRREFERENCE = SBSYMS[IDI]; + pGRRD->GRREFERENCEDX = RDXI; + pGRRD->GRREFERENCEDY = RDYI; + pGRRD->TPGRON = 0; + pGRRD->GRAT[0] = SDRAT[0]; + pGRRD->GRAT[1] = SDRAT[1]; + pGRRD->GRAT[2] = SDRAT[2]; + pGRRD->GRAT[3] = SDRAT[3]; + BS = pGRRD->decode(pArithDecoder, grContext->data()); + if (!BS) { + FX_Free(SBSYMS); + goto failed; + } + FX_Free(SBSYMS); + } + } + SDNEWSYMS[NSYMSDECODED] = BS; + BS = nullptr; + NSYMSDECODED = NSYMSDECODED + 1; + } + } + EXINDEX = 0; + CUREXFLAG = 0; + EXFLAGS = FX_Alloc(FX_BOOL, SDNUMINSYMS + SDNUMNEWSYMS); + while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) { + IAEX->decode(pArithDecoder, (int*)&EXRUNLENGTH); + if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS) { + FX_Free(EXFLAGS); + goto failed; + } + if (EXRUNLENGTH != 0) { + for (I = EXINDEX; I < EXINDEX + EXRUNLENGTH; I++) { + EXFLAGS[I] = CUREXFLAG; + } + } + EXINDEX = EXINDEX + EXRUNLENGTH; + CUREXFLAG = !CUREXFLAG; + } + pDict.reset(new CJBig2_SymbolDict); + I = J = 0; + for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; I++) { + if (EXFLAGS[I] && J < SDNUMEXSYMS) { + if (I < SDNUMINSYMS) { + pDict->AddImage(SDINSYMS[I] ? new CJBig2_Image(*SDINSYMS[I]) : nullptr); + } else { + pDict->AddImage(SDNEWSYMS[I - SDNUMINSYMS]); + } + ++J; + } else if (!EXFLAGS[I] && I >= SDNUMINSYMS) { + delete SDNEWSYMS[I - SDNUMINSYMS]; + } + } + FX_Free(EXFLAGS); + FX_Free(SDNEWSYMS); + return pDict.release(); +failed: + for (I = 0; I < NSYMSDECODED; I++) { + if (SDNEWSYMS[I]) { + delete SDNEWSYMS[I]; + SDNEWSYMS[I] = nullptr; + } + } + FX_Free(SDNEWSYMS); + return nullptr; +} + +CJBig2_SymbolDict* CJBig2_SDDProc::decode_Huffman( + CJBig2_BitStream* pStream, + std::vector<JBig2ArithCtx>* gbContext, + std::vector<JBig2ArithCtx>* grContext, + IFX_Pause* pPause) { + CJBig2_Image** SDNEWSYMS; + FX_DWORD* SDNEWSYMWIDTHS; + FX_DWORD HCHEIGHT, NSYMSDECODED; + int32_t HCDH; + FX_DWORD SYMWIDTH, TOTWIDTH, HCFIRSTSYM; + int32_t DW; + CJBig2_Image *BS, *BHC; + FX_DWORD I, J, REFAGGNINST; + FX_BOOL* EXFLAGS; + FX_DWORD EXINDEX; + FX_BOOL CUREXFLAG; + FX_DWORD EXRUNLENGTH; + int32_t nVal, nBits; + FX_DWORD nTmp; + FX_DWORD SBNUMSYMS; + uint8_t SBSYMCODELEN; + JBig2HuffmanCode* SBSYMCODES; + FX_DWORD IDI; + int32_t RDXI, RDYI; + FX_DWORD BMSIZE; + FX_DWORD stride; + CJBig2_Image** SBSYMS; + std::unique_ptr<CJBig2_HuffmanDecoder> pHuffmanDecoder( + new CJBig2_HuffmanDecoder(pStream)); + SDNEWSYMS = FX_Alloc(CJBig2_Image*, SDNUMNEWSYMS); + FXSYS_memset(SDNEWSYMS, 0, SDNUMNEWSYMS * sizeof(CJBig2_Image*)); + SDNEWSYMWIDTHS = nullptr; + BHC = nullptr; + if (SDREFAGG == 0) { + SDNEWSYMWIDTHS = FX_Alloc(FX_DWORD, SDNUMNEWSYMS); + FXSYS_memset(SDNEWSYMWIDTHS, 0, SDNUMNEWSYMS * sizeof(FX_DWORD)); + } + std::unique_ptr<CJBig2_SymbolDict> pDict(new CJBig2_SymbolDict()); + std::unique_ptr<CJBig2_HuffmanTable> pTable; + + HCHEIGHT = 0; + NSYMSDECODED = 0; + BS = nullptr; + while (NSYMSDECODED < SDNUMNEWSYMS) { + if (pHuffmanDecoder->decodeAValue(SDHUFFDH, &HCDH) != 0) { + goto failed; + } + HCHEIGHT = HCHEIGHT + HCDH; + if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE) { + goto failed; + } + SYMWIDTH = 0; + TOTWIDTH = 0; + HCFIRSTSYM = NSYMSDECODED; + for (;;) { + nVal = pHuffmanDecoder->decodeAValue(SDHUFFDW, &DW); + if (nVal == JBIG2_OOB) { + break; + } else if (nVal != 0) { + goto failed; + } else { + if (NSYMSDECODED >= SDNUMNEWSYMS) { + goto failed; + } + SYMWIDTH = SYMWIDTH + DW; + if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > JBIG2_MAX_IMAGE_SIZE) { + goto failed; + } else if (HCHEIGHT == 0 || SYMWIDTH == 0) { + TOTWIDTH = TOTWIDTH + SYMWIDTH; + SDNEWSYMS[NSYMSDECODED] = nullptr; + NSYMSDECODED = NSYMSDECODED + 1; + continue; + } + TOTWIDTH = TOTWIDTH + SYMWIDTH; + } + if (SDREFAGG == 1) { + if (pHuffmanDecoder->decodeAValue(SDHUFFAGGINST, (int*)&REFAGGNINST) != + 0) { + goto failed; + } + BS = nullptr; + if (REFAGGNINST > 1) { + std::unique_ptr<CJBig2_TRDProc> pDecoder(new CJBig2_TRDProc()); + pDecoder->SBHUFF = SDHUFF; + pDecoder->SBREFINE = 1; + pDecoder->SBW = SYMWIDTH; + pDecoder->SBH = HCHEIGHT; + pDecoder->SBNUMINSTANCES = REFAGGNINST; + pDecoder->SBSTRIPS = 1; + pDecoder->SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; + SBNUMSYMS = pDecoder->SBNUMSYMS; + SBSYMCODES = FX_Alloc(JBig2HuffmanCode, SBNUMSYMS); + nTmp = 1; + while ((FX_DWORD)(1 << nTmp) < SBNUMSYMS) { + nTmp++; + } + for (I = 0; I < SBNUMSYMS; I++) { + SBSYMCODES[I].codelen = nTmp; + SBSYMCODES[I].code = I; + } + pDecoder->SBSYMCODES = SBSYMCODES; + SBSYMS = FX_Alloc(CJBig2_Image*, SBNUMSYMS); + JBIG2_memcpy(SBSYMS, SDINSYMS, SDNUMINSYMS * sizeof(CJBig2_Image*)); + JBIG2_memcpy(SBSYMS + SDNUMINSYMS, SDNEWSYMS, + NSYMSDECODED * sizeof(CJBig2_Image*)); + pDecoder->SBSYMS = SBSYMS; + pDecoder->SBDEFPIXEL = 0; + pDecoder->SBCOMBOP = JBIG2_COMPOSE_OR; + pDecoder->TRANSPOSED = 0; + pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT; + pDecoder->SBDSOFFSET = 0; + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFFS(new CJBig2_HuffmanTable( + HuffmanTable_B6, FX_ArraySize(HuffmanTable_B6), + HuffmanTable_HTOOB_B6)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFDS(new CJBig2_HuffmanTable( + HuffmanTable_B8, FX_ArraySize(HuffmanTable_B8), + HuffmanTable_HTOOB_B8)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFDT(new CJBig2_HuffmanTable( + HuffmanTable_B11, FX_ArraySize(HuffmanTable_B11), + HuffmanTable_HTOOB_B11)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDW( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDH( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDX( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDY( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRSIZE( + new CJBig2_HuffmanTable(HuffmanTable_B1, + FX_ArraySize(HuffmanTable_B1), + HuffmanTable_HTOOB_B1)); + pDecoder->SBHUFFFS = SBHUFFFS.get(); + pDecoder->SBHUFFDS = SBHUFFDS.get(); + pDecoder->SBHUFFDT = SBHUFFDT.get(); + pDecoder->SBHUFFRDW = SBHUFFRDW.get(); + pDecoder->SBHUFFRDH = SBHUFFRDH.get(); + pDecoder->SBHUFFRDX = SBHUFFRDX.get(); + pDecoder->SBHUFFRDY = SBHUFFRDY.get(); + pDecoder->SBHUFFRSIZE = SBHUFFRSIZE.get(); + pDecoder->SBRTEMPLATE = SDRTEMPLATE; + pDecoder->SBRAT[0] = SDRAT[0]; + pDecoder->SBRAT[1] = SDRAT[1]; + pDecoder->SBRAT[2] = SDRAT[2]; + pDecoder->SBRAT[3] = SDRAT[3]; + BS = pDecoder->decode_Huffman(pStream, grContext->data()); + if (!BS) { + FX_Free(SBSYMCODES); + FX_Free(SBSYMS); + goto failed; + } + FX_Free(SBSYMCODES); + FX_Free(SBSYMS); + } else if (REFAGGNINST == 1) { + SBNUMSYMS = SDNUMINSYMS + SDNUMNEWSYMS; + nTmp = 1; + while ((FX_DWORD)(1 << nTmp) < SBNUMSYMS) { + nTmp++; + } + SBSYMCODELEN = (uint8_t)nTmp; + SBSYMCODES = FX_Alloc(JBig2HuffmanCode, SBNUMSYMS); + for (I = 0; I < SBNUMSYMS; I++) { + SBSYMCODES[I].codelen = SBSYMCODELEN; + SBSYMCODES[I].code = I; + } + nVal = 0; + nBits = 0; + for (;;) { + if (pStream->read1Bit(&nTmp) != 0) { + FX_Free(SBSYMCODES); + goto failed; + } + nVal = (nVal << 1) | nTmp; + for (IDI = 0; IDI < SBNUMSYMS; IDI++) { + if ((nVal == SBSYMCODES[IDI].code) && + (nBits == SBSYMCODES[IDI].codelen)) { + break; + } + } + if (IDI < SBNUMSYMS) { + break; + } + } + FX_Free(SBSYMCODES); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRDX( + new CJBig2_HuffmanTable(HuffmanTable_B15, + FX_ArraySize(HuffmanTable_B15), + HuffmanTable_HTOOB_B15)); + std::unique_ptr<CJBig2_HuffmanTable> SBHUFFRSIZE( + new CJBig2_HuffmanTable(HuffmanTable_B1, + FX_ArraySize(HuffmanTable_B1), + HuffmanTable_HTOOB_B1)); + if ((pHuffmanDecoder->decodeAValue(SBHUFFRDX.get(), &RDXI) != 0) || + (pHuffmanDecoder->decodeAValue(SBHUFFRDX.get(), &RDYI) != 0) || + (pHuffmanDecoder->decodeAValue(SBHUFFRSIZE.get(), &nVal) != 0)) { + goto failed; + } + pStream->alignByte(); + nTmp = pStream->getOffset(); + SBSYMS = FX_Alloc(CJBig2_Image*, SBNUMSYMS); + JBIG2_memcpy(SBSYMS, SDINSYMS, SDNUMINSYMS * sizeof(CJBig2_Image*)); + JBIG2_memcpy(SBSYMS + SDNUMINSYMS, SDNEWSYMS, + NSYMSDECODED * sizeof(CJBig2_Image*)); + std::unique_ptr<CJBig2_GRRDProc> pGRRD(new CJBig2_GRRDProc()); + pGRRD->GRW = SYMWIDTH; + pGRRD->GRH = HCHEIGHT; + pGRRD->GRTEMPLATE = SDRTEMPLATE; + pGRRD->GRREFERENCE = SBSYMS[IDI]; + pGRRD->GRREFERENCEDX = RDXI; + pGRRD->GRREFERENCEDY = RDYI; + pGRRD->TPGRON = 0; + pGRRD->GRAT[0] = SDRAT[0]; + pGRRD->GRAT[1] = SDRAT[1]; + pGRRD->GRAT[2] = SDRAT[2]; + pGRRD->GRAT[3] = SDRAT[3]; + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(pStream)); + BS = pGRRD->decode(pArithDecoder.get(), grContext->data()); + if (!BS) { + FX_Free(SBSYMS); + goto failed; + } + pStream->alignByte(); + pStream->offset(2); + if ((FX_DWORD)nVal != (pStream->getOffset() - nTmp)) { + delete BS; + FX_Free(SBSYMS); + goto failed; + } + FX_Free(SBSYMS); + } + SDNEWSYMS[NSYMSDECODED] = BS; + } + if (SDREFAGG == 0) { + SDNEWSYMWIDTHS[NSYMSDECODED] = SYMWIDTH; + } + NSYMSDECODED = NSYMSDECODED + 1; + } + if (SDREFAGG == 0) { + if (pHuffmanDecoder->decodeAValue(SDHUFFBMSIZE, (int32_t*)&BMSIZE) != 0) { + goto failed; + } + pStream->alignByte(); + if (BMSIZE == 0) { + stride = (TOTWIDTH + 7) >> 3; + if (pStream->getByteLeft() >= stride * HCHEIGHT) { + BHC = new CJBig2_Image(TOTWIDTH, HCHEIGHT); + for (I = 0; I < HCHEIGHT; I++) { + JBIG2_memcpy(BHC->m_pData + I * BHC->m_nStride, + pStream->getPointer(), stride); + pStream->offset(stride); + } + } else { + goto failed; + } + } else { + std::unique_ptr<CJBig2_GRDProc> pGRD(new CJBig2_GRDProc()); + pGRD->MMR = 1; + pGRD->GBW = TOTWIDTH; + pGRD->GBH = HCHEIGHT; + FXCODEC_STATUS status = pGRD->Start_decode_MMR(&BHC, pStream, nullptr); + while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) { + pGRD->Continue_decode(pPause); + } + pStream->alignByte(); + } + nTmp = 0; + if (!BHC) { + continue; + } + for (I = HCFIRSTSYM; I < NSYMSDECODED; I++) { + SDNEWSYMS[I] = BHC->subImage(nTmp, 0, SDNEWSYMWIDTHS[I], HCHEIGHT); + nTmp += SDNEWSYMWIDTHS[I]; + } + delete BHC; + BHC = nullptr; + } + } + EXINDEX = 0; + CUREXFLAG = 0; + pTable.reset(new CJBig2_HuffmanTable( + HuffmanTable_B1, FX_ArraySize(HuffmanTable_B1), HuffmanTable_HTOOB_B1)); + EXFLAGS = FX_Alloc(FX_BOOL, SDNUMINSYMS + SDNUMNEWSYMS); + while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) { + if (pHuffmanDecoder->decodeAValue(pTable.get(), (int*)&EXRUNLENGTH) != 0) { + FX_Free(EXFLAGS); + goto failed; + } + if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS) { + FX_Free(EXFLAGS); + goto failed; + } + if (EXRUNLENGTH != 0) { + for (I = EXINDEX; I < EXINDEX + EXRUNLENGTH; I++) { + EXFLAGS[I] = CUREXFLAG; + } + } + EXINDEX = EXINDEX + EXRUNLENGTH; + CUREXFLAG = !CUREXFLAG; + } + I = J = 0; + for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; I++) { + if (EXFLAGS[I] && J < SDNUMEXSYMS) { + if (I < SDNUMINSYMS) { + pDict->AddImage(SDINSYMS[I] ? new CJBig2_Image(*SDINSYMS[I]) : nullptr); + } else { + pDict->AddImage(SDNEWSYMS[I - SDNUMINSYMS]); + } + ++J; + } else if (!EXFLAGS[I] && I >= SDNUMINSYMS) { + delete SDNEWSYMS[I - SDNUMINSYMS]; + } + } + FX_Free(EXFLAGS); + FX_Free(SDNEWSYMS); + if (SDREFAGG == 0) { + FX_Free(SDNEWSYMWIDTHS); + } + return pDict.release(); +failed: + for (I = 0; I < NSYMSDECODED; I++) { + delete SDNEWSYMS[I]; + } + FX_Free(SDNEWSYMS); + if (SDREFAGG == 0) { + FX_Free(SDNEWSYMWIDTHS); + } + return nullptr; +} diff --git a/core/fxcodec/jbig2/JBig2_SddProc.h b/core/fxcodec/jbig2/JBig2_SddProc.h new file mode 100644 index 0000000000..fa1211b4b4 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_SddProc.h @@ -0,0 +1,49 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_ + +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_BitStream; +class CJBig2_HuffmanTable; +class CJBig2_Image; +class CJBig2_SymbolDict; +class IFX_Pause; + +class CJBig2_SDDProc { + public: + CJBig2_SymbolDict* decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + std::vector<JBig2ArithCtx>* gbContext, + std::vector<JBig2ArithCtx>* grContext); + + CJBig2_SymbolDict* decode_Huffman(CJBig2_BitStream* pStream, + std::vector<JBig2ArithCtx>* gbContext, + std::vector<JBig2ArithCtx>* grContext, + IFX_Pause* pPause); + + public: + FX_BOOL SDHUFF; + FX_BOOL SDREFAGG; + FX_DWORD SDNUMINSYMS; + CJBig2_Image** SDINSYMS; + FX_DWORD SDNUMNEWSYMS; + FX_DWORD SDNUMEXSYMS; + CJBig2_HuffmanTable* SDHUFFDH; + CJBig2_HuffmanTable* SDHUFFDW; + CJBig2_HuffmanTable* SDHUFFBMSIZE; + CJBig2_HuffmanTable* SDHUFFAGGINST; + uint8_t SDTEMPLATE; + int8_t SDAT[8]; + FX_BOOL SDRTEMPLATE; + int8_t SDRAT[4]; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_ diff --git a/core/fxcodec/jbig2/JBig2_Segment.cpp b/core/fxcodec/jbig2/JBig2_Segment.cpp new file mode 100644 index 0000000000..a6bac36141 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Segment.cpp @@ -0,0 +1,44 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_Segment.h" + +#include "core/include/fxcrt/fx_memory.h" + +CJBig2_Segment::CJBig2_Segment() { + m_dwNumber = 0; + m_cFlags.c = 0; + m_nReferred_to_segment_count = 0; + m_pReferred_to_segment_numbers = NULL; + m_dwPage_association = 0; + m_dwData_length = 0; + m_dwHeader_Length = 0; + m_dwObjNum = 0; + m_dwDataOffset = 0; + m_State = JBIG2_SEGMENT_HEADER_UNPARSED; + m_nResultType = JBIG2_VOID_POINTER; + m_Result.vd = NULL; +} +CJBig2_Segment::~CJBig2_Segment() { + FX_Free(m_pReferred_to_segment_numbers); + + switch (m_nResultType) { + case JBIG2_IMAGE_POINTER: + delete m_Result.im; + break; + case JBIG2_SYMBOL_DICT_POINTER: + delete m_Result.sd; + break; + case JBIG2_PATTERN_DICT_POINTER: + delete m_Result.pd; + break; + case JBIG2_HUFFMAN_TABLE_POINTER: + delete m_Result.ht; + break; + default: + FX_Free(m_Result.vd); + } +} diff --git a/core/fxcodec/jbig2/JBig2_Segment.h b/core/fxcodec/jbig2/JBig2_Segment.h new file mode 100644 index 0000000000..61550ada09 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_Segment.h @@ -0,0 +1,66 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_SEGMENT_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_SEGMENT_H_ + +#include "core/fxcodec/jbig2/JBig2_Define.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanTable.h" +#include "core/fxcodec/jbig2/JBig2_PatternDict.h" +#include "core/fxcodec/jbig2/JBig2_SymbolDict.h" + +#define JBIG2_GET_INT32(buf) \ + (((buf)[0] << 24) | ((buf)[1] << 16) | ((buf)[2] << 8) | (buf)[3]) +#define JBIG2_GET_INT16(buf) (((buf)[0] << 8) | (buf)[1]) +typedef enum { + JBIG2_SEGMENT_HEADER_UNPARSED, + JBIG2_SEGMENT_DATA_UNPARSED, + JBIG2_SEGMENT_PARSE_COMPLETE, + JBIG2_SEGMENT_PAUSED, + JBIG2_SEGMENT_ERROR +} JBig2_SegmentState; +typedef enum { + JBIG2_VOID_POINTER = 0, + JBIG2_IMAGE_POINTER, + JBIG2_SYMBOL_DICT_POINTER, + JBIG2_PATTERN_DICT_POINTER, + JBIG2_HUFFMAN_TABLE_POINTER +} JBig2_ResultType; +class CJBig2_Segment { + public: + CJBig2_Segment(); + + ~CJBig2_Segment(); + + FX_DWORD m_dwNumber; + union { + struct { + uint8_t type : 6; + uint8_t page_association_size : 1; + uint8_t deferred_non_retain : 1; + } s; + uint8_t c; + } m_cFlags; + int32_t m_nReferred_to_segment_count; + FX_DWORD* m_pReferred_to_segment_numbers; + FX_DWORD m_dwPage_association; + FX_DWORD m_dwData_length; + + FX_DWORD m_dwHeader_Length; + FX_DWORD m_dwObjNum; + FX_DWORD m_dwDataOffset; + JBig2_SegmentState m_State; + JBig2_ResultType m_nResultType; + union { + CJBig2_SymbolDict* sd; + CJBig2_PatternDict* pd; + CJBig2_Image* im; + CJBig2_HuffmanTable* ht; + void* vd; + } m_Result; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_SEGMENT_H_ diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp new file mode 100644 index 0000000000..9fe3d54a0c --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp @@ -0,0 +1,26 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_SymbolDict.h" + +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/include/fxcrt/fx_memory.h" + +CJBig2_SymbolDict::CJBig2_SymbolDict() {} + +CJBig2_SymbolDict::~CJBig2_SymbolDict() {} + +std::unique_ptr<CJBig2_SymbolDict> CJBig2_SymbolDict::DeepCopy() const { + const CJBig2_SymbolDict* src = this; + std::unique_ptr<CJBig2_SymbolDict> dst(new CJBig2_SymbolDict); + for (size_t i = 0; i < src->m_SDEXSYMS.size(); ++i) { + CJBig2_Image* image = src->m_SDEXSYMS.get(i); + dst->m_SDEXSYMS.push_back(image ? new CJBig2_Image(*image) : nullptr); + } + dst->m_gbContext = src->m_gbContext; + dst->m_grContext = src->m_grContext; + return dst; +} diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.h b/core/fxcodec/jbig2/JBig2_SymbolDict.h new file mode 100644 index 0000000000..64ab881596 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_SymbolDict.h @@ -0,0 +1,48 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_SYMBOLDICT_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_SYMBOLDICT_H_ + +#include <memory> +#include <vector> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/fxcodec/jbig2/JBig2_List.h" +#include "core/include/fxcrt/fx_basic.h" + +class CJBig2_Image; + +class CJBig2_SymbolDict { + public: + CJBig2_SymbolDict(); + ~CJBig2_SymbolDict(); + + std::unique_ptr<CJBig2_SymbolDict> DeepCopy() const; + + // Takes ownership of |image|. + void AddImage(CJBig2_Image* image) { m_SDEXSYMS.push_back(image); } + + size_t NumImages() const { return m_SDEXSYMS.size(); } + CJBig2_Image* GetImage(size_t index) const { return m_SDEXSYMS.get(index); } + + const std::vector<JBig2ArithCtx>& GbContext() const { return m_gbContext; } + const std::vector<JBig2ArithCtx>& GrContext() const { return m_grContext; } + + void SetGbContext(const std::vector<JBig2ArithCtx>& gbContext) { + m_gbContext = gbContext; + } + void SetGrContext(const std::vector<JBig2ArithCtx>& grContext) { + m_grContext = grContext; + } + + private: + std::vector<JBig2ArithCtx> m_gbContext; + std::vector<JBig2ArithCtx> m_grContext; + CJBig2_List<CJBig2_Image> m_SDEXSYMS; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_SYMBOLDICT_H_ diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.cpp b/core/fxcodec/jbig2/JBig2_TrdProc.cpp new file mode 100644 index 0000000000..401249e3f2 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_TrdProc.cpp @@ -0,0 +1,407 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/jbig2/JBig2_TrdProc.h" + +#include <memory> + +#include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +#include "core/fxcodec/jbig2/JBig2_ArithIntDecoder.h" +#include "core/fxcodec/jbig2/JBig2_GrrdProc.h" +#include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h" + +CJBig2_Image* CJBig2_TRDProc::decode_Huffman(CJBig2_BitStream* pStream, + JBig2ArithCtx* grContext) { + int32_t STRIPT, FIRSTS; + FX_DWORD NINSTANCES; + int32_t DT, DFS, CURS; + int32_t SI, TI; + CJBig2_Image* IBI; + FX_DWORD WI, HI; + int32_t IDS; + FX_BOOL RI; + int32_t RDWI, RDHI, RDXI, RDYI; + CJBig2_Image* IBOI; + FX_DWORD WOI, HOI; + FX_BOOL bFirst; + FX_DWORD nTmp; + int32_t nVal, nBits; + std::unique_ptr<CJBig2_HuffmanDecoder> pHuffmanDecoder( + new CJBig2_HuffmanDecoder(pStream)); + std::unique_ptr<CJBig2_Image> SBREG(new CJBig2_Image(SBW, SBH)); + SBREG->fill(SBDEFPIXEL); + if (pHuffmanDecoder->decodeAValue(SBHUFFDT, &STRIPT) != 0) + return nullptr; + + STRIPT *= SBSTRIPS; + STRIPT = -STRIPT; + FIRSTS = 0; + NINSTANCES = 0; + while (NINSTANCES < SBNUMINSTANCES) { + if (pHuffmanDecoder->decodeAValue(SBHUFFDT, &DT) != 0) + return nullptr; + + DT *= SBSTRIPS; + STRIPT = STRIPT + DT; + bFirst = TRUE; + for (;;) { + if (bFirst) { + if (pHuffmanDecoder->decodeAValue(SBHUFFFS, &DFS) != 0) + return nullptr; + + FIRSTS = FIRSTS + DFS; + CURS = FIRSTS; + bFirst = FALSE; + } else { + nVal = pHuffmanDecoder->decodeAValue(SBHUFFDS, &IDS); + if (nVal == JBIG2_OOB) { + break; + } else if (nVal != 0) { + return nullptr; + } else { + CURS = CURS + IDS + SBDSOFFSET; + } + } + uint8_t CURT = 0; + if (SBSTRIPS != 1) { + nTmp = 1; + while ((FX_DWORD)(1 << nTmp) < SBSTRIPS) { + nTmp++; + } + if (pStream->readNBits(nTmp, &nVal) != 0) + return nullptr; + + CURT = nVal; + } + TI = STRIPT + CURT; + nVal = 0; + nBits = 0; + FX_DWORD IDI; + for (;;) { + if (pStream->read1Bit(&nTmp) != 0) + return nullptr; + + nVal = (nVal << 1) | nTmp; + nBits++; + for (IDI = 0; IDI < SBNUMSYMS; IDI++) { + if ((nBits == SBSYMCODES[IDI].codelen) && + (nVal == SBSYMCODES[IDI].code)) { + break; + } + } + if (IDI < SBNUMSYMS) { + break; + } + } + if (SBREFINE == 0) { + RI = 0; + } else { + if (pStream->read1Bit(&RI) != 0) { + return nullptr; + } + } + if (RI == 0) { + IBI = SBSYMS[IDI]; + } else { + if ((pHuffmanDecoder->decodeAValue(SBHUFFRDW, &RDWI) != 0) || + (pHuffmanDecoder->decodeAValue(SBHUFFRDH, &RDHI) != 0) || + (pHuffmanDecoder->decodeAValue(SBHUFFRDX, &RDXI) != 0) || + (pHuffmanDecoder->decodeAValue(SBHUFFRDY, &RDYI) != 0) || + (pHuffmanDecoder->decodeAValue(SBHUFFRSIZE, &nVal) != 0)) { + return nullptr; + } + pStream->alignByte(); + nTmp = pStream->getOffset(); + IBOI = SBSYMS[IDI]; + if (!IBOI) + return nullptr; + + WOI = IBOI->m_nWidth; + HOI = IBOI->m_nHeight; + if ((int)(WOI + RDWI) < 0 || (int)(HOI + RDHI) < 0) + return nullptr; + + std::unique_ptr<CJBig2_GRRDProc> pGRRD(new CJBig2_GRRDProc()); + pGRRD->GRW = WOI + RDWI; + pGRRD->GRH = HOI + RDHI; + pGRRD->GRTEMPLATE = SBRTEMPLATE; + pGRRD->GRREFERENCE = IBOI; + pGRRD->GRREFERENCEDX = (RDWI >> 2) + RDXI; + pGRRD->GRREFERENCEDY = (RDHI >> 2) + RDYI; + pGRRD->TPGRON = 0; + pGRRD->GRAT[0] = SBRAT[0]; + pGRRD->GRAT[1] = SBRAT[1]; + pGRRD->GRAT[2] = SBRAT[2]; + pGRRD->GRAT[3] = SBRAT[3]; + + { + std::unique_ptr<CJBig2_ArithDecoder> pArithDecoder( + new CJBig2_ArithDecoder(pStream)); + IBI = pGRRD->decode(pArithDecoder.get(), grContext); + if (!IBI) + return nullptr; + } + + pStream->alignByte(); + pStream->offset(2); + if ((FX_DWORD)nVal != (pStream->getOffset() - nTmp)) { + delete IBI; + return nullptr; + } + } + if (!IBI) { + continue; + } + WI = IBI->m_nWidth; + HI = IBI->m_nHeight; + if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPRIGHT) || + (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { + CURS = CURS + WI - 1; + } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_BOTTOMLEFT) || + (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { + CURS = CURS + HI - 1; + } + SI = CURS; + if (TRANSPOSED == 0) { + switch (REFCORNER) { + case JBIG2_CORNER_TOPLEFT: + SBREG->composeFrom(SI, TI, IBI, SBCOMBOP); + break; + case JBIG2_CORNER_TOPRIGHT: + SBREG->composeFrom(SI - WI + 1, TI, IBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMLEFT: + SBREG->composeFrom(SI, TI - HI + 1, IBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMRIGHT: + SBREG->composeFrom(SI - WI + 1, TI - HI + 1, IBI, SBCOMBOP); + break; + } + } else { + switch (REFCORNER) { + case JBIG2_CORNER_TOPLEFT: + SBREG->composeFrom(TI, SI, IBI, SBCOMBOP); + break; + case JBIG2_CORNER_TOPRIGHT: + SBREG->composeFrom(TI - WI + 1, SI, IBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMLEFT: + SBREG->composeFrom(TI, SI - HI + 1, IBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMRIGHT: + SBREG->composeFrom(TI - WI + 1, SI - HI + 1, IBI, SBCOMBOP); + break; + } + } + if (RI != 0) { + delete IBI; + } + if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) || + (REFCORNER == JBIG2_CORNER_BOTTOMLEFT))) { + CURS = CURS + WI - 1; + } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) || + (REFCORNER == JBIG2_CORNER_TOPRIGHT))) { + CURS = CURS + HI - 1; + } + NINSTANCES = NINSTANCES + 1; + } + } + return SBREG.release(); +} + +CJBig2_Image* CJBig2_TRDProc::decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext, + JBig2IntDecoderState* pIDS) { + std::unique_ptr<CJBig2_ArithIntDecoder> IADT; + std::unique_ptr<CJBig2_ArithIntDecoder> IAFS; + std::unique_ptr<CJBig2_ArithIntDecoder> IADS; + std::unique_ptr<CJBig2_ArithIntDecoder> IAIT; + std::unique_ptr<CJBig2_ArithIntDecoder> IARI; + std::unique_ptr<CJBig2_ArithIntDecoder> IARDW; + std::unique_ptr<CJBig2_ArithIntDecoder> IARDH; + std::unique_ptr<CJBig2_ArithIntDecoder> IARDX; + std::unique_ptr<CJBig2_ArithIntDecoder> IARDY; + std::unique_ptr<CJBig2_ArithIaidDecoder> IAID; + CJBig2_ArithIntDecoder* pIADT; + CJBig2_ArithIntDecoder* pIAFS; + CJBig2_ArithIntDecoder* pIADS; + CJBig2_ArithIntDecoder* pIAIT; + CJBig2_ArithIntDecoder* pIARI; + CJBig2_ArithIntDecoder* pIARDW; + CJBig2_ArithIntDecoder* pIARDH; + CJBig2_ArithIntDecoder* pIARDX; + CJBig2_ArithIntDecoder* pIARDY; + CJBig2_ArithIaidDecoder* pIAID; + if (pIDS) { + pIADT = pIDS->IADT; + pIAFS = pIDS->IAFS; + pIADS = pIDS->IADS; + pIAIT = pIDS->IAIT; + pIARI = pIDS->IARI; + pIARDW = pIDS->IARDW; + pIARDH = pIDS->IARDH; + pIARDX = pIDS->IARDX; + pIARDY = pIDS->IARDY; + pIAID = pIDS->IAID; + } else { + IADT.reset(new CJBig2_ArithIntDecoder()); + IAFS.reset(new CJBig2_ArithIntDecoder()); + IADS.reset(new CJBig2_ArithIntDecoder()); + IAIT.reset(new CJBig2_ArithIntDecoder()); + IARI.reset(new CJBig2_ArithIntDecoder()); + IARDW.reset(new CJBig2_ArithIntDecoder()); + IARDH.reset(new CJBig2_ArithIntDecoder()); + IARDX.reset(new CJBig2_ArithIntDecoder()); + IARDY.reset(new CJBig2_ArithIntDecoder()); + IAID.reset(new CJBig2_ArithIaidDecoder(SBSYMCODELEN)); + pIADT = IADT.get(); + pIAFS = IAFS.get(); + pIADS = IADS.get(); + pIAIT = IAIT.get(); + pIARI = IARI.get(); + pIARDW = IARDW.get(); + pIARDH = IARDH.get(); + pIARDX = IARDX.get(); + pIARDY = IARDY.get(); + pIAID = IAID.get(); + } + std::unique_ptr<CJBig2_Image> SBREG(new CJBig2_Image(SBW, SBH)); + SBREG->fill(SBDEFPIXEL); + int32_t STRIPT; + pIADT->decode(pArithDecoder, &STRIPT); + STRIPT *= SBSTRIPS; + STRIPT = -STRIPT; + int32_t FIRSTS = 0; + FX_DWORD NINSTANCES = 0; + while (NINSTANCES < SBNUMINSTANCES) { + int32_t CURS; + int32_t DT; + pIADT->decode(pArithDecoder, &DT); + DT *= SBSTRIPS; + STRIPT += DT; + bool bFirst = true; + for (;;) { + if (bFirst) { + int32_t DFS; + pIAFS->decode(pArithDecoder, &DFS); + FIRSTS += DFS; + CURS = FIRSTS; + bFirst = false; + } else { + int32_t IDS; + if (!pIADS->decode(pArithDecoder, &IDS)) + break; + CURS += IDS + SBDSOFFSET; + } + if (NINSTANCES >= SBNUMINSTANCES) { + break; + } + int CURT = 0; + if (SBSTRIPS != 1) + pIAIT->decode(pArithDecoder, &CURT); + + int32_t TI = STRIPT + CURT; + FX_DWORD IDI; + pIAID->decode(pArithDecoder, &IDI); + if (IDI >= SBNUMSYMS) + return nullptr; + + int RI; + if (SBREFINE == 0) + RI = 0; + else + pIARI->decode(pArithDecoder, &RI); + + std::unique_ptr<CJBig2_Image> IBI; + CJBig2_Image* pIBI; + if (RI == 0) { + pIBI = SBSYMS[IDI]; + } else { + int32_t RDWI; + int32_t RDHI; + int32_t RDXI; + int32_t RDYI; + pIARDW->decode(pArithDecoder, &RDWI); + pIARDH->decode(pArithDecoder, &RDHI); + pIARDX->decode(pArithDecoder, &RDXI); + pIARDY->decode(pArithDecoder, &RDYI); + CJBig2_Image* IBOI = SBSYMS[IDI]; + FX_DWORD WOI = IBOI->m_nWidth; + FX_DWORD HOI = IBOI->m_nHeight; + if ((int)(WOI + RDWI) < 0 || (int)(HOI + RDHI) < 0) { + return nullptr; + } + std::unique_ptr<CJBig2_GRRDProc> pGRRD(new CJBig2_GRRDProc()); + pGRRD->GRW = WOI + RDWI; + pGRRD->GRH = HOI + RDHI; + pGRRD->GRTEMPLATE = SBRTEMPLATE; + pGRRD->GRREFERENCE = IBOI; + pGRRD->GRREFERENCEDX = (RDWI >> 1) + RDXI; + pGRRD->GRREFERENCEDY = (RDHI >> 1) + RDYI; + pGRRD->TPGRON = 0; + pGRRD->GRAT[0] = SBRAT[0]; + pGRRD->GRAT[1] = SBRAT[1]; + pGRRD->GRAT[2] = SBRAT[2]; + pGRRD->GRAT[3] = SBRAT[3]; + IBI.reset(pGRRD->decode(pArithDecoder, grContext)); + pIBI = IBI.get(); + } + if (!pIBI) + return nullptr; + + FX_DWORD WI = pIBI->m_nWidth; + FX_DWORD HI = pIBI->m_nHeight; + if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPRIGHT) || + (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { + CURS += WI - 1; + } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_BOTTOMLEFT) || + (REFCORNER == JBIG2_CORNER_BOTTOMRIGHT))) { + CURS += HI - 1; + } + int32_t SI = CURS; + if (TRANSPOSED == 0) { + switch (REFCORNER) { + case JBIG2_CORNER_TOPLEFT: + SBREG->composeFrom(SI, TI, pIBI, SBCOMBOP); + break; + case JBIG2_CORNER_TOPRIGHT: + SBREG->composeFrom(SI - WI + 1, TI, pIBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMLEFT: + SBREG->composeFrom(SI, TI - HI + 1, pIBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMRIGHT: + SBREG->composeFrom(SI - WI + 1, TI - HI + 1, pIBI, SBCOMBOP); + break; + } + } else { + switch (REFCORNER) { + case JBIG2_CORNER_TOPLEFT: + SBREG->composeFrom(TI, SI, pIBI, SBCOMBOP); + break; + case JBIG2_CORNER_TOPRIGHT: + SBREG->composeFrom(TI - WI + 1, SI, pIBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMLEFT: + SBREG->composeFrom(TI, SI - HI + 1, pIBI, SBCOMBOP); + break; + case JBIG2_CORNER_BOTTOMRIGHT: + SBREG->composeFrom(TI - WI + 1, SI - HI + 1, pIBI, SBCOMBOP); + break; + } + } + if (TRANSPOSED == 0 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) || + (REFCORNER == JBIG2_CORNER_BOTTOMLEFT))) { + CURS += WI - 1; + } else if (TRANSPOSED == 1 && ((REFCORNER == JBIG2_CORNER_TOPLEFT) || + (REFCORNER == JBIG2_CORNER_TOPRIGHT))) { + CURS += HI - 1; + } + ++NINSTANCES; + } + } + return SBREG.release(); +} diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.h b/core/fxcodec/jbig2/JBig2_TrdProc.h new file mode 100644 index 0000000000..090e564726 --- /dev/null +++ b/core/fxcodec/jbig2/JBig2_TrdProc.h @@ -0,0 +1,82 @@ +// Copyright 2015 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_ +#define CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_ + +#include "core/fxcodec/jbig2/JBig2_Image.h" +#include "core/include/fxcrt/fx_system.h" + +class CJBig2_ArithDecoder; +class CJBig2_ArithIaidDecoder; +class CJBig2_ArithIntDecoder; +class CJBig2_BitStream; +class CJBig2_HuffmanTable; +struct JBig2ArithCtx; +struct JBig2HuffmanCode; + +struct JBig2IntDecoderState { + CJBig2_ArithIntDecoder* IADT; + CJBig2_ArithIntDecoder* IAFS; + CJBig2_ArithIntDecoder* IADS; + CJBig2_ArithIntDecoder* IAIT; + CJBig2_ArithIntDecoder* IARI; + CJBig2_ArithIntDecoder* IARDW; + CJBig2_ArithIntDecoder* IARDH; + CJBig2_ArithIntDecoder* IARDX; + CJBig2_ArithIntDecoder* IARDY; + CJBig2_ArithIaidDecoder* IAID; +}; + +enum JBig2Corner { + JBIG2_CORNER_BOTTOMLEFT = 0, + JBIG2_CORNER_TOPLEFT = 1, + JBIG2_CORNER_BOTTOMRIGHT = 2, + JBIG2_CORNER_TOPRIGHT = 3 +}; + +class CJBig2_TRDProc { + public: + CJBig2_Image* decode_Huffman(CJBig2_BitStream* pStream, + JBig2ArithCtx* grContext); + + CJBig2_Image* decode_Arith(CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext, + JBig2IntDecoderState* pIDS); + + public: + FX_BOOL SBHUFF; + FX_BOOL SBREFINE; + FX_DWORD SBW; + FX_DWORD SBH; + FX_DWORD SBNUMINSTANCES; + FX_DWORD SBSTRIPS; + FX_DWORD SBNUMSYMS; + + JBig2HuffmanCode* SBSYMCODES; + uint8_t SBSYMCODELEN; + + CJBig2_Image** SBSYMS; + FX_BOOL SBDEFPIXEL; + + JBig2ComposeOp SBCOMBOP; + FX_BOOL TRANSPOSED; + + JBig2Corner REFCORNER; + int8_t SBDSOFFSET; + CJBig2_HuffmanTable* SBHUFFFS; + CJBig2_HuffmanTable* SBHUFFDS; + CJBig2_HuffmanTable* SBHUFFDT; + CJBig2_HuffmanTable* SBHUFFRDW; + CJBig2_HuffmanTable* SBHUFFRDH; + CJBig2_HuffmanTable* SBHUFFRDX; + CJBig2_HuffmanTable* SBHUFFRDY; + CJBig2_HuffmanTable* SBHUFFRSIZE; + FX_BOOL SBRTEMPLATE; + int8_t SBRAT[4]; +}; + +#endif // CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_ diff --git a/core/fxcodec/lbmp/fx_bmp.cpp b/core/fxcodec/lbmp/fx_bmp.cpp new file mode 100644 index 0000000000..15384d56ff --- /dev/null +++ b/core/fxcodec/lbmp/fx_bmp.cpp @@ -0,0 +1,948 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/lbmp/fx_bmp.h" + +#include <algorithm> + +namespace { + +const size_t kBmpCoreHeaderSize = 12; +const size_t kBmpInfoHeaderSize = 40; + +// TODO(thestig): Replace with FXDWORD_GET_LSBFIRST? +FX_DWORD GetDWord_LSBFirst(uint8_t* p) { + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + +void SetDWord_LSBFirst(uint8_t* p, FX_DWORD v) { + p[0] = (uint8_t)v; + p[1] = (uint8_t)(v >> 8); + p[2] = (uint8_t)(v >> 16); + p[3] = (uint8_t)(v >> 24); +} +} // namespace + +FX_WORD GetWord_LSBFirst(uint8_t* p) { + return p[0] | (p[1] << 8); +} +void SetWord_LSBFirst(uint8_t* p, FX_WORD v) { + p[0] = (uint8_t)v; + p[1] = (uint8_t)(v >> 8); +} +void bmp_error(bmp_decompress_struct_p bmp_ptr, const FX_CHAR* err_msg) { + if (bmp_ptr && bmp_ptr->bmp_error_fn) { + bmp_ptr->bmp_error_fn(bmp_ptr, err_msg); + } +} +bmp_decompress_struct_p bmp_create_decompress() { + bmp_decompress_struct_p bmp_ptr = FX_Alloc(bmp_decompress_struct, 1); + if (bmp_ptr == NULL) { + return NULL; + } + FXSYS_memset(bmp_ptr, 0, sizeof(bmp_decompress_struct)); + bmp_ptr->decode_status = BMP_D_STATUS_HEADER; + bmp_ptr->bmp_header_ptr = FX_Alloc(BmpFileHeader, 1); + return bmp_ptr; +} +void bmp_destroy_decompress(bmp_decompress_struct_pp bmp_ptr_ptr) { + if (bmp_ptr_ptr == NULL || *bmp_ptr_ptr == NULL) { + return; + } + bmp_decompress_struct_p bmp_ptr = *bmp_ptr_ptr; + *bmp_ptr_ptr = NULL; + if (bmp_ptr->out_row_buffer) { + FX_Free(bmp_ptr->out_row_buffer); + } + FX_Free(bmp_ptr->pal_ptr); + FX_Free(bmp_ptr->bmp_header_ptr); + FX_Free(bmp_ptr); +} +int32_t bmp_read_header(bmp_decompress_struct_p bmp_ptr) { + if (bmp_ptr == NULL) { + return 0; + } + FX_DWORD skip_size_org = bmp_ptr->skip_size; + if (bmp_ptr->decode_status == BMP_D_STATUS_HEADER) { + ASSERT(sizeof(BmpFileHeader) == 14); + BmpFileHeader* bmp_header_ptr = NULL; + if (bmp_read_data(bmp_ptr, (uint8_t**)&bmp_header_ptr, 14) == NULL) { + return 2; + } + bmp_ptr->bmp_header_ptr->bfType = + GetWord_LSBFirst((uint8_t*)&bmp_header_ptr->bfType); + bmp_ptr->bmp_header_ptr->bfOffBits = + GetDWord_LSBFirst((uint8_t*)&bmp_header_ptr->bfOffBits); + bmp_ptr->data_size = GetDWord_LSBFirst((uint8_t*)&bmp_header_ptr->bfSize); + if (bmp_ptr->bmp_header_ptr->bfType != BMP_SIGNATURE) { + bmp_error(bmp_ptr, "Not A Bmp Image"); + return 0; + } + if (bmp_ptr->avail_in < sizeof(FX_DWORD)) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + bmp_ptr->img_ifh_size = + GetDWord_LSBFirst(bmp_ptr->next_in + bmp_ptr->skip_size); + bmp_ptr->pal_type = 0; + static_assert(sizeof(BmpCoreHeader) == kBmpCoreHeaderSize, + "BmpCoreHeader has wrong size"); + static_assert(sizeof(BmpInfoHeader) == kBmpInfoHeaderSize, + "BmpInfoHeader has wrong size"); + switch (bmp_ptr->img_ifh_size) { + case kBmpCoreHeaderSize: { + bmp_ptr->pal_type = 1; + BmpCoreHeaderPtr bmp_core_header_ptr = NULL; + if (bmp_read_data(bmp_ptr, (uint8_t**)&bmp_core_header_ptr, + bmp_ptr->img_ifh_size) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + bmp_ptr->width = + GetWord_LSBFirst((uint8_t*)&bmp_core_header_ptr->bcWidth); + bmp_ptr->height = + GetWord_LSBFirst((uint8_t*)&bmp_core_header_ptr->bcHeight); + bmp_ptr->bitCounts = + GetWord_LSBFirst((uint8_t*)&bmp_core_header_ptr->bcBitCount); + bmp_ptr->compress_flag = BMP_RGB; + bmp_ptr->imgTB_flag = FALSE; + } break; + case kBmpInfoHeaderSize: { + BmpInfoHeaderPtr bmp_info_header_ptr = NULL; + if (bmp_read_data(bmp_ptr, (uint8_t**)&bmp_info_header_ptr, + bmp_ptr->img_ifh_size) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + bmp_ptr->width = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biWidth); + bmp_ptr->height = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biHeight); + bmp_ptr->bitCounts = + GetWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biBitCount); + bmp_ptr->compress_flag = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biCompression); + bmp_ptr->color_used = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biClrUsed); + bmp_ptr->dpi_x = (int32_t)GetDWord_LSBFirst( + (uint8_t*)&bmp_info_header_ptr->biXPelsPerMeter); + bmp_ptr->dpi_y = (int32_t)GetDWord_LSBFirst( + (uint8_t*)&bmp_info_header_ptr->biYPelsPerMeter); + if (bmp_ptr->height < 0) { + bmp_ptr->height = -bmp_ptr->height; + bmp_ptr->imgTB_flag = TRUE; + } + } break; + default: { + if (bmp_ptr->img_ifh_size > + std::min(kBmpInfoHeaderSize, sizeof(BmpInfoHeader))) { + BmpInfoHeaderPtr bmp_info_header_ptr = NULL; + if (bmp_read_data(bmp_ptr, (uint8_t**)&bmp_info_header_ptr, + bmp_ptr->img_ifh_size) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + FX_WORD biPlanes; + bmp_ptr->width = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biWidth); + bmp_ptr->height = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biHeight); + bmp_ptr->bitCounts = + GetWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biBitCount); + bmp_ptr->compress_flag = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biCompression); + bmp_ptr->color_used = + GetDWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biClrUsed); + biPlanes = GetWord_LSBFirst((uint8_t*)&bmp_info_header_ptr->biPlanes); + bmp_ptr->dpi_x = GetDWord_LSBFirst( + (uint8_t*)&bmp_info_header_ptr->biXPelsPerMeter); + bmp_ptr->dpi_y = GetDWord_LSBFirst( + (uint8_t*)&bmp_info_header_ptr->biYPelsPerMeter); + if (bmp_ptr->height < 0) { + bmp_ptr->height = -bmp_ptr->height; + bmp_ptr->imgTB_flag = TRUE; + } + if (bmp_ptr->compress_flag == BMP_RGB && biPlanes == 1 && + bmp_ptr->color_used == 0) { + break; + } + } + bmp_error(bmp_ptr, "Unsupported Bmp File"); + return 0; + } + } + ASSERT(bmp_ptr->width > 0); + ASSERT(bmp_ptr->compress_flag <= BMP_BITFIELDS); + switch (bmp_ptr->bitCounts) { + case 1: + case 4: + case 8: + case 16: + case 24: { + if (bmp_ptr->color_used > ((FX_DWORD)1) << bmp_ptr->bitCounts) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + } + case 32: { + if (bmp_ptr->width <= 0 || bmp_ptr->compress_flag > BMP_BITFIELDS) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + } break; + default: + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + bmp_ptr->src_row_bytes = BMP_WIDTHBYTES(bmp_ptr->width, bmp_ptr->bitCounts); + switch (bmp_ptr->bitCounts) { + case 1: + case 4: + case 8: + bmp_ptr->out_row_bytes = BMP_WIDTHBYTES(bmp_ptr->width, 8); + bmp_ptr->components = 1; + break; + case 16: + case 24: + bmp_ptr->out_row_bytes = BMP_WIDTHBYTES(bmp_ptr->width, 24); + bmp_ptr->components = 3; + break; + case 32: + bmp_ptr->out_row_bytes = bmp_ptr->src_row_bytes; + bmp_ptr->components = 4; + break; + } + FX_Free(bmp_ptr->out_row_buffer); + bmp_ptr->out_row_buffer = FX_Alloc(uint8_t, bmp_ptr->out_row_bytes); + FXSYS_memset(bmp_ptr->out_row_buffer, 0, bmp_ptr->out_row_bytes); + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_PAL); + } + if (bmp_ptr->decode_status == BMP_D_STATUS_PAL) { + skip_size_org = bmp_ptr->skip_size; + if (bmp_ptr->compress_flag == BMP_BITFIELDS) { + if (bmp_ptr->bitCounts != 16 && bmp_ptr->bitCounts != 32) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + FX_DWORD* mask; + if (bmp_read_data(bmp_ptr, (uint8_t**)&mask, 3 * sizeof(FX_DWORD)) == + NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + bmp_ptr->mask_red = GetDWord_LSBFirst((uint8_t*)&mask[0]); + bmp_ptr->mask_green = GetDWord_LSBFirst((uint8_t*)&mask[1]); + bmp_ptr->mask_blue = GetDWord_LSBFirst((uint8_t*)&mask[2]); + if (bmp_ptr->mask_red & bmp_ptr->mask_green || + bmp_ptr->mask_red & bmp_ptr->mask_blue || + bmp_ptr->mask_green & bmp_ptr->mask_blue) { + bmp_error(bmp_ptr, "The Bitfield Bmp File Is Corrupt"); + return 0; + } + if (bmp_ptr->bmp_header_ptr->bfOffBits < 26 + bmp_ptr->img_ifh_size) { + bmp_ptr->bmp_header_ptr->bfOffBits = 26 + bmp_ptr->img_ifh_size; + } + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_DATA_PRE); + return 1; + } else if (bmp_ptr->bitCounts == 16) { + bmp_ptr->mask_red = 0x7C00; + bmp_ptr->mask_green = 0x03E0; + bmp_ptr->mask_blue = 0x001F; + } + bmp_ptr->pal_num = 0; + if (bmp_ptr->bitCounts < 16) { + bmp_ptr->pal_num = 1 << bmp_ptr->bitCounts; + if (bmp_ptr->color_used != 0) { + bmp_ptr->pal_num = bmp_ptr->color_used; + } + uint8_t* src_pal_ptr = NULL; + FX_DWORD src_pal_size = bmp_ptr->pal_num * (bmp_ptr->pal_type ? 3 : 4); + if (bmp_read_data(bmp_ptr, (uint8_t**)&src_pal_ptr, src_pal_size) == + NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + FX_Free(bmp_ptr->pal_ptr); + bmp_ptr->pal_ptr = FX_Alloc(FX_DWORD, bmp_ptr->pal_num); + int32_t src_pal_index = 0; + if (bmp_ptr->pal_type == BMP_PAL_OLD) { + while (src_pal_index < bmp_ptr->pal_num) { + bmp_ptr->pal_ptr[src_pal_index++] = BMP_PAL_ENCODE( + 0x00, src_pal_ptr[2], src_pal_ptr[1], src_pal_ptr[0]); + src_pal_ptr += 3; + } + } else { + while (src_pal_index < bmp_ptr->pal_num) { + bmp_ptr->pal_ptr[src_pal_index++] = BMP_PAL_ENCODE( + src_pal_ptr[3], src_pal_ptr[2], src_pal_ptr[1], src_pal_ptr[0]); + src_pal_ptr += 4; + } + } + } + if (bmp_ptr->bmp_header_ptr->bfOffBits < + 14 + bmp_ptr->img_ifh_size + + bmp_ptr->pal_num * (bmp_ptr->pal_type ? 3 : 4)) { + bmp_ptr->bmp_header_ptr->bfOffBits = + 14 + bmp_ptr->img_ifh_size + + bmp_ptr->pal_num * (bmp_ptr->pal_type ? 3 : 4); + } + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_DATA_PRE); + } + return 1; +} +int32_t bmp_decode_image(bmp_decompress_struct_p bmp_ptr) { + if (bmp_ptr->decode_status == BMP_D_STATUS_DATA_PRE) { + bmp_ptr->avail_in = 0; + if (!bmp_ptr->bmp_get_data_position_fn( + bmp_ptr, bmp_ptr->bmp_header_ptr->bfOffBits)) { + bmp_ptr->decode_status = BMP_D_STATUS_TAIL; + bmp_error(bmp_ptr, "The Bmp File Is Corrupt, Unexpected Stream Offset"); + return 0; + } + bmp_ptr->row_num = 0; + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_DATA); + } + if (bmp_ptr->decode_status == BMP_D_STATUS_DATA) { + switch (bmp_ptr->compress_flag) { + case BMP_RGB: + case BMP_BITFIELDS: + return bmp_decode_rgb(bmp_ptr); + case BMP_RLE8: + return bmp_decode_rle8(bmp_ptr); + case BMP_RLE4: + return bmp_decode_rle4(bmp_ptr); + } + } + bmp_error(bmp_ptr, "Any Uncontrol Error"); + return 0; +} +int32_t bmp_decode_rgb(bmp_decompress_struct_p bmp_ptr) { + uint8_t* row_buf = bmp_ptr->out_row_buffer; + uint8_t* des_buf = NULL; + while (bmp_ptr->row_num < bmp_ptr->height) { + if (bmp_read_data(bmp_ptr, &des_buf, bmp_ptr->src_row_bytes) == NULL) { + return 2; + } + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_DATA); + switch (bmp_ptr->bitCounts) { + case 1: { + for (int32_t col = 0; col < bmp_ptr->width; col++) { + *row_buf++ = des_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00; + } + } break; + case 4: { + for (int32_t col = 0; col < bmp_ptr->width; col++) { + *row_buf++ = (col & 0x01) ? (des_buf[col >> 1] & 0x0F) + : ((des_buf[col >> 1] & 0xF0) >> 4); + } + } break; + case 16: { + FX_WORD* buf = (FX_WORD*)des_buf; + uint8_t blue_bits = 0; + uint8_t green_bits = 0; + uint8_t red_bits = 0; + for (int32_t i = 0; i < 16; i++) { + if ((bmp_ptr->mask_blue >> i) & 0x01) { + blue_bits++; + } + if ((bmp_ptr->mask_green >> i) & 0x01) { + green_bits++; + } + if ((bmp_ptr->mask_red >> i) & 0x01) { + red_bits++; + } + } + green_bits += blue_bits; + red_bits += green_bits; + blue_bits = 8 - blue_bits; + green_bits -= 8; + red_bits -= 8; + for (int32_t col = 0; col < bmp_ptr->width; col++) { + *buf = GetWord_LSBFirst((uint8_t*)buf); + *row_buf++ = (uint8_t)((*buf & bmp_ptr->mask_blue) << blue_bits); + *row_buf++ = (uint8_t)((*buf & bmp_ptr->mask_green) >> green_bits); + *row_buf++ = (uint8_t)((*buf++ & bmp_ptr->mask_red) >> red_bits); + } + } break; + case 8: + case 24: + case 32: + FXSYS_memcpy(bmp_ptr->out_row_buffer, des_buf, bmp_ptr->src_row_bytes); + break; + } + row_buf = bmp_ptr->out_row_buffer; + bmp_ptr->bmp_get_row_fn(bmp_ptr, + bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + } + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_TAIL); + return 1; +} +int32_t bmp_decode_rle8(bmp_decompress_struct_p bmp_ptr) { + uint8_t* first_byte_ptr = NULL; + uint8_t* second_byte_ptr = NULL; + bmp_ptr->col_num = 0; + while (TRUE) { + FX_DWORD skip_size_org = bmp_ptr->skip_size; + if (bmp_read_data(bmp_ptr, &first_byte_ptr, 1) == NULL) { + return 2; + } + switch (*first_byte_ptr) { + case RLE_MARKER: { + if (bmp_read_data(bmp_ptr, &first_byte_ptr, 1) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + switch (*first_byte_ptr) { + case RLE_EOL: { + if (bmp_ptr->row_num >= bmp_ptr->height) { + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_TAIL); + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + bmp_ptr->bmp_get_row_fn( + bmp_ptr, bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + bmp_ptr->col_num = 0; + FXSYS_memset(bmp_ptr->out_row_buffer, 0, bmp_ptr->out_row_bytes); + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_DATA); + continue; + } + case RLE_EOI: { + if (bmp_ptr->row_num < bmp_ptr->height) { + bmp_ptr->bmp_get_row_fn( + bmp_ptr, bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + } + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_TAIL); + return 1; + } + case RLE_DELTA: { + uint8_t* delta_ptr; + if (bmp_read_data(bmp_ptr, &delta_ptr, 2) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + bmp_ptr->col_num += (int32_t)delta_ptr[0]; + int32_t bmp_row_num_next = bmp_ptr->row_num + (int32_t)delta_ptr[1]; + if (bmp_ptr->col_num >= bmp_ptr->out_row_bytes || + bmp_row_num_next >= bmp_ptr->height) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt Or Not Supported"); + return 0; + } + while (bmp_ptr->row_num < bmp_row_num_next) { + FXSYS_memset(bmp_ptr->out_row_buffer, 0, bmp_ptr->out_row_bytes); + bmp_ptr->bmp_get_row_fn( + bmp_ptr, bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + } + } break; + default: { + if ((int32_t)(*first_byte_ptr) > + bmp_ptr->src_row_bytes - bmp_ptr->col_num) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + if (bmp_read_data(bmp_ptr, &second_byte_ptr, + *first_byte_ptr & 1 ? *first_byte_ptr + 1 + : *first_byte_ptr) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + FXSYS_memcpy(bmp_ptr->out_row_buffer + bmp_ptr->col_num, + second_byte_ptr, *first_byte_ptr); + bmp_ptr->col_num += (int32_t)(*first_byte_ptr); + } + } + } break; + default: { + if (bmp_read_data(bmp_ptr, &second_byte_ptr, 1) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + if ((int32_t)(*first_byte_ptr) > + bmp_ptr->src_row_bytes - bmp_ptr->col_num) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + FXSYS_memset(bmp_ptr->out_row_buffer + bmp_ptr->col_num, + *second_byte_ptr, *first_byte_ptr); + bmp_ptr->col_num += (int32_t)(*first_byte_ptr); + } + } + } + bmp_error(bmp_ptr, "Any Uncontrol Error"); + return 0; +} +int32_t bmp_decode_rle4(bmp_decompress_struct_p bmp_ptr) { + uint8_t* first_byte_ptr = NULL; + uint8_t* second_byte_ptr = NULL; + bmp_ptr->col_num = 0; + while (TRUE) { + FX_DWORD skip_size_org = bmp_ptr->skip_size; + if (bmp_read_data(bmp_ptr, &first_byte_ptr, 1) == NULL) { + return 2; + } + switch (*first_byte_ptr) { + case RLE_MARKER: { + if (bmp_read_data(bmp_ptr, &first_byte_ptr, 1) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + switch (*first_byte_ptr) { + case RLE_EOL: { + if (bmp_ptr->row_num >= bmp_ptr->height) { + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_TAIL); + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + bmp_ptr->bmp_get_row_fn( + bmp_ptr, bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + bmp_ptr->col_num = 0; + FXSYS_memset(bmp_ptr->out_row_buffer, 0, bmp_ptr->out_row_bytes); + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_DATA); + continue; + } + case RLE_EOI: { + if (bmp_ptr->row_num < bmp_ptr->height) { + bmp_ptr->bmp_get_row_fn( + bmp_ptr, bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + } + bmp_save_decoding_status(bmp_ptr, BMP_D_STATUS_TAIL); + return 1; + } + case RLE_DELTA: { + uint8_t* delta_ptr; + if (bmp_read_data(bmp_ptr, &delta_ptr, 2) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + bmp_ptr->col_num += (int32_t)delta_ptr[0]; + int32_t bmp_row_num_next = bmp_ptr->row_num + (int32_t)delta_ptr[1]; + if (bmp_ptr->col_num >= bmp_ptr->out_row_bytes || + bmp_row_num_next >= bmp_ptr->height) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt Or Not Supported"); + return 0; + } + while (bmp_ptr->row_num < bmp_row_num_next) { + FXSYS_memset(bmp_ptr->out_row_buffer, 0, bmp_ptr->out_row_bytes); + bmp_ptr->bmp_get_row_fn( + bmp_ptr, bmp_ptr->imgTB_flag + ? bmp_ptr->row_num++ + : (bmp_ptr->height - 1 - bmp_ptr->row_num++), + bmp_ptr->out_row_buffer); + } + } break; + default: { + uint8_t size = (uint8_t)(((FX_WORD)(*first_byte_ptr) + 1) >> 1); + if ((int32_t)*first_byte_ptr >= + bmp_ptr->out_row_bytes - bmp_ptr->col_num) { + if (size + (bmp_ptr->col_num >> 1) > bmp_ptr->src_row_bytes) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + *first_byte_ptr = bmp_ptr->out_row_bytes - bmp_ptr->col_num - 1; + } + if (bmp_read_data(bmp_ptr, &second_byte_ptr, + size & 1 ? size + 1 : size) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + for (uint8_t i = 0; i < *first_byte_ptr; i++) { + if (i & 0x01) { + *(bmp_ptr->out_row_buffer + bmp_ptr->col_num++) = + (*second_byte_ptr++ & 0x0F); + } else { + *(bmp_ptr->out_row_buffer + bmp_ptr->col_num++) = + ((*second_byte_ptr & 0xF0) >> 4); + } + } + } + } + } break; + default: { + if (bmp_read_data(bmp_ptr, &second_byte_ptr, 1) == NULL) { + bmp_ptr->skip_size = skip_size_org; + return 2; + } + if ((int32_t)*first_byte_ptr > + bmp_ptr->out_row_bytes - bmp_ptr->col_num) { + uint8_t size = (uint8_t)(((FX_WORD)(*first_byte_ptr) + 1) >> 1); + if (size + (bmp_ptr->col_num >> 1) > bmp_ptr->src_row_bytes) { + bmp_error(bmp_ptr, "The Bmp File Is Corrupt"); + return 0; + } + *first_byte_ptr = bmp_ptr->out_row_bytes - bmp_ptr->col_num - 1; + } + for (uint8_t i = 0; i < *first_byte_ptr; i++) { + if (i & 0x01) { + *(bmp_ptr->out_row_buffer + bmp_ptr->col_num++) = + (*second_byte_ptr & 0x0F); + } else { + *(bmp_ptr->out_row_buffer + bmp_ptr->col_num++) = + ((*second_byte_ptr & 0xF0) >> 4); + } + } + } + } + } + bmp_error(bmp_ptr, "Any Uncontrol Error"); + return 0; +} +uint8_t* bmp_read_data(bmp_decompress_struct_p bmp_ptr, + uint8_t** des_buf_pp, + FX_DWORD data_size) { + if (bmp_ptr == NULL || bmp_ptr->avail_in < bmp_ptr->skip_size + data_size) { + return NULL; + } + *des_buf_pp = bmp_ptr->next_in + bmp_ptr->skip_size; + bmp_ptr->skip_size += data_size; + return *des_buf_pp; +} +void bmp_save_decoding_status(bmp_decompress_struct_p bmp_ptr, int32_t status) { + bmp_ptr->decode_status = status; + bmp_ptr->next_in += bmp_ptr->skip_size; + bmp_ptr->avail_in -= bmp_ptr->skip_size; + bmp_ptr->skip_size = 0; +} +void bmp_input_buffer(bmp_decompress_struct_p bmp_ptr, + uint8_t* src_buf, + FX_DWORD src_size) { + bmp_ptr->next_in = src_buf; + bmp_ptr->avail_in = src_size; + bmp_ptr->skip_size = 0; +} +FX_DWORD bmp_get_avail_input(bmp_decompress_struct_p bmp_ptr, + uint8_t** avial_buf_ptr) { + if (avial_buf_ptr) { + *avial_buf_ptr = NULL; + if (bmp_ptr->avail_in > 0) { + *avial_buf_ptr = bmp_ptr->next_in; + } + } + return bmp_ptr->avail_in; +} +bmp_compress_struct_p bmp_create_compress() { + bmp_compress_struct_p bmp_ptr; + bmp_ptr = FX_Alloc(bmp_compress_struct, 1); + if (bmp_ptr) { + FXSYS_memset(bmp_ptr, 0, sizeof(bmp_compress_struct)); + } + return bmp_ptr; +} +void bmp_destroy_compress(bmp_compress_struct_p bmp_ptr) { + if (bmp_ptr) { + if (bmp_ptr->src_free && bmp_ptr->src_buf) { + FX_Free(bmp_ptr->src_buf); + } + FX_Free(bmp_ptr); + } +} +static void WriteFileHeader(BmpFileHeaderPtr head_ptr, uint8_t* dst_buf) { + FX_DWORD offset; + offset = 0; + SetWord_LSBFirst(&dst_buf[offset], head_ptr->bfType); + offset += 2; + SetDWord_LSBFirst(&dst_buf[offset], head_ptr->bfSize); + offset += 4; + SetWord_LSBFirst(&dst_buf[offset], head_ptr->bfReserved1); + offset += 2; + SetWord_LSBFirst(&dst_buf[offset], head_ptr->bfReserved2); + offset += 2; + SetDWord_LSBFirst(&dst_buf[offset], head_ptr->bfOffBits); + offset += 4; +} +static void WriteInfoHeader(BmpInfoHeaderPtr info_head_ptr, uint8_t* dst_buf) { + FX_DWORD offset; + offset = sizeof(BmpFileHeader); + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biSize); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biWidth); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biHeight); + offset += 4; + SetWord_LSBFirst(&dst_buf[offset], info_head_ptr->biPlanes); + offset += 2; + SetWord_LSBFirst(&dst_buf[offset], info_head_ptr->biBitCount); + offset += 2; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biCompression); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biSizeImage); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biXPelsPerMeter); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biYPelsPerMeter); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biClrUsed); + offset += 4; + SetDWord_LSBFirst(&dst_buf[offset], info_head_ptr->biClrImportant); + offset += 4; +} +static void bmp_encode_bitfields(bmp_compress_struct_p bmp_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_size) { + if (bmp_ptr->info_header.biBitCount != 16 && + bmp_ptr->info_header.biBitCount != 32) { + return; + } + FX_DWORD size, dst_pos, i; + size = bmp_ptr->src_pitch * bmp_ptr->src_row * + bmp_ptr->info_header.biBitCount / 16; + dst_pos = bmp_ptr->file_header.bfOffBits; + dst_size += size; + dst_buf = FX_Realloc(uint8_t, dst_buf, dst_size); + if (dst_buf == NULL) { + return; + } + FXSYS_memset(&dst_buf[dst_pos], 0, size); + FX_DWORD mask_red; + FX_DWORD mask_green; + FX_DWORD mask_blue; + mask_red = 0x7C00; + mask_green = 0x03E0; + mask_blue = 0x001F; + if (bmp_ptr->info_header.biCompression == BMP_BITFIELDS) { + if (bmp_ptr->bit_type == BMP_BIT_565) { + mask_red = 0xF800; + mask_green = 0x07E0; + mask_blue = 0x001F; + } + if (bmp_ptr->info_header.biBitCount == 32) { + mask_red = 0xFF0000; + mask_green = 0x00FF00; + mask_blue = 0x0000FF; + } + SetDWord_LSBFirst(&dst_buf[dst_pos], mask_red); + dst_pos += 4; + SetDWord_LSBFirst(&dst_buf[dst_pos], mask_green); + dst_pos += 4; + SetDWord_LSBFirst(&dst_buf[dst_pos], mask_blue); + dst_pos += 4; + bmp_ptr->file_header.bfOffBits = dst_pos; + } + uint8_t blue_bits = 0; + uint8_t green_bits = 0; + uint8_t red_bits = 0; + for (i = 0; i < bmp_ptr->info_header.biBitCount; i++) { + if ((mask_blue >> i) & 0x01) { + blue_bits++; + } + if ((mask_green >> i) & 0x01) { + green_bits++; + } + if ((mask_red >> i) & 0x01) { + red_bits++; + } + } + green_bits += blue_bits; + red_bits += green_bits; + blue_bits = 8 - blue_bits; + green_bits -= 8; + red_bits -= 8; + i = 0; + for (int32_t row_num = bmp_ptr->src_row - 1; row_num > -1; row_num--, i = 0) { + while (i < bmp_ptr->src_width * bmp_ptr->src_bpp / 8) { + uint8_t b = bmp_ptr->src_buf[row_num * bmp_ptr->src_pitch + i++]; + uint8_t g = bmp_ptr->src_buf[row_num * bmp_ptr->src_pitch + i++]; + uint8_t r = bmp_ptr->src_buf[row_num * bmp_ptr->src_pitch + i++]; + if (bmp_ptr->src_bpp == 32) { + i++; + } + FX_DWORD pix_val = 0; + pix_val |= (b >> blue_bits) & mask_blue; + pix_val |= (g << green_bits) & mask_green; + pix_val |= (r << red_bits) & mask_red; + if (bmp_ptr->info_header.biBitCount == 16) { + SetWord_LSBFirst(&dst_buf[dst_pos], pix_val); + dst_pos += 2; + } else { + SetDWord_LSBFirst(&dst_buf[dst_pos], pix_val); + dst_pos += 4; + } + } + } + dst_size = dst_pos; +} + +static void bmp_encode_rgb(bmp_compress_struct_p bmp_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_size) { + if (bmp_ptr->info_header.biBitCount == 16) { + bmp_encode_bitfields(bmp_ptr, dst_buf, dst_size); + return; + } + FX_DWORD size, dst_pos; + FX_DWORD dst_pitch = + (bmp_ptr->src_width * bmp_ptr->info_header.biBitCount + 31) / 32 * 4; + size = dst_pitch * bmp_ptr->src_row; + dst_pos = bmp_ptr->file_header.bfOffBits; + dst_size += size; + dst_buf = FX_Realloc(uint8_t, dst_buf, dst_size); + if (dst_buf == NULL) { + return; + } + FXSYS_memset(&dst_buf[dst_pos], 0, size); + for (int32_t row_num = bmp_ptr->src_row - 1; row_num > -1; row_num--) { + FXSYS_memcpy(&dst_buf[dst_pos], + &bmp_ptr->src_buf[row_num * bmp_ptr->src_pitch], + bmp_ptr->src_pitch); + dst_pos += dst_pitch; + } + dst_size = dst_pos; +} +static uint8_t bmp_rle8_search(const uint8_t* buf, int32_t len) { + uint8_t num; + num = 1; + while (num < len) { + if (buf[num - 1] != buf[num] || num == 0xFF) { + break; + } + num++; + } + return num; +} +static void bmp_encode_rle8(bmp_compress_struct_p bmp_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_size) { + FX_DWORD size, dst_pos, index; + uint8_t rle[2] = {0}; + size = bmp_ptr->src_pitch * bmp_ptr->src_row * 2; + dst_pos = bmp_ptr->file_header.bfOffBits; + dst_size += size; + dst_buf = FX_Realloc(uint8_t, dst_buf, dst_size); + if (dst_buf == NULL) { + return; + } + FXSYS_memset(&dst_buf[dst_pos], 0, size); + for (int32_t row_num = bmp_ptr->src_row - 1, i = 0; row_num > -1;) { + index = row_num * bmp_ptr->src_pitch; + rle[0] = bmp_rle8_search(&bmp_ptr->src_buf[index + i], size - index - i); + rle[1] = bmp_ptr->src_buf[index + i]; + if (i + rle[0] >= (int32_t)bmp_ptr->src_pitch) { + rle[0] = uint8_t(bmp_ptr->src_pitch - i); + if (rle[0]) { + dst_buf[dst_pos++] = rle[0]; + dst_buf[dst_pos++] = rle[1]; + } + dst_buf[dst_pos++] = RLE_MARKER; + dst_buf[dst_pos++] = RLE_EOL; + i = 0; + row_num--; + } else { + i += rle[0]; + dst_buf[dst_pos++] = rle[0]; + dst_buf[dst_pos++] = rle[1]; + } + } + dst_buf[dst_pos++] = RLE_MARKER; + dst_buf[dst_pos++] = RLE_EOI; + dst_size = dst_pos; +} +static uint8_t bmp_rle4_search(const uint8_t* buf, int32_t len) { + uint8_t num; + num = 2; + while (num < len) { + if (buf[num - 2] != buf[num] || num == 0xFF) { + break; + } + num++; + } + return num; +} +static void bmp_encode_rle4(bmp_compress_struct_p bmp_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_size) { + FX_DWORD size, dst_pos, index; + uint8_t rle[2] = {0}; + size = bmp_ptr->src_pitch * bmp_ptr->src_row; + dst_pos = bmp_ptr->file_header.bfOffBits; + dst_size += size; + dst_buf = FX_Realloc(uint8_t, dst_buf, dst_size); + if (dst_buf == NULL) { + return; + } + FXSYS_memset(&dst_buf[dst_pos], 0, size); + for (int32_t row_num = bmp_ptr->src_row - 1, i = 0; row_num > -1; + rle[1] = 0) { + index = row_num * bmp_ptr->src_pitch; + rle[0] = bmp_rle4_search(&bmp_ptr->src_buf[index + i], size - index - i); + rle[1] |= (bmp_ptr->src_buf[index + i] & 0x0f) << 4; + rle[1] |= bmp_ptr->src_buf[index + i + 1] & 0x0f; + if (i + rle[0] >= (int32_t)bmp_ptr->src_pitch) { + rle[0] = uint8_t(bmp_ptr->src_pitch - i); + if (rle[0]) { + dst_buf[dst_pos++] = rle[0]; + dst_buf[dst_pos++] = rle[1]; + } + dst_buf[dst_pos++] = RLE_MARKER; + dst_buf[dst_pos++] = RLE_EOL; + i = 0; + row_num--; + } else { + i += rle[0]; + dst_buf[dst_pos++] = rle[0]; + dst_buf[dst_pos++] = rle[1]; + } + } + dst_buf[dst_pos++] = RLE_MARKER; + dst_buf[dst_pos++] = RLE_EOI; + dst_size = dst_pos; +} +FX_BOOL bmp_encode_image(bmp_compress_struct_p bmp_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_size) { + FX_DWORD head_size = sizeof(BmpFileHeader) + sizeof(BmpInfoHeader); + FX_DWORD pal_size = sizeof(FX_DWORD) * bmp_ptr->pal_num; + if (bmp_ptr->info_header.biClrUsed > 0 && + bmp_ptr->info_header.biClrUsed < bmp_ptr->pal_num) { + pal_size = sizeof(FX_DWORD) * bmp_ptr->info_header.biClrUsed; + } + dst_size = head_size + sizeof(FX_DWORD) * bmp_ptr->pal_num; + dst_buf = FX_TryAlloc(uint8_t, dst_size); + if (dst_buf == NULL) { + return FALSE; + } + FXSYS_memset(dst_buf, 0, dst_size); + bmp_ptr->file_header.bfOffBits = head_size; + if (bmp_ptr->pal_ptr && pal_size) { + FXSYS_memcpy(&dst_buf[head_size], bmp_ptr->pal_ptr, pal_size); + bmp_ptr->file_header.bfOffBits += pal_size; + } + WriteInfoHeader(&bmp_ptr->info_header, dst_buf); + switch (bmp_ptr->info_header.biCompression) { + case BMP_RGB: + bmp_encode_rgb(bmp_ptr, dst_buf, dst_size); + break; + case BMP_BITFIELDS: + bmp_encode_bitfields(bmp_ptr, dst_buf, dst_size); + break; + case BMP_RLE8: + bmp_encode_rle8(bmp_ptr, dst_buf, dst_size); + break; + case BMP_RLE4: + bmp_encode_rle4(bmp_ptr, dst_buf, dst_size); + break; + default: + break; + } + bmp_ptr->file_header.bfSize = dst_size; + WriteFileHeader(&bmp_ptr->file_header, dst_buf); + return TRUE; +} diff --git a/core/fxcodec/lbmp/fx_bmp.h b/core/fxcodec/lbmp/fx_bmp.h new file mode 100644 index 0000000000..2ef0c0a8b0 --- /dev/null +++ b/core/fxcodec/lbmp/fx_bmp.h @@ -0,0 +1,155 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_LBMP_FX_BMP_H_ +#define CORE_FXCODEC_LBMP_FX_BMP_H_ + +#include <setjmp.h> + +#include "core/include/fxcrt/fx_basic.h" + +#define BMP_WIDTHBYTES(width, bitCount) ((width * bitCount) + 31) / 32 * 4 +#define BMP_PAL_ENCODE(a, r, g, b) \ + (((FX_DWORD)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#define BMP_D_STATUS_HEADER 0x01 +#define BMP_D_STATUS_PAL 0x02 +#define BMP_D_STATUS_DATA_PRE 0x03 +#define BMP_D_STATUS_DATA 0x04 +#define BMP_D_STATUS_TAIL 0x00 +#define BMP_SIGNATURE 0x4D42 +#define BMP_PAL_NEW 0 +#define BMP_PAL_OLD 1 +#define RLE_MARKER 0 +#define RLE_EOL 0 +#define RLE_EOI 1 +#define RLE_DELTA 2 +#define BMP_RGB 0L +#define BMP_RLE8 1L +#define BMP_RLE4 2L +#define BMP_BITFIELDS 3L +#define BMP_BIT_555 0 +#define BMP_BIT_565 1 +#define BMP_MAX_ERROR_SIZE 256 +#pragma pack(1) +typedef struct tagBmpFileHeader { + FX_WORD bfType; + FX_DWORD bfSize; + FX_WORD bfReserved1; + FX_WORD bfReserved2; + FX_DWORD bfOffBits; +} BmpFileHeader, *BmpFileHeaderPtr; +typedef struct tagBmpCoreHeader { + FX_DWORD bcSize; + FX_WORD bcWidth; + FX_WORD bcHeight; + FX_WORD bcPlanes; + FX_WORD bcBitCount; +} BmpCoreHeader, *BmpCoreHeaderPtr; +typedef struct tagBmpInfoHeader { + FX_DWORD biSize; + int32_t biWidth; + int32_t biHeight; + FX_WORD biPlanes; + FX_WORD biBitCount; + FX_DWORD biCompression; + FX_DWORD biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + FX_DWORD biClrUsed; + FX_DWORD biClrImportant; +} BmpInfoHeader, *BmpInfoHeaderPtr; +#pragma pack() + +typedef struct tag_bmp_decompress_struct bmp_decompress_struct; +typedef bmp_decompress_struct* bmp_decompress_struct_p; +typedef bmp_decompress_struct_p* bmp_decompress_struct_pp; +struct tag_bmp_decompress_struct { + jmp_buf jmpbuf; + FX_CHAR* err_ptr; + void (*bmp_error_fn)(bmp_decompress_struct_p gif_ptr, const FX_CHAR* err_msg); + + void* context_ptr; + + BmpFileHeaderPtr bmp_header_ptr; + BmpInfoHeaderPtr bmp_infoheader_ptr; + int32_t width; + int32_t height; + FX_DWORD compress_flag; + int32_t components; + int32_t src_row_bytes; + int32_t out_row_bytes; + uint8_t* out_row_buffer; + FX_WORD bitCounts; + FX_DWORD color_used; + FX_BOOL imgTB_flag; + int32_t pal_num; + int32_t pal_type; + FX_DWORD* pal_ptr; + FX_DWORD data_size; + FX_DWORD img_data_offset; + FX_DWORD img_ifh_size; + int32_t row_num; + int32_t col_num; + int32_t dpi_x; + int32_t dpi_y; + FX_DWORD mask_red; + FX_DWORD mask_green; + FX_DWORD mask_blue; + + FX_BOOL (*bmp_get_data_position_fn)(bmp_decompress_struct_p bmp_ptr, + FX_DWORD cur_pos); + void (*bmp_get_row_fn)(bmp_decompress_struct_p bmp_ptr, + int32_t row_num, + uint8_t* row_buf); + uint8_t* next_in; + FX_DWORD avail_in; + FX_DWORD skip_size; + int32_t decode_status; +}; +void bmp_error(bmp_decompress_struct_p bmp_ptr, const FX_CHAR* err_msg); +bmp_decompress_struct_p bmp_create_decompress(); +void bmp_destroy_decompress(bmp_decompress_struct_pp bmp_ptr_ptr); +int32_t bmp_read_header(bmp_decompress_struct_p bmp_ptr); +int32_t bmp_decode_image(bmp_decompress_struct_p bmp_ptr); +int32_t bmp_decode_rgb(bmp_decompress_struct_p bmp_ptr); +int32_t bmp_decode_rle8(bmp_decompress_struct_p bmp_ptr); +int32_t bmp_decode_rle4(bmp_decompress_struct_p bmp_ptr); +uint8_t* bmp_read_data(bmp_decompress_struct_p bmp_ptr, + uint8_t** des_buf_pp, + FX_DWORD data_size); +void bmp_save_decoding_status(bmp_decompress_struct_p bmp_ptr, int32_t status); +void bmp_input_buffer(bmp_decompress_struct_p bmp_ptr, + uint8_t* src_buf, + FX_DWORD src_size); +FX_DWORD bmp_get_avail_input(bmp_decompress_struct_p bmp_ptr, + uint8_t** avial_buf_ptr); +typedef struct tag_bmp_compress_struct bmp_compress_struct; +typedef bmp_compress_struct* bmp_compress_struct_p; +typedef bmp_compress_struct_p* bmp_compress_struct_pp; +struct tag_bmp_compress_struct { + BmpFileHeader file_header; + BmpInfoHeader info_header; + uint8_t* src_buf; + FX_DWORD src_pitch; + FX_DWORD src_row; + uint8_t src_bpp; + FX_DWORD src_width; + FX_BOOL src_free; + FX_DWORD* pal_ptr; + FX_WORD pal_num; + uint8_t bit_type; +}; + +bmp_compress_struct_p bmp_create_compress(); +void bmp_destroy_compress(bmp_compress_struct_p bmp_ptr); +FX_BOOL bmp_encode_image(bmp_compress_struct_p bmp_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_size); + +FX_WORD GetWord_LSBFirst(uint8_t* p); +void SetWord_LSBFirst(uint8_t* p, FX_WORD v); + +#endif // CORE_FXCODEC_LBMP_FX_BMP_H_ diff --git a/core/fxcodec/lgif/fx_gif.cpp b/core/fxcodec/lgif/fx_gif.cpp new file mode 100644 index 0000000000..af447dc84b --- /dev/null +++ b/core/fxcodec/lgif/fx_gif.cpp @@ -0,0 +1,1229 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcodec/lgif/fx_gif.h" + +#include "core/fxcodec/lbmp/fx_bmp.h" + +void CGifLZWDecoder::Input(uint8_t* src_buf, FX_DWORD src_size) { + next_in = src_buf; + avail_in = src_size; +} +FX_DWORD CGifLZWDecoder::GetAvailInput() { + return avail_in; +} +void CGifLZWDecoder::InitTable(uint8_t code_len) { + code_size = code_len; + code_clear = 1 << code_size; + code_end = code_clear + 1; + bits_left = 0; + code_store = 0; + next_in = NULL; + avail_in = 0; + stack_size = 0; + code_first = 0; + ClearTable(); +} +void CGifLZWDecoder::ClearTable() { + code_size_cur = code_size + 1; + code_next = code_end + 1; + code_old = (FX_WORD)-1; + FXSYS_memset(code_table, 0, sizeof(tag_Table) * GIF_MAX_LZW_CODE); + FXSYS_memset(stack, 0, GIF_MAX_LZW_CODE); + for (FX_WORD i = 0; i < code_clear; i++) { + code_table[i].suffix = (uint8_t)i; + } +} +void CGifLZWDecoder::DecodeString(FX_WORD code) { + stack_size = 0; + while (TRUE) { + ASSERT(code <= code_next); + if (code < code_clear || code > code_next) { + break; + } + stack[GIF_MAX_LZW_CODE - 1 - stack_size++] = code_table[code].suffix; + code = code_table[code].prefix; + } + stack[GIF_MAX_LZW_CODE - 1 - stack_size++] = (uint8_t)code; + code_first = (uint8_t)code; +} +void CGifLZWDecoder::AddCode(FX_WORD prefix_code, uint8_t append_char) { + if (code_next == GIF_MAX_LZW_CODE) { + return; + } + code_table[code_next].prefix = prefix_code; + code_table[code_next].suffix = append_char; + if (++code_next < GIF_MAX_LZW_CODE) { + if (code_next >> code_size_cur) { + code_size_cur++; + } + } +} +int32_t CGifLZWDecoder::Decode(uint8_t* des_buf, FX_DWORD& des_size) { + if (des_size == 0) { + return 3; + } + FX_DWORD i = 0; + if (stack_size != 0) { + if (des_size < stack_size) { + FXSYS_memcpy(des_buf, &stack[GIF_MAX_LZW_CODE - stack_size], des_size); + stack_size -= (FX_WORD)des_size; + return 3; + } + FXSYS_memcpy(des_buf, &stack[GIF_MAX_LZW_CODE - stack_size], stack_size); + des_buf += stack_size; + i += stack_size; + stack_size = 0; + } + FX_WORD code = 0; + while (i <= des_size && (avail_in > 0 || bits_left >= code_size_cur)) { + if (code_size_cur > 12) { + if (err_msg_ptr) { + FXSYS_strncpy(err_msg_ptr, "Code Length Out Of Range", + GIF_MAX_ERROR_SIZE - 1); + } + return 0; + } + if (avail_in > 0) { + code_store |= (*next_in++) << bits_left; + avail_in--; + bits_left += 8; + } + while (bits_left >= code_size_cur) { + code = (FX_WORD)code_store & ((1 << code_size_cur) - 1); + code_store >>= code_size_cur; + bits_left -= code_size_cur; + if (code == code_clear) { + ClearTable(); + continue; + } else if (code == code_end) { + des_size = i; + return 1; + } else { + if (code_old != (FX_WORD)-1) { + if (code_next < GIF_MAX_LZW_CODE) { + if (code == code_next) { + AddCode(code_old, code_first); + DecodeString(code); + } else if (code > code_next) { + if (err_msg_ptr) { + FXSYS_strncpy(err_msg_ptr, "Decode Error, Out Of Range", + GIF_MAX_ERROR_SIZE - 1); + } + return 0; + } else { + DecodeString(code); + uint8_t append_char = stack[GIF_MAX_LZW_CODE - stack_size]; + AddCode(code_old, append_char); + } + } + } else { + DecodeString(code); + } + code_old = code; + if (i + stack_size > des_size) { + FXSYS_memcpy(des_buf, &stack[GIF_MAX_LZW_CODE - stack_size], + des_size - i); + stack_size -= (FX_WORD)(des_size - i); + return 3; + } + FXSYS_memcpy(des_buf, &stack[GIF_MAX_LZW_CODE - stack_size], + stack_size); + des_buf += stack_size; + i += stack_size; + stack_size = 0; + } + } + } + if (avail_in == 0) { + des_size = i; + return 2; + } + return 0; +} +static FX_BOOL gif_grow_buf(uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD size) { + if (dst_len < size) { + FX_DWORD len_org = dst_len; + while (dst_buf && dst_len < size) { + dst_len <<= 1; + dst_buf = FX_Realloc(uint8_t, dst_buf, dst_len); + } + if (dst_buf == NULL) { + dst_len = size; + dst_buf = FX_Realloc(uint8_t, dst_buf, dst_len); + if (dst_buf == NULL) { + return FALSE; + } + } + FXSYS_memset(dst_buf + len_org, 0, dst_len - len_org); + return dst_buf != NULL; + } + return TRUE; +} +static inline void gif_cut_index(uint8_t& val, + FX_DWORD index, + uint8_t index_bit, + uint8_t index_bit_use, + uint8_t bit_use) { + FX_DWORD cut = ((1 << (index_bit - index_bit_use)) - 1) << index_bit_use; + val |= ((index & cut) >> index_bit_use) << bit_use; +} +static inline uint8_t gif_cut_buf(const uint8_t* buf, + FX_DWORD& offset, + uint8_t bit_cut, + uint8_t& bit_offset, + FX_DWORD& bit_num) { + if (bit_cut != 8) { + FX_WORD index = 0; + index |= ((1 << bit_cut) - 1) << (7 - bit_offset); + uint8_t ret = ((index & buf[offset]) >> (7 - bit_offset)); + bit_offset += bit_cut; + if (bit_offset >= 8) { + if (bit_offset > 8) { + ret |= ((index & (buf[offset + 1] << 8)) >> 8); + } + bit_offset -= 8; + offset++; + } + bit_num += bit_cut; + return ret; + } + bit_num += bit_cut; + return buf[offset++]; +} +CGifLZWEncoder::CGifLZWEncoder() { + FXSYS_memset(this, 0, sizeof(CGifLZWEncoder)); +} +CGifLZWEncoder::~CGifLZWEncoder() {} +void CGifLZWEncoder::ClearTable() { + index_bit_cur = code_size + 1; + index_num = code_end + 1; + table_cur = code_end + 1; + for (FX_WORD i = 0; i < GIF_MAX_LZW_CODE; i++) { + code_table[i].prefix = 0; + code_table[i].suffix = 0; + } +} +void CGifLZWEncoder::Start(uint8_t code_len, + const uint8_t* src_buf, + uint8_t*& dst_buf, + FX_DWORD& offset) { + code_size = code_len + 1; + src_bit_cut = code_size; + if (code_len == 0) { + src_bit_cut = 1; + code_size = 2; + } + code_clear = 1 << code_size; + code_end = code_clear + 1; + dst_buf[offset++] = code_size; + bit_offset = 0; + ClearTable(); + src_offset = 0; + src_bit_offset = 0; + src_bit_num = 0; + code_table[index_num].prefix = gif_cut_buf(src_buf, src_offset, src_bit_cut, + src_bit_offset, src_bit_num); + code_table[index_num].suffix = gif_cut_buf(src_buf, src_offset, src_bit_cut, + src_bit_offset, src_bit_num); +} +void CGifLZWEncoder::WriteBlock(uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& offset) { + if (!gif_grow_buf(dst_buf, dst_len, offset + GIF_DATA_BLOCK + 1)) { + longjmp(jmp, 1); + } + dst_buf[offset++] = index_buf_len; + FXSYS_memcpy(&dst_buf[offset], index_buf, index_buf_len); + offset += index_buf_len; + FXSYS_memset(index_buf, 0, GIF_DATA_BLOCK); + index_buf_len = 0; +} +void CGifLZWEncoder::EncodeString(FX_DWORD index, + uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& offset) { + uint8_t index_bit_use; + index_bit_use = 0; + if (index_buf_len == GIF_DATA_BLOCK) { + WriteBlock(dst_buf, dst_len, offset); + } + gif_cut_index(index_buf[index_buf_len], index, index_bit_cur, index_bit_use, + bit_offset); + if (index_bit_cur <= (8 - bit_offset)) { + bit_offset += index_bit_cur; + } else if (index_bit_cur <= (16 - bit_offset)) { + index_bit_use += (8 - bit_offset); + bit_offset = 0; + index_buf_len++; + if (index_buf_len == GIF_DATA_BLOCK) { + WriteBlock(dst_buf, dst_len, offset); + } + gif_cut_index(index_buf[index_buf_len], index, index_bit_cur, index_bit_use, + bit_offset); + bit_offset = index_bit_cur - index_bit_use; + } else { + index_bit_use += (8 - bit_offset); + bit_offset = 0; + index_buf_len++; + if (index_buf_len == GIF_DATA_BLOCK) { + WriteBlock(dst_buf, dst_len, offset); + } + gif_cut_index(index_buf[index_buf_len], index, index_bit_cur, index_bit_use, + bit_offset); + index_bit_use += 8; + bit_offset = 0; + index_buf_len++; + if (index_buf_len == GIF_DATA_BLOCK) { + WriteBlock(dst_buf, dst_len, offset); + } + gif_cut_index(index_buf[index_buf_len], index, index_bit_cur, index_bit_use, + bit_offset); + bit_offset = index_bit_cur - index_bit_use; + } + if (bit_offset == 8) { + bit_offset = 0; + index_buf_len++; + if (index_buf_len == GIF_DATA_BLOCK) { + WriteBlock(dst_buf, dst_len, offset); + } + } + if (index == code_end) { + index_buf_len++; + WriteBlock(dst_buf, dst_len, offset); + } + if (index_num++ >> index_bit_cur) { + index_bit_cur++; + } +} +FX_BOOL CGifLZWEncoder::Encode(const uint8_t* src_buf, + FX_DWORD src_len, + uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& offset) { + uint8_t suffix; + if (setjmp(jmp)) { + return FALSE; + } + while (src_bit_num < src_len) { + if (!LookUpInTable(src_buf, src_offset, src_bit_offset)) { + EncodeString(code_table[index_num].prefix, dst_buf, dst_len, offset); + if (index_num == GIF_MAX_LZW_CODE) { + suffix = code_table[index_num - 1].suffix; + EncodeString(code_clear, dst_buf, dst_len, offset); + ClearTable(); + code_table[index_num].prefix = suffix; + code_table[index_num].suffix = gif_cut_buf( + src_buf, src_offset, src_bit_cut, src_bit_offset, src_bit_num); + } else { + code_table[index_num].prefix = code_table[index_num - 1].suffix; + code_table[index_num].suffix = gif_cut_buf( + src_buf, src_offset, src_bit_cut, src_bit_offset, src_bit_num); + } + } + } + src_offset = 0; + src_bit_offset = 0; + src_bit_num = 0; + return TRUE; +} +FX_BOOL CGifLZWEncoder::LookUpInTable(const uint8_t* buf, + FX_DWORD& offset, + uint8_t& bit_offset) { + for (FX_WORD i = table_cur; i < index_num; i++) { + if (code_table[i].prefix == code_table[index_num].prefix && + code_table[i].suffix == code_table[index_num].suffix) { + code_table[index_num].prefix = i; + code_table[index_num].suffix = + gif_cut_buf(buf, offset, src_bit_cut, bit_offset, src_bit_num); + table_cur = i; + return TRUE; + } + } + table_cur = code_end + 1; + return FALSE; +} +void CGifLZWEncoder::Finish(uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& offset) { + EncodeString(code_table[index_num].prefix, dst_buf, dst_len, offset); + EncodeString(code_end, dst_buf, dst_len, offset); + bit_offset = 0; + ClearTable(); +} +gif_decompress_struct_p gif_create_decompress() { + gif_decompress_struct_p gif_ptr = + (gif_decompress_struct*)FX_Alloc(uint8_t, sizeof(gif_decompress_struct)); + if (gif_ptr == NULL) { + return NULL; + } + FXSYS_memset(gif_ptr, 0, sizeof(gif_decompress_struct)); + gif_ptr->decode_status = GIF_D_STATUS_SIG; + gif_ptr->img_ptr_arr_ptr = new CFX_ArrayTemplate<GifImage*>; + gif_ptr->cmt_data_ptr = new CFX_ByteString; + gif_ptr->pt_ptr_arr_ptr = new CFX_ArrayTemplate<GifPlainText*>; + return gif_ptr; +} +void gif_destroy_decompress(gif_decompress_struct_pp gif_ptr_ptr) { + if (gif_ptr_ptr == NULL || *gif_ptr_ptr == NULL) { + return; + } + gif_decompress_struct_p gif_ptr = *gif_ptr_ptr; + *gif_ptr_ptr = NULL; + FX_Free(gif_ptr->global_pal_ptr); + delete gif_ptr->img_decoder_ptr; + if (gif_ptr->img_ptr_arr_ptr) { + int32_t size_img_arr = gif_ptr->img_ptr_arr_ptr->GetSize(); + for (int32_t i = 0; i < size_img_arr; i++) { + GifImage* p = gif_ptr->img_ptr_arr_ptr->GetAt(i); + FX_Free(p->image_info_ptr); + FX_Free(p->image_gce_ptr); + FX_Free(p->image_row_buf); + if (p->local_pal_ptr && p->local_pal_ptr != gif_ptr->global_pal_ptr) { + FX_Free(p->local_pal_ptr); + } + FX_Free(p); + } + gif_ptr->img_ptr_arr_ptr->RemoveAll(); + delete gif_ptr->img_ptr_arr_ptr; + } + delete gif_ptr->cmt_data_ptr; + FX_Free(gif_ptr->gce_ptr); + if (gif_ptr->pt_ptr_arr_ptr) { + int32_t size_pt_arr = gif_ptr->pt_ptr_arr_ptr->GetSize(); + for (int32_t i = 0; i < size_pt_arr; i++) { + GifPlainText* p = gif_ptr->pt_ptr_arr_ptr->GetAt(i); + FX_Free(p->gce_ptr); + FX_Free(p->pte_ptr); + delete p->string_ptr; + } + gif_ptr->pt_ptr_arr_ptr->RemoveAll(); + delete gif_ptr->pt_ptr_arr_ptr; + } + FX_Free(gif_ptr); +} +gif_compress_struct_p gif_create_compress() { + gif_compress_struct_p gif_ptr = + (gif_compress_struct*)FX_Alloc(uint8_t, sizeof(gif_compress_struct)); + if (gif_ptr == NULL) { + return NULL; + } + FXSYS_memset(gif_ptr, 0, sizeof(gif_compress_struct)); + gif_ptr->img_encoder_ptr = new CGifLZWEncoder; + gif_ptr->header_ptr = (GifHeader*)FX_Alloc(uint8_t, sizeof(GifHeader)); + if (gif_ptr->header_ptr == NULL) { + delete (gif_ptr->img_encoder_ptr); + FX_Free(gif_ptr); + return NULL; + } + FXSYS_memcpy(gif_ptr->header_ptr->signature, GIF_SIGNATURE, 3); + FXSYS_memcpy(gif_ptr->header_ptr->version, "89a", 3); + gif_ptr->lsd_ptr = (GifLSD*)FX_Alloc(uint8_t, sizeof(GifLSD)); + if (gif_ptr->lsd_ptr == NULL) { + FX_Free(gif_ptr->header_ptr); + delete (gif_ptr->img_encoder_ptr); + FX_Free(gif_ptr); + return NULL; + } + FXSYS_memset(gif_ptr->lsd_ptr, 0, sizeof(GifLSD)); + gif_ptr->image_info_ptr = + (GifImageInfo*)FX_Alloc(uint8_t, sizeof(GifImageInfo)); + if (gif_ptr->image_info_ptr == NULL) { + FX_Free(gif_ptr->lsd_ptr); + FX_Free(gif_ptr->header_ptr); + delete (gif_ptr->img_encoder_ptr); + FX_Free(gif_ptr); + return NULL; + } + FXSYS_memset(gif_ptr->image_info_ptr, 0, sizeof(GifImageInfo)); + gif_ptr->gce_ptr = (GifGCE*)FX_Alloc(uint8_t, sizeof(GifGCE)); + if (gif_ptr->gce_ptr == NULL) { + FX_Free(gif_ptr->image_info_ptr); + FX_Free(gif_ptr->lsd_ptr); + FX_Free(gif_ptr->header_ptr); + delete (gif_ptr->img_encoder_ptr); + FX_Free(gif_ptr); + return NULL; + } + gif_ptr->pte_ptr = (GifPTE*)FX_Alloc(uint8_t, sizeof(GifPTE)); + if (gif_ptr->pte_ptr == NULL) { + FX_Free(gif_ptr->gce_ptr); + FX_Free(gif_ptr->image_info_ptr); + FX_Free(gif_ptr->lsd_ptr); + FX_Free(gif_ptr->header_ptr); + delete (gif_ptr->img_encoder_ptr); + FX_Free(gif_ptr); + return NULL; + } + FXSYS_memset(gif_ptr->pte_ptr, 0, sizeof(GifPTE)); + gif_ptr->pte_ptr->block_size = 12; + return gif_ptr; +} +void gif_destroy_compress(gif_compress_struct_pp gif_ptr_ptr) { + if (gif_ptr_ptr == NULL || *gif_ptr_ptr == NULL) { + return; + } + gif_compress_struct_p gif_ptr = *gif_ptr_ptr; + *gif_ptr_ptr = NULL; + FX_Free(gif_ptr->header_ptr); + FX_Free(gif_ptr->lsd_ptr); + FX_Free(gif_ptr->global_pal); + FX_Free(gif_ptr->image_info_ptr); + FX_Free(gif_ptr->local_pal); + delete gif_ptr->img_encoder_ptr; + FX_Free(gif_ptr->gce_ptr); + FX_Free(gif_ptr->cmt_data_ptr); + FX_Free(gif_ptr->pte_ptr); + FX_Free(gif_ptr); +} +void gif_error(gif_decompress_struct_p gif_ptr, const FX_CHAR* err_msg) { + if (gif_ptr && gif_ptr->gif_error_fn) { + gif_ptr->gif_error_fn(gif_ptr, err_msg); + } +} +void gif_warn(gif_decompress_struct_p gif_ptr, const FX_CHAR* err_msg) {} +int32_t gif_read_header(gif_decompress_struct_p gif_ptr) { + if (gif_ptr == NULL) { + return 0; + } + FX_DWORD skip_size_org = gif_ptr->skip_size; + ASSERT(sizeof(GifHeader) == 6); + GifHeader* gif_header_ptr = NULL; + if (gif_read_data(gif_ptr, (uint8_t**)&gif_header_ptr, 6) == NULL) { + return 2; + } + if (FXSYS_strncmp(gif_header_ptr->signature, GIF_SIGNATURE, 3) != 0 || + gif_header_ptr->version[0] != '8' || gif_header_ptr->version[2] != 'a') { + gif_error(gif_ptr, "Not A Gif Image"); + return 0; + } + ASSERT(sizeof(GifLSD) == 7); + GifLSD* gif_lsd_ptr = NULL; + if (gif_read_data(gif_ptr, (uint8_t**)&gif_lsd_ptr, 7) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + if (((GifGF*)&gif_lsd_ptr->global_flag)->global_pal) { + gif_ptr->global_pal_num = 2 + << ((GifGF*)&gif_lsd_ptr->global_flag)->pal_bits; + ASSERT(sizeof(GifPalette) == 3); + int32_t global_pal_size = gif_ptr->global_pal_num * 3; + uint8_t* global_pal_ptr = NULL; + if (gif_read_data(gif_ptr, &global_pal_ptr, global_pal_size) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + gif_ptr->global_sort_flag = ((GifGF*)&gif_lsd_ptr->global_flag)->sort_flag; + gif_ptr->global_color_resolution = + ((GifGF*)&gif_lsd_ptr->global_flag)->color_resolution; + FX_Free(gif_ptr->global_pal_ptr); + gif_ptr->global_pal_ptr = (GifPalette*)FX_Alloc(uint8_t, global_pal_size); + FXSYS_memcpy(gif_ptr->global_pal_ptr, global_pal_ptr, global_pal_size); + } + gif_ptr->width = (int)GetWord_LSBFirst((uint8_t*)&gif_lsd_ptr->width); + gif_ptr->height = (int)GetWord_LSBFirst((uint8_t*)&gif_lsd_ptr->height); + gif_ptr->bc_index = gif_lsd_ptr->bc_index; + gif_ptr->pixel_aspect = gif_lsd_ptr->pixel_aspect; + return 1; +} +int32_t gif_get_frame(gif_decompress_struct_p gif_ptr) { + if (gif_ptr == NULL) { + return 0; + } + int32_t ret = 1; + while (TRUE) { + switch (gif_ptr->decode_status) { + case GIF_D_STATUS_TAIL: + return 1; + case GIF_D_STATUS_SIG: { + uint8_t* sig_ptr = NULL; + if (gif_read_data(gif_ptr, &sig_ptr, 1) == NULL) { + return 2; + } + switch (*sig_ptr) { + case GIF_SIG_EXTENSION: + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_EXT); + continue; + case GIF_SIG_IMAGE: + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_IMG_INFO); + continue; + case GIF_SIG_TRAILER: + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_TAIL); + return 1; + default: + if (gif_ptr->avail_in) { + gif_warn(gif_ptr, "The Gif File has non_standard Tag!"); + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_SIG); + continue; + } + gif_warn(gif_ptr, "The Gif File Doesn't have Trailer Tag!"); + return 1; + } + } + case GIF_D_STATUS_EXT: { + uint8_t* ext_ptr = NULL; + if (gif_read_data(gif_ptr, &ext_ptr, 1) == NULL) { + return 2; + } + switch (*ext_ptr) { + case GIF_BLOCK_CE: + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_EXT_CE); + continue; + case GIF_BLOCK_GCE: + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_EXT_GCE); + continue; + case GIF_BLOCK_PTE: + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_EXT_PTE); + continue; + default: { + int32_t status = GIF_D_STATUS_EXT_UNE; + if (*ext_ptr == GIF_BLOCK_PTE) { + status = GIF_D_STATUS_EXT_PTE; + } + gif_save_decoding_status(gif_ptr, status); + continue; + } + } + } + case GIF_D_STATUS_IMG_INFO: { + ret = gif_decode_image_info(gif_ptr); + if (ret != 1) { + return ret; + } + continue; + } + case GIF_D_STATUS_IMG_DATA: { + uint8_t* data_size_ptr = NULL; + uint8_t* data_ptr = NULL; + FX_DWORD skip_size_org = gif_ptr->skip_size; + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + return 2; + } + while (*data_size_ptr != GIF_BLOCK_TERMINAL) { + if (gif_read_data(gif_ptr, &data_ptr, *data_size_ptr) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_IMG_DATA); + skip_size_org = gif_ptr->skip_size; + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + return 2; + } + } + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_SIG); + continue; + } + default: { + ret = gif_decode_extension(gif_ptr); + if (ret != 1) { + return ret; + } + continue; + } + } + } + return 1; +} +void gif_takeover_gce_ptr(gif_decompress_struct_p gif_ptr, + GifGCE** gce_ptr_ptr) { + *gce_ptr_ptr = NULL; + if (gif_ptr->gce_ptr && gce_ptr_ptr) { + *gce_ptr_ptr = gif_ptr->gce_ptr; + gif_ptr->gce_ptr = NULL; + } +} +int32_t gif_decode_extension(gif_decompress_struct_p gif_ptr) { + uint8_t* data_size_ptr = NULL; + uint8_t* data_ptr = NULL; + FX_DWORD skip_size_org = gif_ptr->skip_size; + switch (gif_ptr->decode_status) { + case GIF_D_STATUS_EXT_CE: { + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + gif_ptr->cmt_data_ptr->Empty(); + while (*data_size_ptr != GIF_BLOCK_TERMINAL) { + uint8_t data_size = *data_size_ptr; + if (gif_read_data(gif_ptr, &data_ptr, *data_size_ptr) == NULL || + gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + *(gif_ptr->cmt_data_ptr) += + CFX_ByteString((const FX_CHAR*)data_ptr, data_size); + } + } break; + case GIF_D_STATUS_EXT_PTE: { + ASSERT(sizeof(GifPTE) == 13); + GifPTE* gif_pte_ptr = NULL; + if (gif_read_data(gif_ptr, (uint8_t**)&gif_pte_ptr, 13) == NULL) { + return 2; + } + GifPlainText* gif_pt_ptr = FX_Alloc(GifPlainText, 1); + FXSYS_memset(gif_pt_ptr, 0, sizeof(GifPlainText)); + gif_takeover_gce_ptr(gif_ptr, &gif_pt_ptr->gce_ptr); + gif_pt_ptr->pte_ptr = (GifPTE*)FX_Alloc(uint8_t, sizeof(GifPTE)); + gif_pt_ptr->string_ptr = new CFX_ByteString; + gif_pt_ptr->pte_ptr->block_size = gif_pte_ptr->block_size; + gif_pt_ptr->pte_ptr->grid_left = + GetWord_LSBFirst((uint8_t*)&gif_pte_ptr->grid_left); + gif_pt_ptr->pte_ptr->grid_top = + GetWord_LSBFirst((uint8_t*)&gif_pte_ptr->grid_top); + gif_pt_ptr->pte_ptr->grid_width = + GetWord_LSBFirst((uint8_t*)&gif_pte_ptr->grid_width); + gif_pt_ptr->pte_ptr->grid_height = + GetWord_LSBFirst((uint8_t*)&gif_pte_ptr->grid_height); + gif_pt_ptr->pte_ptr->char_width = gif_pte_ptr->char_width; + gif_pt_ptr->pte_ptr->char_height = gif_pte_ptr->char_height; + gif_pt_ptr->pte_ptr->fc_index = gif_pte_ptr->fc_index; + gif_pt_ptr->pte_ptr->bc_index = gif_pte_ptr->bc_index; + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + gif_ptr->skip_size = skip_size_org; + if (gif_pt_ptr) { + FX_Free(gif_pt_ptr->gce_ptr); + FX_Free(gif_pt_ptr->pte_ptr); + delete gif_pt_ptr->string_ptr; + FX_Free(gif_pt_ptr); + } + return 2; + } + while (*data_size_ptr != GIF_BLOCK_TERMINAL) { + uint8_t data_size = *data_size_ptr; + if (gif_read_data(gif_ptr, &data_ptr, *data_size_ptr) == NULL || + gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + gif_ptr->skip_size = skip_size_org; + if (gif_pt_ptr) { + FX_Free(gif_pt_ptr->gce_ptr); + FX_Free(gif_pt_ptr->pte_ptr); + delete gif_pt_ptr->string_ptr; + FX_Free(gif_pt_ptr); + } + return 2; + } + *(gif_pt_ptr->string_ptr) += + CFX_ByteString((const FX_CHAR*)data_ptr, data_size); + } + gif_ptr->pt_ptr_arr_ptr->Add(gif_pt_ptr); + } break; + case GIF_D_STATUS_EXT_GCE: { + ASSERT(sizeof(GifGCE) == 5); + GifGCE* gif_gce_ptr = NULL; + if (gif_read_data(gif_ptr, (uint8_t**)&gif_gce_ptr, 6) == NULL) { + return 2; + } + if (gif_ptr->gce_ptr == NULL) { + gif_ptr->gce_ptr = (GifGCE*)FX_Alloc(uint8_t, sizeof(GifGCE)); + } + gif_ptr->gce_ptr->block_size = gif_gce_ptr->block_size; + gif_ptr->gce_ptr->gce_flag = gif_gce_ptr->gce_flag; + gif_ptr->gce_ptr->delay_time = + GetWord_LSBFirst((uint8_t*)&gif_gce_ptr->delay_time); + gif_ptr->gce_ptr->trans_index = gif_gce_ptr->trans_index; + } break; + default: { + if (gif_ptr->decode_status == GIF_D_STATUS_EXT_PTE) { + FX_Free(gif_ptr->gce_ptr); + gif_ptr->gce_ptr = NULL; + } + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + return 2; + } + while (*data_size_ptr != GIF_BLOCK_TERMINAL) { + if (gif_read_data(gif_ptr, &data_ptr, *data_size_ptr) == NULL || + gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + } + } + } + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_SIG); + return 1; +} +int32_t gif_decode_image_info(gif_decompress_struct_p gif_ptr) { + if (gif_ptr->width == 0 || gif_ptr->height == 0) { + gif_error(gif_ptr, "No Image Header Info"); + return 0; + } + FX_DWORD skip_size_org = gif_ptr->skip_size; + ASSERT(sizeof(GifImageInfo) == 9); + GifImageInfo* gif_img_info_ptr = NULL; + if (gif_read_data(gif_ptr, (uint8_t**)&gif_img_info_ptr, 9) == NULL) { + return 2; + } + GifImage* gif_image_ptr = (GifImage*)FX_Alloc(uint8_t, sizeof(GifImage)); + FXSYS_memset(gif_image_ptr, 0, sizeof(GifImage)); + gif_image_ptr->image_info_ptr = + (GifImageInfo*)FX_Alloc(uint8_t, sizeof(GifImageInfo)); + gif_image_ptr->image_info_ptr->left = + GetWord_LSBFirst((uint8_t*)&gif_img_info_ptr->left); + gif_image_ptr->image_info_ptr->top = + GetWord_LSBFirst((uint8_t*)&gif_img_info_ptr->top); + gif_image_ptr->image_info_ptr->width = + GetWord_LSBFirst((uint8_t*)&gif_img_info_ptr->width); + gif_image_ptr->image_info_ptr->height = + GetWord_LSBFirst((uint8_t*)&gif_img_info_ptr->height); + gif_image_ptr->image_info_ptr->local_flag = gif_img_info_ptr->local_flag; + if (gif_image_ptr->image_info_ptr->left + + gif_image_ptr->image_info_ptr->width > + gif_ptr->width || + gif_image_ptr->image_info_ptr->top + + gif_image_ptr->image_info_ptr->height > + gif_ptr->height) { + FX_Free(gif_image_ptr->image_info_ptr); + FX_Free(gif_image_ptr->image_row_buf); + FX_Free(gif_image_ptr); + gif_error(gif_ptr, "Image Data Out Of LSD, The File May Be Corrupt"); + return 0; + } + GifLF* gif_img_info_lf_ptr = (GifLF*)&gif_img_info_ptr->local_flag; + if (gif_img_info_lf_ptr->local_pal) { + ASSERT(sizeof(GifPalette) == 3); + int32_t loc_pal_size = (2 << gif_img_info_lf_ptr->pal_bits) * 3; + uint8_t* loc_pal_ptr = NULL; + if (gif_read_data(gif_ptr, &loc_pal_ptr, loc_pal_size) == NULL) { + gif_ptr->skip_size = skip_size_org; + FX_Free(gif_image_ptr->image_info_ptr); + FX_Free(gif_image_ptr->image_row_buf); + FX_Free(gif_image_ptr); + return 2; + } + gif_image_ptr->local_pal_ptr = + (GifPalette*)gif_ptr->gif_ask_buf_for_pal_fn(gif_ptr, loc_pal_size); + if (gif_image_ptr->local_pal_ptr) { + FXSYS_memcpy((uint8_t*)gif_image_ptr->local_pal_ptr, loc_pal_ptr, + loc_pal_size); + } + } + uint8_t* code_size_ptr = NULL; + if (gif_read_data(gif_ptr, &code_size_ptr, 1) == NULL) { + gif_ptr->skip_size = skip_size_org; + FX_Free(gif_image_ptr->image_info_ptr); + FX_Free(gif_image_ptr->local_pal_ptr); + FX_Free(gif_image_ptr->image_row_buf); + FX_Free(gif_image_ptr); + return 2; + } + gif_image_ptr->image_code_size = *code_size_ptr; + gif_ptr->gif_record_current_position_fn(gif_ptr, + &gif_image_ptr->image_data_pos); + gif_image_ptr->image_data_pos += gif_ptr->skip_size; + gif_takeover_gce_ptr(gif_ptr, &gif_image_ptr->image_gce_ptr); + gif_ptr->img_ptr_arr_ptr->Add(gif_image_ptr); + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_IMG_DATA); + return 1; +} +int32_t gif_load_frame(gif_decompress_struct_p gif_ptr, int32_t frame_num) { + if (gif_ptr == NULL || frame_num < 0 || + frame_num >= gif_ptr->img_ptr_arr_ptr->GetSize()) { + return 0; + } + uint8_t* data_size_ptr = NULL; + uint8_t* data_ptr = NULL; + FX_DWORD skip_size_org = gif_ptr->skip_size; + GifImage* gif_image_ptr = gif_ptr->img_ptr_arr_ptr->GetAt(frame_num); + FX_DWORD gif_img_row_bytes = gif_image_ptr->image_info_ptr->width; + if (gif_ptr->decode_status == GIF_D_STATUS_TAIL) { + if (gif_image_ptr->image_row_buf) { + FX_Free(gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_buf = NULL; + } + gif_image_ptr->image_row_buf = FX_Alloc(uint8_t, gif_img_row_bytes); + GifGCE* gif_img_gce_ptr = gif_image_ptr->image_gce_ptr; + int32_t loc_pal_num = + ((GifLF*)&gif_image_ptr->image_info_ptr->local_flag)->local_pal + ? (2 << ((GifLF*)&gif_image_ptr->image_info_ptr->local_flag) + ->pal_bits) + : 0; + gif_ptr->avail_in = 0; + if (gif_img_gce_ptr == NULL) { + FX_BOOL bRes = gif_ptr->gif_get_record_position_fn( + gif_ptr, gif_image_ptr->image_data_pos, + gif_image_ptr->image_info_ptr->left, + gif_image_ptr->image_info_ptr->top, + gif_image_ptr->image_info_ptr->width, + gif_image_ptr->image_info_ptr->height, loc_pal_num, + gif_image_ptr->local_pal_ptr, 0, 0, -1, 0, + (FX_BOOL)((GifLF*)&gif_image_ptr->image_info_ptr->local_flag) + ->interlace); + if (!bRes) { + FX_Free(gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_buf = NULL; + gif_error(gif_ptr, "Error Read Record Position Data"); + return 0; + } + } else { + FX_BOOL bRes = gif_ptr->gif_get_record_position_fn( + gif_ptr, gif_image_ptr->image_data_pos, + gif_image_ptr->image_info_ptr->left, + gif_image_ptr->image_info_ptr->top, + gif_image_ptr->image_info_ptr->width, + gif_image_ptr->image_info_ptr->height, loc_pal_num, + gif_image_ptr->local_pal_ptr, + (int32_t)gif_image_ptr->image_gce_ptr->delay_time, + (FX_BOOL)((GifCEF*)&gif_image_ptr->image_gce_ptr->gce_flag) + ->user_input, + ((GifCEF*)&gif_image_ptr->image_gce_ptr->gce_flag)->transparency + ? (int32_t)gif_image_ptr->image_gce_ptr->trans_index + : -1, + (int32_t)((GifCEF*)&gif_image_ptr->image_gce_ptr->gce_flag) + ->disposal_method, + (FX_BOOL)((GifLF*)&gif_image_ptr->image_info_ptr->local_flag) + ->interlace); + if (!bRes) { + FX_Free(gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_buf = NULL; + gif_error(gif_ptr, "Error Read Record Position Data"); + return 0; + } + } + if (gif_ptr->img_decoder_ptr == NULL) { + gif_ptr->img_decoder_ptr = new CGifLZWDecoder(gif_ptr->err_ptr); + } + gif_ptr->img_decoder_ptr->InitTable(gif_image_ptr->image_code_size); + gif_ptr->img_row_offset = 0; + gif_ptr->img_row_avail_size = 0; + gif_ptr->img_pass_num = 0; + gif_image_ptr->image_row_num = 0; + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_IMG_DATA); + } + CGifLZWDecoder* img_decoder_ptr = gif_ptr->img_decoder_ptr; + if (gif_ptr->decode_status == GIF_D_STATUS_IMG_DATA) { + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + return 2; + } + if (*data_size_ptr != GIF_BLOCK_TERMINAL) { + if (gif_read_data(gif_ptr, &data_ptr, *data_size_ptr) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + img_decoder_ptr->Input(data_ptr, *data_size_ptr); + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_IMG_DATA); + gif_ptr->img_row_offset += gif_ptr->img_row_avail_size; + gif_ptr->img_row_avail_size = gif_img_row_bytes - gif_ptr->img_row_offset; + int32_t ret = img_decoder_ptr->Decode( + gif_image_ptr->image_row_buf + gif_ptr->img_row_offset, + gif_ptr->img_row_avail_size); + if (ret == 0) { + FX_Free(gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_buf = NULL; + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_TAIL); + gif_error(gif_ptr, "Decode Image Data Error"); + return 0; + } + while (ret != 0) { + if (ret == 1) { + gif_ptr->gif_get_row_fn(gif_ptr, gif_image_ptr->image_row_num, + gif_image_ptr->image_row_buf); + FX_Free(gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_buf = NULL; + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_TAIL); + return 1; + } + if (ret == 2) { + ASSERT(img_decoder_ptr->GetAvailInput() == 0); + skip_size_org = gif_ptr->skip_size; + if (gif_read_data(gif_ptr, &data_size_ptr, 1) == NULL) { + return 2; + } + if (*data_size_ptr != GIF_BLOCK_TERMINAL) { + if (gif_read_data(gif_ptr, &data_ptr, *data_size_ptr) == NULL) { + gif_ptr->skip_size = skip_size_org; + return 2; + } + img_decoder_ptr->Input(data_ptr, *data_size_ptr); + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_IMG_DATA); + gif_ptr->img_row_offset += gif_ptr->img_row_avail_size; + gif_ptr->img_row_avail_size = + gif_img_row_bytes - gif_ptr->img_row_offset; + ret = img_decoder_ptr->Decode( + gif_image_ptr->image_row_buf + gif_ptr->img_row_offset, + gif_ptr->img_row_avail_size); + } + } + if (ret == 3) { + if (((GifLF*)&gif_image_ptr->image_info_ptr->local_flag)->interlace) { + gif_ptr->gif_get_row_fn(gif_ptr, gif_image_ptr->image_row_num, + gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_num += + s_gif_interlace_step[gif_ptr->img_pass_num]; + if (gif_image_ptr->image_row_num >= + (int32_t)gif_image_ptr->image_info_ptr->height) { + gif_ptr->img_pass_num++; + gif_image_ptr->image_row_num = + s_gif_interlace_step[gif_ptr->img_pass_num] / 2; + } + } else { + gif_ptr->gif_get_row_fn(gif_ptr, gif_image_ptr->image_row_num++, + gif_image_ptr->image_row_buf); + } + gif_ptr->img_row_offset = 0; + gif_ptr->img_row_avail_size = gif_img_row_bytes; + ret = img_decoder_ptr->Decode( + gif_image_ptr->image_row_buf + gif_ptr->img_row_offset, + gif_ptr->img_row_avail_size); + } + if (ret == 0) { + FX_Free(gif_image_ptr->image_row_buf); + gif_image_ptr->image_row_buf = NULL; + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_TAIL); + gif_error(gif_ptr, "Decode Image Data Error"); + return 0; + } + } + } + gif_save_decoding_status(gif_ptr, GIF_D_STATUS_TAIL); + } + gif_error(gif_ptr, "Decode Image Data Error"); + return 0; +} +void gif_save_decoding_status(gif_decompress_struct_p gif_ptr, int32_t status) { + gif_ptr->decode_status = status; + gif_ptr->next_in += gif_ptr->skip_size; + gif_ptr->avail_in -= gif_ptr->skip_size; + gif_ptr->skip_size = 0; +} +uint8_t* gif_read_data(gif_decompress_struct_p gif_ptr, + uint8_t** des_buf_pp, + FX_DWORD data_size) { + if (gif_ptr == NULL || gif_ptr->avail_in < gif_ptr->skip_size + data_size) { + return NULL; + } + *des_buf_pp = gif_ptr->next_in + gif_ptr->skip_size; + gif_ptr->skip_size += data_size; + return *des_buf_pp; +} +void gif_input_buffer(gif_decompress_struct_p gif_ptr, + uint8_t* src_buf, + FX_DWORD src_size) { + gif_ptr->next_in = src_buf; + gif_ptr->avail_in = src_size; + gif_ptr->skip_size = 0; +} +FX_DWORD gif_get_avail_input(gif_decompress_struct_p gif_ptr, + uint8_t** avial_buf_ptr) { + if (avial_buf_ptr) { + *avial_buf_ptr = NULL; + if (gif_ptr->avail_in > 0) { + *avial_buf_ptr = gif_ptr->next_in; + } + } + return gif_ptr->avail_in; +} +int32_t gif_get_frame_num(gif_decompress_struct_p gif_ptr) { + return gif_ptr->img_ptr_arr_ptr->GetSize(); +} +static FX_BOOL gif_write_header(gif_compress_struct_p gif_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_len) { + if (gif_ptr->cur_offset) { + return TRUE; + } + dst_len = sizeof(GifHeader) + sizeof(GifLSD) + sizeof(GifGF); + dst_buf = FX_TryAlloc(uint8_t, dst_len); + if (dst_buf == NULL) { + return FALSE; + } + FXSYS_memset(dst_buf, 0, dst_len); + FXSYS_memcpy(dst_buf, gif_ptr->header_ptr, sizeof(GifHeader)); + gif_ptr->cur_offset += sizeof(GifHeader); + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, gif_ptr->lsd_ptr->width); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, gif_ptr->lsd_ptr->height); + gif_ptr->cur_offset += 2; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->lsd_ptr->global_flag; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->lsd_ptr->bc_index; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->lsd_ptr->pixel_aspect; + if (gif_ptr->global_pal) { + FX_WORD size = sizeof(GifPalette) * gif_ptr->gpal_num; + if (!gif_grow_buf(dst_buf, dst_len, gif_ptr->cur_offset + size)) { + return FALSE; + } + FXSYS_memcpy(&dst_buf[gif_ptr->cur_offset], gif_ptr->global_pal, size); + gif_ptr->cur_offset += size; + } + return TRUE; +} +void interlace_buf(const uint8_t* buf, FX_DWORD pitch, FX_DWORD height) { + CFX_ArrayTemplate<uint8_t*> pass[4]; + int i, j; + FX_DWORD row; + row = 0; + uint8_t* temp; + while (row < height) { + if (row % 8 == 0) { + j = 0; + } else if (row % 4 == 0) { + j = 1; + } else if (row % 2 == 0) { + j = 2; + } else { + j = 3; + } + temp = FX_Alloc(uint8_t, pitch); + if (temp == NULL) { + return; + } + FXSYS_memcpy(temp, &buf[pitch * row], pitch); + pass[j].Add(temp); + row++; + } + for (i = 0, row = 0; i < 4; i++) { + for (j = 0; j < pass[i].GetSize(); j++, row++) { + FXSYS_memcpy((uint8_t*)&buf[pitch * row], pass[i].GetAt(j), pitch); + FX_Free(pass[i].GetAt(j)); + } + } +} +static void gif_write_block_data(const uint8_t* src_buf, + FX_DWORD src_len, + uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& dst_offset) { + FX_DWORD src_offset = 0; + while (src_len > GIF_DATA_BLOCK) { + dst_buf[dst_offset++] = GIF_DATA_BLOCK; + FXSYS_memcpy(&dst_buf[dst_offset], &src_buf[src_offset], GIF_DATA_BLOCK); + dst_offset += GIF_DATA_BLOCK; + src_offset += GIF_DATA_BLOCK; + src_len -= GIF_DATA_BLOCK; + } + dst_buf[dst_offset++] = (uint8_t)src_len; + FXSYS_memcpy(&dst_buf[dst_offset], &src_buf[src_offset], src_len); + dst_offset += src_len; +} +static FX_BOOL gif_write_data(gif_compress_struct_p gif_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_len) { + if (!gif_grow_buf(dst_buf, dst_len, gif_ptr->cur_offset + GIF_DATA_BLOCK)) { + return FALSE; + } + if (FXSYS_memcmp(gif_ptr->header_ptr->version, "89a", 3) == 0) { + dst_buf[gif_ptr->cur_offset++] = GIF_SIG_EXTENSION; + dst_buf[gif_ptr->cur_offset++] = GIF_BLOCK_GCE; + gif_ptr->gce_ptr->block_size = 4; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->gce_ptr->block_size; + gif_ptr->gce_ptr->gce_flag = 0; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->gce_ptr->gce_flag; + gif_ptr->gce_ptr->delay_time = 10; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->gce_ptr->delay_time); + gif_ptr->cur_offset += 2; + gif_ptr->gce_ptr->trans_index = 0; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->gce_ptr->trans_index; + dst_buf[gif_ptr->cur_offset++] = 0; + } + dst_buf[gif_ptr->cur_offset++] = GIF_SIG_IMAGE; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->image_info_ptr->left); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, gif_ptr->image_info_ptr->top); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->image_info_ptr->width); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->image_info_ptr->height); + gif_ptr->cur_offset += 2; + GifLF& lf = (GifLF&)gif_ptr->image_info_ptr->local_flag; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->image_info_ptr->local_flag; + if (gif_ptr->local_pal) { + FX_DWORD pal_size = sizeof(GifPalette) * gif_ptr->lpal_num; + if (!gif_grow_buf(dst_buf, dst_len, pal_size + gif_ptr->cur_offset)) { + return FALSE; + } + FXSYS_memcpy(&dst_buf[gif_ptr->cur_offset], gif_ptr->local_pal, pal_size); + gif_ptr->cur_offset += pal_size; + } + if (lf.interlace) { + interlace_buf(gif_ptr->src_buf, gif_ptr->src_pitch, + gif_ptr->image_info_ptr->height); + } + uint8_t code_bit = lf.pal_bits; + if (lf.local_pal == 0) { + GifGF& gf = (GifGF&)gif_ptr->lsd_ptr->global_flag; + code_bit = gf.pal_bits; + } + gif_ptr->img_encoder_ptr->Start(code_bit, gif_ptr->src_buf, dst_buf, + gif_ptr->cur_offset); + FX_DWORD i; + for (i = 0; i < gif_ptr->src_row; i++) { + if (!gif_ptr->img_encoder_ptr->Encode( + &gif_ptr->src_buf[i * gif_ptr->src_pitch], + gif_ptr->src_width * (code_bit + 1), dst_buf, dst_len, + gif_ptr->cur_offset)) { + return FALSE; + } + } + gif_ptr->img_encoder_ptr->Finish(dst_buf, dst_len, gif_ptr->cur_offset); + dst_buf[gif_ptr->cur_offset++] = 0; + if (FXSYS_memcmp(gif_ptr->header_ptr->version, "89a", 3) == 0 && + gif_ptr->cmt_data_ptr) { + dst_buf[gif_ptr->cur_offset++] = GIF_SIG_EXTENSION; + dst_buf[gif_ptr->cur_offset++] = GIF_BLOCK_CE; + gif_write_block_data(gif_ptr->cmt_data_ptr, gif_ptr->cmt_data_len, dst_buf, + dst_len, gif_ptr->cur_offset); + dst_buf[gif_ptr->cur_offset++] = 0; + } + if (FXSYS_memcmp(gif_ptr->header_ptr->version, "89a", 3) == 0 && + gif_ptr->pte_data_ptr) { + dst_buf[gif_ptr->cur_offset++] = GIF_SIG_EXTENSION; + dst_buf[gif_ptr->cur_offset++] = GIF_BLOCK_PTE; + dst_buf[gif_ptr->cur_offset++] = gif_ptr->pte_ptr->block_size; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->pte_ptr->grid_left); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, gif_ptr->pte_ptr->grid_top); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->pte_ptr->grid_width); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->pte_ptr->grid_height); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->pte_ptr->char_width); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, + gif_ptr->pte_ptr->char_height); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, gif_ptr->pte_ptr->fc_index); + gif_ptr->cur_offset += 2; + SetWord_LSBFirst(dst_buf + gif_ptr->cur_offset, gif_ptr->pte_ptr->bc_index); + gif_ptr->cur_offset += 2; + gif_write_block_data(gif_ptr->pte_data_ptr, gif_ptr->pte_data_len, dst_buf, + dst_len, gif_ptr->cur_offset); + gif_ptr->cur_offset += gif_ptr->pte_data_len; + dst_buf[gif_ptr->cur_offset++] = 0; + } + dst_buf[gif_ptr->cur_offset++] = GIF_SIG_TRAILER; + return TRUE; +} +FX_BOOL gif_encode(gif_compress_struct_p gif_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_len) { + if (!gif_write_header(gif_ptr, dst_buf, dst_len)) { + return FALSE; + } + FX_DWORD cur_offset = gif_ptr->cur_offset; + FX_BOOL res = TRUE; + if (gif_ptr->frames) { + gif_ptr->cur_offset--; + } + if (!gif_write_data(gif_ptr, dst_buf, dst_len)) { + gif_ptr->cur_offset = cur_offset; + res = FALSE; + } + dst_len = gif_ptr->cur_offset; + dst_buf[dst_len - 1] = GIF_SIG_TRAILER; + if (res) { + gif_ptr->frames++; + } + return res; +} diff --git a/core/fxcodec/lgif/fx_gif.h b/core/fxcodec/lgif/fx_gif.h new file mode 100644 index 0000000000..c44fb7a7cb --- /dev/null +++ b/core/fxcodec/lgif/fx_gif.h @@ -0,0 +1,296 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCODEC_LGIF_FX_GIF_H_ +#define CORE_FXCODEC_LGIF_FX_GIF_H_ + +#include <setjmp.h> + +#include "core/include/fxcrt/fx_basic.h" + +#define GIF_SIGNATURE "GIF" +#define GIF_SIG_EXTENSION 0x21 +#define GIF_SIG_IMAGE 0x2C +#define GIF_SIG_TRAILER 0x3B +#define GIF_BLOCK_GCE 0xF9 +#define GIF_BLOCK_PTE 0x01 +#define GIF_BLOCK_CE 0xFE +#define GIF_BLOCK_AE 0xFF +#define GIF_BLOCK_TERMINAL 0x00 +#define GIF_MAX_LZW_CODE 4096 +#define GIF_DATA_BLOCK 255 +#define GIF_MAX_ERROR_SIZE 256 +#define GIF_D_STATUS_SIG 0x01 +#define GIF_D_STATUS_TAIL 0x02 +#define GIF_D_STATUS_EXT 0x03 +#define GIF_D_STATUS_EXT_AE 0x04 +#define GIF_D_STATUS_EXT_CE 0x05 +#define GIF_D_STATUS_EXT_GCE 0x06 +#define GIF_D_STATUS_EXT_PTE 0x07 +#define GIF_D_STATUS_EXT_UNE 0x08 +#define GIF_D_STATUS_IMG_INFO 0x09 +#define GIF_D_STATUS_IMG_DATA 0x0A +#pragma pack(1) +typedef struct tagGifGF { + uint8_t pal_bits : 3; + uint8_t sort_flag : 1; + uint8_t color_resolution : 3; + uint8_t global_pal : 1; +} GifGF; +typedef struct tagGifLF { + uint8_t pal_bits : 3; + uint8_t reserved : 2; + uint8_t sort_flag : 1; + uint8_t interlace : 1; + uint8_t local_pal : 1; +} GifLF; +typedef struct tagGifHeader { + char signature[3]; + char version[3]; +} GifHeader; +typedef struct tagGifLSD { + FX_WORD width; + FX_WORD height; + uint8_t global_flag; + uint8_t bc_index; + uint8_t pixel_aspect; +} GifLSD; +typedef struct tagGifImageInfo { + FX_WORD left; + FX_WORD top; + FX_WORD width; + FX_WORD height; + + uint8_t local_flag; +} GifImageInfo; +typedef struct tagGifCEF { + uint8_t transparency : 1; + uint8_t user_input : 1; + uint8_t disposal_method : 3; + uint8_t reserved : 3; +} GifCEF; +typedef struct tagGifGCE { + uint8_t block_size; + uint8_t gce_flag; + FX_WORD delay_time; + uint8_t trans_index; +} GifGCE; +typedef struct tagGifPTE { + uint8_t block_size; + FX_WORD grid_left; + FX_WORD grid_top; + FX_WORD grid_width; + FX_WORD grid_height; + + uint8_t char_width; + uint8_t char_height; + + uint8_t fc_index; + uint8_t bc_index; +} GifPTE; +typedef struct tagGifAE { + uint8_t block_size; + uint8_t app_identify[8]; + uint8_t app_authentication[3]; +} GifAE; +typedef struct tagGifPalette { uint8_t r, g, b; } GifPalette; +#pragma pack() +typedef struct tagGifImage { + GifGCE* image_gce_ptr; + GifPalette* local_pal_ptr; + GifImageInfo* image_info_ptr; + uint8_t image_code_size; + FX_DWORD image_data_pos; + uint8_t* image_row_buf; + int32_t image_row_num; +} GifImage; +typedef struct tagGifPlainText { + GifGCE* gce_ptr; + GifPTE* pte_ptr; + CFX_ByteString* string_ptr; +} GifPlainText; +class CGifLZWDecoder { + public: + struct tag_Table { + FX_WORD prefix; + uint8_t suffix; + }; + CGifLZWDecoder(FX_CHAR* error_ptr = NULL) { err_msg_ptr = error_ptr; } + void InitTable(uint8_t code_len); + + int32_t Decode(uint8_t* des_buf, FX_DWORD& des_size); + + void Input(uint8_t* src_buf, FX_DWORD src_size); + FX_DWORD GetAvailInput(); + + private: + void ClearTable(); + void AddCode(FX_WORD prefix_code, uint8_t append_char); + void DecodeString(FX_WORD code); + uint8_t code_size; + uint8_t code_size_cur; + FX_WORD code_clear; + FX_WORD code_end; + FX_WORD code_next; + uint8_t code_first; + uint8_t stack[GIF_MAX_LZW_CODE]; + FX_WORD stack_size; + tag_Table code_table[GIF_MAX_LZW_CODE]; + FX_WORD code_old; + + uint8_t* next_in; + FX_DWORD avail_in; + + uint8_t bits_left; + FX_DWORD code_store; + + FX_CHAR* err_msg_ptr; +}; +class CGifLZWEncoder { + public: + struct tag_Table { + FX_WORD prefix; + uint8_t suffix; + }; + CGifLZWEncoder(); + ~CGifLZWEncoder(); + void Start(uint8_t code_len, + const uint8_t* src_buf, + uint8_t*& dst_buf, + FX_DWORD& offset); + FX_BOOL Encode(const uint8_t* src_buf, + FX_DWORD src_len, + uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& offset); + void Finish(uint8_t*& dst_buf, FX_DWORD& dst_len, FX_DWORD& offset); + + private: + void ClearTable(); + FX_BOOL LookUpInTable(const uint8_t* buf, + FX_DWORD& offset, + uint8_t& bit_offset); + void EncodeString(FX_DWORD index, + uint8_t*& dst_buf, + FX_DWORD& dst_len, + FX_DWORD& offset); + void WriteBlock(uint8_t*& dst_buf, FX_DWORD& dst_len, FX_DWORD& offset); + jmp_buf jmp; + FX_DWORD src_offset; + uint8_t src_bit_offset; + uint8_t src_bit_cut; + FX_DWORD src_bit_num; + uint8_t code_size; + FX_WORD code_clear; + FX_WORD code_end; + FX_WORD index_num; + uint8_t bit_offset; + uint8_t index_bit_cur; + uint8_t index_buf[GIF_DATA_BLOCK]; + uint8_t index_buf_len; + tag_Table code_table[GIF_MAX_LZW_CODE]; + FX_WORD table_cur; +}; +typedef struct tag_gif_decompress_struct gif_decompress_struct; +typedef gif_decompress_struct* gif_decompress_struct_p; +typedef gif_decompress_struct_p* gif_decompress_struct_pp; +static const int32_t s_gif_interlace_step[4] = {8, 8, 4, 2}; +struct tag_gif_decompress_struct { + jmp_buf jmpbuf; + FX_CHAR* err_ptr; + void (*gif_error_fn)(gif_decompress_struct_p gif_ptr, const FX_CHAR* err_msg); + void* context_ptr; + int width; + int height; + GifPalette* global_pal_ptr; + int32_t global_pal_num; + uint8_t global_sort_flag; + uint8_t global_color_resolution; + + uint8_t bc_index; + uint8_t pixel_aspect; + CGifLZWDecoder* img_decoder_ptr; + FX_DWORD img_row_offset; + FX_DWORD img_row_avail_size; + uint8_t img_pass_num; + CFX_ArrayTemplate<GifImage*>* img_ptr_arr_ptr; + uint8_t* (*gif_ask_buf_for_pal_fn)(gif_decompress_struct_p gif_ptr, + int32_t pal_size); + uint8_t* next_in; + FX_DWORD avail_in; + int32_t decode_status; + FX_DWORD skip_size; + void (*gif_record_current_position_fn)(gif_decompress_struct_p gif_ptr, + FX_DWORD* cur_pos_ptr); + void (*gif_get_row_fn)(gif_decompress_struct_p gif_ptr, + int32_t row_num, + uint8_t* row_buf); + FX_BOOL (*gif_get_record_position_fn)(gif_decompress_struct_p gif_ptr, + FX_DWORD cur_pos, + int32_t left, int32_t top, int32_t width, int32_t height, + int32_t pal_num, void* pal_ptr, + int32_t delay_time, FX_BOOL user_input, + int32_t trans_index, int32_t disposal_method, FX_BOOL interlace); + CFX_ByteString* cmt_data_ptr; + GifGCE* gce_ptr; + CFX_ArrayTemplate<GifPlainText*>* pt_ptr_arr_ptr; +}; +typedef struct tag_gif_compress_struct gif_compress_struct; +typedef gif_compress_struct* gif_compress_struct_p; +typedef gif_compress_struct_p* gif_compress_struct_pp; +struct tag_gif_compress_struct { + const uint8_t* src_buf; + FX_DWORD src_pitch; + FX_DWORD src_width; + FX_DWORD src_row; + FX_DWORD cur_offset; + FX_DWORD frames; + GifHeader* header_ptr; + GifLSD* lsd_ptr; + GifPalette* global_pal; + FX_WORD gpal_num; + GifPalette* local_pal; + FX_WORD lpal_num; + GifImageInfo* image_info_ptr; + CGifLZWEncoder* img_encoder_ptr; + + uint8_t* cmt_data_ptr; + FX_DWORD cmt_data_len; + GifGCE* gce_ptr; + GifPTE* pte_ptr; + const uint8_t* pte_data_ptr; + FX_DWORD pte_data_len; +}; + +void gif_error(gif_decompress_struct_p gif_ptr, const FX_CHAR* err_msg); +void gif_warn(gif_decompress_struct_p gif_ptr, const FX_CHAR* err_msg); +gif_decompress_struct_p gif_create_decompress(); +void gif_destroy_decompress(gif_decompress_struct_pp gif_ptr_ptr); +gif_compress_struct_p gif_create_compress(); +void gif_destroy_compress(gif_compress_struct_pp gif_ptr_ptr); +int32_t gif_read_header(gif_decompress_struct_p gif_ptr); +int32_t gif_get_frame(gif_decompress_struct_p gif_ptr); +int32_t gif_get_frame_num(gif_decompress_struct_p gif_ptr); +int32_t gif_decode_extension(gif_decompress_struct_p gif_ptr); +int32_t gif_decode_image_info(gif_decompress_struct_p gif_ptr); +void gif_takeover_gce_ptr(gif_decompress_struct_p gif_ptr, + GifGCE** gce_ptr_ptr); +int32_t gif_load_frame(gif_decompress_struct_p gif_ptr, int32_t frame_num); +uint8_t* gif_read_data(gif_decompress_struct_p gif_ptr, + uint8_t** des_buf_pp, + FX_DWORD data_size); +void gif_save_decoding_status(gif_decompress_struct_p gif_ptr, int32_t status); +void gif_input_buffer(gif_decompress_struct_p gif_ptr, + uint8_t* src_buf, + FX_DWORD src_size); +FX_DWORD gif_get_avail_input(gif_decompress_struct_p gif_ptr, + uint8_t** avial_buf_ptr); +void interlace_buf(const uint8_t* buf, FX_DWORD width, FX_DWORD height); +FX_BOOL gif_encode(gif_compress_struct_p gif_ptr, + uint8_t*& dst_buf, + FX_DWORD& dst_len); + +#endif // CORE_FXCODEC_LGIF_FX_GIF_H_ |