• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/cstretchengine.h"
8 
9 #include <algorithm>
10 #include <utility>
11 
12 #include "core/fxcrt/pauseindicator_iface.h"
13 #include "core/fxge/dib/cfx_dibbase.h"
14 #include "core/fxge/dib/cfx_dibitmap.h"
15 #include "core/fxge/dib/scanlinecomposer_iface.h"
16 #include "core/fxge/fx_dib.h"
17 #include "third_party/base/stl_util.h"
18 
19 namespace {
20 
21 constexpr int kMaxDestValue = 16711680;
22 
GetPitchRoundUpTo4Bytes(int bits_per_pixel)23 int GetPitchRoundUpTo4Bytes(int bits_per_pixel) {
24   return (bits_per_pixel + 31) / 32 * 4;
25 }
26 
27 }  // namespace
28 
29 CStretchEngine::CWeightTable::CWeightTable() = default;
30 
31 CStretchEngine::CWeightTable::~CWeightTable() = default;
32 
GetPixelWeightSize() const33 size_t CStretchEngine::CWeightTable::GetPixelWeightSize() const {
34   return m_ItemSize / sizeof(int) - 2;
35 }
36 
Calc(int dest_len,int dest_min,int dest_max,int src_len,int src_min,int src_max,const FXDIB_ResampleOptions & options)37 bool CStretchEngine::CWeightTable::Calc(int dest_len,
38                                         int dest_min,
39                                         int dest_max,
40                                         int src_len,
41                                         int src_min,
42                                         int src_max,
43                                         const FXDIB_ResampleOptions& options) {
44   m_WeightTables.clear();
45   m_dwWeightTablesSize = 0;
46   const double scale = static_cast<float>(src_len) / dest_len;
47   const double base = dest_len < 0 ? src_len : 0;
48   const int ext_size = options.bInterpolateBicubic ? 3 : 1;
49   m_ItemSize =
50       sizeof(int) * 2 +
51       static_cast<int>(sizeof(int) *
52                        (ceil(fabs(static_cast<float>(scale))) + ext_size));
53 
54   m_DestMin = dest_min;
55   if (dest_max - dest_min > static_cast<int>((1U << 30) - 4) / m_ItemSize)
56     return false;
57 
58   m_dwWeightTablesSize = (dest_max - dest_min) * m_ItemSize + 4;
59   m_WeightTables.resize(m_dwWeightTablesSize);
60   if (options.bNoSmoothing || fabs(static_cast<float>(scale)) < 1.0f) {
61     for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
62       PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
63       double src_pos = dest_pixel * scale + scale / 2 + base;
64       if (options.bInterpolateBilinear) {
65         pixel_weights.m_SrcStart =
66             static_cast<int>(floor(static_cast<float>(src_pos) - 1.0f / 2));
67         pixel_weights.m_SrcEnd =
68             static_cast<int>(floor(static_cast<float>(src_pos) + 1.0f / 2));
69         pixel_weights.m_SrcStart = std::max(pixel_weights.m_SrcStart, src_min);
70         pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1);
71         if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
72           pixel_weights.m_Weights[0] = 65536;
73         } else {
74           pixel_weights.m_Weights[1] =
75               FXSYS_roundf(static_cast<float>(
76                                src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
77                            65536);
78           pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
79         }
80       } else if (options.bInterpolateBicubic) {
81         pixel_weights.m_SrcStart =
82             static_cast<int>(floor(static_cast<float>(src_pos) - 1.0f / 2));
83         pixel_weights.m_SrcEnd =
84             static_cast<int>(floor(static_cast<float>(src_pos) + 1.0f / 2));
85         int start = pixel_weights.m_SrcStart - 1;
86         int end = pixel_weights.m_SrcEnd + 1;
87         start = std::max(start, src_min);
88         end = std::min(end, src_max - 1);
89         if (pixel_weights.m_SrcStart < src_min) {
90           src_pos += src_min - pixel_weights.m_SrcStart;
91           pixel_weights.m_SrcStart = src_min;
92         }
93         pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1);
94         int weight = FXSYS_roundf(
95             static_cast<float>(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) *
96             256);
97         if (start == end) {
98           pixel_weights.m_Weights[0] =
99               (SDP_Table[256 + weight] + SDP_Table[weight] +
100                SDP_Table[256 - weight] + SDP_Table[512 - weight])
101               << 8;
102         } else if ((start == pixel_weights.m_SrcStart &&
103                     (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd ||
104                      end == pixel_weights.m_SrcEnd) &&
105                     start < end) ||
106                    (start < pixel_weights.m_SrcStart &&
107                     pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd &&
108                     end == pixel_weights.m_SrcEnd)) {
109           if (start < pixel_weights.m_SrcStart) {
110             pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8;
111             pixel_weights.m_Weights[1] =
112                 (SDP_Table[weight] + SDP_Table[256 - weight] +
113                  SDP_Table[512 - weight])
114                 << 8;
115           } else {
116             if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
117               pixel_weights.m_Weights[0] =
118                   (SDP_Table[256 + weight] + SDP_Table[weight] +
119                    SDP_Table[256 - weight])
120                   << 8;
121               pixel_weights.m_Weights[1] = SDP_Table[512 - weight] << 8;
122             } else {
123               pixel_weights.m_Weights[0] =
124                   (SDP_Table[256 + weight] + SDP_Table[weight]) << 8;
125               pixel_weights.m_Weights[1] =
126                   (SDP_Table[256 - weight] + SDP_Table[512 - weight]) << 8;
127             }
128           }
129           if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
130             pixel_weights.m_SrcEnd = end;
131           }
132           if (start < pixel_weights.m_SrcStart) {
133             pixel_weights.m_SrcStart = start;
134           }
135         } else if (start == pixel_weights.m_SrcStart &&
136                    start < pixel_weights.m_SrcEnd &&
137                    pixel_weights.m_SrcEnd < end) {
138           pixel_weights.m_Weights[0] =
139               (SDP_Table[256 + weight] + SDP_Table[weight]) << 8;
140           pixel_weights.m_Weights[1] = SDP_Table[256 - weight] << 8;
141           pixel_weights.m_Weights[2] = SDP_Table[512 - weight] << 8;
142           pixel_weights.m_SrcEnd = end;
143         } else if (start < pixel_weights.m_SrcStart &&
144                    pixel_weights.m_SrcStart < pixel_weights.m_SrcEnd &&
145                    pixel_weights.m_SrcEnd == end) {
146           pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8;
147           pixel_weights.m_Weights[1] = SDP_Table[weight] << 8;
148           pixel_weights.m_Weights[2] =
149               (SDP_Table[256 - weight] + SDP_Table[512 - weight]) << 8;
150           pixel_weights.m_SrcStart = start;
151         } else {
152           pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8;
153           pixel_weights.m_Weights[1] = SDP_Table[weight] << 8;
154           pixel_weights.m_Weights[2] = SDP_Table[256 - weight] << 8;
155           pixel_weights.m_Weights[3] = SDP_Table[512 - weight] << 8;
156           pixel_weights.m_SrcStart = start;
157           pixel_weights.m_SrcEnd = end;
158         }
159       } else {
160         int pixel_pos = static_cast<int>(floor(static_cast<float>(src_pos)));
161         pixel_weights.m_SrcStart = std::max(pixel_pos, src_min);
162         pixel_weights.m_SrcEnd = std::min(pixel_pos, src_max - 1);
163         pixel_weights.m_Weights[0] = 65536;
164       }
165     }
166     return true;
167   }
168 
169   for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
170     PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
171     double src_start = dest_pixel * scale + base;
172     double src_end = src_start + scale;
173     int start_i = floor(std::min(src_start, src_end));
174     int end_i = floor(std::max(src_start, src_end));
175     start_i = std::max(start_i, src_min);
176     end_i = std::min(end_i, src_max - 1);
177     if (start_i > end_i) {
178       start_i = std::min(start_i, src_max - 1);
179       pixel_weights.m_SrcStart = start_i;
180       pixel_weights.m_SrcEnd = start_i;
181       continue;
182     }
183     pixel_weights.m_SrcStart = start_i;
184     pixel_weights.m_SrcEnd = end_i;
185     for (int j = start_i; j <= end_i; ++j) {
186       double dest_start = (j - base) / scale;
187       double dest_end = (j + 1 - base) / scale;
188       if (dest_start > dest_end)
189         std::swap(dest_start, dest_end);
190       double area_start = std::max(dest_start, static_cast<double>(dest_pixel));
191       double area_end = std::min(dest_end, static_cast<double>(dest_pixel + 1));
192       double weight = std::max(0.0, area_end - area_start);
193       if (weight == 0 && j == end_i) {
194         --pixel_weights.m_SrcEnd;
195         break;
196       }
197       size_t idx = j - start_i;
198       if (idx >= GetPixelWeightSize())
199         return false;
200 
201       pixel_weights.m_Weights[idx] = FXSYS_roundf(weight * 65536);
202     }
203   }
204   return true;
205 }
206 
GetPixelWeight(int pixel) const207 const PixelWeight* CStretchEngine::CWeightTable::GetPixelWeight(
208     int pixel) const {
209   ASSERT(pixel >= m_DestMin);
210   return reinterpret_cast<const PixelWeight*>(
211       &m_WeightTables[(pixel - m_DestMin) * m_ItemSize]);
212 }
213 
GetValueFromPixelWeight(PixelWeight * pWeight,int index) const214 int* CStretchEngine::CWeightTable::GetValueFromPixelWeight(PixelWeight* pWeight,
215                                                            int index) const {
216   if (index < pWeight->m_SrcStart)
217     return nullptr;
218 
219   size_t idx = index - pWeight->m_SrcStart;
220   return idx < GetPixelWeightSize() ? &pWeight->m_Weights[idx] : nullptr;
221 }
222 
CStretchEngine(ScanlineComposerIface * pDestBitmap,FXDIB_Format dest_format,int dest_width,int dest_height,const FX_RECT & clip_rect,const RetainPtr<CFX_DIBBase> & pSrcBitmap,const FXDIB_ResampleOptions & options)223 CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap,
224                                FXDIB_Format dest_format,
225                                int dest_width,
226                                int dest_height,
227                                const FX_RECT& clip_rect,
228                                const RetainPtr<CFX_DIBBase>& pSrcBitmap,
229                                const FXDIB_ResampleOptions& options)
230     : m_DestFormat(dest_format),
231       m_DestBpp(GetBppFromFormat(dest_format)),
232       m_SrcBpp(GetBppFromFormat(pSrcBitmap->GetFormat())),
233       m_bHasAlpha(GetIsAlphaFromFormat(pSrcBitmap->GetFormat())),
234       m_pSource(pSrcBitmap),
235       m_pSrcPalette(pSrcBitmap->GetPalette()),
236       m_SrcWidth(pSrcBitmap->GetWidth()),
237       m_SrcHeight(pSrcBitmap->GetHeight()),
238       m_pDestBitmap(pDestBitmap),
239       m_DestWidth(dest_width),
240       m_DestHeight(dest_height),
241       m_DestClip(clip_rect) {
242   uint32_t size = clip_rect.Width();
243   if (size && m_DestBpp > static_cast<int>(INT_MAX / size))
244     return;
245 
246   size *= m_DestBpp;
247   if (size > INT_MAX - 31)
248     return;
249 
250   size = GetPitchRoundUpTo4Bytes(size);
251   m_DestScanline.resize(size);
252   if (dest_format == FXDIB_Rgb32)
253     std::fill(m_DestScanline.begin(), m_DestScanline.end(), 255);
254   m_InterPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * m_DestBpp);
255   m_ExtraMaskPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * 8);
256   if (options.bNoSmoothing) {
257     m_ResampleOptions.bNoSmoothing = true;
258   } else {
259     bool bInterpol =
260         options.bInterpolateBilinear || options.bInterpolateBicubic;
261     if (!bInterpol && abs(dest_width) != 0 &&
262         abs(dest_height) / 8 < static_cast<long long>(m_SrcWidth) *
263                                    m_SrcHeight / abs(dest_width)) {
264       m_ResampleOptions.bInterpolateBilinear = true;
265     } else {
266       m_ResampleOptions = options;
267     }
268   }
269   double scale_x = static_cast<float>(m_SrcWidth) / m_DestWidth;
270   double scale_y = static_cast<float>(m_SrcHeight) / m_DestHeight;
271   double base_x = m_DestWidth > 0 ? 0.0f : m_DestWidth;
272   double base_y = m_DestHeight > 0 ? 0.0f : m_DestHeight;
273   double src_left = scale_x * (clip_rect.left + base_x);
274   double src_right = scale_x * (clip_rect.right + base_x);
275   double src_top = scale_y * (clip_rect.top + base_y);
276   double src_bottom = scale_y * (clip_rect.bottom + base_y);
277   if (src_left > src_right)
278     std::swap(src_left, src_right);
279   if (src_top > src_bottom)
280     std::swap(src_top, src_bottom);
281   m_SrcClip.left = static_cast<int>(floor(src_left));
282   m_SrcClip.right = static_cast<int>(ceil(src_right));
283   m_SrcClip.top = static_cast<int>(floor(src_top));
284   m_SrcClip.bottom = static_cast<int>(ceil(src_bottom));
285   FX_RECT src_rect(0, 0, m_SrcWidth, m_SrcHeight);
286   m_SrcClip.Intersect(src_rect);
287 
288   switch (m_SrcBpp) {
289     case 1:
290       m_TransMethod = m_DestBpp == 8 ? TransformMethod::k1BppTo8Bpp
291                                      : TransformMethod::k1BppToManyBpp;
292       break;
293     case 8:
294       if (m_DestBpp == 8) {
295         m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppTo8BppWithAlpha
296                                     : TransformMethod::k8BppTo8Bpp;
297       } else {
298         m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppToManyBppWithAlpha
299                                     : TransformMethod::k8BppToManyBpp;
300       }
301       break;
302     default:
303       m_TransMethod = m_bHasAlpha ? TransformMethod::kManyBpptoManyBppWithAlpha
304                                   : TransformMethod::kManyBpptoManyBpp;
305       break;
306   }
307 }
308 
~CStretchEngine()309 CStretchEngine::~CStretchEngine() {}
310 
Continue(PauseIndicatorIface * pPause)311 bool CStretchEngine::Continue(PauseIndicatorIface* pPause) {
312   while (m_State == State::kHorizontal) {
313     if (ContinueStretchHorz(pPause))
314       return true;
315 
316     m_State = State::kVertical;
317     StretchVert();
318   }
319   return false;
320 }
321 
StartStretchHorz()322 bool CStretchEngine::StartStretchHorz() {
323   if (m_DestWidth == 0 || m_InterPitch == 0 || m_DestScanline.empty())
324     return false;
325 
326   if (m_SrcClip.Height() == 0 ||
327       m_SrcClip.Height() > (1 << 29) / m_InterPitch) {
328     return false;
329   }
330 
331   m_InterBuf.resize(m_SrcClip.Height() * m_InterPitch);
332   if (m_pSource && m_bHasAlpha && m_pSource->m_pAlphaMask) {
333     m_ExtraAlphaBuf.resize(m_SrcClip.Height(), m_ExtraMaskPitch);
334     m_DestMaskScanline.resize(m_ExtraMaskPitch);
335   }
336   bool ret = m_WeightTable.Calc(m_DestWidth, m_DestClip.left, m_DestClip.right,
337                                 m_SrcWidth, m_SrcClip.left, m_SrcClip.right,
338                                 m_ResampleOptions);
339   if (!ret)
340     return false;
341 
342   m_CurRow = m_SrcClip.top;
343   m_State = State::kHorizontal;
344   return true;
345 }
346 
ContinueStretchHorz(PauseIndicatorIface * pPause)347 bool CStretchEngine::ContinueStretchHorz(PauseIndicatorIface* pPause) {
348   if (!m_DestWidth)
349     return false;
350   if (m_pSource->SkipToScanline(m_CurRow, pPause))
351     return true;
352 
353   int Bpp = m_DestBpp / 8;
354   static const int kStrechPauseRows = 10;
355   int rows_to_go = kStrechPauseRows;
356   for (; m_CurRow < m_SrcClip.bottom; ++m_CurRow) {
357     if (rows_to_go == 0) {
358       if (pPause && pPause->NeedToPauseNow())
359         return true;
360 
361       rows_to_go = kStrechPauseRows;
362     }
363 
364     const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow);
365     uint8_t* dest_scan =
366         m_InterBuf.data() + (m_CurRow - m_SrcClip.top) * m_InterPitch;
367     const uint8_t* src_scan_mask = nullptr;
368     uint8_t* dest_scan_mask = nullptr;
369     if (!m_ExtraAlphaBuf.empty()) {
370       src_scan_mask = m_pSource->m_pAlphaMask->GetScanline(m_CurRow);
371       dest_scan_mask = m_ExtraAlphaBuf.data() +
372                        (m_CurRow - m_SrcClip.top) * m_ExtraMaskPitch;
373     }
374     // TODO(npm): reduce duplicated code here
375     switch (m_TransMethod) {
376       case TransformMethod::k1BppTo8Bpp:
377       case TransformMethod::k1BppToManyBpp: {
378         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
379           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
380           int dest_a = 0;
381           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
382             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
383             if (!pWeight)
384               return false;
385 
386             int pixel_weight = *pWeight;
387             if (src_scan[j / 8] & (1 << (7 - j % 8)))
388               dest_a += pixel_weight * 255;
389           }
390           if (m_ResampleOptions.bInterpolateBicubic)
391             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
392           *dest_scan++ = static_cast<uint8_t>(dest_a >> 16);
393         }
394         break;
395       }
396       case TransformMethod::k8BppTo8Bpp: {
397         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
398           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
399           int dest_a = 0;
400           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
401             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
402             if (!pWeight)
403               return false;
404 
405             int pixel_weight = *pWeight;
406             dest_a += pixel_weight * src_scan[j];
407           }
408           if (m_ResampleOptions.bInterpolateBicubic)
409             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
410           *dest_scan++ = static_cast<uint8_t>(dest_a >> 16);
411         }
412         break;
413       }
414       case TransformMethod::k8BppTo8BppWithAlpha: {
415         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
416           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
417           int dest_a = 0;
418           int dest_r = 0;
419           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
420             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
421             if (!pWeight)
422               return false;
423 
424             int pixel_weight = *pWeight;
425             pixel_weight = pixel_weight * src_scan_mask[j] / 255;
426             dest_r += pixel_weight * src_scan[j];
427             dest_a += pixel_weight;
428           }
429           if (m_ResampleOptions.bInterpolateBicubic) {
430             dest_r = pdfium::clamp(dest_r, 0, kMaxDestValue);
431             dest_a = pdfium::clamp(dest_a, 0, 65536);
432           }
433           *dest_scan++ = static_cast<uint8_t>(dest_r >> 16);
434           *dest_scan_mask++ = static_cast<uint8_t>((dest_a * 255) >> 16);
435         }
436         break;
437       }
438       case TransformMethod::k8BppToManyBpp: {
439         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
440           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
441           int dest_r_y = 0;
442           int dest_g_m = 0;
443           int dest_b_c = 0;
444           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
445             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
446             if (!pWeight)
447               return false;
448 
449             int pixel_weight = *pWeight;
450             unsigned long argb_cmyk = m_pSrcPalette[src_scan[j]];
451             if (m_DestFormat == FXDIB_Rgb) {
452               dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
453               dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
454               dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk);
455             } else {
456               dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 24);
457               dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
458               dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
459             }
460           }
461           if (m_ResampleOptions.bInterpolateBicubic) {
462             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
463             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
464             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
465           }
466           *dest_scan++ = static_cast<uint8_t>(dest_b_c >> 16);
467           *dest_scan++ = static_cast<uint8_t>(dest_g_m >> 16);
468           *dest_scan++ = static_cast<uint8_t>(dest_r_y >> 16);
469         }
470         break;
471       }
472       case TransformMethod::k8BppToManyBppWithAlpha: {
473         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
474           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
475           int dest_a = 0;
476           int dest_r_y = 0;
477           int dest_g_m = 0;
478           int dest_b_c = 0;
479           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
480             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
481             if (!pWeight)
482               return false;
483 
484             int pixel_weight = *pWeight;
485             pixel_weight = pixel_weight * src_scan_mask[j] / 255;
486             unsigned long argb_cmyk = m_pSrcPalette[src_scan[j]];
487             if (m_DestFormat == FXDIB_Rgba) {
488               dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
489               dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
490               dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk);
491             } else {
492               dest_b_c += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 24);
493               dest_g_m += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 16);
494               dest_r_y += pixel_weight * static_cast<uint8_t>(argb_cmyk >> 8);
495             }
496             dest_a += pixel_weight;
497           }
498           if (m_ResampleOptions.bInterpolateBicubic) {
499             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
500             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
501             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
502             dest_a = pdfium::clamp(dest_a, 0, 65536);
503           }
504           *dest_scan++ = static_cast<uint8_t>(dest_b_c >> 16);
505           *dest_scan++ = static_cast<uint8_t>(dest_g_m >> 16);
506           *dest_scan++ = static_cast<uint8_t>(dest_r_y >> 16);
507           *dest_scan_mask++ = static_cast<uint8_t>((dest_a * 255) >> 16);
508         }
509         break;
510       }
511       case TransformMethod::kManyBpptoManyBpp: {
512         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
513           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
514           int dest_r_y = 0;
515           int dest_g_m = 0;
516           int dest_b_c = 0;
517           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
518             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
519             if (!pWeight)
520               return false;
521 
522             int pixel_weight = *pWeight;
523             const uint8_t* src_pixel = src_scan + j * Bpp;
524             dest_b_c += pixel_weight * (*src_pixel++);
525             dest_g_m += pixel_weight * (*src_pixel++);
526             dest_r_y += pixel_weight * (*src_pixel);
527           }
528           if (m_ResampleOptions.bInterpolateBicubic) {
529             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
530             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
531             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
532           }
533           *dest_scan++ = static_cast<uint8_t>((dest_b_c) >> 16);
534           *dest_scan++ = static_cast<uint8_t>((dest_g_m) >> 16);
535           *dest_scan++ = static_cast<uint8_t>((dest_r_y) >> 16);
536           dest_scan += Bpp - 3;
537         }
538         break;
539       }
540       case TransformMethod::kManyBpptoManyBppWithAlpha: {
541         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
542           PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
543           int dest_a = 0;
544           int dest_r_y = 0;
545           int dest_g_m = 0;
546           int dest_b_c = 0;
547           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
548             int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j);
549             if (!pWeight)
550               return false;
551 
552             int pixel_weight = *pWeight;
553             const uint8_t* src_pixel = src_scan + j * Bpp;
554             if (m_DestFormat == FXDIB_Argb) {
555               pixel_weight = pixel_weight * src_pixel[3] / 255;
556             } else {
557               pixel_weight = pixel_weight * src_scan_mask[j] / 255;
558             }
559             dest_b_c += pixel_weight * (*src_pixel++);
560             dest_g_m += pixel_weight * (*src_pixel++);
561             dest_r_y += pixel_weight * (*src_pixel);
562             dest_a += pixel_weight;
563           }
564           if (m_ResampleOptions.bInterpolateBicubic) {
565             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
566             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
567             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
568             dest_a = pdfium::clamp(dest_a, 0, 65536);
569           }
570           *dest_scan++ = static_cast<uint8_t>((dest_b_c) >> 16);
571           *dest_scan++ = static_cast<uint8_t>((dest_g_m) >> 16);
572           *dest_scan++ = static_cast<uint8_t>((dest_r_y) >> 16);
573           if (m_DestFormat == FXDIB_Argb)
574             *dest_scan = static_cast<uint8_t>((dest_a * 255) >> 16);
575           if (dest_scan_mask)
576             *dest_scan_mask++ = static_cast<uint8_t>((dest_a * 255) >> 16);
577           dest_scan += Bpp - 3;
578         }
579         break;
580       }
581     }
582     rows_to_go--;
583   }
584   return false;
585 }
586 
StretchVert()587 void CStretchEngine::StretchVert() {
588   if (m_DestHeight == 0)
589     return;
590 
591   CWeightTable table;
592   bool ret =
593       table.Calc(m_DestHeight, m_DestClip.top, m_DestClip.bottom, m_SrcHeight,
594                  m_SrcClip.top, m_SrcClip.bottom, m_ResampleOptions);
595   if (!ret)
596     return;
597 
598   const int DestBpp = m_DestBpp / 8;
599   for (int row = m_DestClip.top; row < m_DestClip.bottom; ++row) {
600     unsigned char* dest_scan = m_DestScanline.data();
601     unsigned char* dest_scan_mask = m_DestMaskScanline.data();
602     PixelWeight* pWeights = table.GetPixelWeight(row);
603     switch (m_TransMethod) {
604       case TransformMethod::k1BppTo8Bpp:
605       case TransformMethod::k1BppToManyBpp:
606       case TransformMethod::k8BppTo8Bpp: {
607         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
608           unsigned char* src_scan =
609               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
610           int dest_a = 0;
611           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
612             int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
613             if (!pWeight)
614               return;
615 
616             int pixel_weight = *pWeight;
617             dest_a +=
618                 pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch];
619           }
620           if (m_ResampleOptions.bInterpolateBicubic)
621             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
622           *dest_scan = static_cast<uint8_t>(dest_a >> 16);
623           dest_scan += DestBpp;
624         }
625         break;
626       }
627       case TransformMethod::k8BppTo8BppWithAlpha: {
628         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
629           unsigned char* src_scan =
630               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
631           unsigned char* src_scan_mask =
632               m_ExtraAlphaBuf.data() + (col - m_DestClip.left);
633           int dest_a = 0;
634           int dest_k = 0;
635           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
636             int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
637             if (!pWeight)
638               return;
639 
640             int pixel_weight = *pWeight;
641             dest_k +=
642                 pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch];
643             dest_a += pixel_weight *
644                       src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch];
645           }
646           if (m_ResampleOptions.bInterpolateBicubic) {
647             dest_k = pdfium::clamp(dest_k, 0, kMaxDestValue);
648             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
649           }
650           *dest_scan = static_cast<uint8_t>(dest_k >> 16);
651           dest_scan += DestBpp;
652           *dest_scan_mask++ = static_cast<uint8_t>(dest_a >> 16);
653         }
654         break;
655       }
656       case TransformMethod::k8BppToManyBpp:
657       case TransformMethod::kManyBpptoManyBpp: {
658         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
659           unsigned char* src_scan =
660               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
661           int dest_r_y = 0;
662           int dest_g_m = 0;
663           int dest_b_c = 0;
664           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
665             int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
666             if (!pWeight)
667               return;
668 
669             int pixel_weight = *pWeight;
670             const uint8_t* src_pixel =
671                 src_scan + (j - m_SrcClip.top) * m_InterPitch;
672             dest_b_c += pixel_weight * (*src_pixel++);
673             dest_g_m += pixel_weight * (*src_pixel++);
674             dest_r_y += pixel_weight * (*src_pixel);
675           }
676           if (m_ResampleOptions.bInterpolateBicubic) {
677             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
678             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
679             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
680           }
681           dest_scan[0] = static_cast<uint8_t>((dest_b_c) >> 16);
682           dest_scan[1] = static_cast<uint8_t>((dest_g_m) >> 16);
683           dest_scan[2] = static_cast<uint8_t>((dest_r_y) >> 16);
684           dest_scan += DestBpp;
685         }
686         break;
687       }
688       case TransformMethod::k8BppToManyBppWithAlpha:
689       case TransformMethod::kManyBpptoManyBppWithAlpha: {
690         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
691           unsigned char* src_scan =
692               m_InterBuf.data() + (col - m_DestClip.left) * DestBpp;
693           unsigned char* src_scan_mask = nullptr;
694           if (m_DestFormat != FXDIB_Argb)
695             src_scan_mask = m_ExtraAlphaBuf.data() + (col - m_DestClip.left);
696           int dest_a = 0;
697           int dest_r_y = 0;
698           int dest_g_m = 0;
699           int dest_b_c = 0;
700           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
701             int* pWeight = table.GetValueFromPixelWeight(pWeights, j);
702             if (!pWeight)
703               return;
704 
705             int pixel_weight = *pWeight;
706             const uint8_t* src_pixel =
707                 src_scan + (j - m_SrcClip.top) * m_InterPitch;
708             int mask_v = 255;
709             if (src_scan_mask)
710               mask_v = src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch];
711             dest_b_c += pixel_weight * (*src_pixel++);
712             dest_g_m += pixel_weight * (*src_pixel++);
713             dest_r_y += pixel_weight * (*src_pixel);
714             if (m_DestFormat == FXDIB_Argb)
715               dest_a += pixel_weight * (*(src_pixel + 1));
716             else
717               dest_a += pixel_weight * mask_v;
718           }
719           if (m_ResampleOptions.bInterpolateBicubic) {
720             dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue);
721             dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue);
722             dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue);
723             dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue);
724           }
725           if (dest_a) {
726             int r = static_cast<uint32_t>(dest_r_y) * 255 / dest_a;
727             int g = static_cast<uint32_t>(dest_g_m) * 255 / dest_a;
728             int b = static_cast<uint32_t>(dest_b_c) * 255 / dest_a;
729             dest_scan[0] = pdfium::clamp(b, 0, 255);
730             dest_scan[1] = pdfium::clamp(g, 0, 255);
731             dest_scan[2] = pdfium::clamp(r, 0, 255);
732           }
733           if (m_DestFormat == FXDIB_Argb)
734             dest_scan[3] = static_cast<uint8_t>((dest_a) >> 16);
735           else
736             *dest_scan_mask = static_cast<uint8_t>((dest_a) >> 16);
737           dest_scan += DestBpp;
738           if (dest_scan_mask)
739             dest_scan_mask++;
740         }
741         break;
742       }
743     }
744     m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline.data(),
745                                    m_DestMaskScanline.data());
746   }
747 }
748