7#include "core/fxge/dib/cfx_dibitmap.h"
16#include "build/build_config.h"
17#include "core/fxcrt/data_vector.h"
18#include "core/fxcrt/fx_coordinates.h"
19#include "core/fxcrt/fx_safe_types.h"
20#include "core/fxcrt/span_util.h"
21#include "core/fxge/calculate_pitch.h"
22#include "core/fxge/cfx_cliprgn.h"
23#include "core/fxge/cfx_defaultrenderdevice.h"
24#include "core/fxge/dib/cfx_scanlinecompositor.h"
25#include "third_party/base/check.h"
26#include "third_party/base/check_op.h"
27#include "third_party/base/notreached.h"
28#include "third_party/base/numerics/safe_conversions.h"
30CFX_DIBitmap::CFX_DIBitmap() =
default;
47 absl::optional<PitchAndSize> pitch_size =
48 CalculatePitchAndSize(width, height, format, pitch);
49 if (!pitch_size.has_value())
53 m_pBuffer.Reset(pBuffer);
55 FX_SAFE_SIZE_T safe_buffer_size = pitch_size.value().size;
56 safe_buffer_size += 4;
57 if (!safe_buffer_size.IsValid())
60 m_pBuffer = std::unique_ptr<uint8_t, FxFreeDeleter>(
61 FX_TryAlloc(uint8_t, safe_buffer_size.ValueOrDie()));
67 m_Pitch = pitch_size.value().pitch;
75 if (!Create(source->GetWidth(), source->GetHeight(), source->GetFormat())) {
80 for (
int row = 0; row < source->GetHeight(); row++) {
81 memcpy(m_pBuffer.Get() + row * m_Pitch, source->GetScanline(row).data(),
89pdfium::span<
const uint8_t> CFX_DIBitmap::
GetBuffer()
const {
91 return pdfium::span<
const uint8_t>();
93 return {m_pBuffer.Get(), m_Height * m_Pitch};
96pdfium::span<
const uint8_t> CFX_DIBitmap::
GetScanline(
int line)
const {
97 auto buffer_span = GetBuffer();
98 if (buffer_span.empty())
99 return pdfium::span<
const uint8_t>();
105 size_t result = CFX_DIBBase::GetEstimatedImageMemoryBurden();
106 if (!GetBuffer().empty()) {
108 CHECK(pdfium::base::IsValueInRangeForNumericType<size_t>(height));
109 result +=
static_cast<size_t>(height) *
GetPitch();
114#if BUILDFLAG(IS_WIN) || defined(PDF_USE_SKIA)
115RetainPtr<
const CFX_DIBitmap> CFX_DIBitmap::RealizeIfNeeded()
const {
116 if (GetBuffer().empty()) {
119 return pdfium::WrapRetain(
this);
124 m_pBuffer = std::move(pSrcBitmap->m_pBuffer);
125 m_palette = std::move(pSrcBitmap->m_palette);
126 pSrcBitmap->m_pBuffer =
nullptr;
127 m_Format = pSrcBitmap->m_Format;
128 m_Width = pSrcBitmap->m_Width;
129 m_Height = pSrcBitmap->m_Height;
130 m_Pitch = pSrcBitmap->m_Pitch;
133void CFX_DIBitmap::
Clear(uint32_t color) {
137 uint8_t* pBuffer = m_pBuffer.Get();
160 std::tie(a, r, g, b) = ArgbDecode(color);
161 if (r == g && g == b) {
165 for (
int col = 0; col <
m_Width; col++) {
166 pBuffer[byte_pos++] = b;
167 pBuffer[byte_pos++] = g;
168 pBuffer[byte_pos++] = r;
170 for (
int row = 1; row <
m_Height; row++) {
184 for (
int i = 0; i <
m_Width; i++)
185 reinterpret_cast<uint32_t*>(pBuffer)[i] = color;
186 for (
int row = 1; row <
m_Height; row++)
206 source->GetHeight()
, src_left
, src_top
, nullptr)) {
212 if (dest_format != src_format) {
213 return TransferWithUnequalFormats(dest_format, dest_left, dest_top, width,
214 height,
std::move(source), src_left,
219 TransferWithMultipleBPP(dest_left, dest_top, width, height,
220 std::move(source), src_left, src_top);
224 TransferEqualFormatsOneBPP(dest_left, dest_top, width, height,
225 std::move(source), src_left, src_top);
229bool CFX_DIBitmap::TransferWithUnequalFormats(
244 FX_SAFE_UINT32 offset = dest_left;
247 if (!offset.IsValid())
250 pdfium::span<uint8_t> dest_buf = GetWritableBuffer().subspan(
251 dest_top * m_Pitch +
static_cast<uint32_t>(offset.ValueOrDie()));
252 DataVector<uint32_t> dest_palette = ConvertBuffer(
253 dest_format, dest_buf, m_Pitch, width, height, source, src_left, src_top);
254 CHECK(dest_palette.empty());
258void CFX_DIBitmap::TransferWithMultipleBPP(
int dest_left,
266 for (
int row = 0; row < height; ++row) {
268 m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp;
269 const uint8_t* src_scan =
270 source->GetScanline(src_top + row).subspan(src_left * Bpp).data();
271 memcpy(dest_scan, src_scan, width * Bpp);
275void CFX_DIBitmap::TransferEqualFormatsOneBPP(
283 for (
int row = 0; row < height; ++row) {
284 uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch;
285 const uint8_t* src_scan = source->GetScanline(src_top + row).data();
286 for (
int col = 0; col < width; ++col) {
287 int src_idx = src_left + col;
288 int dest_idx = dest_left + col;
289 if (src_scan[(src_idx) / 8] & (1 << (7 - (src_idx) % 8)))
290 dest_scan[(dest_idx) / 8] |= 1 << (7 - (dest_idx) % 8);
292 dest_scan[(dest_idx) / 8] &= ~(1 << (7 - (dest_idx) % 8));
297bool CFX_DIBitmap::SetChannelFromBitmap(Channel dest_channel,
302 if (!source->IsAlphaFormat() && !source->IsMaskFormat()) {
306 if (source->GetBPP() == 1) {
314 if (dest_channel == Channel::kAlpha) {
325 DCHECK_EQ(dest_channel, Channel::kRed);
340 if (source->GetWidth() !=
m_Width || source->GetHeight() !=
m_Height) {
347 RetainPtr<CFX_DIBitmap> dest_bitmap(
this);
348 int src_bytes = source->GetBPP() / 8;
349 int dest_bytes = dest_bitmap->GetBPP() / 8;
350 for (
int row = 0; row <
m_Height; row++) {
352 dest_bitmap->GetWritableScanline(row).subspan(dest_offset).data();
353 const uint8_t* src_pos =
354 source->GetScanline(row).subspan(src_offset).data();
355 for (
int col = 0; col <
m_Width; col++) {
356 *dest_pos = *src_pos;
357 dest_pos += dest_bytes;
358 src_pos += src_bytes;
365 return SetChannelFromBitmap(Channel::kAlpha,
std::move(source));
369 return SetChannelFromBitmap(Channel::kRed,
std::move(source));
383 const int Bpp =
GetBPP() / 8;
385 memset(m_pBuffer.Get(), 0xff, m_Height * m_Pitch);
389 for (
int row = 0; row <
m_Height; row++) {
390 uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch + dest_offset;
391 for (
int col = 0; col <
m_Width; col++) {
400 CHECK(source->IsMaskFormat());
410 RetainPtr<
const CFX_DIBitmap> pSrcClone = source.As<
const CFX_DIBitmap>();
411 if (source->GetWidth() !=
m_Width || source->GetHeight() !=
m_Height) {
421 for (
int row = 0; row <
m_Height; row++) {
422 uint8_t* dest_scan = m_pBuffer.Get() + m_Pitch * row;
423 uint8_t* src_scan = pSrcClone->m_pBuffer.Get() + pSrcClone->m_Pitch * row;
424 if (pSrcClone->GetBPP() == 1) {
425 for (
int col = 0; col <
m_Width; col++) {
426 if (!((1 << (7 - col % 8)) & src_scan[col / 8]))
430 for (
int col = 0; col <
m_Width; col++) {
431 *dest_scan = (*dest_scan) * src_scan[col] / 255;
440 if (pSrcClone->GetBPP() == 1)
443 for (
int row = 0; row <
m_Height; row++) {
444 uint8_t* dest_scan = m_pBuffer.Get() + m_Pitch * row + 3;
445 uint8_t* src_scan = pSrcClone->m_pBuffer.Get() + pSrcClone->m_Pitch * row;
446 for (
int col = 0; col <
m_Width; col++) {
447 *dest_scan = (*dest_scan) * src_scan[col] / 255;
455 CHECK_GE(alpha, 0.0f);
456 CHECK_LE(alpha, 1.0f);
474 const int bitmap_alpha =
static_cast<
int>(alpha * 255);
475 for (
int row = 0; row <
m_Height; row++) {
476 uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch;
477 for (
int col = 0; col <
m_Width; col++) {
478 scan_line[col] = scan_line[col] * bitmap_alpha / 255;
484 const int bitmap_alpha =
static_cast<
int>(alpha * 255);
485 for (
int row = 0; row <
m_Height; row++) {
486 uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch + 3;
487 for (
int col = 0; col <
m_Width; col++) {
488 *scan_line = (*scan_line) * bitmap_alpha / 255;
495 DCHECK(!IsAlphaFormat());
505#if defined(PDF_USE_SKIA)
506uint32_t CFX_DIBitmap::GetPixelForTesting(
int x,
int y)
const {
510 FX_SAFE_UINT32 offset = x;
513 if (!offset.IsValid())
516 uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + offset.ValueOrDie();
517 switch (GetFormat()) {
518 case FXDIB_Format::k1bppMask: {
519 if ((*pos) & (1 << (7 - x % 8))) {
524 case FXDIB_Format::k1bppRgb: {
525 if ((*pos) & (1 << (7 - x % 8))) {
526 return HasPalette() ? GetPaletteSpan()[1] : 0xffffffff;
528 return HasPalette() ? GetPaletteSpan()[0] : 0xff000000;
530 case FXDIB_Format::k8bppMask:
532 case FXDIB_Format::k8bppRgb:
533 return HasPalette() ? GetPaletteSpan()[*pos]
534 : ArgbEncode(0xff, *pos, *pos, *pos);
535 case FXDIB_Format::kRgb:
536 case FXDIB_Format::kRgb32:
537 return FXARGB_GETDIB(pos) | 0xff000000;
538 case FXDIB_Format::kArgb:
539 return FXARGB_GETDIB(pos);
547void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor,
548 uint32_t backcolor) {
556 if (forecolor == 0 && backcolor == 0xffffff && !
HasPalette())
561 for (
int i = 0; i < size; ++i) {
565 ArgbEncode(0xff, br + (fr - br) * gray / 255,
566 bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255);
570 if (forecolor == 0 && backcolor == 0xffffff) {
571 for (
int row = 0; row <
m_Height; ++row) {
572 uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch;
574 for (
int col = 0; col <
m_Width; ++col) {
575 int gray =
FXRGB2GRAY(scanline[2], scanline[1], scanline[0]);
584 for (
int row = 0; row <
m_Height; ++row) {
585 uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch;
587 for (
int col = 0; col <
m_Width; ++col) {
588 int gray =
FXRGB2GRAY(scanline[2], scanline[1], scanline[0]);
589 *scanline++ = bb + (fb - bb) * gray / 255;
590 *scanline++ = bg + (fg - bg) * gray / 255;
591 *scanline = br + (fr - br) * gray / 255;
598 if (!m_pBuffer || IsMaskFormat())
601 ConvertBGRColorScale(forecolor, backcolor);
606absl::optional<CFX_DIBitmap::PitchAndSize> CFX_DIBitmap::CalculatePitchAndSize(
611 if (width <= 0 || height <= 0) {
612 return absl::nullopt;
616 return absl::nullopt;
619 absl::optional<uint32_t> pitch32 = fxge::CalculatePitch32(bpp, width);
620 if (!pitch32.has_value()) {
621 return absl::nullopt;
623 pitch = pitch32.value();
625 absl::optional<uint32_t> actual_pitch =
626 fxge::CalculatePitch8(bpp, 1, width);
627 if (!actual_pitch.has_value() || pitch < actual_pitch.value()) {
628 return absl::nullopt;
631 size_t safe_size = pitch;
634 if (((std::numeric_limits<std::size_t>::max() - 4) / pitch) <
static_cast<uint32_t>(height))
635 return absl::nullopt;
649 bool bRgbByteOrder) {
651 CHECK(!source->IsMaskFormat());
660 source->GetHeight()
, src_left
, src_top
, pClipRgn
)) {
667 pClipMask = pClipRgn->GetMask();
672 source->GetPaletteSpan()
, 0
, blend_type
,
673 pClipMask !=
nullptr, bRgbByteOrder
)) {
677 const int src_Bpp = source->GetBPP() / 8;
678 const bool bRgb = src_Bpp > 1;
679 if (!bRgb && !source->HasPalette()) {
683 for (
int row = 0; row < height; row++) {
684 pdfium::span<uint8_t> dest_scan =
685 GetWritableScanline(dest_top + row).subspan(dest_left * dest_Bpp);
686 pdfium::span<
const uint8_t> src_scan =
687 source->GetScanline(src_top + row).subspan(src_left * src_Bpp);
688 pdfium::span<
const uint8_t> clip_scan;
690 clip_scan = pClipMask->GetWritableScanline(dest_top + row - clip_box
.top)
691 .subspan(dest_left - clip_box
.left);
713 bool bRgbByteOrder) {
715 CHECK(pMask->IsMaskFormat());
723 if (!GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(),
724 pMask->GetHeight(), src_left, src_top, pClipRgn)) {
735 pClipMask = pClipRgn->GetMask();
738 int src_bpp = pMask->GetBPP();
741 if (!compositor.Init(GetFormat(), pMask->GetFormat(), {}, color, blend_type,
742 pClipMask !=
nullptr, bRgbByteOrder)) {
745 for (
int row = 0; row < height; row++) {
746 pdfium::span<uint8_t> dest_scan =
747 GetWritableScanline(dest_top + row).subspan(dest_left * Bpp);
748 pdfium::span<
const uint8_t> src_scan = pMask->GetScanline(src_top + row);
749 pdfium::span<
const uint8_t> clip_scan;
751 clip_scan = pClipMask->GetScanline(dest_top + row - clip_box
.top)
752 .subspan(dest_left - clip_box
.left);
777 source->GetHeight()
, src_left
, src_top
, nullptr)) {
781 for (
int row = 0; row < height; ++row) {
782 uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch;
783 const uint8_t* src_scan = source->GetScanline(src_top + row).data();
784 for (
int col = 0; col < width; ++col) {
785 int src_idx = src_left + col;
786 int dest_idx = dest_left + col;
787 if (src_scan[src_idx / 8] & (1 << (7 - src_idx % 8))) {
788 dest_scan[dest_idx / 8] |= 1 << (7 - dest_idx % 8);
812 uint32_t dst_color = color;
813 uint8_t* color_p =
reinterpret_cast<uint8_t*>(&dst_color);
817 color_p[1], color_p[0]);
818 for (
int row = rect
.top; row < rect
.bottom; row++) {
819 uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left;
820 if (src_alpha == 255) {
821 memset(dest_scan, gray, width);
823 for (
int col = 0; col < width; col++) {
832 int left_shift = rect
.left % 8;
833 int right_shift = rect
.right % 8;
837 for (
int i = 0; i < 2; i++) {
838 if (GetPaletteSpan()[i] == color)
842 index = (
static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
844 for (
int row = rect
.top; row < rect
.bottom; row++) {
845 uint8_t* dest_scan_top =
846 GetWritableScanline(row).subspan(rect.left / 8).data();
847 uint8_t* dest_scan_top_r =
848 GetWritableScanline(row).subspan(rect.right / 8).data();
849 uint8_t left_flag = *dest_scan_top & (255 << (8 - left_shift));
850 uint8_t right_flag = *dest_scan_top_r & (255 >> right_shift);
852 memset(dest_scan_top + 1, index ? 255 : 0, new_width - 1);
854 *dest_scan_top &= left_flag;
855 *dest_scan_top_r &= right_flag;
857 *dest_scan_top |= ~left_flag;
858 *dest_scan_top_r |= ~right_flag;
862 *dest_scan_top &= left_flag | right_flag;
864 *dest_scan_top |= ~(left_flag | right_flag);
872 color_p[3] =
static_cast<uint8_t>(src_alpha);
879 if (src_alpha == 255) {
880 for (
int row = rect
.top; row < rect
.bottom; row++) {
881 uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp;
883 uint32_t* scan =
reinterpret_cast<uint32_t*>(dest_scan);
884 for (
int col = 0; col < width; col++)
887 for (
int col = 0; col < width; col++) {
888 *dest_scan++ = color_p[0];
889 *dest_scan++ = color_p[1];
890 *dest_scan++ = color_p[2];
896 for (
int row = rect
.top; row < rect
.bottom; row++) {
897 uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp;
899 for (
int col = 0; col < width; col++) {
900 uint8_t back_alpha = dest_scan[3];
901 if (back_alpha == 0) {
908 back_alpha + src_alpha - back_alpha * src_alpha / 255;
909 int alpha_ratio = src_alpha * 255 / dest_alpha;
916 *dest_scan++ = dest_alpha;
919 for (
int col = 0; col < width; col++) {
920 for (
int comps = 0; comps < Bpp; comps++) {
935 DCHECK(dest_format == FXDIB_Format::k8bppMask ||
936 dest_format == FXDIB_Format::kArgb ||
937 dest_format == FXDIB_Format::kRgb32 ||
938 dest_format == FXDIB_Format::kRgb);
950 for (
int row = 0; row <
m_Height; row++) {
951 uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch + 3;
952 for (
int col = 0; col <
m_Width; col++) {
961 const size_t dest_buf_size = dest_pitch * m_Height + 4;
963 FX_TryAlloc(uint8_t, dest_buf_size));
968 memset(dest_buf.get(), 0xff, dest_buf_size);
972 ConvertBuffer(dest_format, {dest_buf.get(), dest_buf_size}, dest_pitch,
973 m_Width, m_Height, holder, 0, 0);
974 m_pBuffer = std::move(dest_buf);
const FX_RECT & GetBox() const
int FindPalette(uint32_t color) const
static constexpr FXDIB_Format kPlatformRGBFormat
void SetPalette(pdfium::span< const uint32_t > src_palette)
FXDIB_Format GetFormat() 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_ClipRgn *pClipRgn) const
bool IsAlphaFormat() const
bool IsOpaqueImage() const
uint32_t GetPitch() const
bool IsMaskFormat() const
bool TransferBitmap(int dest_left, int dest_top, int width, int height, RetainPtr< const CFX_DIBBase > source, int src_left, int src_top)
bool SetAlphaFromBitmap(RetainPtr< const CFX_DIBBase > source)
bool SetUniformOpaqueAlpha()
bool Create(int width, int height, FXDIB_Format format)
pdfium::span< const uint8_t > GetBuffer() const
bool CompositeMask(int dest_left, int dest_top, int width, int height, const RetainPtr< const CFX_DIBBase > &pMask, uint32_t color, int src_left, int src_top, BlendMode blend_type, const CFX_ClipRgn *pClipRgn, bool bRgbByteOrder)
bool SetRedFromBitmap(RetainPtr< const CFX_DIBBase > source)
bool MultiplyAlphaMask(RetainPtr< const CFX_DIBBase > source)
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_ClipRgn *pClipRgn, bool bRgbByteOrder)
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 bool UseSkiaRenderer()
void CompositeByteMaskLine(pdfium::span< uint8_t > dest_scan, pdfium::span< const uint8_t > src_scan, int width, pdfium::span< const uint8_t > clip_scan) const
void CompositePalBitmapLine(pdfium::span< uint8_t > dest_scan, pdfium::span< const uint8_t > src_scan, int src_left, int width, pdfium::span< const uint8_t > clip_scan) const
bool Init(FXDIB_Format dest_format, FXDIB_Format src_format, pdfium::span< const uint32_t > src_palette, uint32_t mask_color, BlendMode blend_type, bool bClip, bool bRgbByteOrder)
void CompositeRgbBitmapLine(pdfium::span< uint8_t > dest_scan, pdfium::span< const uint8_t > src_scan, int width, pdfium::span< const uint8_t > clip_scan) const
void CompositeBitMaskLine(pdfium::span< uint8_t > dest_scan, pdfium::span< const uint8_t > src_scan, int src_left, int width, pdfium::span< const uint8_t > clip_scan) const
#define FXRGB2GRAY(r, g, b)
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
#define FXARGB_SETDIB(p, argb)
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)
uint32_t CalculatePitch32OrDie(int bpp, int width)
FX_RECT & operator=(const FX_RECT &that)=default
void Intersect(int l, int t, int r, int b)
constexpr FX_RECT(int l, int t, int r, int b)