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
cfx_dibitmap.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/cfx_dibitmap.h"
8
9#include <limits>
10#include <memory>
11#include <utility>
12
13#include "build/build_config.h"
14#include "core/fxcrt/check.h"
15#include "core/fxcrt/check_op.h"
16#include "core/fxcrt/compiler_specific.h"
17#include "core/fxcrt/containers/contains.h"
18#include "core/fxcrt/data_vector.h"
19#include "core/fxcrt/fx_coordinates.h"
20#include "core/fxcrt/fx_memcpy_wrappers.h"
21#include "core/fxcrt/fx_safe_types.h"
22#include "core/fxcrt/notreached.h"
23#include "core/fxcrt/numerics/safe_conversions.h"
24#include "core/fxcrt/span.h"
25#include "core/fxcrt/span_util.h"
26#include "core/fxcrt/stl_util.h"
27#include "core/fxge/agg/cfx_agg_cliprgn.h"
28#include "core/fxge/calculate_pitch.h"
29#include "core/fxge/cfx_defaultrenderdevice.h"
30#include "core/fxge/dib/cfx_scanlinecompositor.h"
31
32namespace {
33
34size_t GetAllocSizeOrZero(uint32_t size) {
35 FX_SAFE_SIZE_T safe_buffer_size = size;
36 safe_buffer_size += 4;
37 return safe_buffer_size.ValueOrDefault(0);
38}
39
40} // namespace
41
42CFX_DIBitmap::CFX_DIBitmap() = default;
43
44bool CFX_DIBitmap::Create(int width, int height, FXDIB_Format format) {
45 return Create(width, height, format, nullptr, 0);
46}
47
48bool CFX_DIBitmap::Create(int width,
49 int height,
50 FXDIB_Format format,
51 uint8_t* pBuffer,
52 uint32_t pitch) {
53 m_pBuffer = nullptr;
54 SetFormat(format);
55 SetWidth(0);
56 SetHeight(0);
57 SetPitch(0);
58
59 std::optional<PitchAndSize> pitch_size =
60 CalculatePitchAndSize(width, height, format, pitch);
61 if (!pitch_size.has_value()) {
62 return false;
63 }
64
65 if (pBuffer) {
66 m_pBuffer.Reset(pBuffer);
67 } else {
68 const size_t buffer_size = GetAllocSizeOrZero(pitch_size.value().size);
69 if (buffer_size == 0) {
70 return false;
71 }
72
73 m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
74 FX_TryAlloc(uint8_t, buffer_size));
75 if (!m_pBuffer) {
76 return false;
77 }
78 }
79 SetWidth(width);
80 SetHeight(height);
81 SetPitch(pitch_size.value().pitch);
82 return true;
83}
84
85bool CFX_DIBitmap::Copy(RetainPtr<const CFX_DIBBase> source) {
86 if (m_pBuffer)
87 return false;
88
89 if (!Create(source->GetWidth(), source->GetHeight(), source->GetFormat())) {
90 return false;
91 }
92
93 SetPalette(source->GetPaletteSpan());
94 for (int row = 0; row < source->GetHeight(); row++) {
95 UNSAFE_TODO(FXSYS_memcpy(m_pBuffer.Get() + row * GetPitch(),
96 source->GetScanline(row).data(), GetPitch()));
97 }
98 return true;
99}
100
101CFX_DIBitmap::~CFX_DIBitmap() = default;
102
103pdfium::span<const uint8_t> CFX_DIBitmap::GetBuffer() const {
104 if (!m_pBuffer)
105 return pdfium::span<const uint8_t>();
106
107 return UNSAFE_TODO(
108 pdfium::make_span(m_pBuffer.Get(), GetHeight() * GetPitch()));
109}
110
111pdfium::span<const uint8_t> CFX_DIBitmap::GetScanline(int line) const {
112 auto buffer_span = GetBuffer();
113 if (buffer_span.empty())
114 return pdfium::span<const uint8_t>();
115
116 return buffer_span.subspan(line * GetPitch(), GetPitch());
117}
118
120 size_t result = CFX_DIBBase::GetEstimatedImageMemoryBurden();
121 if (!GetBuffer().empty()) {
122 int height = GetHeight();
123 CHECK(pdfium::IsValueInRangeForNumericType<size_t>(height));
124 result += static_cast<size_t>(height) * GetPitch();
125 }
126 return result;
127}
128
129#if BUILDFLAG(IS_WIN) || defined(PDF_USE_SKIA)
130RetainPtr<const CFX_DIBitmap> CFX_DIBitmap::RealizeIfNeeded() const {
131 if (GetBuffer().empty()) {
132 return Realize();
133 }
134 return pdfium::WrapRetain(this);
135}
136#endif
137
138void CFX_DIBitmap::TakeOver(RetainPtr<CFX_DIBitmap>&& pSrcBitmap) {
139 m_pBuffer = std::move(pSrcBitmap->m_pBuffer);
140 palette_ = std::move(pSrcBitmap->palette_);
141 pSrcBitmap->m_pBuffer = nullptr;
142 SetFormat(pSrcBitmap->GetFormat());
143 SetWidth(pSrcBitmap->GetWidth());
144 SetHeight(pSrcBitmap->GetHeight());
145 SetPitch(pSrcBitmap->GetPitch());
146}
147
148void CFX_DIBitmap::Clear(uint32_t color) {
149 auto buffer = GetWritableBuffer();
150 if (buffer.empty()) {
151 return;
152 }
153
154 switch (GetFormat()) {
156 break;
158 fxcrt::Fill(buffer, (color & 0xff000000) ? 0xff : 0);
159 break;
161 fxcrt::Fill(buffer, FindPalette(color) ? 0xff : 0);
162 break;
164 fxcrt::Fill(buffer, color >> 24);
165 break;
167 fxcrt::Fill(buffer, FindPalette(color));
168 break;
169 case FXDIB_Format::kBgr: {
170 const FX_BGR_STRUCT<uint8_t> bgr = ArgbToBGRStruct(color);
171 if (bgr.red == bgr.green && bgr.green == bgr.blue) {
172 fxcrt::Fill(buffer, bgr.red);
173 } else {
174 for (int row = 0; row < GetHeight(); row++) {
175 fxcrt::Fill(GetWritableScanlineAs<FX_BGR_STRUCT<uint8_t>>(row), bgr);
176 }
177 }
178 break;
179 }
181 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
182 // TODO(crbug.com/pdfium/2016): This is not reliable because alpha may
183 // be modified outside of this operation.
184 color |= 0xFF000000;
185 }
186 [[fallthrough]];
188 for (int row = 0; row < GetHeight(); row++) {
189 fxcrt::Fill(GetWritableScanlineAs<uint32_t>(row), color);
190 }
191 break;
192#if defined(PDF_USE_SKIA)
193 case FXDIB_Format::kBgraPremul: {
194 CHECK(CFX_DefaultRenderDevice::UseSkiaRenderer());
195 const FX_BGRA_STRUCT<uint8_t> bgra =
196 PreMultiplyColor(ArgbToBGRAStruct(color));
197 for (int row = 0; row < GetHeight(); row++) {
198 fxcrt::Fill(GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row), bgra);
199 }
200 break;
201 }
202#endif // defined(PDF_USE_SKIA)
203 }
204}
205
206bool CFX_DIBitmap::TransferBitmap(int width,
207 int height,
208 RetainPtr<const CFX_DIBBase> source,
209 int src_left,
210 int src_top) {
211 if (!m_pBuffer)
212 return false;
213
214 int dest_left = 0;
215 int dest_top = 0;
216 if (!GetOverlapRect(dest_left, dest_top, width, height, source->GetWidth(),
217 source->GetHeight(), src_left, src_top, nullptr)) {
218 return true;
219 }
220
221 FXDIB_Format dest_format = GetFormat();
222 FXDIB_Format src_format = source->GetFormat();
223 if (dest_format != src_format) {
224 return TransferWithUnequalFormats(dest_format, dest_left, dest_top, width,
225 height, std::move(source), src_left,
226 src_top);
227 }
228
229 if (GetBPP() != 1) {
230 TransferWithMultipleBPP(dest_left, dest_top, width, height,
231 std::move(source), src_left, src_top);
232 return true;
233 }
234
235 TransferEqualFormatsOneBPP(dest_left, dest_top, width, height,
236 std::move(source), src_left, src_top);
237 return true;
238}
239
240bool CFX_DIBitmap::TransferWithUnequalFormats(
241 FXDIB_Format dest_format,
242 int dest_left,
243 int dest_top,
244 int width,
245 int height,
246 RetainPtr<const CFX_DIBBase> source,
247 int src_left,
248 int src_top) {
249 if (HasPalette())
250 return false;
251
252 if (GetBPP() == 8) {
253 dest_format = FXDIB_Format::k8bppMask;
254 }
255
256 FX_SAFE_UINT32 offset = dest_left;
257 offset *= GetBPP();
258 offset /= 8;
259 if (!offset.IsValid())
260 return false;
261
262 pdfium::span<uint8_t> dest_buf = GetWritableBuffer().subspan(
263 dest_top * GetPitch() + static_cast<uint32_t>(offset.ValueOrDie()));
264 DataVector<uint32_t> dest_palette =
265 ConvertBuffer(dest_format, dest_buf, GetPitch(), width, height, source,
266 src_left, src_top);
267 CHECK(dest_palette.empty());
268 return true;
269}
270
271void CFX_DIBitmap::TransferWithMultipleBPP(int dest_left,
272 int dest_top,
273 int width,
274 int height,
275 RetainPtr<const CFX_DIBBase> source,
276 int src_left,
277 int src_top) {
278 const int bytes_per_pixel = GetBPP() / 8;
280 for (int row = 0; row < height; ++row) {
281 uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * GetPitch() +
282 dest_left * bytes_per_pixel;
283 const uint8_t* src_scan = source->GetScanline(src_top + row)
284 .subspan(src_left * bytes_per_pixel)
285 .data();
286 FXSYS_memcpy(dest_scan, src_scan, width * bytes_per_pixel);
287 }
288 });
289}
290
291void CFX_DIBitmap::TransferEqualFormatsOneBPP(
292 int dest_left,
293 int dest_top,
294 int width,
295 int height,
296 RetainPtr<const CFX_DIBBase> source,
297 int src_left,
298 int src_top) {
300 for (int row = 0; row < height; ++row) {
301 uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * GetPitch();
302 const uint8_t* src_scan = source->GetScanline(src_top + row).data();
303 for (int col = 0; col < width; ++col) {
304 int src_idx = src_left + col;
305 int dest_idx = dest_left + col;
306 if (src_scan[(src_idx) / 8] & (1 << (7 - (src_idx) % 8))) {
307 dest_scan[(dest_idx) / 8] |= 1 << (7 - (dest_idx) % 8);
308 } else {
309 dest_scan[(dest_idx) / 8] &= ~(1 << (7 - (dest_idx) % 8));
310 }
311 }
312 }
313 });
314}
315
316void CFX_DIBitmap::SetRedFromAlpha() {
318 CHECK(m_pBuffer);
319
320 for (int row = 0; row < GetHeight(); row++) {
321 auto scanline =
322 GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row).first(GetWidth());
323 for (auto& pixel : scanline) {
324 pixel.red = pixel.alpha;
325 }
326 }
327}
328
329void CFX_DIBitmap::SetUniformOpaqueAlpha() {
331 CHECK(m_pBuffer);
332
333 for (int row = 0; row < GetHeight(); row++) {
334 auto scanline =
335 GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row).first(GetWidth());
336 for (auto& pixel : scanline) {
337 pixel.alpha = 0xff;
338 }
339 }
340}
341
342bool CFX_DIBitmap::MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask) {
343 CHECK_EQ(GetWidth(), mask->GetWidth());
344 CHECK_EQ(GetHeight(), mask->GetHeight());
345 CHECK_EQ(FXDIB_Format::k8bppMask, mask->GetFormat());
346 CHECK(m_pBuffer);
347
349 // TODO(crbug.com/42271020): Consider adding support for
350 // `FXDIB_Format::kBgraPremul`
352 return false;
353 }
354
355 for (int row = 0; row < GetHeight(); row++) {
356 auto dest_scan =
357 GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row).first(GetWidth());
358 auto mask_scan = mask->GetScanline(row).first(GetWidth());
359 for (int col = 0; col < GetWidth(); col++) {
360 // Since the `dest_scan` value always starts out as 255 in this case,
361 // simplify 255 * x / 255.
362 dest_scan[col].alpha = mask_scan[col];
363 }
364 }
365 return true;
366 }
367
369 for (int row = 0; row < GetHeight(); row++) {
370 auto dest_scan =
371 GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row).first(GetWidth());
372 auto mask_scan = mask->GetScanline(row).first(GetWidth());
373 for (int col = 0; col < GetWidth(); col++) {
374 dest_scan[col].alpha = dest_scan[col].alpha * mask_scan[col] / 255;
375 }
376 }
377 return true;
378}
379
380bool CFX_DIBitmap::MultiplyAlpha(float alpha) {
381 CHECK_GE(alpha, 0.0f);
382 CHECK_LE(alpha, 1.0f);
384
385 if (alpha == 1.0f) {
386 return true;
387 }
388
389 if (!m_pBuffer) {
390 return false;
391 }
392
393 // TODO(crbug.com/42271020): Consider adding support for
394 // `FXDIB_Format::kBgraPremul`
396 return false;
397 }
398
399 const int bitmap_alpha = static_cast<int>(alpha * 255.0f);
400 for (int row = 0; row < GetHeight(); row++) {
401 auto dest_scan =
402 GetWritableScanlineAs<FX_BGRA_STRUCT<uint8_t>>(row).first(GetWidth());
403 for (auto& pixel : dest_scan) {
404 pixel.alpha = pixel.alpha * bitmap_alpha / 255;
405 }
406 }
407 return true;
408}
409
410#if defined(PDF_USE_SKIA)
411uint32_t CFX_DIBitmap::GetPixelForTesting(int x, int y) const {
412 if (!m_pBuffer)
413 return 0;
414
415 FX_SAFE_UINT32 offset = x;
416 offset *= GetBPP();
417 offset /= 8;
418 if (!offset.IsValid())
419 return 0;
420
421 uint8_t* pos =
422 UNSAFE_TODO(m_pBuffer.Get() + y * GetPitch() + offset.ValueOrDie());
423 switch (GetFormat()) {
424 case FXDIB_Format::kInvalid:
425 return 0;
426 case FXDIB_Format::k1bppMask: {
427 if ((*pos) & (1 << (7 - x % 8))) {
428 return 0xff000000;
429 }
430 return 0;
431 }
432 case FXDIB_Format::k1bppRgb: {
433 if ((*pos) & (1 << (7 - x % 8))) {
434 return HasPalette() ? GetPaletteSpan()[1] : 0xffffffff;
435 }
436 return HasPalette() ? GetPaletteSpan()[0] : 0xff000000;
437 }
438 case FXDIB_Format::k8bppMask:
439 return (*pos) << 24;
440 case FXDIB_Format::k8bppRgb:
441 return HasPalette() ? GetPaletteSpan()[*pos]
442 : ArgbEncode(0xff, *pos, *pos, *pos);
443 case FXDIB_Format::kBgr:
444 case FXDIB_Format::kBgrx:
445 return UNSAFE_TODO(FXARGB_GetDIB(pos) | 0xff000000);
446 case FXDIB_Format::kBgra:
447 return UNSAFE_TODO(FXARGB_GetDIB(pos));
448 case FXDIB_Format::kBgraPremul: {
449 // TODO(crbug.com/42271020): Consider testing with
450 // `FXDIB_Format::kBgraPremul`
451 NOTREACHED_NORETURN();
452 }
453 }
454}
455#endif // defined(PDF_USE_SKIA)
456
457void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor,
458 uint32_t backcolor) {
459 int fr = FXSYS_GetRValue(forecolor);
460 int fg = FXSYS_GetGValue(forecolor);
461 int fb = FXSYS_GetBValue(forecolor);
462 int br = FXSYS_GetRValue(backcolor);
463 int bg = FXSYS_GetGValue(backcolor);
464 int bb = FXSYS_GetBValue(backcolor);
465 if (GetBPP() <= 8) {
466 if (forecolor == 0 && backcolor == 0xffffff && !HasPalette())
467 return;
468
469 BuildPalette();
470 int size = 1 << GetBPP();
471 for (int i = 0; i < size; ++i) {
472 int gray = FXRGB2GRAY(FXARGB_R(palette_[i]), FXARGB_G(palette_[i]),
473 FXARGB_B(palette_[i]));
474 palette_[i] =
475 ArgbEncode(0xff, br + (fr - br) * gray / 255,
476 bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255);
477 }
478 return;
479 }
480 if (forecolor == 0 && backcolor == 0xffffff) {
481 for (int row = 0; row < GetHeight(); ++row) {
483 uint8_t* scanline = m_pBuffer.Get() + row * GetPitch();
484 const int gap = GetBPP() / 8 - 2;
485 for (int col = 0; col < GetWidth(); ++col) {
486 int gray = FXRGB2GRAY(scanline[2], scanline[1], scanline[0]);
487 *scanline++ = gray;
488 *scanline++ = gray;
489 *scanline = gray;
490 scanline += gap;
491 }
492 });
493 }
494 return;
495 }
496 for (int row = 0; row < GetHeight(); ++row) {
498 uint8_t* scanline = m_pBuffer.Get() + row * GetPitch();
499 const int gap = GetBPP() / 8 - 2;
500 for (int col = 0; col < GetWidth(); ++col) {
501 int gray = FXRGB2GRAY(scanline[2], scanline[1], scanline[0]);
502 *scanline++ = bb + (fb - bb) * gray / 255;
503 *scanline++ = bg + (fg - bg) * gray / 255;
504 *scanline = br + (fr - br) * gray / 255;
505 scanline += gap;
506 }
507 });
508 }
509}
510
511bool CFX_DIBitmap::ConvertColorScale(uint32_t forecolor, uint32_t backcolor) {
512 if (!m_pBuffer || IsMaskFormat())
513 return false;
514
515 ConvertBGRColorScale(forecolor, backcolor);
516 return true;
517}
518
519// static
520std::optional<CFX_DIBitmap::PitchAndSize> CFX_DIBitmap::CalculatePitchAndSize(
521 int width,
522 int height,
523 FXDIB_Format format,
524 uint32_t pitch) {
525 if (width <= 0 || height <= 0) {
526 return std::nullopt;
527 }
528 int bpp = GetBppFromFormat(format);
529 if (!bpp) {
530 return std::nullopt;
531 }
532 if (pitch == 0) {
533 std::optional<uint32_t> pitch32 = fxge::CalculatePitch32(bpp, width);
534 if (!pitch32.has_value()) {
535 return std::nullopt;
536 }
537 pitch = pitch32.value();
538 } else {
539 std::optional<uint32_t> actual_pitch =
540 fxge::CalculatePitch8(bpp, /*components=*/1, width);
541 if (!actual_pitch.has_value() || pitch < actual_pitch.value()) {
542 return std::nullopt;
543 }
544 }
545 size_t safe_size = pitch;
546 safe_size *= height;
547 // The final size of the buffer will be (*size + 4). We are trying to not exceed this.
548 if (((std::numeric_limits<std::size_t>::max() - 4) / pitch) < static_cast<uint32_t>(height))
549 return std::nullopt;
550
551 return PitchAndSize{pitch, safe_size};
552}
553
554bool CFX_DIBitmap::CompositeBitmap(int dest_left,
555 int dest_top,
556 int width,
557 int height,
558 RetainPtr<const CFX_DIBBase> source,
559 int src_left,
560 int src_top,
561 BlendMode blend_type,
562 const CFX_AggClipRgn* pClipRgn,
563 bool bRgbByteOrder) {
564 // Should have called CompositeMask().
565 CHECK(!source->IsMaskFormat());
566
567 if (!m_pBuffer)
568 return false;
569
570 if (GetBPP() < 8) {
571 return false;
572 }
573
574 if (!GetOverlapRect(dest_left, dest_top, width, height, source->GetWidth(),
575 source->GetHeight(), src_left, src_top, pClipRgn)) {
576 return true;
577 }
578
579 RetainPtr<CFX_DIBitmap> pClipMask;
580 FX_RECT clip_box;
581 if (pClipRgn && pClipRgn->GetType() != CFX_AggClipRgn::kRectI) {
582 pClipMask = pClipRgn->GetMask();
583 clip_box = pClipRgn->GetBox();
584 }
585 CFX_ScanlineCompositor compositor;
586 if (!compositor.Init(GetFormat(), source->GetFormat(),
587 source->GetPaletteSpan(), 0, blend_type,
588 bRgbByteOrder)) {
589 return false;
590 }
591 const int dest_bytes_per_pixel = GetBPP() / 8;
592 const int src_bytes_per_pixel = source->GetBPP() / 8;
593 const bool bRgb = src_bytes_per_pixel > 1;
594 if (!bRgb && !source->HasPalette()) {
595 return false;
596 }
597
598 for (int row = 0; row < height; row++) {
599 pdfium::span<uint8_t> dest_scan =
600 GetWritableScanline(dest_top + row)
601 .subspan(dest_left * dest_bytes_per_pixel);
602 pdfium::span<const uint8_t> src_scan =
603 source->GetScanline(src_top + row)
604 .subspan(src_left * src_bytes_per_pixel);
605 pdfium::span<const uint8_t> clip_scan;
606 if (pClipMask) {
607 clip_scan = pClipMask->GetWritableScanline(dest_top + row - clip_box.top)
608 .subspan(dest_left - clip_box.left);
609 }
610 if (bRgb) {
611 compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan);
612 } else {
613 compositor.CompositePalBitmapLine(dest_scan, src_scan, src_left, width,
614 clip_scan);
615 }
616 }
617 return true;
618}
619
620bool CFX_DIBitmap::CompositeMask(int dest_left,
621 int dest_top,
622 int width,
623 int height,
624 RetainPtr<const CFX_DIBBase> pMask,
625 uint32_t color,
626 int src_left,
627 int src_top,
628 BlendMode blend_type,
629 const CFX_AggClipRgn* pClipRgn,
630 bool bRgbByteOrder) {
631 // Should have called CompositeBitmap().
632 CHECK(pMask->IsMaskFormat());
633
634 if (!m_pBuffer)
635 return false;
636
637 if (GetBPP() < 8) {
638 return false;
639 }
640
641 if (!GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(),
642 pMask->GetHeight(), src_left, src_top, pClipRgn)) {
643 return true;
644 }
645
646 int src_alpha = FXARGB_A(color);
647 if (src_alpha == 0)
648 return true;
649
650 RetainPtr<CFX_DIBitmap> pClipMask;
651 FX_RECT clip_box;
652 if (pClipRgn && pClipRgn->GetType() != CFX_AggClipRgn::kRectI) {
653 pClipMask = pClipRgn->GetMask();
654 clip_box = pClipRgn->GetBox();
655 }
656 const int src_bpp = pMask->GetBPP();
657 const int bytes_per_pixel = GetBPP() / 8;
658 CFX_ScanlineCompositor compositor;
659 if (!compositor.Init(GetFormat(), pMask->GetFormat(), {}, color, blend_type,
660 bRgbByteOrder)) {
661 return false;
662 }
663 for (int row = 0; row < height; row++) {
664 pdfium::span<uint8_t> dest_scan = GetWritableScanline(dest_top + row)
665 .subspan(dest_left * bytes_per_pixel);
666 pdfium::span<const uint8_t> src_scan = pMask->GetScanline(src_top + row);
667 pdfium::span<const uint8_t> clip_scan;
668 if (pClipMask) {
669 clip_scan = pClipMask->GetScanline(dest_top + row - clip_box.top)
670 .subspan(dest_left - clip_box.left);
671 }
672 if (src_bpp == 1) {
673 compositor.CompositeBitMaskLine(dest_scan, src_scan, src_left, width,
674 clip_scan);
675 } else {
676 compositor.CompositeByteMaskLine(dest_scan, src_scan.subspan(src_left),
677 width, clip_scan);
678 }
679 }
680 return true;
681}
682
683void CFX_DIBitmap::CompositeOneBPPMask(int dest_left,
684 int dest_top,
685 int width,
686 int height,
687 RetainPtr<const CFX_DIBBase> source,
688 int src_left,
689 int src_top) {
690 if (GetBPP() != 1) {
691 return;
692 }
693 if (!GetOverlapRect(dest_left, dest_top, width, height, source->GetWidth(),
694 source->GetHeight(), src_left, src_top, nullptr)) {
695 return;
696 }
698 for (int row = 0; row < height; ++row) {
699 uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * GetPitch();
700 const uint8_t* src_scan = source->GetScanline(src_top + row).data();
701 for (int col = 0; col < width; ++col) {
702 int src_idx = src_left + col;
703 int dest_idx = dest_left + col;
704 if (src_scan[src_idx / 8] & (1 << (7 - src_idx % 8))) {
705 dest_scan[dest_idx / 8] |= 1 << (7 - dest_idx % 8);
706 }
707 }
708 }
709 });
710}
711
712bool CFX_DIBitmap::CompositeRect(int left,
713 int top,
714 int width,
715 int height,
716 uint32_t color) {
717 if (!m_pBuffer)
718 return false;
719
720 int src_alpha = FXARGB_A(color);
721 if (src_alpha == 0)
722 return true;
723
724 FX_RECT rect(left, top, left + width, top + height);
725 rect.Intersect(0, 0, GetWidth(), GetHeight());
726 if (rect.IsEmpty())
727 return true;
728
729 width = rect.Width();
730 uint32_t dst_color = color;
731 uint8_t* color_p = reinterpret_cast<uint8_t*>(&dst_color);
732 if (GetBPP() == 8) {
734 uint8_t gray =
736 ? 255
737 : (uint8_t)FXRGB2GRAY((int)color_p[2], color_p[1], color_p[0]);
738 for (int row = rect.top; row < rect.bottom; row++) {
739 uint8_t* dest_scan = m_pBuffer.Get() + row * GetPitch() + rect.left;
740 if (src_alpha == 255) {
741 FXSYS_memset(dest_scan, gray, width);
742 } else {
743 for (int col = 0; col < width; col++) {
744 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha);
745 dest_scan++;
746 }
747 }
748 }
749 });
750 return true;
751 }
752 if (GetBPP() == 1) {
754 int left_shift = rect.left % 8;
755 int right_shift = rect.right % 8;
756 int new_width = rect.right / 8 - rect.left / 8;
757 int index = 0;
758 if (HasPalette()) {
759 for (int i = 0; i < 2; i++) {
760 if (GetPaletteSpan()[i] == color) {
761 index = i;
762 }
763 }
764 } else {
765 index = (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
766 }
767 for (int row = rect.top; row < rect.bottom; row++) {
768 uint8_t* dest_scan_top =
769 GetWritableScanline(row).subspan(rect.left / 8).data();
770 uint8_t* dest_scan_top_r =
771 GetWritableScanline(row).subspan(rect.right / 8).data();
772 uint8_t left_flag = *dest_scan_top & (255 << (8 - left_shift));
773 uint8_t right_flag = *dest_scan_top_r & (255 >> right_shift);
774 if (new_width) {
775 FXSYS_memset(dest_scan_top + 1, index ? 255 : 0, new_width - 1);
776 if (!index) {
777 *dest_scan_top &= left_flag;
778 *dest_scan_top_r &= right_flag;
779 } else {
780 *dest_scan_top |= ~left_flag;
781 *dest_scan_top_r |= ~right_flag;
782 }
783 } else {
784 if (!index) {
785 *dest_scan_top &= left_flag | right_flag;
786 } else {
787 *dest_scan_top |= ~(left_flag | right_flag);
788 }
789 }
790 }
791 });
792 return true;
793 }
794
795 CHECK_GE(GetBPP(), 24);
796 UNSAFE_TODO({ color_p[3] = static_cast<uint8_t>(src_alpha); });
797 const int bytes_per_pixel = GetBPP() / 8;
798 const bool bAlpha = IsAlphaFormat();
799 if (bAlpha) {
800 // Other formats with alpha have already been handled above.
801 //
802 // TODO(crbug.com/42271020): Consider adding support for
803 // `FXDIB_Format::kBgraPremul`
805 }
806 if (src_alpha == 255) {
807 for (int row = rect.top; row < rect.bottom; row++) {
809 uint8_t* dest_scan =
810 m_pBuffer.Get() + row * GetPitch() + rect.left * bytes_per_pixel;
811 if (bytes_per_pixel == 4) {
812 uint32_t* scan = reinterpret_cast<uint32_t*>(dest_scan);
813 for (int col = 0; col < width; col++) {
814 *scan++ = dst_color;
815 }
816 } else {
817 for (int col = 0; col < width; col++) {
818 *dest_scan++ = color_p[0];
819 *dest_scan++ = color_p[1];
820 *dest_scan++ = color_p[2];
821 }
822 }
823 });
824 }
825 return true;
826 }
827 if (bAlpha) {
828 for (int row = rect.top; row < rect.bottom; row++) {
830 uint8_t* dest_scan =
831 m_pBuffer.Get() + row * GetPitch() + rect.left * bytes_per_pixel;
832 for (int col = 0; col < width; col++) {
833 uint8_t back_alpha = dest_scan[3];
834 if (back_alpha == 0) {
835 FXARGB_SetDIB(dest_scan, ArgbEncode(src_alpha, color_p[2],
836 color_p[1], color_p[0]));
837 dest_scan += 4;
838 continue;
839 }
840 uint8_t dest_alpha =
841 back_alpha + src_alpha - back_alpha * src_alpha / 255;
842 int alpha_ratio = src_alpha * 255 / dest_alpha;
843 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[0], alpha_ratio);
844 dest_scan++;
845 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[1], alpha_ratio);
846 dest_scan++;
847 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[2], alpha_ratio);
848 dest_scan++;
849 *dest_scan++ = dest_alpha;
850 }
851 });
852 }
853 return true;
854 }
855
856 for (int row = rect.top; row < rect.bottom; row++) {
858 uint8_t* dest_scan =
859 m_pBuffer.Get() + row * GetPitch() + rect.left * bytes_per_pixel;
860 for (int col = 0; col < width; col++) {
861 for (int comps = 0; comps < bytes_per_pixel; comps++) {
862 if (comps == 3) {
863 *dest_scan++ = 255;
864 continue;
865 }
866 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[comps], src_alpha);
867 dest_scan++;
868 }
869 }
870 });
871 }
872 return true;
873}
874
875bool CFX_DIBitmap::ConvertFormat(FXDIB_Format dest_format) {
876 static constexpr FXDIB_Format kAllowedDestFormats[] = {
879#if defined(PDF_USE_SKIA)
880 FXDIB_Format::kBgraPremul,
881#endif
883 };
884 CHECK(pdfium::Contains(kAllowedDestFormats, dest_format));
885
886 if (dest_format == GetFormat()) {
887 return true;
888 }
889
890 switch (dest_format) {
894 return true;
895 }
896 break;
897
902 return true;
903 }
904#if defined(PDF_USE_SKIA)
905 if (GetFormat() == FXDIB_Format::kBgraPremul) {
906 UnPreMultiply();
907 return true;
908 }
909#endif // defined(PDF_USE_SKIA)
910 break;
911
912#if defined(PDF_USE_SKIA)
913 case FXDIB_Format::kBgraPremul:
914 if (GetFormat() == FXDIB_Format::kBgrx) {
915 SetFormat(FXDIB_Format::kBgraPremul);
916 SetUniformOpaqueAlpha();
917 return true;
918 }
919 if (GetFormat() == FXDIB_Format::kBgra) {
920 PreMultiply();
921 return true;
922 }
923 break;
924#endif // defined(PDF_USE_SKIA)
925
926 default:
927 break;
928 }
929
930 std::optional<PitchAndSize> pitch_size =
931 CalculatePitchAndSize(GetWidth(), GetHeight(), dest_format, /*pitch=*/0);
932 if (!pitch_size.has_value()) {
933 return false;
934 }
935
936 const size_t dest_buf_size = GetAllocSizeOrZero(pitch_size.value().size);
937 if (dest_buf_size == 0) {
938 return false;
939 }
940
941 std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf(
942 FX_TryAlloc(uint8_t, dest_buf_size));
943 if (!dest_buf) {
944 return false;
945 }
946
947 // SAFETY: `dest_buf` allocated with `dest_buf_size` bytes above.
948 auto dest_span =
949 UNSAFE_BUFFERS(pdfium::make_span(dest_buf.get(), dest_buf_size));
950 if (dest_format == FXDIB_Format::kBgra) {
951 fxcrt::Fill(dest_span, 0xff);
952 }
953
954 RetainPtr<CFX_DIBBase> holder(this);
955 const uint32_t dest_pitch = pitch_size.value().pitch;
956 palette_ = ConvertBuffer(dest_format, dest_span, dest_pitch, GetWidth(),
957 GetHeight(), holder, /*src_left=*/0,
958 /*src_top=*/0);
959 m_pBuffer = std::move(dest_buf);
960 SetFormat(dest_format);
961 SetPitch(dest_pitch);
962 return true;
963}
964
965#if defined(PDF_USE_SKIA)
966CFX_DIBitmap::ScopedPremultiplier::ScopedPremultiplier(
967 RetainPtr<CFX_DIBitmap> bitmap,
968 bool do_premultiply)
969 : bitmap_(std::move(bitmap)), do_premultiply_(do_premultiply) {
970 CHECK(!bitmap_->IsPremultiplied());
971 if (do_premultiply_) {
972 bitmap_->PreMultiply();
973 }
974}
975
976CFX_DIBitmap::ScopedPremultiplier::~ScopedPremultiplier() {
977 if (do_premultiply_) {
978 bitmap_->UnPreMultiply();
979 }
980 CHECK(!bitmap_->IsPremultiplied());
981}
982#endif // defined(PDF_USE_SKIA)
#define CHECK_EQ(x, y)
Definition check_op.h:10
#define CHECK_GE(x, y)
Definition check_op.h:15
#define CHECK_LE(x, y)
Definition check_op.h:14
#define DCHECK_EQ(x, y)
Definition check_op.h:17
const FX_RECT & GetBox() const
ClipType GetType() const
bool GetOverlapRect(int &dest_left, int &dest_top, int &width, int &height, int src_width, int src_height, int &src_left, int &src_top, const CFX_AggClipRgn *pClipRgn) const
int FindPalette(uint32_t color) const
void SetWidth(int width)
bool HasPalette() const
Definition cfx_dibbase.h:78
FXDIB_Format GetFormat() const
Definition cfx_dibbase.h:69
int GetBPP() const
Definition cfx_dibbase.h:72
void SetHeight(int height)
void BuildPalette()
bool IsAlphaFormat() const
Definition cfx_dibbase.h:75
void SetFormat(FXDIB_Format format)
uint32_t GetPitch() const
Definition cfx_dibbase.h:67
int GetHeight() const
Definition cfx_dibbase.h:66
int GetWidth() const
Definition cfx_dibbase.h:65
bool IsMaskFormat() const
Definition cfx_dibbase.h:74
void SetPitch(uint32_t pitch)
bool CompositeBitmap(int dest_left, int dest_top, int width, int height, RetainPtr< const CFX_DIBBase > source, int src_left, int src_top, BlendMode blend_type, const CFX_AggClipRgn *pClipRgn, bool bRgbByteOrder)
bool CompositeMask(int dest_left, int dest_top, int width, int height, RetainPtr< const CFX_DIBBase > pMask, uint32_t color, int src_left, int src_top, BlendMode blend_type, const CFX_AggClipRgn *pClipRgn, bool bRgbByteOrder)
void SetUniformOpaqueAlpha()
bool Create(int width, int height, FXDIB_Format format)
bool TransferBitmap(int width, int height, RetainPtr< const CFX_DIBBase > source, int src_left, int src_top)
pdfium::span< const uint8_t > GetBuffer() const
bool MultiplyAlphaMask(RetainPtr< const CFX_DIBitmap > mask)
bool ConvertColorScale(uint32_t forecolor, uint32_t backcolor)
bool ConvertFormat(FXDIB_Format format)
bool CompositeRect(int dest_left, int dest_top, int width, int height, uint32_t color)
pdfium::span< const uint8_t > GetScanline(int line) const override
size_t GetEstimatedImageMemoryBurden() const override
bool MultiplyAlpha(float alpha)
bool Create(int width, int height, FXDIB_Format format, uint8_t *pBuffer, uint32_t pitch)
void TakeOver(RetainPtr< CFX_DIBitmap > &&pSrcBitmap)
void Clear(uint32_t color)
bool Copy(RetainPtr< const CFX_DIBBase > source)
void SetRedFromAlpha()
void CompositeOneBPPMask(int dest_left, int dest_top, int width, int height, RetainPtr< const CFX_DIBBase > source, int src_left, int src_top)
static std::optional< PitchAndSize > CalculatePitchAndSize(int width, int height, FXDIB_Format format, uint32_t pitch)
~CFX_DIBitmap() override
#define UNSAFE_BUFFERS(...)
#define UNSAFE_TODO(...)
UNSAFE_BUFFER_USAGE void FXARGB_SetDIB(uint8_t *p, uint32_t argb)
Definition fx_dib.h:221
BlendMode
Definition fx_dib.h:119
#define FXRGB2GRAY(r, g, b)
Definition fx_dib.h:203
#define FXARGB_B(argb)
Definition fx_dib.h:199
#define FXARGB_G(argb)
Definition fx_dib.h:198
#define FXARGB_A(argb)
Definition fx_dib.h:196
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
Definition fx_dib.h:188
int GetBppFromFormat(FXDIB_Format format)
Definition fx_dib.h:160
#define FXARGB_R(argb)
Definition fx_dib.h:197
constexpr uint8_t FXSYS_GetRValue(uint32_t bgr)
Definition fx_dib.h:143
FXDIB_Format
Definition fx_dib.h:21
constexpr uint8_t FXSYS_GetGValue(uint32_t bgr)
Definition fx_dib.h:147
#define FXDIB_ALPHA_MERGE(backdrop, source, source_alpha)
Definition fx_dib.h:204
constexpr uint8_t FXSYS_GetBValue(uint32_t bgr)
Definition fx_dib.h:151
UNSAFE_BUFFER_USAGE void * FXSYS_memset(void *ptr1, int val, size_t len)
UNSAFE_BUFFER_USAGE void * FXSYS_memcpy(void *ptr1, const void *ptr2, size_t len)
pdfium::CheckedNumeric< uint32_t > FX_SAFE_UINT32
#define CHECK(cvref)
FX_RECT & operator=(const FX_RECT &that)=default
int32_t bottom
int32_t right
int Width() const
int32_t top
int32_t left
bool IsEmpty() const
constexpr FX_RECT(int l, int t, int r, int b)