LCOV - code coverage report
Current view: top level - src/document - _bitmap_utils.dart Coverage Total Hit
Test: lcov.info Lines: 100.0 % 24 24
Test Date: 2026-08-24 04:28:45 Functions: - 0 0

            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              : }
        

Generated by: LCOV version 2.0-1