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
32 namespace {
33
GetAllocSizeOrZero(uint32_t size)34 size_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
42 CFX_DIBitmap::CFX_DIBitmap() = default;
43
Create(int width,int height,FXDIB_Format format)44 bool CFX_DIBitmap::Create(int width, int height, FXDIB_Format format) {
45 return Create(width, height, format, nullptr, 0);
46 }
47
Create(int width,int height,FXDIB_Format format,uint8_t * pBuffer,uint32_t pitch)48 bool 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
Copy(RetainPtr<const CFX_DIBBase> source)85 bool 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
101 CFX_DIBitmap::~CFX_DIBitmap() = default;
102
GetBuffer() const103 pdfium::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
GetScanline(int line) const111 pdfium::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
GetEstimatedImageMemoryBurden() const119 size_t CFX_DIBitmap::GetEstimatedImageMemoryBurden() const {
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)
RealizeIfNeeded() const130 RetainPtr<const CFX_DIBitmap> CFX_DIBitmap::RealizeIfNeeded() const {
131 if (GetBuffer().empty()) {
132 return Realize();
133 }
134 return pdfium::WrapRetain(this);
135 }
136 #endif
137
TakeOver(RetainPtr<CFX_DIBitmap> && pSrcBitmap)138 void 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
Clear(uint32_t color)148 void CFX_DIBitmap::Clear(uint32_t color) {
149 auto buffer = GetWritableBuffer();
150 if (buffer.empty()) {
151 return;
152 }
153
154 switch (GetFormat()) {
155 case FXDIB_Format::kInvalid:
156 break;
157 case FXDIB_Format::k1bppMask:
158 fxcrt::Fill(buffer, (color & 0xff000000) ? 0xff : 0);
159 break;
160 case FXDIB_Format::k1bppRgb:
161 fxcrt::Fill(buffer, FindPalette(color) ? 0xff : 0);
162 break;
163 case FXDIB_Format::k8bppMask:
164 fxcrt::Fill(buffer, color >> 24);
165 break;
166 case FXDIB_Format::k8bppRgb:
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 }
180 case FXDIB_Format::kBgrx:
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]];
187 case FXDIB_Format::kBgra:
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
TransferBitmap(int width,int height,RetainPtr<const CFX_DIBBase> source,int src_left,int src_top)206 bool 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
TransferWithUnequalFormats(FXDIB_Format dest_format,int dest_left,int dest_top,int width,int height,RetainPtr<const CFX_DIBBase> source,int src_left,int src_top)240 bool 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
TransferWithMultipleBPP(int dest_left,int dest_top,int width,int height,RetainPtr<const CFX_DIBBase> source,int src_left,int src_top)271 void 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;
279 UNSAFE_TODO({
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
TransferEqualFormatsOneBPP(int dest_left,int dest_top,int width,int height,RetainPtr<const CFX_DIBBase> source,int src_left,int src_top)291 void 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) {
299 UNSAFE_TODO({
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
SetRedFromAlpha()316 void CFX_DIBitmap::SetRedFromAlpha() {
317 CHECK_EQ(FXDIB_Format::kBgra, GetFormat());
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
SetUniformOpaqueAlpha()329 void CFX_DIBitmap::SetUniformOpaqueAlpha() {
330 CHECK_EQ(FXDIB_Format::kBgra, GetFormat());
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
MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask)342 bool 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
348 if (GetFormat() == FXDIB_Format::kBgrx) {
349 // TODO(crbug.com/42271020): Consider adding support for
350 // `FXDIB_Format::kBgraPremul`
351 if (!ConvertFormat(FXDIB_Format::kBgra)) {
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
368 CHECK_EQ(GetFormat(), FXDIB_Format::kBgra);
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
MultiplyAlpha(float alpha)380 bool CFX_DIBitmap::MultiplyAlpha(float alpha) {
381 CHECK_GE(alpha, 0.0f);
382 CHECK_LE(alpha, 1.0f);
383 CHECK(!IsMaskFormat());
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`
395 if (!ConvertFormat(FXDIB_Format::kBgra)) {
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)
GetPixelForTesting(int x,int y) const411 uint32_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
ConvertBGRColorScale(uint32_t forecolor,uint32_t backcolor)457 void 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) {
482 UNSAFE_TODO({
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) {
497 UNSAFE_TODO({
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
ConvertColorScale(uint32_t forecolor,uint32_t backcolor)511 bool 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
CalculatePitchAndSize(int width,int height,FXDIB_Format format,uint32_t pitch)520 std::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 FX_SAFE_UINT32 safe_size = pitch;
546 safe_size *= height;
547 if (!safe_size.IsValid())
548 return std::nullopt;
549
550 return PitchAndSize{pitch, safe_size.ValueOrDie()};
551 }
552
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)553 bool CFX_DIBitmap::CompositeBitmap(int dest_left,
554 int dest_top,
555 int width,
556 int height,
557 RetainPtr<const CFX_DIBBase> source,
558 int src_left,
559 int src_top,
560 BlendMode blend_type,
561 const CFX_AggClipRgn* pClipRgn,
562 bool bRgbByteOrder) {
563 // Should have called CompositeMask().
564 CHECK(!source->IsMaskFormat());
565
566 if (!m_pBuffer)
567 return false;
568
569 if (GetBPP() < 8) {
570 return false;
571 }
572
573 if (!GetOverlapRect(dest_left, dest_top, width, height, source->GetWidth(),
574 source->GetHeight(), src_left, src_top, pClipRgn)) {
575 return true;
576 }
577
578 RetainPtr<CFX_DIBitmap> pClipMask;
579 FX_RECT clip_box;
580 if (pClipRgn && pClipRgn->GetType() != CFX_AggClipRgn::kRectI) {
581 pClipMask = pClipRgn->GetMask();
582 clip_box = pClipRgn->GetBox();
583 }
584 CFX_ScanlineCompositor compositor;
585 if (!compositor.Init(GetFormat(), source->GetFormat(),
586 source->GetPaletteSpan(), 0, blend_type,
587 bRgbByteOrder)) {
588 return false;
589 }
590 const int dest_bytes_per_pixel = GetBPP() / 8;
591 const int src_bytes_per_pixel = source->GetBPP() / 8;
592 const bool bRgb = src_bytes_per_pixel > 1;
593 if (!bRgb && !source->HasPalette()) {
594 return false;
595 }
596
597 for (int row = 0; row < height; row++) {
598 pdfium::span<uint8_t> dest_scan =
599 GetWritableScanline(dest_top + row)
600 .subspan(dest_left * dest_bytes_per_pixel);
601 pdfium::span<const uint8_t> src_scan =
602 source->GetScanline(src_top + row)
603 .subspan(src_left * src_bytes_per_pixel);
604 pdfium::span<const uint8_t> clip_scan;
605 if (pClipMask) {
606 clip_scan = pClipMask->GetWritableScanline(dest_top + row - clip_box.top)
607 .subspan(dest_left - clip_box.left);
608 }
609 if (bRgb) {
610 compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan);
611 } else {
612 compositor.CompositePalBitmapLine(dest_scan, src_scan, src_left, width,
613 clip_scan);
614 }
615 }
616 return true;
617 }
618
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)619 bool CFX_DIBitmap::CompositeMask(int dest_left,
620 int dest_top,
621 int width,
622 int height,
623 RetainPtr<const CFX_DIBBase> pMask,
624 uint32_t color,
625 int src_left,
626 int src_top,
627 BlendMode blend_type,
628 const CFX_AggClipRgn* pClipRgn,
629 bool bRgbByteOrder) {
630 // Should have called CompositeBitmap().
631 CHECK(pMask->IsMaskFormat());
632
633 if (!m_pBuffer)
634 return false;
635
636 if (GetBPP() < 8) {
637 return false;
638 }
639
640 if (!GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(),
641 pMask->GetHeight(), src_left, src_top, pClipRgn)) {
642 return true;
643 }
644
645 int src_alpha = FXARGB_A(color);
646 if (src_alpha == 0)
647 return true;
648
649 RetainPtr<CFX_DIBitmap> pClipMask;
650 FX_RECT clip_box;
651 if (pClipRgn && pClipRgn->GetType() != CFX_AggClipRgn::kRectI) {
652 pClipMask = pClipRgn->GetMask();
653 clip_box = pClipRgn->GetBox();
654 }
655 const int src_bpp = pMask->GetBPP();
656 const int bytes_per_pixel = GetBPP() / 8;
657 CFX_ScanlineCompositor compositor;
658 if (!compositor.Init(GetFormat(), pMask->GetFormat(), {}, color, blend_type,
659 bRgbByteOrder)) {
660 return false;
661 }
662 for (int row = 0; row < height; row++) {
663 pdfium::span<uint8_t> dest_scan = GetWritableScanline(dest_top + row)
664 .subspan(dest_left * bytes_per_pixel);
665 pdfium::span<const uint8_t> src_scan = pMask->GetScanline(src_top + row);
666 pdfium::span<const uint8_t> clip_scan;
667 if (pClipMask) {
668 clip_scan = pClipMask->GetScanline(dest_top + row - clip_box.top)
669 .subspan(dest_left - clip_box.left);
670 }
671 if (src_bpp == 1) {
672 compositor.CompositeBitMaskLine(dest_scan, src_scan, src_left, width,
673 clip_scan);
674 } else {
675 compositor.CompositeByteMaskLine(dest_scan, src_scan.subspan(src_left),
676 width, clip_scan);
677 }
678 }
679 return true;
680 }
681
CompositeOneBPPMask(int dest_left,int dest_top,int width,int height,RetainPtr<const CFX_DIBBase> source,int src_left,int src_top)682 void CFX_DIBitmap::CompositeOneBPPMask(int dest_left,
683 int dest_top,
684 int width,
685 int height,
686 RetainPtr<const CFX_DIBBase> source,
687 int src_left,
688 int src_top) {
689 if (GetBPP() != 1) {
690 return;
691 }
692 if (!GetOverlapRect(dest_left, dest_top, width, height, source->GetWidth(),
693 source->GetHeight(), src_left, src_top, nullptr)) {
694 return;
695 }
696 UNSAFE_TODO({
697 for (int row = 0; row < height; ++row) {
698 uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * GetPitch();
699 const uint8_t* src_scan = source->GetScanline(src_top + row).data();
700 for (int col = 0; col < width; ++col) {
701 int src_idx = src_left + col;
702 int dest_idx = dest_left + col;
703 if (src_scan[src_idx / 8] & (1 << (7 - src_idx % 8))) {
704 dest_scan[dest_idx / 8] |= 1 << (7 - dest_idx % 8);
705 }
706 }
707 }
708 });
709 }
710
CompositeRect(int left,int top,int width,int height,uint32_t color)711 bool CFX_DIBitmap::CompositeRect(int left,
712 int top,
713 int width,
714 int height,
715 uint32_t color) {
716 if (!m_pBuffer)
717 return false;
718
719 int src_alpha = FXARGB_A(color);
720 if (src_alpha == 0)
721 return true;
722
723 FX_RECT rect(left, top, left + width, top + height);
724 rect.Intersect(0, 0, GetWidth(), GetHeight());
725 if (rect.IsEmpty())
726 return true;
727
728 width = rect.Width();
729 uint32_t dst_color = color;
730 uint8_t* color_p = reinterpret_cast<uint8_t*>(&dst_color);
731 if (GetBPP() == 8) {
732 UNSAFE_TODO({
733 uint8_t gray =
734 IsMaskFormat()
735 ? 255
736 : (uint8_t)FXRGB2GRAY((int)color_p[2], color_p[1], color_p[0]);
737 for (int row = rect.top; row < rect.bottom; row++) {
738 uint8_t* dest_scan = m_pBuffer.Get() + row * GetPitch() + rect.left;
739 if (src_alpha == 255) {
740 FXSYS_memset(dest_scan, gray, width);
741 } else {
742 for (int col = 0; col < width; col++) {
743 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha);
744 dest_scan++;
745 }
746 }
747 }
748 });
749 return true;
750 }
751 if (GetBPP() == 1) {
752 UNSAFE_TODO({
753 int left_shift = rect.left % 8;
754 int right_shift = rect.right % 8;
755 int new_width = rect.right / 8 - rect.left / 8;
756 int index = 0;
757 if (HasPalette()) {
758 for (int i = 0; i < 2; i++) {
759 if (GetPaletteSpan()[i] == color) {
760 index = i;
761 }
762 }
763 } else {
764 index = (static_cast<uint8_t>(color) == 0xff) ? 1 : 0;
765 }
766 for (int row = rect.top; row < rect.bottom; row++) {
767 uint8_t* dest_scan_top =
768 GetWritableScanline(row).subspan(rect.left / 8).data();
769 uint8_t* dest_scan_top_r =
770 GetWritableScanline(row).subspan(rect.right / 8).data();
771 uint8_t left_flag = *dest_scan_top & (255 << (8 - left_shift));
772 uint8_t right_flag = *dest_scan_top_r & (255 >> right_shift);
773 if (new_width) {
774 FXSYS_memset(dest_scan_top + 1, index ? 255 : 0, new_width - 1);
775 if (!index) {
776 *dest_scan_top &= left_flag;
777 *dest_scan_top_r &= right_flag;
778 } else {
779 *dest_scan_top |= ~left_flag;
780 *dest_scan_top_r |= ~right_flag;
781 }
782 } else {
783 if (!index) {
784 *dest_scan_top &= left_flag | right_flag;
785 } else {
786 *dest_scan_top |= ~(left_flag | right_flag);
787 }
788 }
789 }
790 });
791 return true;
792 }
793
794 CHECK_GE(GetBPP(), 24);
795 UNSAFE_TODO({ color_p[3] = static_cast<uint8_t>(src_alpha); });
796 const int bytes_per_pixel = GetBPP() / 8;
797 const bool bAlpha = IsAlphaFormat();
798 if (bAlpha) {
799 // Other formats with alpha have already been handled above.
800 //
801 // TODO(crbug.com/42271020): Consider adding support for
802 // `FXDIB_Format::kBgraPremul`
803 DCHECK_EQ(GetFormat(), FXDIB_Format::kBgra);
804 }
805 if (src_alpha == 255) {
806 for (int row = rect.top; row < rect.bottom; row++) {
807 UNSAFE_TODO({
808 uint8_t* dest_scan =
809 m_pBuffer.Get() + row * GetPitch() + rect.left * bytes_per_pixel;
810 if (bytes_per_pixel == 4) {
811 uint32_t* scan = reinterpret_cast<uint32_t*>(dest_scan);
812 for (int col = 0; col < width; col++) {
813 *scan++ = dst_color;
814 }
815 } else {
816 for (int col = 0; col < width; col++) {
817 *dest_scan++ = color_p[0];
818 *dest_scan++ = color_p[1];
819 *dest_scan++ = color_p[2];
820 }
821 }
822 });
823 }
824 return true;
825 }
826 if (bAlpha) {
827 for (int row = rect.top; row < rect.bottom; row++) {
828 UNSAFE_TODO({
829 uint8_t* dest_scan =
830 m_pBuffer.Get() + row * GetPitch() + rect.left * bytes_per_pixel;
831 for (int col = 0; col < width; col++) {
832 uint8_t back_alpha = dest_scan[3];
833 if (back_alpha == 0) {
834 FXARGB_SetDIB(dest_scan, ArgbEncode(src_alpha, color_p[2],
835 color_p[1], color_p[0]));
836 dest_scan += 4;
837 continue;
838 }
839 uint8_t dest_alpha =
840 back_alpha + src_alpha - back_alpha * src_alpha / 255;
841 int alpha_ratio = src_alpha * 255 / dest_alpha;
842 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[0], alpha_ratio);
843 dest_scan++;
844 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[1], alpha_ratio);
845 dest_scan++;
846 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[2], alpha_ratio);
847 dest_scan++;
848 *dest_scan++ = dest_alpha;
849 }
850 });
851 }
852 return true;
853 }
854
855 for (int row = rect.top; row < rect.bottom; row++) {
856 UNSAFE_TODO({
857 uint8_t* dest_scan =
858 m_pBuffer.Get() + row * GetPitch() + rect.left * bytes_per_pixel;
859 for (int col = 0; col < width; col++) {
860 for (int comps = 0; comps < bytes_per_pixel; comps++) {
861 if (comps == 3) {
862 *dest_scan++ = 255;
863 continue;
864 }
865 *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[comps], src_alpha);
866 dest_scan++;
867 }
868 }
869 });
870 }
871 return true;
872 }
873
ConvertFormat(FXDIB_Format dest_format)874 bool CFX_DIBitmap::ConvertFormat(FXDIB_Format dest_format) {
875 static constexpr FXDIB_Format kAllowedDestFormats[] = {
876 FXDIB_Format::k8bppMask,
877 FXDIB_Format::kBgra,
878 #if defined(PDF_USE_SKIA)
879 FXDIB_Format::kBgraPremul,
880 #endif
881 FXDIB_Format::kBgr,
882 };
883 CHECK(pdfium::Contains(kAllowedDestFormats, dest_format));
884
885 if (dest_format == GetFormat()) {
886 return true;
887 }
888
889 switch (dest_format) {
890 case FXDIB_Format::k8bppMask:
891 if (GetFormat() == FXDIB_Format::k8bppRgb && !HasPalette()) {
892 SetFormat(FXDIB_Format::k8bppMask);
893 return true;
894 }
895 break;
896
897 case FXDIB_Format::kBgra:
898 if (GetFormat() == FXDIB_Format::kBgrx) {
899 SetFormat(FXDIB_Format::kBgra);
900 SetUniformOpaqueAlpha();
901 return true;
902 }
903 #if defined(PDF_USE_SKIA)
904 if (GetFormat() == FXDIB_Format::kBgraPremul) {
905 UnPreMultiply();
906 return true;
907 }
908 #endif // defined(PDF_USE_SKIA)
909 break;
910
911 #if defined(PDF_USE_SKIA)
912 case FXDIB_Format::kBgraPremul:
913 if (GetFormat() == FXDIB_Format::kBgrx) {
914 SetFormat(FXDIB_Format::kBgraPremul);
915 SetUniformOpaqueAlpha();
916 return true;
917 }
918 if (GetFormat() == FXDIB_Format::kBgra) {
919 PreMultiply();
920 return true;
921 }
922 break;
923 #endif // defined(PDF_USE_SKIA)
924
925 default:
926 break;
927 }
928
929 std::optional<PitchAndSize> pitch_size =
930 CalculatePitchAndSize(GetWidth(), GetHeight(), dest_format, /*pitch=*/0);
931 if (!pitch_size.has_value()) {
932 return false;
933 }
934
935 const size_t dest_buf_size = GetAllocSizeOrZero(pitch_size.value().size);
936 if (dest_buf_size == 0) {
937 return false;
938 }
939
940 std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf(
941 FX_TryAlloc(uint8_t, dest_buf_size));
942 if (!dest_buf) {
943 return false;
944 }
945
946 // SAFETY: `dest_buf` allocated with `dest_buf_size` bytes above.
947 auto dest_span =
948 UNSAFE_BUFFERS(pdfium::make_span(dest_buf.get(), dest_buf_size));
949 if (dest_format == FXDIB_Format::kBgra) {
950 fxcrt::Fill(dest_span, 0xff);
951 }
952
953 RetainPtr<CFX_DIBBase> holder(this);
954 const uint32_t dest_pitch = pitch_size.value().pitch;
955 palette_ = ConvertBuffer(dest_format, dest_span, dest_pitch, GetWidth(),
956 GetHeight(), holder, /*src_left=*/0,
957 /*src_top=*/0);
958 m_pBuffer = std::move(dest_buf);
959 SetFormat(dest_format);
960 SetPitch(dest_pitch);
961 return true;
962 }
963
964 #if defined(PDF_USE_SKIA)
ScopedPremultiplier(RetainPtr<CFX_DIBitmap> bitmap,bool do_premultiply)965 CFX_DIBitmap::ScopedPremultiplier::ScopedPremultiplier(
966 RetainPtr<CFX_DIBitmap> bitmap,
967 bool do_premultiply)
968 : bitmap_(std::move(bitmap)), do_premultiply_(do_premultiply) {
969 CHECK(!bitmap_->IsPremultiplied());
970 if (do_premultiply_) {
971 bitmap_->PreMultiply();
972 }
973 }
974
~ScopedPremultiplier()975 CFX_DIBitmap::ScopedPremultiplier::~ScopedPremultiplier() {
976 if (do_premultiply_) {
977 bitmap_->UnPreMultiply();
978 }
979 CHECK(!bitmap_->IsPremultiplied());
980 }
981 #endif // defined(PDF_USE_SKIA)
982