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 <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
24namespace {
25
26constexpr int kBase = 256;
27constexpr float kFix16 = 0.05f;
28constexpr uint8_t kOpaqueAlpha = 0xff;
29
30uint8_t BilinearInterpolate(const uint8_t* buf,
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
48class CFX_BilinearMatrix {
49 public:
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
58 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:
71 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
84bool 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
89void 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.
100template <typename F>
101void 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++) {
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
134 const CFX_Matrix& matrix,
135 const FXDIB_ResampleOptions& options,
136 const FX_RECT* pClip)
137 : m_pSrc(std::move(source)), m_matrix(matrix), m_ResampleOptions(options) {
138 FX_RECT result_rect = m_matrix.GetUnitRect().GetClosestRect();
139 FX_RECT result_clip = result_rect;
140 if (pClip)
141 result_clip.Intersect(*pClip);
142
143 if (result_clip.IsEmpty())
144 return;
145
146 m_result = result_clip;
147 if (fabs(m_matrix.a) < fabs(m_matrix.b) / 20 &&
148 fabs(m_matrix.d) < fabs(m_matrix.c) / 20 && fabs(m_matrix.a) < 0.5f &&
149 fabs(m_matrix.d) < 0.5f) {
150 int dest_width = result_rect.Width();
151 int dest_height = result_rect.Height();
152 result_clip.Offset(-result_rect.left, -result_rect.top);
153 result_clip = result_clip.SwappedClipBox(dest_width, dest_height,
154 m_matrix.c > 0, m_matrix.b < 0);
155 m_Stretcher = std::make_unique<CFX_ImageStretcher>(
156 &m_Storer, m_pSrc, dest_height, dest_width, result_clip,
157 m_ResampleOptions);
158 m_Stretcher->Start();
159 m_type = StretchType::kRotate;
160 return;
161 }
162 if (fabs(m_matrix.b) < kFix16 && fabs(m_matrix.c) < kFix16) {
163 int dest_width =
164 static_cast<int>(m_matrix.a > 0 ? ceil(m_matrix.a) : floor(m_matrix.a));
165 int dest_height = static_cast<int>(m_matrix.d > 0 ? -ceil(m_matrix.d)
166 : -floor(m_matrix.d));
167 result_clip.Offset(-result_rect.left, -result_rect.top);
168 m_Stretcher = std::make_unique<CFX_ImageStretcher>(
169 &m_Storer, m_pSrc, dest_width, dest_height, result_clip,
170 m_ResampleOptions);
171 m_Stretcher->Start();
172 m_type = StretchType::kNormal;
173 return;
174 }
175
176 int stretch_width =
177 static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.a, m_matrix.b)));
178 int stretch_height =
179 static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.c, m_matrix.d)));
180 CFX_Matrix stretch_to_dest(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, stretch_height);
181 stretch_to_dest.Concat(
182 CFX_Matrix(m_matrix.a / stretch_width, m_matrix.b / stretch_width,
183 m_matrix.c / stretch_height, m_matrix.d / stretch_height,
184 m_matrix.e, m_matrix.f));
185 CFX_Matrix dest_to_strech = stretch_to_dest.GetInverse();
186
187 FX_RECT stretch_clip =
188 dest_to_strech.TransformRect(CFX_FloatRect(result_clip)).GetOuterRect();
189 if (!stretch_clip.Valid())
190 return;
191
192 stretch_clip.Intersect(0, 0, stretch_width, stretch_height);
193 if (!stretch_clip.Valid())
194 return;
195
196 m_dest2stretch = dest_to_strech;
197 m_StretchClip = stretch_clip;
198 m_Stretcher = std::make_unique<CFX_ImageStretcher>(
199 &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip,
200 m_ResampleOptions);
201 m_Stretcher->Start();
202 m_type = StretchType::kOther;
203}
204
206
208 if (m_type == StretchType::kNone) {
209 return false;
210 }
211
212 if (m_Stretcher->Continue(pPause))
213 return true;
214
215 switch (m_type) {
216 case StretchType::kNone:
217 // Already handled separately at the beginning of this method.
218 NOTREACHED_NORETURN();
219 case StretchType::kNormal:
220 return false;
221 case StretchType::kRotate:
222 ContinueRotate(pPause);
223 return false;
224 case StretchType::kOther:
225 ContinueOther(pPause);
226 return false;
227 }
228}
229
230void CFX_ImageTransformer::ContinueRotate(PauseIndicatorIface* pPause) {
231 if (m_Storer.GetBitmap()) {
232 m_Storer.Replace(
233 m_Storer.GetBitmap()->SwapXY(m_matrix.c > 0, m_matrix.b < 0));
234 }
235}
236
237void CFX_ImageTransformer::ContinueOther(PauseIndicatorIface* pPause) {
238 if (!m_Storer.GetBitmap())
239 return;
240
241 auto pTransformed = pdfium::MakeRetain<CFX_DIBitmap>();
242 FXDIB_Format format = m_Stretcher->source()->IsMaskFormat()
243 ? FXDIB_Format::k8bppMask
244 : FXDIB_Format::kArgb;
245 if (!pTransformed->Create(m_result.Width(), m_result.Height(), format))
246 return;
247
248 CFX_Matrix result2stretch(1.0f, 0.0f, 0.0f, 1.0f, m_result.left,
249 m_result.top);
250 result2stretch.Concat(m_dest2stretch);
251 result2stretch.Translate(-m_StretchClip.left, -m_StretchClip.top);
252
253 CalcData calc_data = {pTransformed.Get(), result2stretch,
254 m_Storer.GetBitmap()->GetBuffer().data(),
255 m_Storer.GetBitmap()->GetPitch()};
256 if (m_Storer.GetBitmap()->IsMaskFormat()) {
257 CalcAlpha(calc_data);
258 } else {
259 int Bpp = m_Storer.GetBitmap()->GetBPP() / 8;
260 if (Bpp == 1)
261 CalcMono(calc_data);
262 else
263 CalcColor(calc_data, format, Bpp);
264 }
265 m_Storer.Replace(std::move(pTransformed));
266}
267
269 return m_Storer.Detach();
270}
271
272void CFX_ImageTransformer::CalcAlpha(const CalcData& calc_data) {
273 auto func = [&calc_data](const BilinearData& data, uint8_t* dest) {
274 *dest = BilinearInterpolate(calc_data.buf, data, 1, 0);
275 };
276 DoBilinearLoop(calc_data, m_result, m_StretchClip, 1, func);
277}
278
279void CFX_ImageTransformer::CalcMono(const CalcData& calc_data) {
280 uint32_t argb[256];
281 if (m_Storer.GetBitmap()->HasPalette()) {
282 pdfium::span<const uint32_t> palette =
283 m_Storer.GetBitmap()->GetPaletteSpan();
284 for (size_t i = 0; i < std::size(argb); i++)
285 argb[i] = palette[i];
286 } else {
287 for (size_t i = 0; i < std::size(argb); i++) {
288 uint32_t v = static_cast<uint32_t>(i);
289 argb[i] = ArgbEncode(0xff, v, v, v);
290 }
291 }
292 int destBpp = calc_data.bitmap->GetBPP() / 8;
293 auto func = [&calc_data, &argb](const BilinearData& data, uint8_t* dest) {
294 uint8_t idx = BilinearInterpolate(calc_data.buf, data, 1, 0);
295 *reinterpret_cast<uint32_t*>(dest) = argb[idx];
296 };
297 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
298}
299
300void CFX_ImageTransformer::CalcColor(const CalcData& calc_data,
301 FXDIB_Format format,
302 int Bpp) {
303 DCHECK(format == FXDIB_Format::k8bppMask || format == FXDIB_Format::kArgb);
304 const int destBpp = calc_data.bitmap->GetBPP() / 8;
305 if (!m_Storer.GetBitmap()->IsAlphaFormat()) {
306 auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
307 uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
308 uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
309 uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
310 *reinterpret_cast<uint32_t*>(dest) = ArgbEncode(kOpaqueAlpha, r, g, b);
311 };
312 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
313 return;
314 }
315
316 if (format == FXDIB_Format::kArgb) {
317 auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
318 uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
319 uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
320 uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
321 uint8_t alpha = BilinearInterpolate(calc_data.buf, data, Bpp, 3);
322 *reinterpret_cast<uint32_t*>(dest) = ArgbEncode(alpha, r, g, b);
323 };
324 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
325 return;
326 }
327
328 auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) {
329 uint8_t c = BilinearInterpolate(calc_data.buf, data, Bpp, 0);
330 uint8_t m = BilinearInterpolate(calc_data.buf, data, Bpp, 1);
331 uint8_t y = BilinearInterpolate(calc_data.buf, data, Bpp, 2);
332 uint8_t k = BilinearInterpolate(calc_data.buf, data, Bpp, 3);
333 *reinterpret_cast<uint32_t*>(dest) = FXCMYK_TODIB(CmykEncode(c, m, y, k));
334 };
335 DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func);
336}
int GetBPP() const
Definition cfx_dibbase.h:57
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
CFX_Matrix(float a1, float b1, float c1, float d1, float e1, float f1)
void Translate(int32_t x, int32_t y)
CFX_FloatRect GetUnitRect() const
CFX_Matrix GetInverse() const
void Concat(const CFX_Matrix &right)
CFX_Matrix(const CFX_Matrix &other)=default
constexpr FX_CMYK CmykEncode(uint32_t c, uint32_t m, uint32_t y, uint32_t k)
Definition fx_dib.h:105
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
Definition fx_dib.h:118
FXDIB_Format
Definition fx_dib.h:19
#define FXCMYK_TODIB(cmyk)
Definition fx_dib.h:147
int FXSYS_roundf(float f)
Definition fx_system.cpp:92
float FXSYS_sqrt2(float a, float b)
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