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