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