70 static constexpr size_t kMaxTableBytesAllowed = 512 * 1024 * 1024;
77 m_WeightTablesSizeBytes = 0;
78 m_WeightTables.clear();
82 if (dest_min > dest_max)
87 const double scale =
static_cast<
double>(src_len) / dest_len;
88 const double base = dest_len < 0 ? src_len : 0;
89 const size_t weight_count =
static_cast<size_t>(ceil(fabs(scale))) + 1;
90 m_ItemSizeBytes = TotalBytesForWeightCount(weight_count);
92 const size_t dest_range =
static_cast<size_t>(dest_max - dest_min);
93 const size_t kMaxTableItemsAllowed = kMaxTableBytesAllowed / m_ItemSizeBytes;
94 if (dest_range > kMaxTableItemsAllowed)
97 m_WeightTablesSizeBytes = dest_range * m_ItemSizeBytes;
98 m_WeightTables.resize(m_WeightTablesSizeBytes);
101 for (
int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
103 double src_pos = dest_pixel * scale + scale / 2 + base;
105 int src_start =
static_cast<
int>(floor(src_pos - 0.5));
106 int src_end =
static_cast<
int>(floor(src_pos + 0.5));
107 src_start =
std::max(src_start, src_min);
108 src_end =
std::min(src_end, src_max - 1);
109 pixel_weights.SetStartEnd(src_start, src_end, weight_count);
120 int pixel_pos =
static_cast<
int>(floor(src_pos));
121 int src_start =
std::max(pixel_pos, src_min);
122 int src_end =
std::min(pixel_pos, src_max - 1);
123 pixel_weights.SetStartEnd(src_start, src_end, weight_count);
130 for (
int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) {
132 double src_start = dest_pixel * scale + base;
133 double src_end = src_start + scale;
134 int start_i = floor(
std::min(src_start, src_end));
135 int end_i = floor(
std::max(src_start, src_end));
136 start_i =
std::max(start_i, src_min);
137 end_i =
std::min(end_i, src_max - 1);
138 if (start_i > end_i) {
139 start_i =
std::min(start_i, src_max - 1);
140 pixel_weights.SetStartEnd(start_i, start_i, weight_count);
143 pixel_weights.SetStartEnd(start_i, end_i, weight_count);
145 double rounding_error = 0.0;
146 for (
int j = start_i; j < end_i; ++j) {
147 double dest_start = (j - base) / scale;
148 double dest_end = (j + 1 - base) / scale;
149 if (dest_start > dest_end) {
150 std::swap(dest_start, dest_end);
153 std::max(dest_start,
static_cast<
double>(dest_pixel));
155 std::min(dest_end,
static_cast<
double>(dest_pixel + 1));
156 double weight =
std::max(0.0, area_end - area_start);
159 remaining -= fixed_weight;
194 : m_DestFormat(dest_format),
203 m_DestWidth(dest_width),
204 m_DestHeight(dest_height),
205 m_DestClip
(clip_rect
) {
211 DCHECK_EQ(m_pSource->GetFormat(), FXDIB_Format::kBgra);
215 std::optional<uint32_t> maybe_size =
216 fxge::CalculatePitch32(m_DestBpp, clip_rect.Width());
217 if (!maybe_size.has_value())
220 m_DestScanline.resize(maybe_size.value());
222 std::fill(m_DestScanline.begin(), m_DestScanline.end(), 255);
233 m_ResampleOptions = options;
236 double scale_x =
static_cast<
float>(m_SrcWidth) / m_DestWidth;
237 double scale_y =
static_cast<
float>(m_SrcHeight) / m_DestHeight;
238 double base_x = m_DestWidth > 0 ? 0.0f : m_DestWidth;
239 double base_y = m_DestHeight > 0 ? 0.0f : m_DestHeight;
240 double src_left = scale_x * (clip_rect
.left + base_x);
241 double src_right = scale_x * (clip_rect
.right + base_x);
242 double src_top = scale_y * (clip_rect
.top + base_y);
243 double src_bottom = scale_y * (clip_rect
.bottom + base_y);
244 if (src_left > src_right)
245 std::swap(src_left, src_right);
246 if (src_top > src_bottom)
247 std::swap(src_top, src_bottom);
248 m_SrcClip
.left =
static_cast<
int>(floor(src_left));
249 m_SrcClip
.right =
static_cast<
int>(ceil(src_right));
250 m_SrcClip
.top =
static_cast<
int>(floor(src_top));
251 m_SrcClip
.bottom =
static_cast<
int>(ceil(src_bottom));
257 m_TransMethod = m_DestBpp == 8 ? TransformMethod::k1BppTo8Bpp
258 : TransformMethod::k1BppToManyBpp;
261 m_TransMethod = m_DestBpp == 8 ? TransformMethod::k8BppTo8Bpp
262 : TransformMethod::k8BppToManyBpp;
265 m_TransMethod = m_bHasAlpha ? TransformMethod::kManyBpptoManyBppWithAlpha
266 : TransformMethod::kManyBpptoManyBpp;
285 if (m_DestWidth == 0 || m_InterPitch == 0 || m_DestScanline.empty())
288 FX_SAFE_SIZE_T safe_size = m_SrcClip
.Height();
289 safe_size *= m_InterPitch;
290 const size_t size = safe_size.ValueOrDefault(0);
294 m_InterBuf = FixedSizeDataVector<uint8_t>::TryZeroed(size);
295 if (m_InterBuf.empty()) {
298 if (!m_WeightTable.CalculateWeights(
299 m_DestWidth, m_DestClip.left, m_DestClip.right, m_SrcWidth,
300 m_SrcClip.left, m_SrcClip.right, m_ResampleOptions)) {
303 m_CurRow = m_SrcClip
.top;
304 m_State = State::kHorizontal;
311 if (m_pSource->SkipToScanline(m_CurRow, pPause))
314 int Bpp = m_DestBpp / 8;
315 static const int kStrechPauseRows = 10;
316 int rows_to_go = kStrechPauseRows;
317 for (; m_CurRow < m_SrcClip
.bottom; ++m_CurRow) {
318 if (rows_to_go == 0) {
322 rows_to_go = kStrechPauseRows;
325 const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow).data();
326 pdfium::span<uint8_t> dest_span = m_InterBuf.subspan(
327 (m_CurRow - m_SrcClip.top) * m_InterPitch, m_InterPitch);
328 size_t dest_span_index = 0;
331 switch (m_TransMethod) {
332 case TransformMethod::k1BppTo8Bpp:
333 case TransformMethod::k1BppToManyBpp: {
334 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
335 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
339 if (src_scan[j / 8] & (1 << (7 - j % 8))) {
340 dest_a += pixel_weight * 255;
347 case TransformMethod::k8BppTo8Bpp: {
348 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
349 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
353 dest_a += pixel_weight * src_scan[j];
359 case TransformMethod::k8BppToManyBpp: {
360 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
361 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
367 FX_ARGB argb = m_pSrcPalette[src_scan[j]];
369 dest_r += pixel_weight *
static_cast<uint8_t>(argb >> 16);
370 dest_g += pixel_weight *
static_cast<uint8_t>(argb >> 8);
371 dest_b += pixel_weight *
static_cast<uint8_t>(argb);
373 dest_b += pixel_weight *
static_cast<uint8_t>(argb >> 24);
374 dest_g += pixel_weight *
static_cast<uint8_t>(argb >> 16);
375 dest_r += pixel_weight *
static_cast<uint8_t>(argb >> 8);
384 case TransformMethod::kManyBpptoManyBpp: {
385 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
386 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
392 const uint8_t* src_pixel = src_scan + j * Bpp;
393 dest_b += pixel_weight * (*src_pixel++);
394 dest_g += pixel_weight * (*src_pixel++);
395 dest_r += pixel_weight * (*src_pixel);
400 dest_span_index += Bpp - 3;
404 case TransformMethod::kManyBpptoManyBppWithAlpha: {
406 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
407 PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col);
413 const uint8_t* src_pixel = src_scan + j * Bpp;
414 uint32_t pixel_weight =
416 dest_b += pixel_weight * (*src_pixel++);
417 dest_g += pixel_weight * (*src_pixel++);
418 dest_r += pixel_weight * (*src_pixel);
419 dest_a += pixel_weight;
425 dest_span_index += Bpp - 3;
437 if (m_DestHeight == 0)
443 m_ResampleOptions
)) {
447 const int DestBpp = m_DestBpp / 8;
449 for (
int row = m_DestClip
.top; row < m_DestClip
.bottom; ++row) {
450 unsigned char* dest_scan = m_DestScanline.data();
452 switch (m_TransMethod) {
453 case TransformMethod::k1BppTo8Bpp:
454 case TransformMethod::k1BppToManyBpp:
455 case TransformMethod::k8BppTo8Bpp: {
456 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
457 pdfium::span<
const uint8_t> src_span =
458 m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
463 pixel_weight * src_span[(j - m_SrcClip
.top) * m_InterPitch];
466 dest_scan += DestBpp;
470 case TransformMethod::k8BppToManyBpp:
471 case TransformMethod::kManyBpptoManyBpp: {
472 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
473 pdfium::span<
const uint8_t> src_span =
474 m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
480 pdfium::span<
const uint8_t> src_pixel =
481 src_span.subspan((j - m_SrcClip
.top) * m_InterPitch, 3);
482 dest_b += pixel_weight * src_pixel[0];
483 dest_g += pixel_weight * src_pixel[1];
484 dest_r += pixel_weight * src_pixel[2];
489 dest_scan += DestBpp;
493 case TransformMethod::kManyBpptoManyBppWithAlpha: {
495 for (
int col = m_DestClip
.left; col < m_DestClip
.right; ++col) {
496 pdfium::span<
const uint8_t> src_span =
497 m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
502 constexpr size_t kPixelBytes = 4;
505 pdfium::span<
const uint8_t> src_pixel = src_span.subspan(
506 (j - m_SrcClip
.top) * m_InterPitch, kPixelBytes);
507 dest_b += pixel_weight * src_pixel[0];
508 dest_g += pixel_weight * src_pixel[1];
509 dest_r += pixel_weight * src_pixel[2];
510 dest_a += pixel_weight * src_pixel[3];
513 int r =
static_cast<uint32_t>(dest_r) * 255 / dest_a;
514 int g =
static_cast<uint32_t>(dest_g) * 255 / dest_a;
515 int b =
static_cast<uint32_t>(dest_b) * 255 / dest_a;
516 dest_scan[0] =
std::clamp(b, 0, 255);
517 dest_scan[1] =
std::clamp(g, 0, 255);
518 dest_scan[2] =
std::clamp(r, 0, 255);
521 dest_scan += DestBpp;
526 m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline);
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)