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