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
SourceSizeWithinLimit(int width,int height)22 bool SourceSizeWithinLimit(int width, int height) {
23 return !height || width < kMaxProgressiveStretchPixels / height;
24 }
25
GetStretchedFormat(const CFX_DIBSource & src)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
CmykDecode(const uint32_t cmyk)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
CFX_ImageStretcher(IFX_ScanlineComposer * pDest,const RetainPtr<CFX_DIBSource> & pSource,int dest_width,int dest_height,const FX_RECT & bitmap_rect,uint32_t flags)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
~CFX_ImageStretcher()63 CFX_ImageStretcher::~CFX_ImageStretcher() {}
64
Start()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
Continue(IFX_PauseIndicator * pPause)127 bool CFX_ImageStretcher::Continue(IFX_PauseIndicator* pPause) {
128 if (m_Flags & FXDIB_DOWNSAMPLE)
129 return ContinueQuickStretch(pPause);
130 return ContinueStretch(pPause);
131 }
132
StartStretch()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
ContinueStretch(IFX_PauseIndicator * pPause)145 bool CFX_ImageStretcher::ContinueStretch(IFX_PauseIndicator* pPause) {
146 return m_pStretchEngine && m_pStretchEngine->Continue(pPause);
147 }
148
StartQuickStretch()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
ContinueQuickStretch(IFX_PauseIndicator * pPause)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