Home | History | Annotate | Download | only in dib
      1 // Copyright 2017 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "core/fxge/dib/cfx_imagestretcher.h"
      8 
      9 #include <climits>
     10 #include <tuple>
     11 
     12 #include "core/fxge/dib/cfx_dibitmap.h"
     13 #include "core/fxge/dib/cfx_dibsource.h"
     14 #include "core/fxge/dib/cstretchengine.h"
     15 #include "core/fxge/fx_dib.h"
     16 #include "third_party/base/ptr_util.h"
     17 
     18 namespace {
     19 
     20 const int kMaxProgressiveStretchPixels = 1000000;
     21 
     22 bool SourceSizeWithinLimit(int width, int height) {
     23   return !height || width < kMaxProgressiveStretchPixels / height;
     24 }
     25 
     26 FXDIB_Format GetStretchedFormat(const CFX_DIBSource& src) {
     27   FXDIB_Format format = src.GetFormat();
     28   if (format == FXDIB_1bppMask)
     29     return FXDIB_8bppMask;
     30   if (format == FXDIB_1bppRgb)
     31     return FXDIB_8bppRgb;
     32   if (format == FXDIB_8bppRgb && src.GetPalette())
     33     return FXDIB_Rgb;
     34   return format;
     35 }
     36 
     37 // Returns tuple c, m, y, k
     38 std::tuple<int, int, int, int> CmykDecode(const uint32_t cmyk) {
     39   return std::make_tuple(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk),
     40                          FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk));
     41 }
     42 
     43 }  // namespace
     44 
     45 CFX_ImageStretcher::CFX_ImageStretcher(IFX_ScanlineComposer* pDest,
     46                                        const RetainPtr<CFX_DIBSource>& pSource,
     47                                        int dest_width,
     48                                        int dest_height,
     49                                        const FX_RECT& bitmap_rect,
     50                                        uint32_t flags)
     51     : m_pDest(pDest),
     52       m_pSource(pSource),
     53       m_Flags(flags),
     54       m_bFlipX(false),
     55       m_bFlipY(false),
     56       m_DestWidth(dest_width),
     57       m_DestHeight(dest_height),
     58       m_ClipRect(bitmap_rect),
     59       m_DestFormat(GetStretchedFormat(*pSource)),
     60       m_DestBPP(m_DestFormat & 0xff),
     61       m_LineIndex(0) {}
     62 
     63 CFX_ImageStretcher::~CFX_ImageStretcher() {}
     64 
     65 bool CFX_ImageStretcher::Start() {
     66   if (m_DestWidth == 0 || m_DestHeight == 0)
     67     return false;
     68 
     69   if (m_pSource->GetFormat() == FXDIB_1bppRgb && m_pSource->GetPalette()) {
     70     FX_ARGB pal[256];
     71     int a0;
     72     int r0;
     73     int g0;
     74     int b0;
     75     std::tie(a0, r0, g0, b0) = ArgbDecode(m_pSource->GetPaletteArgb(0));
     76     int a1;
     77     int r1;
     78     int g1;
     79     int b1;
     80     std::tie(a1, r1, g1, b1) = ArgbDecode(m_pSource->GetPaletteArgb(1));
     81     for (int i = 0; i < 256; ++i) {
     82       int a = a0 + (a1 - a0) * i / 255;
     83       int r = r0 + (r1 - r0) * i / 255;
     84       int g = g0 + (g1 - g0) * i / 255;
     85       int b = b0 + (b1 - b0) * i / 255;
     86       pal[i] = ArgbEncode(a, r, g, b);
     87     }
     88     if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat,
     89                           pal)) {
     90       return false;
     91     }
     92   } else if (m_pSource->GetFormat() == FXDIB_1bppCmyk &&
     93              m_pSource->GetPalette()) {
     94     FX_CMYK pal[256];
     95     int c0;
     96     int m0;
     97     int y0;
     98     int k0;
     99     std::tie(c0, m0, y0, k0) = CmykDecode(m_pSource->GetPaletteArgb(0));
    100     int c1;
    101     int m1;
    102     int y1;
    103     int k1;
    104     std::tie(c1, m1, y1, k1) = CmykDecode(m_pSource->GetPaletteArgb(1));
    105     for (int i = 0; i < 256; ++i) {
    106       int c = c0 + (c1 - c0) * i / 255;
    107       int m = m0 + (m1 - m0) * i / 255;
    108       int y = y0 + (y1 - y0) * i / 255;
    109       int k = k0 + (k1 - k0) * i / 255;
    110       pal[i] = CmykEncode(c, m, y, k);
    111     }
    112     if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat,
    113                           pal)) {
    114       return false;
    115     }
    116   } else if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(),
    117                                m_DestFormat, nullptr)) {
    118     return false;
    119   }
    120 
    121   if (m_Flags & FXDIB_DOWNSAMPLE)
    122     return StartQuickStretch();
    123 
    124   return StartStretch();
    125 }
    126 
    127 bool CFX_ImageStretcher::Continue(IFX_PauseIndicator* pPause) {
    128   if (m_Flags & FXDIB_DOWNSAMPLE)
    129     return ContinueQuickStretch(pPause);
    130   return ContinueStretch(pPause);
    131 }
    132 
    133 bool CFX_ImageStretcher::StartStretch() {
    134   m_pStretchEngine = pdfium::MakeUnique<CStretchEngine>(
    135       m_pDest.Get(), m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect,
    136       m_pSource, m_Flags);
    137   m_pStretchEngine->StartStretchHorz();
    138   if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) {
    139     m_pStretchEngine->Continue(nullptr);
    140     return false;
    141   }
    142   return true;
    143 }
    144 
    145 bool CFX_ImageStretcher::ContinueStretch(IFX_PauseIndicator* pPause) {
    146   return m_pStretchEngine && m_pStretchEngine->Continue(pPause);
    147 }
    148 
    149 bool CFX_ImageStretcher::StartQuickStretch() {
    150   if (m_DestWidth < 0) {
    151     m_bFlipX = true;
    152     m_DestWidth = -m_DestWidth;
    153   }
    154   if (m_DestHeight < 0) {
    155     m_bFlipY = true;
    156     m_DestHeight = -m_DestHeight;
    157   }
    158   uint32_t size = m_ClipRect.Width();
    159   if (size && m_DestBPP > static_cast<int>(INT_MAX / size))
    160     return false;
    161 
    162   size *= m_DestBPP;
    163   m_pScanline.reset(FX_Alloc(uint8_t, (size / 8 + 3) / 4 * 4));
    164   if (m_pSource->m_pAlphaMask)
    165     m_pMaskScanline.reset(FX_Alloc(uint8_t, (m_ClipRect.Width() + 3) / 4 * 4));
    166 
    167   if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) {
    168     ContinueQuickStretch(nullptr);
    169     return false;
    170   }
    171   return true;
    172 }
    173 
    174 bool CFX_ImageStretcher::ContinueQuickStretch(IFX_PauseIndicator* pPause) {
    175   if (!m_pScanline)
    176     return false;
    177 
    178   int result_width = m_ClipRect.Width();
    179   int result_height = m_ClipRect.Height();
    180   int src_height = m_pSource->GetHeight();
    181   for (; m_LineIndex < result_height; ++m_LineIndex) {
    182     int dest_y;
    183     int src_y;
    184     if (m_bFlipY) {
    185       dest_y = result_height - m_LineIndex - 1;
    186       src_y = (m_DestHeight - (dest_y + m_ClipRect.top) - 1) * src_height /
    187               m_DestHeight;
    188     } else {
    189       dest_y = m_LineIndex;
    190       src_y = (dest_y + m_ClipRect.top) * src_height / m_DestHeight;
    191     }
    192     src_y = pdfium::clamp(src_y, 0, src_height - 1);
    193 
    194     if (m_pSource->SkipToScanline(src_y, pPause))
    195       return true;
    196 
    197     m_pSource->DownSampleScanline(src_y, m_pScanline.get(), m_DestBPP,
    198                                   m_DestWidth, m_bFlipX, m_ClipRect.left,
    199                                   result_width);
    200     if (m_pMaskScanline) {
    201       m_pSource->m_pAlphaMask->DownSampleScanline(
    202           src_y, m_pMaskScanline.get(), 1, m_DestWidth, m_bFlipX,
    203           m_ClipRect.left, result_width);
    204     }
    205     m_pDest->ComposeScanline(dest_y, m_pScanline.get(), m_pMaskScanline.get());
    206   }
    207   return false;
    208 }
    209