• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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