Move core/src/ up to core/.

This CL moves the core/src/ files up to core/ and fixes up the include guards,
includes and build files.

R=tsepez@chromium.org

Review URL: https://codereview.chromium.org/1800523005 .
diff --git a/core/fxcodec/codec/DEPS b/core/fxcodec/codec/DEPS
new file mode 100644
index 0000000..495d288
--- /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 0000000..d2f44dd
--- /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 0000000..f1298f9
--- /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 0000000..d2299d6
--- /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 0000000..13ffcda
--- /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 0000000..49a4f34
--- /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 0000000..60ca00f
--- /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 0000000..fc8113e
--- /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 0000000..557d246
--- /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 0000000..efbeb79
--- /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 0000000..486cb92
--- /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(&parameters);
+  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, &parameters)) {
+    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 0000000..d58b152
--- /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 0000000..a3882ba
--- /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 0000000..b22d203
--- /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 0000000..c9ee389
--- /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 0000000..2af92f2
--- /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;
+}