Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cstretchengine.cpp
Go to the documentation of this file.
1// Copyright 2017 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fxge/dib/cstretchengine.h"
8
9#include <math.h>
10
11#include <algorithm>
12#include <type_traits>
13#include <utility>
14
15#include "core/fxcrt/fx_safe_types.h"
16#include "core/fxcrt/fx_system.h"
17#include "core/fxcrt/pauseindicator_iface.h"
18#include "core/fxge/calculate_pitch.h"
19#include "core/fxge/dib/cfx_dibbase.h"
20#include "core/fxge/dib/cfx_dibitmap.h"
21#include "core/fxge/dib/fx_dib.h"
22#include "core/fxge/dib/scanlinecomposer_iface.h"
23#include "third_party/base/check.h"
24
25static_assert(
26 std::is_trivially_destructible<CStretchEngine::PixelWeight>::value,
27 "PixelWeight storage may be re-used without invoking its destructor");
28
29// static
31 const FXDIB_ResampleOptions& options,
32 int dest_width,
33 int dest_height,
34 int src_width,
35 int src_height) {
36 return !options.bInterpolateBilinear && !options.bNoSmoothing &&
37 abs(dest_width) != 0 &&
38 abs(dest_height) / 8 <
39 static_cast<long long>(src_width) * src_height / abs(dest_width);
40}
41
42// static
43size_t CStretchEngine::PixelWeight::TotalBytesForWeightCount(
44 size_t weight_count) {
45 // Always room for one weight even for empty ranges due to declaration
46 // of m_Weights[1] in the header. Don't shrink below this since
47 // CalculateWeights() relies on this later.
48 const size_t extra_weights = weight_count > 0 ? weight_count - 1 : 0;
49 FX_SAFE_SIZE_T total_bytes = extra_weights;
50 total_bytes *= sizeof(m_Weights[0]);
51 total_bytes += sizeof(PixelWeight);
52 return total_bytes.ValueOrDie();
53}
54
56
58
60 int dest_len,
61 int dest_min,
62 int dest_max,
63 int src_len,
64 int src_min,
65 int src_max,
66 const FXDIB_ResampleOptions& options) {
67 // 512MB should be large enough for this while preventing OOM.
68 static constexpr size_t kMaxTableBytesAllowed = 512 * 1024 * 1024;
69
70 // Help the compiler realize that these can't change during a loop iteration:
71 const bool bilinear = options.bInterpolateBilinear;
72
73 m_DestMin = 0;
74 m_ItemSizeBytes = 0;
75 m_WeightTablesSizeBytes = 0;
76 m_WeightTables.clear();
77 if (dest_len == 0)
78 return true;
79
80 if (dest_min > dest_max)
81 return false;
82
83 m_DestMin = dest_min;
84
85 const double scale = static_cast<double>(src_len) / dest_len;
86 const double base = dest_len < 0 ? src_len : 0;
87 const size_t weight_count = static_cast<size_t>(ceil(fabs(scale))) + 1;
88 m_ItemSizeBytes = PixelWeight::TotalBytesForWeightCount(weight_count);
89
90 const size_t dest_range = static_cast<size_t>(dest_max - dest_min);
91 const size_t kMaxTableItemsAllowed = kMaxTableBytesAllowed / m_ItemSizeBytes;
92 if (dest_range > kMaxTableItemsAllowed)
93 return false;
94
95 m_WeightTablesSizeBytes = dest_range * m_ItemSizeBytes;
96 m_WeightTables.resize(m_WeightTablesSizeBytes);
97 if (options.bNoSmoothing || fabs(scale) < 1.0f) {
98 for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
99 PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
100 double src_pos = dest_pixel * scale + scale / 2 + base;
101 if (bilinear) {
102 int src_start = static_cast<int>(floor(src_pos - 0.5));
103 int src_end = static_cast<int>(floor(src_pos + 0.5));
104 src_start = std::max(src_start, src_min);
105 src_end = std::min(src_end, src_max - 1);
106 pixel_weights.SetStartEnd(src_start, src_end, weight_count);
107 if (pixel_weights.m_SrcStart >= pixel_weights.m_SrcEnd) {
108 // Always room for one weight per size calculation.
109 pixel_weights.m_Weights[0] = kFixedPointOne;
110 } else {
111 pixel_weights.m_Weights[1] =
112 FixedFromDouble(src_pos - pixel_weights.m_SrcStart - 0.5f);
113 pixel_weights.m_Weights[0] =
114 kFixedPointOne - pixel_weights.m_Weights[1];
115 }
116 } else {
117 int pixel_pos = static_cast<int>(floor(src_pos));
118 int src_start = std::max(pixel_pos, src_min);
119 int src_end = std::min(pixel_pos, src_max - 1);
120 pixel_weights.SetStartEnd(src_start, src_end, weight_count);
121 pixel_weights.m_Weights[0] = kFixedPointOne;
122 }
123 }
124 return true;
125 }
126
127 for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
128 PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
129 double src_start = dest_pixel * scale + base;
130 double src_end = src_start + scale;
131 int start_i = floor(std::min(src_start, src_end));
132 int end_i = floor(std::max(src_start, src_end));
133 start_i = std::max(start_i, src_min);
134 end_i = std::min(end_i, src_max - 1);
135 if (start_i > end_i) {
136 start_i = std::min(start_i, src_max - 1);
137 pixel_weights.SetStartEnd(start_i, start_i, weight_count);
138 continue;
139 }
140 pixel_weights.SetStartEnd(start_i, end_i, weight_count);
141 uint32_t remaining = kFixedPointOne;
142 double rounding_error = 0.0;
143 for (int j = start_i; j < end_i; ++j) {
144 double dest_start = (j - base) / scale;
145 double dest_end = (j + 1 - base) / scale;
146 if (dest_start > dest_end)
147 std::swap(dest_start, dest_end);
148 double area_start = std::max(dest_start, static_cast<double>(dest_pixel));
149 double area_end = std::min(dest_end, static_cast<double>(dest_pixel + 1));
150 double weight = std::max(0.0, area_end - area_start);
151 uint32_t fixed_weight = FixedFromDouble(weight + rounding_error);
152 pixel_weights.SetWeightForPosition(j, fixed_weight);
153 remaining -= fixed_weight;
154 rounding_error =
155 weight - static_cast<double>(fixed_weight) / kFixedPointOne;
156 }
157 // Note: underflow is defined behaviour for unsigned types and will
158 // result in an out-of-range value.
159 if (remaining && remaining <= kFixedPointOne) {
160 pixel_weights.SetWeightForPosition(end_i, remaining);
161 } else {
162 pixel_weights.RemoveLastWeightAndAdjust(remaining);
163 }
164 }
165 return true;
166}
167
169 int pixel) const {
170 DCHECK(pixel >= m_DestMin);
171 return reinterpret_cast<const PixelWeight*>(
172 &m_WeightTables[(pixel - m_DestMin) * m_ItemSizeBytes]);
173}
174
176 int pixel) {
177 return const_cast<PixelWeight*>(std::as_const(*this).GetPixelWeight(pixel));
178}
179
181 FXDIB_Format dest_format,
182 int dest_width,
183 int dest_height,
184 const FX_RECT& clip_rect,
185 const RetainPtr<const CFX_DIBBase>& pSrcBitmap,
186 const FXDIB_ResampleOptions& options)
187 : m_DestFormat(dest_format),
188 m_DestBpp(GetBppFromFormat(dest_format)),
196 m_DestWidth(dest_width),
197 m_DestHeight(dest_height),
198 m_DestClip(clip_rect) {
199 if (m_bHasAlpha) {
200 DCHECK_EQ(m_DestFormat, FXDIB_Format::kArgb);
201 DCHECK_EQ(m_DestBpp, GetBppFromFormat(FXDIB_Format::kArgb));
202 DCHECK_EQ(m_pSource->GetFormat(), FXDIB_Format::kArgb);
203 DCHECK_EQ(m_SrcBpp, GetBppFromFormat(FXDIB_Format::kArgb));
204 }
205
206 absl::optional<uint32_t> maybe_size =
207 fxge::CalculatePitch32(m_DestBpp, clip_rect.Width());
208 if (!maybe_size.has_value())
209 return;
210
211 m_DestScanline.resize(maybe_size.value());
212 if (dest_format == FXDIB_Format::kRgb32)
213 std::fill(m_DestScanline.begin(), m_DestScanline.end(), 255);
214 m_InterPitch = fxge::CalculatePitch32OrDie(m_DestBpp, m_DestClip.Width());
215 m_ExtraMaskPitch = fxge::CalculatePitch32OrDie(8, m_DestClip.Width());
216 if (options.bNoSmoothing) {
217 m_ResampleOptions.bNoSmoothing = true;
218 } else {
219 if (UseInterpolateBilinear(options, dest_width, dest_height, m_SrcWidth,
220 m_SrcHeight)) {
221 m_ResampleOptions.bInterpolateBilinear = true;
222 } else {
223 m_ResampleOptions = options;
224 }
225 }
226 double scale_x = static_cast<float>(m_SrcWidth) / m_DestWidth;
227 double scale_y = static_cast<float>(m_SrcHeight) / m_DestHeight;
228 double base_x = m_DestWidth > 0 ? 0.0f : m_DestWidth;
229 double base_y = m_DestHeight > 0 ? 0.0f : m_DestHeight;
230 double src_left = scale_x * (clip_rect.left + base_x);
231 double src_right = scale_x * (clip_rect.right + base_x);
232 double src_top = scale_y * (clip_rect.top + base_y);
233 double src_bottom = scale_y * (clip_rect.bottom + base_y);
234 if (src_left > src_right)
235 std::swap(src_left, src_right);
236 if (src_top > src_bottom)
237 std::swap(src_top, src_bottom);
238 m_SrcClip.left = static_cast<int>(floor(src_left));
239 m_SrcClip.right = static_cast<int>(ceil(src_right));
240 m_SrcClip.top = static_cast<int>(floor(src_top));
241 m_SrcClip.bottom = static_cast<int>(ceil(src_bottom));
242 FX_RECT src_rect(0, 0, m_SrcWidth, m_SrcHeight);
243 m_SrcClip.Intersect(src_rect);
244
245 switch (m_SrcBpp) {
246 case 1:
247 m_TransMethod = m_DestBpp == 8 ? TransformMethod::k1BppTo8Bpp
248 : TransformMethod::k1BppToManyBpp;
249 break;
250 case 8:
251 m_TransMethod = m_DestBpp == 8 ? TransformMethod::k8BppTo8Bpp
252 : TransformMethod::k8BppToManyBpp;
253 break;
254 default:
255 m_TransMethod = m_bHasAlpha ? TransformMethod::kManyBpptoManyBppWithAlpha
256 : TransformMethod::kManyBpptoManyBpp;
257 break;
258 }
259}
260
261CStretchEngine::~CStretchEngine() = default;
262
264 while (m_State == State::kHorizontal) {
265 if (ContinueStretchHorz(pPause))
266 return true;
267
268 m_State = State::kVertical;
270 }
271 return false;
272}
273
275 if (m_DestWidth == 0 || m_InterPitch == 0 || m_DestScanline.empty())
276 return false;
277
278 FX_SAFE_SIZE_T safe_size = m_SrcClip.Height();
279 safe_size *= m_InterPitch;
280 const size_t size = safe_size.ValueOrDefault(0);
281 if (size == 0) {
282 return false;
283 }
284 m_InterBuf = FixedSizeDataVector<uint8_t>::TryZeroed(size);
285 if (m_InterBuf.empty()) {
286 return false;
287 }
288 if (!m_WeightTable.CalculateWeights(
289 m_DestWidth, m_DestClip.left, m_DestClip.right, m_SrcWidth,
290 m_SrcClip.left, m_SrcClip.right, m_ResampleOptions)) {
291 return false;
292 }
293 m_CurRow = m_SrcClip.top;
294 m_State = State::kHorizontal;
295 return true;
296}
297
299 if (!m_DestWidth)
300 return false;
301 if (m_pSource->SkipToScanline(m_CurRow, pPause))
302 return true;
303
304 int Bpp = m_DestBpp / 8;
305 static const int kStrechPauseRows = 10;
306 int rows_to_go = kStrechPauseRows;
307 for (; m_CurRow < m_SrcClip.bottom; ++m_CurRow) {
308 if (rows_to_go == 0) {
309 if (pPause && pPause->NeedToPauseNow())
310 return true;
311
312 rows_to_go = kStrechPauseRows;
313 }
314
315 const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow).data();
316 pdfium::span<uint8_t> dest_span = m_InterBuf.subspan(
317 (m_CurRow - m_SrcClip.top) * m_InterPitch, m_InterPitch);
318 size_t dest_span_index = 0;
319 // TODO(npm): reduce duplicated code here
320 switch (m_TransMethod) {
321 case TransformMethod::k1BppTo8Bpp:
322 case TransformMethod::k1BppToManyBpp: {
323 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
324 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
325 uint32_t dest_a = 0;
326 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
327 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
328 if (src_scan[j / 8] & (1 << (7 - j % 8)))
329 dest_a += pixel_weight * 255;
330 }
331 dest_span[dest_span_index++] = PixelFromFixed(dest_a);
332 }
333 break;
334 }
335 case TransformMethod::k8BppTo8Bpp: {
336 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
337 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
338 uint32_t dest_a = 0;
339 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
340 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
341 dest_a += pixel_weight * src_scan[j];
342 }
343 dest_span[dest_span_index++] = PixelFromFixed(dest_a);
344 }
345 break;
346 }
347 case TransformMethod::k8BppToManyBpp: {
348 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
349 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
350 uint32_t dest_r = 0;
351 uint32_t dest_g = 0;
352 uint32_t dest_b = 0;
353 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
354 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
355 unsigned long argb = m_pSrcPalette[src_scan[j]];
356 if (m_DestFormat == FXDIB_Format::kRgb) {
357 dest_r += pixel_weight * static_cast<uint8_t>(argb >> 16);
358 dest_g += pixel_weight * static_cast<uint8_t>(argb >> 8);
359 dest_b += pixel_weight * static_cast<uint8_t>(argb);
360 } else {
361 dest_b += pixel_weight * static_cast<uint8_t>(argb >> 24);
362 dest_g += pixel_weight * static_cast<uint8_t>(argb >> 16);
363 dest_r += pixel_weight * static_cast<uint8_t>(argb >> 8);
364 }
365 }
366 dest_span[dest_span_index++] = PixelFromFixed(dest_b);
367 dest_span[dest_span_index++] = PixelFromFixed(dest_g);
368 dest_span[dest_span_index++] = PixelFromFixed(dest_r);
369 }
370 break;
371 }
372 case TransformMethod::kManyBpptoManyBpp: {
373 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
374 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
375 uint32_t dest_r = 0;
376 uint32_t dest_g = 0;
377 uint32_t dest_b = 0;
378 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
379 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
380 const uint8_t* src_pixel = src_scan + j * Bpp;
381 dest_b += pixel_weight * (*src_pixel++);
382 dest_g += pixel_weight * (*src_pixel++);
383 dest_r += pixel_weight * (*src_pixel);
384 }
385 dest_span[dest_span_index++] = PixelFromFixed(dest_b);
386 dest_span[dest_span_index++] = PixelFromFixed(dest_g);
387 dest_span[dest_span_index++] = PixelFromFixed(dest_r);
388 dest_span_index += Bpp - 3;
389 }
390 break;
391 }
392 case TransformMethod::kManyBpptoManyBppWithAlpha: {
393 DCHECK(m_bHasAlpha);
394 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
395 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
396 uint32_t dest_a = 0;
397 uint32_t dest_r = 0;
398 uint32_t dest_g = 0;
399 uint32_t dest_b = 0;
400 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
401 const uint8_t* src_pixel = src_scan + j * Bpp;
402 uint32_t pixel_weight =
403 pWeights->GetWeightForPosition(j) * src_pixel[3] / 255;
404 dest_b += pixel_weight * (*src_pixel++);
405 dest_g += pixel_weight * (*src_pixel++);
406 dest_r += pixel_weight * (*src_pixel);
407 dest_a += pixel_weight;
408 }
409 dest_span[dest_span_index++] = PixelFromFixed(dest_b);
410 dest_span[dest_span_index++] = PixelFromFixed(dest_g);
411 dest_span[dest_span_index++] = PixelFromFixed(dest_r);
412 dest_span[dest_span_index] = PixelFromFixed(255 * dest_a);
413 dest_span_index += Bpp - 3;
414 }
415 break;
416 }
417 }
418 rows_to_go--;
419 }
420 return false;
421}
422
424 if (m_DestHeight == 0)
425 return;
426
427 WeightTable table;
428 if (!table.CalculateWeights(m_DestHeight, m_DestClip.top, m_DestClip.bottom,
429 m_SrcHeight, m_SrcClip.top, m_SrcClip.bottom,
430 m_ResampleOptions)) {
431 return;
432 }
433
434 const int DestBpp = m_DestBpp / 8;
435 for (int row = m_DestClip.top; row < m_DestClip.bottom; ++row) {
436 unsigned char* dest_scan = m_DestScanline.data();
437 PixelWeight* pWeights = table.GetPixelWeight(row);
438 switch (m_TransMethod) {
439 case TransformMethod::k1BppTo8Bpp:
440 case TransformMethod::k1BppToManyBpp:
441 case TransformMethod::k8BppTo8Bpp: {
442 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
443 pdfium::span<const uint8_t> src_span =
444 m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
445 uint32_t dest_a = 0;
446 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
447 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
448 dest_a +=
449 pixel_weight * src_span[(j - m_SrcClip.top) * m_InterPitch];
450 }
451 *dest_scan = PixelFromFixed(dest_a);
452 dest_scan += DestBpp;
453 }
454 break;
455 }
456 case TransformMethod::k8BppToManyBpp:
457 case TransformMethod::kManyBpptoManyBpp: {
458 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
459 pdfium::span<const uint8_t> src_span =
460 m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
461 uint32_t dest_r = 0;
462 uint32_t dest_g = 0;
463 uint32_t dest_b = 0;
464 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
465 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
466 pdfium::span<const uint8_t> src_pixel =
467 src_span.subspan((j - m_SrcClip.top) * m_InterPitch, 3);
468 dest_b += pixel_weight * src_pixel[0];
469 dest_g += pixel_weight * src_pixel[1];
470 dest_r += pixel_weight * src_pixel[2];
471 }
472 dest_scan[0] = PixelFromFixed(dest_b);
473 dest_scan[1] = PixelFromFixed(dest_g);
474 dest_scan[2] = PixelFromFixed(dest_r);
475 dest_scan += DestBpp;
476 }
477 break;
478 }
479 case TransformMethod::kManyBpptoManyBppWithAlpha: {
480 DCHECK(m_bHasAlpha);
481 for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
482 pdfium::span<const uint8_t> src_span =
483 m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
484 uint32_t dest_a = 0;
485 uint32_t dest_r = 0;
486 uint32_t dest_g = 0;
487 uint32_t dest_b = 0;
488 constexpr size_t kPixelBytes = 4;
489 for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
490 uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
491 pdfium::span<const uint8_t> src_pixel = src_span.subspan(
492 (j - m_SrcClip.top) * m_InterPitch, kPixelBytes);
493 dest_b += pixel_weight * src_pixel[0];
494 dest_g += pixel_weight * src_pixel[1];
495 dest_r += pixel_weight * src_pixel[2];
496 dest_a += pixel_weight * src_pixel[3];
497 }
498 if (dest_a) {
499 int r = static_cast<uint32_t>(dest_r) * 255 / dest_a;
500 int g = static_cast<uint32_t>(dest_g) * 255 / dest_a;
501 int b = static_cast<uint32_t>(dest_b) * 255 / dest_a;
502 dest_scan[0] = std::clamp(b, 0, 255);
503 dest_scan[1] = std::clamp(g, 0, 255);
504 dest_scan[2] = std::clamp(r, 0, 255);
505 }
506 dest_scan[3] = PixelFromFixed(dest_a);
507 dest_scan += DestBpp;
508 }
509 break;
510 }
511 }
512 m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline);
513 }
514}
PixelWeight * GetPixelWeight(int pixel)
const PixelWeight * GetPixelWeight(int pixel) const
bool CalculateWeights(int dest_len, int dest_min, int dest_max, int src_len, int src_min, int src_max, const FXDIB_ResampleOptions &options)
bool Continue(PauseIndicatorIface *pPause)
bool ContinueStretchHorz(PauseIndicatorIface *pPause)
static bool UseInterpolateBilinear(const FXDIB_ResampleOptions &options, int dest_width, int dest_height, int src_width, int src_height)
static uint32_t FixedFromDouble(double d)
static uint8_t PixelFromFixed(uint32_t fixed)
CStretchEngine(ScanlineComposerIface *pDestBitmap, FXDIB_Format dest_format, int dest_width, int dest_height, const FX_RECT &clip_rect, const RetainPtr< const CFX_DIBBase > &pSrcBitmap, const FXDIB_ResampleOptions &options)
static constexpr uint32_t kFixedPointOne
virtual bool NeedToPauseNow()=0
int GetBppFromFormat(FXDIB_Format format)
Definition fx_dib.h:90
FXDIB_Format
Definition fx_dib.h:19
uint32_t CalculatePitch32OrDie(int bpp, int width)
void RemoveLastWeightAndAdjust(uint32_t weight_change)
uint32_t GetWeightForPosition(int position) const
void SetWeightForPosition(int position, uint32_t weight)
bool bInterpolateBilinear
Definition fx_dib.h:41
int32_t bottom
int32_t right
int Width() const
FX_RECT(const FX_RECT &that)=default
int32_t top
int32_t left
void Intersect(const FX_RECT &src)
constexpr FX_RECT(int l, int t, int r, int b)