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_imagetransformer.h"
8 
9 #include <cmath>
10 #include <memory>
11 #include <utility>
12 
13 #include "core/fxge/dib/cfx_dibitmap.h"
14 #include "core/fxge/dib/cfx_imagestretcher.h"
15 #include "core/fxge/fx_dib.h"
16 #include "third_party/base/compiler_specific.h"
17 #include "third_party/base/numerics/safe_conversions.h"
18 #include "third_party/base/ptr_util.h"
19 #include "third_party/base/stl_util.h"
20 
21 namespace {
22 
23 constexpr int kBase = 256;
24 constexpr float kFix16 = 0.05f;
25 constexpr uint8_t kOpaqueAlpha = 0xff;
26 
bilinear_interpol(const uint8_t * buf,int row_offset_l,int row_offset_r,int src_col_l,int src_col_r,int res_x,int res_y,int bpp,int c_offset)27 uint8_t bilinear_interpol(const uint8_t* buf,
28                           int row_offset_l,
29                           int row_offset_r,
30                           int src_col_l,
31                           int src_col_r,
32                           int res_x,
33                           int res_y,
34                           int bpp,
35                           int c_offset) {
36   int i_resx = 255 - res_x;
37   int col_bpp_l = src_col_l * bpp;
38   int col_bpp_r = src_col_r * bpp;
39   const uint8_t* buf_u = buf + row_offset_l + c_offset;
40   const uint8_t* buf_d = buf + row_offset_r + c_offset;
41   const uint8_t* src_pos0 = buf_u + col_bpp_l;
42   const uint8_t* src_pos1 = buf_u + col_bpp_r;
43   const uint8_t* src_pos2 = buf_d + col_bpp_l;
44   const uint8_t* src_pos3 = buf_d + col_bpp_r;
45   uint8_t r_pos_0 = (*src_pos0 * i_resx + *src_pos1 * res_x) >> 8;
46   uint8_t r_pos_1 = (*src_pos2 * i_resx + *src_pos3 * res_x) >> 8;
47   return (r_pos_0 * (255 - res_y) + r_pos_1 * res_y) >> 8;
48 }
49 
bicubic_interpol(const uint8_t * buf,uint32_t pitch,const int pos_pixel[],const int u_w[],const int v_w[],int res_x,int res_y,int bpp,int c_offset)50 uint8_t bicubic_interpol(const uint8_t* buf,
51                          uint32_t pitch,
52                          const int pos_pixel[],
53                          const int u_w[],
54                          const int v_w[],
55                          int res_x,
56                          int res_y,
57                          int bpp,
58                          int c_offset) {
59   int s_result = 0;
60   for (int i = 0; i < 4; i++) {
61     int a_result = 0;
62     for (int j = 0; j < 4; j++) {
63       uint8_t val =
64           *(buf + pos_pixel[i + 4] * pitch + pos_pixel[j] * bpp + c_offset);
65       a_result += u_w[j] * val;
66     }
67     s_result += a_result * v_w[i];
68   }
69   s_result >>= 16;
70   return static_cast<uint8_t>(pdfium::clamp(s_result, 0, 255));
71 }
72 
bicubic_get_pos_weight(int pos_pixel[],int u_w[],int v_w[],int src_col_l,int src_row_l,int res_x,int res_y,int stretch_width,int stretch_height)73 void bicubic_get_pos_weight(int pos_pixel[],
74                             int u_w[],
75                             int v_w[],
76                             int src_col_l,
77                             int src_row_l,
78                             int res_x,
79                             int res_y,
80                             int stretch_width,
81                             int stretch_height) {
82   pos_pixel[0] = src_col_l - 1;
83   pos_pixel[1] = src_col_l;
84   pos_pixel[2] = src_col_l + 1;
85   pos_pixel[3] = src_col_l + 2;
86   pos_pixel[4] = src_row_l - 1;
87   pos_pixel[5] = src_row_l;
88   pos_pixel[6] = src_row_l + 1;
89   pos_pixel[7] = src_row_l + 2;
90   for (int i = 0; i < 4; i++) {
91     pos_pixel[i] = pdfium::clamp(pos_pixel[i], 0, stretch_width - 1);
92     pos_pixel[i + 4] = pdfium::clamp(pos_pixel[i + 4], 0, stretch_height - 1);
93   }
94   u_w[0] = SDP_Table[256 + res_x];
95   u_w[1] = SDP_Table[res_x];
96   u_w[2] = SDP_Table[256 - res_x];
97   u_w[3] = SDP_Table[512 - res_x];
98   v_w[0] = SDP_Table[256 + res_y];
99   v_w[1] = SDP_Table[res_y];
100   v_w[2] = SDP_Table[256 - res_y];
101   v_w[3] = SDP_Table[512 - res_y];
102 }
103 
GetTransformedFormat(const RetainPtr<CFX_DIBBase> & pDrc)104 FXDIB_Format GetTransformedFormat(const RetainPtr<CFX_DIBBase>& pDrc) {
105   if (pDrc->IsAlphaMask())
106     return FXDIB_8bppMask;
107 
108   FXDIB_Format format = pDrc->GetFormat();
109   if (format >= 1025)
110     return FXDIB_Cmyka;
111   if (format <= 32 || format == FXDIB_Argb)
112     return FXDIB_Argb;
113   return FXDIB_Rgba;
114 }
115 
WriteMonoResult(uint32_t r_bgra_cmyk,FXDIB_Format format,uint8_t * dest)116 void WriteMonoResult(uint32_t r_bgra_cmyk, FXDIB_Format format, uint8_t* dest) {
117   if (format == FXDIB_Rgba) {
118     dest[0] = static_cast<uint8_t>(r_bgra_cmyk >> 24);
119     dest[1] = static_cast<uint8_t>(r_bgra_cmyk >> 16);
120     dest[2] = static_cast<uint8_t>(r_bgra_cmyk >> 8);
121   } else {
122     *reinterpret_cast<uint32_t*>(dest) = r_bgra_cmyk;
123   }
124 }
125 
126 // Let the compiler deduce the type for |func|, which cheaper than specifying it
127 // with std::function.
128 template <typename F>
WriteColorResult(const F & func,bool bHasAlpha,FXDIB_Format format,uint8_t * dest)129 void WriteColorResult(const F& func,
130                       bool bHasAlpha,
131                       FXDIB_Format format,
132                       uint8_t* dest) {
133   uint8_t blue_c = func(0);
134   uint8_t green_m = func(1);
135   uint8_t red_y = func(2);
136 
137   uint32_t* dest32 = reinterpret_cast<uint32_t*>(dest);
138   if (bHasAlpha) {
139     if (format == FXDIB_Argb) {
140       *dest32 = FXARGB_TODIB(ArgbEncode(func(3), red_y, green_m, blue_c));
141     } else if (format == FXDIB_Rgba) {
142       dest[0] = blue_c;
143       dest[1] = green_m;
144       dest[2] = red_y;
145     } else {
146       *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3)));
147     }
148     return;
149   }
150 
151   if (format == FXDIB_Cmyka) {
152     *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3)));
153   } else {
154     *dest32 = FXARGB_TODIB(ArgbEncode(kOpaqueAlpha, red_y, green_m, blue_c));
155   }
156 }
157 
158 class CPDF_FixedMatrix {
159  public:
CPDF_FixedMatrix(const CFX_Matrix & src)160   explicit CPDF_FixedMatrix(const CFX_Matrix& src)
161       : a(FXSYS_roundf(src.a * kBase)),
162         b(FXSYS_roundf(src.b * kBase)),
163         c(FXSYS_roundf(src.c * kBase)),
164         d(FXSYS_roundf(src.d * kBase)),
165         e(FXSYS_roundf(src.e * kBase)),
166         f(FXSYS_roundf(src.f * kBase)) {}
167 
Transform(int x,int y,int * x1,int * y1) const168   void Transform(int x, int y, int* x1, int* y1) const {
169     std::pair<float, float> val = TransformInternal(x, y);
170     *x1 = pdfium::base::saturated_cast<int>(val.first / kBase);
171     *y1 = pdfium::base::saturated_cast<int>(val.second / kBase);
172   }
173 
174  protected:
TransformInternal(float x,float y) const175   std::pair<float, float> TransformInternal(float x, float y) const {
176     return std::make_pair(a * x + c * y + e + kBase / 2,
177                           b * x + d * y + f + kBase / 2);
178   }
179 
180   const int a;
181   const int b;
182   const int c;
183   const int d;
184   const int e;
185   const int f;
186 };
187 
188 class CFX_BilinearMatrix final : public CPDF_FixedMatrix {
189  public:
CFX_BilinearMatrix(const CFX_Matrix & src)190   explicit CFX_BilinearMatrix(const CFX_Matrix& src) : CPDF_FixedMatrix(src) {}
191 
Transform(int x,int y,int * x1,int * y1,int * res_x,int * res_y) const192   void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const {
193     std::pair<float, float> val = TransformInternal(x, y);
194     *x1 = pdfium::base::saturated_cast<int>(val.first / kBase);
195     *y1 = pdfium::base::saturated_cast<int>(val.second / kBase);
196 
197     *res_x = static_cast<int>(val.first) % kBase;
198     *res_y = static_cast<int>(val.second) % kBase;
199     if (*res_x < 0 && *res_x > -kBase)
200       *res_x = kBase + *res_x;
201     if (*res_y < 0 && *res_y > -kBase)
202       *res_y = kBase + *res_y;
203   }
204 };
205 
InStretchBounds(const FX_RECT & clip_rect,int col,int row)206 bool InStretchBounds(const FX_RECT& clip_rect, int col, int row) {
207   return col >= 0 && col <= clip_rect.Width() && row >= 0 &&
208          row <= clip_rect.Height();
209 }
210 
AdjustCoords(const FX_RECT & clip_rect,int * col,int * row)211 void AdjustCoords(const FX_RECT& clip_rect, int* col, int* row) {
212   int& src_col = *col;
213   int& src_row = *row;
214   if (src_col == clip_rect.Width())
215     src_col--;
216   if (src_row == clip_rect.Height())
217     src_row--;
218 }
219 
220 // Let the compiler deduce the type for |func|, which cheaper than specifying it
221 // with std::function.
222 template <typename F>
DoBilinearLoop(const CFX_ImageTransformer::CalcData & cdata,const FX_RECT & result_rect,const FX_RECT & clip_rect,int increment,const F & func)223 void DoBilinearLoop(const CFX_ImageTransformer::CalcData& cdata,
224                     const FX_RECT& result_rect,
225                     const FX_RECT& clip_rect,
226                     int increment,
227                     const F& func) {
228   CFX_BilinearMatrix matrix_fix(cdata.matrix);
229   for (int row = 0; row < result_rect.Height(); row++) {
230     uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
231     for (int col = 0; col < result_rect.Width(); col++) {
232       CFX_ImageTransformer::BilinearData d;
233       d.res_x = 0;
234       d.res_y = 0;
235       d.src_col_l = 0;
236       d.src_row_l = 0;
237       matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
238                            &d.res_y);
239       if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) {
240         AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l);
241         d.src_col_r = d.src_col_l + 1;
242         d.src_row_r = d.src_row_l + 1;
243         AdjustCoords(clip_rect, &d.src_col_r, &d.src_row_r);
244         d.row_offset_l = d.src_row_l * cdata.pitch;
245         d.row_offset_r = d.src_row_r * cdata.pitch;
246         func(d, dest);
247       }
248       dest += increment;
249     }
250   }
251 }
252 
253 // Let the compiler deduce the type for |func|, which cheaper than specifying it
254 // with std::function.
255 template <typename F>
DoBicubicLoop(const CFX_ImageTransformer::CalcData & cdata,const FX_RECT & result_rect,const FX_RECT & clip_rect,int increment,const F & func)256 void DoBicubicLoop(const CFX_ImageTransformer::CalcData& cdata,
257                    const FX_RECT& result_rect,
258                    const FX_RECT& clip_rect,
259                    int increment,
260                    const F& func) {
261   CFX_BilinearMatrix matrix_fix(cdata.matrix);
262   for (int row = 0; row < result_rect.Height(); row++) {
263     uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
264     for (int col = 0; col < result_rect.Width(); col++) {
265       CFX_ImageTransformer::BicubicData d;
266       d.res_x = 0;
267       d.res_y = 0;
268       d.src_col_l = 0;
269       d.src_row_l = 0;
270       matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x,
271                            &d.res_y);
272       if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) {
273         AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l);
274         bicubic_get_pos_weight(d.pos_pixel, d.u_w, d.v_w, d.src_col_l,
275                                d.src_row_l, d.res_x, d.res_y, clip_rect.Width(),
276                                clip_rect.Height());
277         func(d, dest);
278       }
279       dest += increment;
280     }
281   }
282 }
283 
284 // Let the compiler deduce the type for |func|, which cheaper than specifying it
285 // with std::function.
286 template <typename F>
DoDownSampleLoop(const CFX_ImageTransformer::CalcData & cdata,const FX_RECT & result_rect,const FX_RECT & clip_rect,int increment,const F & func)287 void DoDownSampleLoop(const CFX_ImageTransformer::CalcData& cdata,
288                       const FX_RECT& result_rect,
289                       const FX_RECT& clip_rect,
290                       int increment,
291                       const F& func) {
292   CPDF_FixedMatrix matrix_fix(cdata.matrix);
293   for (int row = 0; row < result_rect.Height(); row++) {
294     uint8_t* dest = cdata.bitmap->GetWritableScanline(row);
295     for (int col = 0; col < result_rect.Width(); col++) {
296       CFX_ImageTransformer::DownSampleData d;
297       d.src_col = 0;
298       d.src_row = 0;
299       matrix_fix.Transform(col, row, &d.src_col, &d.src_row);
300       if (LIKELY(InStretchBounds(clip_rect, d.src_col, d.src_row))) {
301         AdjustCoords(clip_rect, &d.src_col, &d.src_row);
302         func(d, dest);
303       }
304       dest += increment;
305     }
306   }
307 }
308 
309 }  // namespace
310 
CFX_ImageTransformer(const RetainPtr<CFX_DIBBase> & pSrc,const CFX_Matrix & matrix,const FXDIB_ResampleOptions & options,const FX_RECT * pClip)311 CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr<CFX_DIBBase>& pSrc,
312                                            const CFX_Matrix& matrix,
313                                            const FXDIB_ResampleOptions& options,
314                                            const FX_RECT* pClip)
315     : m_pSrc(pSrc), m_matrix(matrix), m_ResampleOptions(options) {
316   FX_RECT result_rect = m_matrix.GetUnitRect().GetClosestRect();
317   FX_RECT result_clip = result_rect;
318   if (pClip)
319     result_clip.Intersect(*pClip);
320 
321   if (result_clip.IsEmpty())
322     return;
323 
324   m_result = result_clip;
325   if (fabs(m_matrix.a) < fabs(m_matrix.b) / 20 &&
326       fabs(m_matrix.d) < fabs(m_matrix.c) / 20 && fabs(m_matrix.a) < 0.5f &&
327       fabs(m_matrix.d) < 0.5f) {
328     int dest_width = result_rect.Width();
329     int dest_height = result_rect.Height();
330     result_clip.Offset(-result_rect.left, -result_rect.top);
331     result_clip = FXDIB_SwapClipBox(result_clip, dest_width, dest_height,
332                                     m_matrix.c > 0, m_matrix.b < 0);
333     m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
334         &m_Storer, m_pSrc, dest_height, dest_width, result_clip,
335         m_ResampleOptions);
336     m_Stretcher->Start();
337     m_type = kRotate;
338     return;
339   }
340   if (fabs(m_matrix.b) < kFix16 && fabs(m_matrix.c) < kFix16) {
341     int dest_width =
342         static_cast<int>(m_matrix.a > 0 ? ceil(m_matrix.a) : floor(m_matrix.a));
343     int dest_height = static_cast<int>(m_matrix.d > 0 ? -ceil(m_matrix.d)
344                                                       : -floor(m_matrix.d));
345     result_clip.Offset(-result_rect.left, -result_rect.top);
346     m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
347         &m_Storer, m_pSrc, dest_width, dest_height, result_clip,
348         m_ResampleOptions);
349     m_Stretcher->Start();
350     m_type = kNormal;
351     return;
352   }
353 
354   int stretch_width =
355       static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.a, m_matrix.b)));
356   int stretch_height =
357       static_cast<int>(ceil(FXSYS_sqrt2(m_matrix.c, m_matrix.d)));
358   CFX_Matrix stretch_to_dest(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, stretch_height);
359   stretch_to_dest.Concat(
360       CFX_Matrix(m_matrix.a / stretch_width, m_matrix.b / stretch_width,
361                  m_matrix.c / stretch_height, m_matrix.d / stretch_height,
362                  m_matrix.e, m_matrix.f));
363   CFX_Matrix dest_to_strech = stretch_to_dest.GetInverse();
364 
365   FX_RECT stretch_clip =
366       dest_to_strech.TransformRect(CFX_FloatRect(result_clip)).GetOuterRect();
367   if (!stretch_clip.Valid())
368     return;
369 
370   stretch_clip.Intersect(0, 0, stretch_width, stretch_height);
371   if (!stretch_clip.Valid())
372     return;
373 
374   m_dest2stretch = dest_to_strech;
375   m_StretchClip = stretch_clip;
376   m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
377       &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip,
378       m_ResampleOptions);
379   m_Stretcher->Start();
380   m_type = kOther;
381 }
382 
383 CFX_ImageTransformer::~CFX_ImageTransformer() = default;
384 
Continue(PauseIndicatorIface * pPause)385 bool CFX_ImageTransformer::Continue(PauseIndicatorIface* pPause) {
386   if (m_type == kNone)
387     return false;
388 
389   if (m_Stretcher->Continue(pPause))
390     return true;
391 
392   switch (m_type) {
393     case kNormal:
394       break;
395     case kRotate:
396       ContinueRotate(pPause);
397       break;
398     case kOther:
399       ContinueOther(pPause);
400       break;
401     default:
402       NOTREACHED();
403       break;
404   }
405   return false;
406 }
407 
ContinueRotate(PauseIndicatorIface * pPause)408 void CFX_ImageTransformer::ContinueRotate(PauseIndicatorIface* pPause) {
409   if (m_Storer.GetBitmap()) {
410     m_Storer.Replace(
411         m_Storer.GetBitmap()->SwapXY(m_matrix.c > 0, m_matrix.b < 0));
412   }
413 }
414 
ContinueOther(PauseIndicatorIface * pPause)415 void CFX_ImageTransformer::ContinueOther(PauseIndicatorIface* pPause) {
416   if (!m_Storer.GetBitmap())
417     return;
418 
419   auto pTransformed = pdfium::MakeRetain<CFX_DIBitmap>();
420   FXDIB_Format format = GetTransformedFormat(m_Stretcher->source());
421   if (!pTransformed->Create(m_result.Width(), m_result.Height(), format))
422     return;
423 
424   const auto& pSrcMask = m_Storer.GetBitmap()->m_pAlphaMask;
425   const uint8_t* pSrcMaskBuf = pSrcMask ? pSrcMask->GetBuffer() : nullptr;
426 
427   pTransformed->Clear(0);
428   auto& pDestMask = pTransformed->m_pAlphaMask;
429   if (pDestMask)
430     pDestMask->Clear(0);
431 
432   CFX_Matrix result2stretch(1.0f, 0.0f, 0.0f, 1.0f, m_result.left,
433                             m_result.top);
434   result2stretch.Concat(m_dest2stretch);
435   result2stretch.Translate(-m_StretchClip.left, -m_StretchClip.top);
436   if (!pSrcMaskBuf && pDestMask) {
437     pDestMask->Clear(0xff000000);
438   } else if (pDestMask) {
439     CalcData cdata = {
440         pDestMask.Get(),
441         result2stretch,
442         pSrcMaskBuf,
443         m_Storer.GetBitmap()->m_pAlphaMask->GetPitch(),
444     };
445     CalcMask(cdata);
446   }
447 
448   CalcData cdata = {pTransformed.Get(), result2stretch,
449                     m_Storer.GetBitmap()->GetBuffer(),
450                     m_Storer.GetBitmap()->GetPitch()};
451   if (m_Storer.GetBitmap()->IsAlphaMask()) {
452     CalcAlpha(cdata);
453   } else {
454     int Bpp = m_Storer.GetBitmap()->GetBPP() / 8;
455     if (Bpp == 1)
456       CalcMono(cdata, format);
457     else
458       CalcColor(cdata, format, Bpp);
459   }
460   m_Storer.Replace(std::move(pTransformed));
461 }
462 
DetachBitmap()463 RetainPtr<CFX_DIBitmap> CFX_ImageTransformer::DetachBitmap() {
464   return m_Storer.Detach();
465 }
466 
CalcMask(const CalcData & cdata)467 void CFX_ImageTransformer::CalcMask(const CalcData& cdata) {
468   if (IsBilinear()) {
469     auto func = [&cdata](const BilinearData& data, uint8_t* dest) {
470       *dest = bilinear_interpol(cdata.buf, data.row_offset_l, data.row_offset_r,
471                                 data.src_col_l, data.src_col_r, data.res_x,
472                                 data.res_y, 1, 0);
473     };
474     DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func);
475   } else if (IsBiCubic()) {
476     auto func = [&cdata](const BicubicData& data, uint8_t* dest) {
477       *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w,
478                                data.v_w, data.res_x, data.res_y, 1, 0);
479     };
480     DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func);
481   } else {
482     auto func = [&cdata](const DownSampleData& data, uint8_t* dest) {
483       *dest = cdata.buf[data.src_row * cdata.pitch + data.src_col];
484     };
485     DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func);
486   }
487 }
488 
CalcAlpha(const CalcData & cdata)489 void CFX_ImageTransformer::CalcAlpha(const CalcData& cdata) {
490   if (IsBilinear()) {
491     auto func = [&cdata](const BilinearData& data, uint8_t* dest) {
492       *dest = bilinear_interpol(cdata.buf, data.row_offset_l, data.row_offset_r,
493                                 data.src_col_l, data.src_col_r, data.res_x,
494                                 data.res_y, 1, 0);
495     };
496     DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func);
497   } else if (IsBiCubic()) {
498     auto func = [&cdata](const BicubicData& data, uint8_t* dest) {
499       *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w,
500                                data.v_w, data.res_x, data.res_y, 1, 0);
501     };
502     DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func);
503   } else {
504     auto func = [&cdata](const DownSampleData& data, uint8_t* dest) {
505       const uint8_t* src_pixel =
506           cdata.buf + cdata.pitch * data.src_row + data.src_col;
507       *dest = *src_pixel;
508     };
509     DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func);
510   }
511 }
512 
CalcMono(const CalcData & cdata,FXDIB_Format format)513 void CFX_ImageTransformer::CalcMono(const CalcData& cdata,
514                                     FXDIB_Format format) {
515   uint32_t argb[256];
516   FX_ARGB* pPal = m_Storer.GetBitmap()->GetPalette();
517   if (pPal) {
518     for (size_t i = 0; i < FX_ArraySize(argb); i++)
519       argb[i] = pPal[i];
520   } else if (m_Storer.GetBitmap()->IsCmykImage()) {
521     for (size_t i = 0; i < FX_ArraySize(argb); i++)
522       argb[i] = 255 - i;
523   } else {
524     for (size_t i = 0; i < FX_ArraySize(argb); i++)
525       argb[i] = 0xff000000 | (i * 0x010101);
526   }
527   int destBpp = cdata.bitmap->GetBPP() / 8;
528   if (IsBilinear()) {
529     auto func = [&cdata, format, &argb](const BilinearData& data,
530                                         uint8_t* dest) {
531       uint8_t idx = bilinear_interpol(
532           cdata.buf, data.row_offset_l, data.row_offset_r, data.src_col_l,
533           data.src_col_r, data.res_x, data.res_y, 1, 0);
534       uint32_t r_bgra_cmyk = argb[idx];
535       WriteMonoResult(r_bgra_cmyk, format, dest);
536     };
537     DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func);
538   } else if (IsBiCubic()) {
539     auto func = [&cdata, format, &argb](const BicubicData& data,
540                                         uint8_t* dest) {
541       uint32_t r_bgra_cmyk = argb[bicubic_interpol(
542           cdata.buf, cdata.pitch, data.pos_pixel, data.u_w, data.v_w,
543           data.res_x, data.res_y, 1, 0)];
544       WriteMonoResult(r_bgra_cmyk, format, dest);
545     };
546     DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func);
547   } else {
548     auto func = [&cdata, format, &argb](const DownSampleData& data,
549                                         uint8_t* dest) {
550       uint32_t r_bgra_cmyk =
551           argb[cdata.buf[data.src_row * cdata.pitch + data.src_col]];
552       WriteMonoResult(r_bgra_cmyk, format, dest);
553     };
554     DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func);
555   }
556 }
557 
CalcColor(const CalcData & cdata,FXDIB_Format format,int Bpp)558 void CFX_ImageTransformer::CalcColor(const CalcData& cdata,
559                                      FXDIB_Format format,
560                                      int Bpp) {
561   bool bHasAlpha = m_Storer.GetBitmap()->HasAlpha();
562   int destBpp = cdata.bitmap->GetBPP() / 8;
563   if (IsBilinear()) {
564     auto func = [&cdata, format, Bpp, bHasAlpha](const BilinearData& data,
565                                                  uint8_t* dest) {
566       auto bilinear_interpol_func = [&cdata, &data, Bpp](int offset) {
567         return bilinear_interpol(
568             cdata.buf, data.row_offset_l, data.row_offset_r, data.src_col_l,
569             data.src_col_r, data.res_x, data.res_y, Bpp, offset);
570       };
571       WriteColorResult(bilinear_interpol_func, bHasAlpha, format, dest);
572     };
573     DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func);
574   } else if (IsBiCubic()) {
575     auto func = [&cdata, format, Bpp, bHasAlpha](const BicubicData& data,
576                                                  uint8_t* dest) {
577       auto bicubic_interpol_func = [&cdata, &data, Bpp](int offset) {
578         return bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel,
579                                 data.u_w, data.v_w, data.res_x, data.res_y, Bpp,
580                                 offset);
581       };
582       WriteColorResult(bicubic_interpol_func, bHasAlpha, format, dest);
583     };
584     DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func);
585   } else {
586     auto func = [&cdata, format, bHasAlpha, Bpp](const DownSampleData& data,
587                                                  uint8_t* dest) {
588       const uint8_t* src_pos =
589           cdata.buf + data.src_row * cdata.pitch + data.src_col * Bpp;
590       auto sample_func = [src_pos](int offset) { return src_pos[offset]; };
591       WriteColorResult(sample_func, bHasAlpha, format, dest);
592     };
593     DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func);
594   }
595 }
596 
IsBilinear() const597 bool CFX_ImageTransformer::IsBilinear() const {
598   return !IsBiCubic();
599 }
600 
IsBiCubic() const601 bool CFX_ImageTransformer::IsBiCubic() const {
602   return m_ResampleOptions.bInterpolateBicubic;
603 }
604