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
18namespace {
19
20const int kMaxProgressiveStretchPixels = 1000000;
21
22bool SourceSizeWithinLimit(int width, int height) {
23  return !height || width < kMaxProgressiveStretchPixels / height;
24}
25
26FXDIB_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
38std::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
45CFX_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
63CFX_ImageStretcher::~CFX_ImageStretcher() {}
64
65bool 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
127bool CFX_ImageStretcher::Continue(IFX_PauseIndicator* pPause) {
128  if (m_Flags & FXDIB_DOWNSAMPLE)
129    return ContinueQuickStretch(pPause);
130  return ContinueStretch(pPause);
131}
132
133bool 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
145bool CFX_ImageStretcher::ContinueStretch(IFX_PauseIndicator* pPause) {
146  return m_pStretchEngine && m_pStretchEngine->Continue(pPause);
147}
148
149bool 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
174bool 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