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