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