xref: /aosp_15_r20/external/pdfium/core/fxge/skia/cfx_dibbase_skia.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2023 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 #include "core/fxge/dib/cfx_dibbase.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <memory>
11 #include <type_traits>
12 #include <utility>
13 
14 #include "core/fxcrt/fx_2d_size.h"
15 #include "core/fxcrt/fx_memory.h"
16 #include "core/fxcrt/fx_memory_wrappers.h"
17 #include "core/fxcrt/fx_safe_types.h"
18 #include "core/fxcrt/retain_ptr.h"
19 #include "core/fxge/calculate_pitch.h"
20 #include "core/fxge/dib/cfx_dibitmap.h"
21 #include "core/fxge/dib/fx_dib.h"
22 #include "third_party/base/check_op.h"
23 #include "third_party/base/containers/span.h"
24 #include "third_party/base/notreached.h"
25 #include "third_party/skia/include/core/SkAlphaType.h"
26 #include "third_party/skia/include/core/SkColor.h"
27 #include "third_party/skia/include/core/SkColorPriv.h"
28 #include "third_party/skia/include/core/SkColorType.h"
29 #include "third_party/skia/include/core/SkImage.h"
30 #include "third_party/skia/include/core/SkImageInfo.h"
31 #include "third_party/skia/include/core/SkPixmap.h"
32 #include "third_party/skia/include/core/SkRefCnt.h"
33 
34 namespace {
35 
36 // Releases `CFX_DIBBase` "leaked" by `CreateSkiaImageFromDib()`.
ReleaseRetainedHeldBySkImage(const void *,SkImages::ReleaseContext context)37 void ReleaseRetainedHeldBySkImage(const void* /*pixels*/,
38                                   SkImages::ReleaseContext context) {
39   RetainPtr<const CFX_DIBBase> retained;
40   retained.Unleak(reinterpret_cast<const CFX_DIBBase*>(context));
41 }
42 
43 // Creates an `SkImage` from a `CFX_DIBBase`, sharing the underlying pixels if
44 // possible.
45 //
46 // Note that an `SkImage` must be immutable, so if sharing pixels, they must not
47 // be modified during the lifetime of the `SkImage`.
CreateSkiaImageFromDib(const CFX_DIBBase * source,SkColorType color_type,SkAlphaType alpha_type)48 sk_sp<SkImage> CreateSkiaImageFromDib(const CFX_DIBBase* source,
49                                       SkColorType color_type,
50                                       SkAlphaType alpha_type) {
51   // Make sure the DIB is backed by a buffer.
52   RetainPtr<const CFX_DIBBase> retained;
53   if (source->GetBuffer().empty()) {
54     retained = source->Realize();
55     if (!retained) {
56       return nullptr;
57     }
58     DCHECK(!retained->GetBuffer().empty());
59   } else {
60     retained.Reset(source);
61   }
62 
63   // Convert unowned pointer to a retained pointer, then "leak" to `SkImage`.
64   source = retained.Leak();
65   SkImageInfo info = SkImageInfo::Make(source->GetWidth(), source->GetHeight(),
66                                        color_type, alpha_type);
67   return SkImages::RasterFromPixmap(
68       SkPixmap(info, source->GetBuffer().data(), source->GetPitch()),
69       /*rasterReleaseProc=*/ReleaseRetainedHeldBySkImage,
70       /*releaseContext=*/const_cast<CFX_DIBBase*>(source));
71 }
72 
73 // Releases allocated memory "leaked" by `CreateSkiaImageFromTransformedDib()`.
ReleaseAllocatedHeldBySkImage(const void * pixels,SkImages::ReleaseContext)74 void ReleaseAllocatedHeldBySkImage(const void* pixels,
75                                    SkImages::ReleaseContext /*context*/) {
76   FX_Free(const_cast<void*>(pixels));
77 }
78 
79 // Template defining traits of a pixel transform function.
80 template <size_t source_bits_per_pixel, typename PixelTransform>
81 class PixelTransformTraits;
82 
83 template <typename PixelTransform>
84 class PixelTransformTraits<1, PixelTransform> {
85  public:
86   using Result = std::invoke_result_t<PixelTransform, bool>;
87 
Invoke(PixelTransform && pixel_transform,const uint8_t * scanline,size_t column)88   static Result Invoke(PixelTransform&& pixel_transform,
89                        const uint8_t* scanline,
90                        size_t column) {
91     uint8_t kMask = 1 << (7 - column % 8);
92     return pixel_transform(!!(scanline[column / 8] & kMask));
93   }
94 };
95 
96 template <typename PixelTransform>
97 class PixelTransformTraits<8, PixelTransform> {
98  public:
99   using Result = std::invoke_result_t<PixelTransform, uint8_t>;
100 
Invoke(PixelTransform && pixel_transform,const uint8_t * scanline,size_t column)101   static Result Invoke(PixelTransform&& pixel_transform,
102                        const uint8_t* scanline,
103                        size_t column) {
104     return pixel_transform(scanline[column]);
105   }
106 };
107 
108 template <typename PixelTransform>
109 class PixelTransformTraits<24, PixelTransform> {
110  public:
111   using Result =
112       std::invoke_result_t<PixelTransform, uint8_t, uint8_t, uint8_t>;
113 
Invoke(PixelTransform && pixel_transform,const uint8_t * scanline,size_t column)114   static Result Invoke(PixelTransform&& pixel_transform,
115                        const uint8_t* scanline,
116                        size_t column) {
117     size_t offset = column * 3;
118     return pixel_transform(scanline[offset + 2], scanline[offset + 1],
119                            scanline[offset]);
120   }
121 };
122 
ValidateScanlineSize(pdfium::span<const uint8_t> scanline,size_t min_row_bytes)123 void ValidateScanlineSize(pdfium::span<const uint8_t> scanline,
124                           size_t min_row_bytes) {
125   DCHECK_GE(scanline.size(), min_row_bytes);
126 }
127 
ValidateBufferSize(pdfium::span<const uint8_t> buffer,const CFX_DIBBase & source)128 void ValidateBufferSize(pdfium::span<const uint8_t> buffer,
129                         const CFX_DIBBase& source) {
130 #if DCHECK_IS_ON()
131   if (source.GetHeight() == 0) {
132     return;
133   }
134 
135   FX_SAFE_SIZE_T buffer_size = source.GetHeight() - 1;
136   buffer_size *= source.GetPitch();
137   buffer_size += fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1,
138                                             source.GetWidth());
139 
140   DCHECK_GE(buffer.size(), buffer_size.ValueOrDie());
141 #endif  // DCHECK_IS_ON()
142 }
143 
144 // Creates an `SkImage` from a `CFX_DIBBase`, transforming the source pixels
145 // using `pixel_transform`.
146 //
147 // TODO(crbug.com/pdfium/2048): Consolidate with `CFX_DIBBase::ConvertBuffer()`.
148 template <size_t source_bits_per_pixel, typename PixelTransform>
CreateSkiaImageFromTransformedDib(const CFX_DIBBase & source,SkColorType color_type,SkAlphaType alpha_type,PixelTransform && pixel_transform)149 sk_sp<SkImage> CreateSkiaImageFromTransformedDib(
150     const CFX_DIBBase& source,
151     SkColorType color_type,
152     SkAlphaType alpha_type,
153     PixelTransform&& pixel_transform) {
154   using Traits = PixelTransformTraits<source_bits_per_pixel, PixelTransform>;
155   using Result = typename Traits::Result;
156 
157   // Allocate output buffer.
158   const int width = source.GetWidth();
159   const int height = source.GetHeight();
160   SkImageInfo info = SkImageInfo::Make(width, height, color_type, alpha_type);
161   DCHECK_EQ(info.minRowBytes(), width * sizeof(Result));
162 
163   size_t output_size = Fx2DSizeOrDie(info.minRowBytes(), height);
164   std::unique_ptr<void, FxFreeDeleter> output(
165       FX_TryAlloc(uint8_t, output_size));
166   if (!output) {
167     return nullptr;
168   }
169 
170   // Transform source pixels to output pixels.
171   pdfium::span<const uint8_t> source_buffer = source.GetBuffer();
172   Result* output_cursor = reinterpret_cast<Result*>(output.get());
173   if (source_buffer.empty()) {
174     // No buffer; iterate by individual scanline.
175     const size_t min_row_bytes =
176         fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1, width);
177     DCHECK_LE(min_row_bytes, source.GetPitch());
178 
179     int line = 0;
180     for (int row = 0; row < height; ++row) {
181       pdfium::span<const uint8_t> scanline = source.GetScanline(line++);
182       ValidateScanlineSize(scanline, min_row_bytes);
183 
184       for (int column = 0; column < width; ++column) {
185         *output_cursor++ =
186             Traits::Invoke(std::forward<PixelTransform>(pixel_transform),
187                            scanline.data(), column);
188       }
189     }
190   } else {
191     // Iterate over the entire buffer.
192     ValidateBufferSize(source_buffer, source);
193     const size_t row_bytes = source.GetPitch();
194 
195     const uint8_t* next_scanline = source_buffer.data();
196     for (int row = 0; row < height; ++row) {
197       const uint8_t* scanline = next_scanline;
198       next_scanline += row_bytes;
199 
200       for (int column = 0; column < width; ++column) {
201         *output_cursor++ = Traits::Invoke(
202             std::forward<PixelTransform>(pixel_transform), scanline, column);
203       }
204     }
205   }
206 
207   // "Leak" allocated memory to `SkImage`.
208   return SkImages::RasterFromPixmap(
209       SkPixmap(info, output.release(), info.minRowBytes()),
210       /*rasterReleaseProc=*/ReleaseAllocatedHeldBySkImage,
211       /*releaseContext=*/nullptr);
212 }
213 
IsRGBColorGrayScale(uint32_t color)214 bool IsRGBColorGrayScale(uint32_t color) {
215   return FXARGB_R(color) == FXARGB_G(color) &&
216          FXARGB_R(color) == FXARGB_B(color);
217 }
218 
219 }  // namespace
220 
RealizeSkImage() const221 sk_sp<SkImage> CFX_DIBBase::RealizeSkImage() const {
222   switch (GetBPP()) {
223     case 1: {
224       // By default, the two colors for grayscale are 0xFF and 0x00 unless they
225       // are specified in the palette.
226       uint8_t color0 = 0x00;
227       uint8_t color1 = 0xFF;
228 
229       if (GetFormat() == FXDIB_Format::k1bppRgb && HasPalette()) {
230         uint32_t palette_color0 = GetPaletteArgb(0);
231         uint32_t palette_color1 = GetPaletteArgb(1);
232         bool use_gray_colors = IsRGBColorGrayScale(palette_color0) &&
233                                IsRGBColorGrayScale(palette_color1);
234         if (!use_gray_colors) {
235           return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/1>(
236               *this, kBGRA_8888_SkColorType, kPremul_SkAlphaType,
237               [palette_color0, palette_color1](bool bit) {
238                 return bit ? palette_color1 : palette_color0;
239               });
240         }
241 
242         color0 = FXARGB_R(palette_color0);
243         color1 = FXARGB_R(palette_color1);
244       }
245 
246       return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/1>(
247           *this, IsMaskFormat() ? kAlpha_8_SkColorType : kGray_8_SkColorType,
248           kPremul_SkAlphaType,
249           [color0, color1](bool bit) { return bit ? color1 : color0; });
250     }
251 
252     case 8:
253       // we upscale ctables to 32bit.
254       if (HasPalette()) {
255         return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/8>(
256             *this, kBGRA_8888_SkColorType, kPremul_SkAlphaType,
257             [palette = GetPaletteSpan().first(GetRequiredPaletteSize())](
258                 uint8_t index) {
259               if (index >= palette.size()) {
260                 index = 0;
261               }
262               return palette[index];
263             });
264       }
265       return CreateSkiaImageFromDib(
266           this, IsMaskFormat() ? kAlpha_8_SkColorType : kGray_8_SkColorType,
267           kPremul_SkAlphaType);
268 
269     case 24:
270       return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/24>(
271           *this, kBGRA_8888_SkColorType, kOpaque_SkAlphaType,
272           [](uint8_t red, uint8_t green, uint8_t blue) {
273             return SkPackARGB32NoCheck(0xFF, red, green, blue);
274           });
275 
276     case 32:
277       return CreateSkiaImageFromDib(
278           this, kBGRA_8888_SkColorType,
279           IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
280 
281     default:
282       NOTREACHED_NORETURN();
283   }
284 }
285 
IsPremultiplied() const286 bool CFX_DIBBase::IsPremultiplied() const {
287   return false;
288 }
289