1 // Copyright 2017 The PDFium Authors
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_imagetransformer.h"
8
9 #include <math.h>
10
11 #include <iterator>
12 #include <memory>
13 #include <utility>
14
15 #include "core/fxcrt/fx_system.h"
16 #include "core/fxge/dib/cfx_dibitmap.h"
17 #include "core/fxge/dib/cfx_imagestretcher.h"
18 #include "core/fxge/dib/fx_dib.h"
19 #include "third_party/base/check.h"
20 #include "third_party/base/compiler_specific.h"
21 #include "third_party/base/notreached.h"
22 #include "third_party/base/numerics/safe_conversions.h"
23
24 namespace {
25
26 constexpr int kBase = 256;
27 constexpr float kFix16 = 0.05f;
28 constexpr uint8_t kOpaqueAlpha = 0xff;
29
BilinearInterpolate(const uint8_t * buf,const CFX_ImageTransformer::BilinearData & data,int bpp,int c_offset)30 uint8_t BilinearInterpolate(const uint8_t* buf,
31 const CFX_ImageTransformer::BilinearData& data,
32 int bpp,
33 int c_offset) {
34 int i_resx = 255 - data.res_x;
35 int col_bpp_l = data.src_col_l * bpp;
36 int col_bpp_r = data.src_col_r * bpp;
37 const uint8_t* buf_u = buf + data.row_offset_l + c_offset;
38 const uint8_t* buf_d = buf + data.row_offset_r + c_offset;
39 const uint8_t* src_pos0 = buf_u + col_bpp_l;
40 const uint8_t* src_pos1 = buf_u + col_bpp_r;
41 const uint8_t* src_pos2 = buf_d + col_bpp_l;
42 const uint8_t* src_pos3 = buf_d + col_bpp_r;
43 uint8_t r_pos_0 = (*src_pos0 * i_resx + *src_pos1 * data.res_x) >> 8;
44 uint8_t r_pos_1 = (*src_pos2 * i_resx + *src_pos3 * data.res_x) >> 8;
45 return (r_pos_0 * (255 - data.res_y) + r_pos_1 * data.res_y) >> 8;
46 }
47
48 class CFX_BilinearMatrix {
49 public:
CFX_BilinearMatrix(const CFX_Matrix & src)50 explicit CFX_BilinearMatrix(const CFX_Matrix& src)
51 : a(FXSYS_roundf(src.a * kBase)),
52 b(FXSYS_roundf(src.b * kBase)),
53 c(FXSYS_roundf(src.c * kBase)),
54 d(FXSYS_roundf(src.d * kBase)),
55 e(FXSYS_roundf(src.e * kBase)),
56 f(FXSYS_roundf(src.f * kBase)) {}
57
Transform(int x,int y,int * x1,int * y1,int * res_x,int * res_y) const58 void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const {
59 CFX_PointF val = TransformInternal(CFX_PointF(x, y));
60 *x1 = pdfium::base::saturated_cast<int>(val.x / kBase);
61 *y1 = pdfium::base::saturated_cast<int>(val.y / kBase);
62 *res_x = static_cast<int>(val.x) % kBase;
63 *res_y = static_cast<int>(val.y) % kBase;
64 if (*res_x < 0 && *res_x > -kBase)
65 *res_x = kBase + *res_x;
66 if (*res_y < 0 && *res_y > -kBase)
67 *res_y = kBase + *res_y;
68 }
69
70 private:
TransformInternal(CFX_PointF pt) const71 CFX_PointF TransformInternal(CFX_PointF pt) const {
72 return CFX_PointF(a * pt.x + c * pt.y + e + kBase / 2,
73 b * pt.x + d * pt.y + f + kBase / 2);
74 }
75
76 const int a;
77 const int b;
78 const int c;
79 const int d;
80 const int e;
81 const int f;
82 };
83
InStretchBounds(const FX_RECT & clip_rect,int col,int row)84 bool InStretchBounds(const FX_RECT& clip_rect, int col, int row) {
85 return col >= 0 && col <= clip_rect.Width() && row >= 0 &&
86 row <= clip_rect.Height();
87 }
88
AdjustCoords(const FX_RECT & clip_rect,int * col,int * row)89 void AdjustCoords(const FX_RECT& clip_rect, int* col, int* row) {
90 int& src_col = *col;
91 int& src_row = *row;
92 if (src_col == clip_rect.Width())
93 src_col--;
94 if (src_row == clip_rect.Height())
95 src_row--;
96 }
97
98 // Let the compiler deduce the type for |func|, which cheaper than specifying it
99 // with std::function.
100 template <typename F>
DoBilinearLoop(const CFX_ImageTransformer::CalcData & calc_data,const FX_RECT & result_rect,const FX_RECT & clip_rect,int increment,const F & func)101 void DoBilinearLoop(const CFX_ImageTransformer::CalcData& calc_data,
102 const FX_RECT& result_rect,
103 const FX_RECT& clip_rect,
104 int increment,
105 const F& func) {
106 CFX_BilinearMatrix matrix_fix(calc_data.matrix);
107 for (int row = 0; row < result_rect.Height(); row++) {
108 uint8_t* dest = calc_data.bitmap->GetWritableScanline(row).data();
109 for (int col = 0; col < result_rect.Width(); col++) {
110 CFX_ImageTransformer::BilinearData d;
111 d.res_x = 0;
112 d.res_y = 0;
113 d.src_col_l = 0;
114 d.src_row_l = 0;
115 matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
116 &d.res_y);
117 if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) {
118 AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l);
119 d.src_col_r = d.src_col_l + 1;
120 d.src_row_r = d.src_row_l + 1;
121 AdjustCoords(clip_rect, &d.src_col_r, &d.src_row_r);
122 d.row_offset_l = d.src_row_l * calc_data.pitch;
123 d.row_offset_r = d.src_row_r * calc_data.pitch;
124 func(d, dest);
125 }
126 dest += increment;
127 }
128 }
129 }
130
131 } // namespace
132
CFX_ImageTransformer(const RetainPtr<const CFX_DIBBase> & pSrc,const CFX_Matrix & matrix,const FXDIB_ResampleOptions & options,const FX_RECT * pClip)133 CFX_ImageTransformer::CFX_ImageTransformer(
134 const RetainPtr<const CFX_DIBBase>& pSrc,
135 const CFX_Matrix& matrix,
136 const FXDIB_ResampleOptions& options,
137 const FX_RECT* pClip)
138 : m_pSrc(pSrc), m_matrix(matrix), m_ResampleOptions(options) {
139 FX_RECT result_rect = m_matrix.GetUnitRect().GetClosestRect();
140 FX_RECT result_clip = result_rect;
141 if (pClip)
142 result_clip.Intersect(*pClip);
143
144 if (result_clip.IsEmpty())
145 return;
146
147 m_result = result_clip;
148 if (fabs(m_matrix.a) < fabs(m_matrix.b) / 20 &&
149 fabs(m_matrix.d) < fabs(m_matrix.c) / 20 && fabs(m_matrix.a) < 0.5f &&
150 fabs(m_matrix.d) < 0.5f) {
151 int dest_width = result_rect.Width();
152 int dest_height = result_rect.Height();
153 result_clip.Offset(-result_rect.left, -result_rect.top);
154 result_clip = result_clip.SwappedClipBox(dest_width, dest_height,
155 m_matrix.c > 0, m_matrix.b < 0);
156 m_Stretcher = std::make_unique<CFX_ImageStretcher>(
157 &m_Storer, m_pSrc, dest_height, dest_width, result_clip,
158 m_ResampleOptions);
159 m_Stretcher->Start();
160 m_type = StretchType::kRotate;
161 return;
162 }
163 if (fabs(m_matrix.b) < kFix16 && fabs(m_matrix.c) < kFix16) {
164 int dest_width =
165 static_cast<int>(m_matrix.a > 0 ? ceil(m_matrix.a) : floor(m_matrix.a));
166 int dest_height = static_cast<int>(m_matrix.d > 0 ? -ceil(m_matrix.d)
167 : -floor(m_matrix.d));
168 result_clip.Offset(-result_rect.left, -result_rect.top);
169 m_Stretcher = std::make_unique<CFX_ImageStretcher>(
170 &m_Storer, m_pSrc, dest_width, dest_height, result_clip,
171 m_ResampleOptions);
172 m_Stretcher->Start();
173 m_type = StretchType::kNormal;
174 return;
175 }
176
177 int stretch_width =
178 static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.a, m_matrix.b)));
179 int stretch_height =
180 static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.c, m_matrix.d)));
181 CFX_Matrix stretch_to_dest(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, stretch_height);
182 stretch_to_dest.Concat(
183 CFX_Matrix(m_matrix.a / stretch_width, m_matrix.b / stretch_width,
184 m_matrix.c / stretch_height, m_matrix.d / stretch_height,
185 m_matrix.e, m_matrix.f));
186 CFX_Matrix dest_to_strech = stretch_to_dest.GetInverse();
187
188 FX_RECT stretch_clip =
189 dest_to_strech.TransformRect(CFX_FloatRect(result_clip)).GetOuterRect();
190 if (!stretch_clip.Valid())
191 return;
192
193 stretch_clip.Intersect(0, 0, stretch_width, stretch_height);
194 if (!stretch_clip.Valid())
195 return;
196
197 m_dest2stretch = dest_to_strech;
198 m_StretchClip = stretch_clip;
199 m_Stretcher = std::make_unique<CFX_ImageStretcher>(
200 &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip,
201 m_ResampleOptions);
202 m_Stretcher->Start();
203 m_type = StretchType::kOther;
204 }
205
206 CFX_ImageTransformer::~CFX_ImageTransformer() = default;
207
Continue(PauseIndicatorIface * pPause)208 bool CFX_ImageTransformer::Continue(PauseIndicatorIface* pPause) {
209 if (m_type == StretchType::kNone) {
210 return false;
211 }
212
213 if (m_Stretcher->Continue(pPause))
214 return true;
215
216 switch (m_type) {
217 case StretchType::kNone:
218 // Already handled separately at the beginning of this method.
219 NOTREACHED_NORETURN();
220 case StretchType::kNormal:
221 return false;
222 case StretchType::kRotate:
223 ContinueRotate(pPause);
224 return false;
225 case StretchType::kOther:
226 ContinueOther(pPause);
227 return false;
228 }
229 }
230
ContinueRotate(PauseIndicatorIface * pPause)231 void CFX_ImageTransformer::ContinueRotate(PauseIndicatorIface* pPause) {
232 if (m_Storer.GetBitmap()) {
233 m_Storer.Replace(
234 m_Storer.GetBitmap()->SwapXY(m_matrix.c > 0, m_matrix.b < 0));
235 }
236 }
237
ContinueOther(PauseIndicatorIface * pPause)238 void CFX_ImageTransformer::ContinueOther(PauseIndicatorIface* pPause) {
239 if (!m_Storer.GetBitmap())
240 return;
241
242 auto pTransformed = pdfium::MakeRetain<CFX_DIBitmap>();
243 FXDIB_Format format = m_Stretcher->source()->IsMaskFormat()
244 ? FXDIB_Format::k8bppMask
245 : FXDIB_Format::kArgb;
246 if (!pTransformed->Create(m_result.Width(), m_result.Height(), format))
247 return;
248
249 CFX_Matrix result2stretch(1.0f, 0.0f, 0.0f, 1.0f, m_result.left,
250 m_result.top);
251 result2stretch.Concat(m_dest2stretch);
252 result2stretch.Translate(-m_StretchClip.left, -m_StretchClip.top);
253
254 CalcData calc_data = {pTransformed.Get(), result2stretch,
255 m_Storer.GetBitmap()->GetBuffer().data(),
256 m_Storer.GetBitmap()->GetPitch()};
257 if (m_Storer.GetBitmap()->IsMaskFormat()) {
258 CalcAlpha(calc_data);
259 } else {
260 int Bpp = m_Storer.GetBitmap()->GetBPP() / 8;
261 if (Bpp == 1)
262 CalcMono(calc_data);
263 else
264 CalcColor(calc_data, format, Bpp);
265 }
266 m_Storer.Replace(std::move(pTransformed));
267 }
268
DetachBitmap()269 RetainPtr<CFX_DIBitmap> CFX_ImageTransformer::DetachBitmap() {
270 return m_Storer.Detach();
271 }
272
CalcAlpha(const CalcData & calc_data)273 void CFX_ImageTransformer::CalcAlpha(const CalcData& calc_data) {
274 auto func = [&calc_data](const BilinearData& data, uint8_t* dest) {
275 *dest = BilinearInterpolate(calc_data.buf, data, 1, 0);
276 };
277 DoBilinearLoop(calc_data, m_result, m_StretchClip, 1, func);
278 }
279
CalcMono(const CalcData & calc_data)280 void CFX_ImageTransformer::CalcMono(const CalcData& calc_data) {
281 uint32_t argb[256];
282 if (m_Storer.GetBitmap()->HasPalette()) {
283 pdfium::span<const uint32_t> palette =
284 m_Storer.GetBitmap()->GetPaletteSpan();
285 for (size_t i = 0; i < std::size(argb); i++)
286 argb[i] = palette[i];
287 } else {
288 for (size_t i = 0; i < std::size(argb); i++) {
289 uint32_t v = static_cast<uint32_t>(i);
290 argb[i] = ArgbEncode(0xff, v, v, v);
291 }
292 }
293 int destBpp = calc_data.bitmap->GetBPP() / 8;
294 auto func = [&calc_data, &argb](const BilinearData& data, uint8_t* dest) {
295 uint8_t idx = BilinearInterpolate(calc_data.buf, data, 1, 0);
296 *reinterpret_cast<uint32_t*>(dest) = argb[idx];
297 };
298 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
299 }
300
CalcColor(const CalcData & calc_data,FXDIB_Format format,int Bpp)301 void CFX_ImageTransformer::CalcColor(const CalcData& calc_data,
302 FXDIB_Format format,
303 int Bpp) {
304 DCHECK(format == FXDIB_Format::k8bppMask || format == FXDIB_Format::kArgb);
305 const int destBpp = calc_data.bitmap->GetBPP() / 8;
306 if (!m_Storer.GetBitmap()->IsAlphaFormat()) {
307 auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
308 uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
309 uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
310 uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
311 *reinterpret_cast<uint32_t*>(dest) = ArgbEncode(kOpaqueAlpha, r, g, b);
312 };
313 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
314 return;
315 }
316
317 if (format == FXDIB_Format::kArgb) {
318 auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
319 uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
320 uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
321 uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
322 uint8_t alpha = BilinearInterpolate(calc_data.buf, data, Bpp, 3);
323 *reinterpret_cast<uint32_t*>(dest) = ArgbEncode(alpha, r, g, b);
324 };
325 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
326 return;
327 }
328
329 auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
330 uint8_t c = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
331 uint8_t m = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
332 uint8_t y = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
333 uint8_t k = BilinearInterpolate(calc_data.buf, data, Bpp, 3);
334 *reinterpret_cast<uint32_t*>(dest) = FXCMYK_TODIB(CmykEncode(c, m, y, k));
335 };
336 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
337 }
338