xref: /aosp_15_r20/external/pdfium/core/fxge/dib/cfx_imagetransformer.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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