7#include "core/fxge/dib/cfx_dibitmap.h"
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"
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);
42CFX_DIBitmap::CFX_DIBitmap() =
default;
60 CalculatePitchAndSize(width, height, format, pitch);
61 if (!pitch_size.has_value()) {
66 m_pBuffer.Reset(pBuffer);
68 const size_t buffer_size = GetAllocSizeOrZero(pitch_size.value().size);
69 if (buffer_size == 0) {
73 m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
74 FX_TryAlloc(uint8_t, buffer_size));
89 if (!Create(source->GetWidth(), source->GetHeight(), source->GetFormat())) {
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()));
105 return pdfium::span<
const uint8_t>();
108 pdfium::make_span(m_pBuffer.Get(), GetHeight() * GetPitch()));
112 auto buffer_span = GetBuffer();
113 if (buffer_span.empty())
114 return pdfium::span<
const uint8_t>();
120 size_t result = CFX_DIBBase::GetEstimatedImageMemoryBurden();
121 if (!GetBuffer().empty()) {
123 CHECK(pdfium::IsValueInRangeForNumericType<size_t>(height));
124 result +=
static_cast<size_t>(height) *
GetPitch();
129#if BUILDFLAG(IS_WIN) || defined(PDF_USE_SKIA)
130RetainPtr<
const CFX_DIBitmap> CFX_DIBitmap::RealizeIfNeeded()
const {
131 if (GetBuffer().empty()) {
134 return pdfium::WrapRetain(
this);
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());
148void CFX_DIBitmap::
Clear(uint32_t color) {
149 auto buffer = GetWritableBuffer();
150 if (buffer.empty()) {
158 fxcrt::Fill(buffer, (color & 0xff000000) ? 0xff : 0);
164 fxcrt::Fill(buffer, color >> 24);
171 if (bgr.red == bgr.green && bgr.green == bgr.blue) {
172 fxcrt::Fill(buffer, bgr.red);
174 for (
int row = 0; row <
GetHeight(); row++) {
188 for (
int row = 0; row <
GetHeight(); row++) {
189 fxcrt::Fill(GetWritableScanlineAs<uint32_t>(row), color);
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);
217 source->GetHeight()
, src_left
, src_top
, nullptr)) {
223 if (dest_format != src_format) {
224 return TransferWithUnequalFormats(dest_format, dest_left, dest_top, width,
225 height,
std::move(source), src_left,
230 TransferWithMultipleBPP(dest_left, dest_top, width, height,
231 std::move(source), src_left, src_top);
235 TransferEqualFormatsOneBPP(dest_left, dest_top, width, height,
236 std::move(source), src_left, src_top);
240bool CFX_DIBitmap::TransferWithUnequalFormats(
259 if (!offset.IsValid())
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,
267 CHECK(dest_palette.empty());
271void CFX_DIBitmap::TransferWithMultipleBPP(
int dest_left,
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)
291void CFX_DIBitmap::TransferEqualFormatsOneBPP(
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);
309 dest_scan[(dest_idx) / 8] &= ~(1 << (7 - (dest_idx) % 8));
320 for (
int row = 0; row <
GetHeight(); row++) {
323 for (
auto& pixel : scanline) {
324 pixel.red = pixel.alpha;
333 for (
int row = 0; row <
GetHeight(); row++) {
336 for (
auto& pixel : scanline) {
355 for (
int row = 0; row <
GetHeight(); row++) {
358 auto mask_scan = mask->GetScanline(row).first(
GetWidth());
359 for (
int col = 0; col <
GetWidth(); col++) {
362 dest_scan[col].alpha = mask_scan[col];
369 for (
int row = 0; row <
GetHeight(); row++) {
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;
399 const int bitmap_alpha =
static_cast<
int>(alpha * 255.0f);
400 for (
int row = 0; row <
GetHeight(); row++) {
403 for (
auto& pixel : dest_scan) {
404 pixel.alpha = pixel.alpha * bitmap_alpha / 255;
410#if defined(PDF_USE_SKIA)
411uint32_t CFX_DIBitmap::GetPixelForTesting(
int x,
int y)
const {
415 FX_SAFE_UINT32 offset = x;
418 if (!offset.IsValid())
422 UNSAFE_TODO(m_pBuffer.Get() + y * GetPitch() + offset.ValueOrDie());
423 switch (GetFormat()) {
424 case FXDIB_Format::kInvalid:
426 case FXDIB_Format::k1bppMask: {
427 if ((*pos) & (1 << (7 - x % 8))) {
432 case FXDIB_Format::k1bppRgb: {
433 if ((*pos) & (1 << (7 - x % 8))) {
434 return HasPalette() ? GetPaletteSpan()[1] : 0xffffffff;
436 return HasPalette() ? GetPaletteSpan()[0] : 0xff000000;
438 case FXDIB_Format::k8bppMask:
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: {
451 NOTREACHED_NORETURN();
457void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor,
458 uint32_t backcolor) {
466 if (forecolor == 0 && backcolor == 0xffffff && !
HasPalette())
471 for (
int i = 0; i < size; ++i) {
475 ArgbEncode(0xff, br + (fr - br) * gray / 255,
476 bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255);
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]);
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;
512 if (!m_pBuffer || IsMaskFormat())
515 ConvertBGRColorScale(forecolor, backcolor);
525 if (width <= 0 || height <= 0) {
533 std::optional<uint32_t> pitch32 = fxge::CalculatePitch32(bpp, width);
534 if (!pitch32.has_value()) {
537 pitch = pitch32.value();
539 std::optional<uint32_t> actual_pitch =
540 fxge::CalculatePitch8(bpp, 1, width);
541 if (!actual_pitch.has_value() || pitch < actual_pitch.value()) {
545 size_t safe_size = pitch;
548 if (((
std::numeric_limits<
std::size_t>::max() - 4) / pitch) <
static_cast<uint32_t>(height))
563 bool bRgbByteOrder) {
565 CHECK(!source->IsMaskFormat());
575 source->GetHeight()
, src_left
, src_top
, pClipRgn
)) {
582 pClipMask = pClipRgn->GetMask();
586 if (!compositor.Init(
GetFormat(), source->GetFormat(),
587 source->GetPaletteSpan(), 0, blend_type,
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()) {
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;
607 clip_scan = pClipMask->GetWritableScanline(dest_top + row - clip_box
.top)
608 .subspan(dest_left - clip_box
.left);
611 compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan);
613 compositor.CompositePalBitmapLine(dest_scan, src_scan, src_left, width,
630 bool bRgbByteOrder) {
632 CHECK(pMask->IsMaskFormat());
642 pMask->GetHeight()
, src_left
, src_top
, pClipRgn
)) {
653 pClipMask = pClipRgn->GetMask();
656 const int src_bpp = pMask->GetBPP();
657 const int bytes_per_pixel =
GetBPP() / 8;
659 if (!compositor.Init(
GetFormat(), pMask->GetFormat(), {}, color, blend_type,
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;
669 clip_scan = pClipMask->GetScanline(dest_top + row - clip_box
.top)
670 .subspan(dest_left - clip_box
.left);
673 compositor.CompositeBitMaskLine(dest_scan, src_scan, src_left, width,
676 compositor.CompositeByteMaskLine(dest_scan, src_scan.subspan(src_left),
694 source->GetHeight()
, src_left
, src_top
, nullptr)) {
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);
730 uint32_t dst_color = color;
731 uint8_t* color_p =
reinterpret_cast<uint8_t*>(&dst_color);
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) {
743 for (
int col = 0; col < width; col++) {
754 int left_shift = rect
.left % 8;
755 int right_shift = rect
.right % 8;
759 for (
int i = 0; i < 2; i++) {
760 if (GetPaletteSpan()[i] == color) {
765 index = (
static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
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);
777 *dest_scan_top &= left_flag;
778 *dest_scan_top_r &= right_flag;
780 *dest_scan_top |= ~left_flag;
781 *dest_scan_top_r |= ~right_flag;
785 *dest_scan_top &= left_flag | right_flag;
787 *dest_scan_top |= ~(left_flag | right_flag);
796 UNSAFE_TODO({ color_p[3] =
static_cast<uint8_t>(src_alpha); });
797 const int bytes_per_pixel =
GetBPP() / 8;
806 if (src_alpha == 255) {
807 for (
int row = rect
.top; row < rect
.bottom; row++) {
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++) {
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];
828 for (
int row = rect
.top; row < rect
.bottom; row++) {
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) {
836 color_p[1]
, color_p[0]
));
841 back_alpha + src_alpha - back_alpha * src_alpha / 255;
842 int alpha_ratio = src_alpha * 255 / dest_alpha;
849 *dest_scan++ = dest_alpha;
856 for (
int row = rect
.top; row < rect
.bottom; row++) {
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++) {
879#if defined(PDF_USE_SKIA)
880 FXDIB_Format::kBgraPremul,
884 CHECK(
pdfium::Contains(kAllowedDestFormats, dest_format));
890 switch (dest_format) {
904#if defined(PDF_USE_SKIA)
905 if (GetFormat() == FXDIB_Format::kBgraPremul) {
912#if defined(PDF_USE_SKIA)
913 case FXDIB_Format::kBgraPremul:
914 if (GetFormat() == FXDIB_Format::kBgrx) {
915 SetFormat(FXDIB_Format::kBgraPremul);
916 SetUniformOpaqueAlpha();
919 if (GetFormat() == FXDIB_Format::kBgra) {
931 CalculatePitchAndSize(GetWidth(), GetHeight(), dest_format, 0);
932 if (!pitch_size.has_value()) {
936 const size_t dest_buf_size = GetAllocSizeOrZero(pitch_size.value().size);
937 if (dest_buf_size == 0) {
942 FX_TryAlloc(uint8_t, dest_buf_size));
951 fxcrt::Fill(dest_span, 0xff);
955 const uint32_t dest_pitch = pitch_size.value().pitch;
956 palette_ = ConvertBuffer(dest_format, dest_span, dest_pitch, GetWidth(),
957 GetHeight(), holder, 0,
959 m_pBuffer = std::move(dest_buf);
965#if defined(PDF_USE_SKIA)
966CFX_DIBitmap::ScopedPremultiplier::ScopedPremultiplier(
967 RetainPtr<CFX_DIBitmap> bitmap,
969 : bitmap_(std::move(bitmap)), do_premultiply_(do_premultiply) {
970 CHECK(!bitmap_->IsPremultiplied());
971 if (do_premultiply_) {
972 bitmap_->PreMultiply();
976CFX_DIBitmap::ScopedPremultiplier::~ScopedPremultiplier() {
977 if (do_premultiply_) {
978 bitmap_->UnPreMultiply();
980 CHECK(!bitmap_->IsPremultiplied());
const FX_RECT & GetBox() 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
FXDIB_Format GetFormat() const
void SetHeight(int height)
bool IsAlphaFormat() const
void SetFormat(FXDIB_Format format)
uint32_t GetPitch() const
bool IsMaskFormat() const
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 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)
static bool UseSkiaRenderer()
#define UNSAFE_BUFFERS(...)
UNSAFE_BUFFER_USAGE void FXARGB_SetDIB(uint8_t *p, uint32_t argb)
#define FXRGB2GRAY(r, g, b)
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
int GetBppFromFormat(FXDIB_Format format)
constexpr uint8_t FXSYS_GetRValue(uint32_t bgr)
constexpr uint8_t FXSYS_GetGValue(uint32_t bgr)
#define FXDIB_ALPHA_MERGE(backdrop, source, source_alpha)
constexpr uint8_t FXSYS_GetBValue(uint32_t bgr)
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
FX_RECT & operator=(const FX_RECT &that)=default
constexpr FX_RECT(int l, int t, int r, int b)