Line data Source code
1 : // Copyright 2026 The Authors.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // https://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : // Shared bitmap utility functions for the betto_pdfium native and web backends.
16 : //
17 : // This file is intentionally free of dart:ffi, dart:io, dart:isolate, and
18 : // dart:js_interop imports so it can be imported by both _document_native.dart
19 : // (via pdfium_isolate.dart) and _document_web.dart without triggering
20 : // conditional-import conflicts.
21 :
22 : import 'dart:typed_data';
23 :
24 : /// Copies a PDFium bitmap buffer into a compact BGRA [Uint8List], stripping
25 : /// any row-padding bytes that PDFium may have added for alignment.
26 : ///
27 : /// PDFium allocates bitmap rows with alignment padding when `width * 4` is not
28 : /// a multiple of its internal stride requirement. The [stride] parameter is the
29 : /// actual byte width of each row in [src] (obtained via
30 : /// `FPDFBitmap_GetStride`). When `stride == width * 4` there is no padding and
31 : /// the buffer is copied directly. When `stride > width * 4` the slow path
32 : /// copies each row individually to produce a compact output buffer.
33 : ///
34 : /// This function is shared by both the native (FFI) and web (WASM) backends:
35 : ///
36 : /// - **Native**: [src] comes from `FPDFBitmap_GetBuffer` via an FFI pointer
37 : /// copy into a `Uint8List`.
38 : /// - **Web**: [src] is a `Uint8List` sublist of `Module["HEAPU8"]` sliced at
39 : /// `[bufferPtr, bufferPtr + stride * height)`.
40 : ///
41 : /// Parameters:
42 : /// [src] — raw pixel buffer from `FPDFBitmap_GetBuffer`, length is
43 : /// `stride * height`.
44 : /// [width] — pixel width of the bitmap.
45 : /// [height] — pixel height of the bitmap.
46 : /// [stride] — byte width of a single row (≥ `width * 4`).
47 : ///
48 : /// Returns a [Uint8List] of exactly `width * height * 4` bytes in BGRA order.
49 : ///
50 : /// Example:
51 : /// ```dart
52 : /// // After obtaining raw pixels from PDFium (native or WASM):
53 : /// final compact = stripBitmapStride(rawPixels, 800, 600, 3200);
54 : /// // compact.length == 800 * 600 * 4 == 1920000
55 : /// ```
56 3 : Uint8List stripBitmapStride(Uint8List src, int width, int height, int stride) {
57 3 : final expectedStride = width * 4;
58 3 : if (stride == expectedStride) {
59 : // Fast path: no padding — copy the contiguous buffer directly.
60 3 : return Uint8List.fromList(src);
61 : }
62 : // Slow path: strip row padding so the output is a compact BGRA buffer.
63 3 : final dst = Uint8List(width * height * 4);
64 2 : for (var row = 0; row < height; row++) {
65 1 : final srcOffset = row * stride;
66 1 : final dstOffset = row * expectedStride;
67 2 : dst.setRange(dstOffset, dstOffset + expectedStride, src, srcOffset);
68 : }
69 : return dst;
70 : }
71 :
72 : /// Converts a raw PDFium bitmap buffer (BGR, BGRx, or BGRA) into a compact
73 : /// BGRA [Uint8List], expanding non-BGRA source formats and stripping row
74 : /// padding in the same pass.
75 : ///
76 : /// [format] is the raw `FPDFBitmap_*` format code: `2` (BGR, 3 bytes/px —
77 : /// expanded to BGRA with alpha forced to `0xFF`), `3` (BGRx, 4 bytes/px whose
78 : /// 4th byte is unused and is replaced with `0xFF`), or `4` (BGRA, 4 bytes/px,
79 : /// copied directly). Returns `null` for any other (unsupported) format so
80 : /// the caller can report or reject it.
81 : ///
82 : /// [srcOffset] is the byte offset into [src] where the bitmap buffer begins.
83 : /// Native callers typically pass a [Uint8List] already sized to exactly the
84 : /// bitmap (`srcOffset: 0`, the default); the web backend indexes directly
85 : /// into a shared WASM heap view, so it passes the bitmap's absolute heap
86 : /// address as [srcOffset].
87 : ///
88 : /// This function is shared by the native (FFI) and web (WASM) backends'
89 : /// embedded-thumbnail handlers, which are the only call sites where the
90 : /// source format genuinely varies. [stripBitmapStride] remains the right
91 : /// choice for render/image call sites that always request BGRA output.
92 : ///
93 : /// Example:
94 : /// ```dart
95 : /// // Embedded thumbnail bitmap reported as BGRx by PDFium:
96 : /// final bgra = convertBitmapToCompactBgra(rawBuffer, 128, 96, 512, 3);
97 : /// // bgra == null only if `format` is not 2, 3, or 4.
98 : /// ```
99 2 : Uint8List? convertBitmapToCompactBgra(
100 : Uint8List src,
101 : int width,
102 : int height,
103 : int stride,
104 : int format, {
105 : int srcOffset = 0,
106 : }) {
107 : final int srcBytesPerPixel;
108 : switch (format) {
109 2 : case 4: // BGRA
110 : srcBytesPerPixel = 4;
111 : break;
112 2 : case 3: // BGRx — no alpha channel; replace with 0xFF.
113 : srcBytesPerPixel = 4;
114 : break;
115 2 : case 2: // BGR — expand to BGRA by appending 0xFF alpha.
116 : srcBytesPerPixel = 3;
117 : break;
118 : default:
119 : return null;
120 : }
121 :
122 6 : final bgra = Uint8List(width * height * 4);
123 4 : for (var row = 0; row < height; row++) {
124 4 : final srcRowBase = srcOffset + row * stride;
125 4 : final dstRowBase = row * width * 4;
126 4 : for (var col = 0; col < width; col++) {
127 4 : final srcOff = srcRowBase + col * srcBytesPerPixel;
128 4 : final dstOff = dstRowBase + col * 4;
129 4 : bgra[dstOff] = src[srcOff]; // B
130 8 : bgra[dstOff + 1] = src[srcOff + 1]; // G
131 8 : bgra[dstOff + 2] = src[srcOff + 2]; // R
132 8 : bgra[dstOff + 3] = (format == 4) ? src[srcOff + 3] : 0xFF; // A
133 : }
134 : }
135 : return bgra;
136 : }
|