Found while porting the WASM client to TypeScript (pdfrx_web project), by code inspection.
When PdfPageRenderFlags.premultipliedAlpha (0x80000000) is set, the alpha premultiplication is performed twice on the WASM backend:
packages/pdfrx/assets/pdfium_worker.js, renderPage(): the copy loop premultiplies BGRA when flags & premultipliedAlpha:
if (flags & premultipliedAlpha) {
for (let i = 0; i < src.length; i += 4) {
const a = src[i + 3];
dest[i] = (src[i] * a + 128) >> 8;
...
packages/pdfrx/lib/src/wasm/pdfrx_wasm.dart, _PdfPageWasm.render(): the received pixels are premultiplied again:
if ((flags & PdfPageRenderFlags.premultipliedAlpha) != 0) {
final count = width * height;
for (var i = 0; i < count; i++) {
...
pixels[i * 4] = b * a ~/ 255;
Since both sides test the same bit on the same flags value, semi-transparent pixels end up with alpha applied twice (RGB gets darker than intended). One of the two loops should be removed (keeping it in the worker avoids the extra pass over the transferred buffer).
Related nit in the same worker function: FPDF_RenderPageBitmap receives masked flags ((flags & 0xffff) | FPDF_ANNOT), but FPDF_FFLDraw receives the raw flags including the non-PDFium 0x80000000 bit (negative as int32). It should probably receive the same masked value.
Found while porting the WASM client to TypeScript (pdfrx_web project), by code inspection.
When
PdfPageRenderFlags.premultipliedAlpha(0x80000000) is set, the alpha premultiplication is performed twice on the WASM backend:packages/pdfrx/assets/pdfium_worker.js,renderPage(): the copy loop premultiplies BGRA whenflags & premultipliedAlpha:packages/pdfrx/lib/src/wasm/pdfrx_wasm.dart,_PdfPageWasm.render(): the received pixels are premultiplied again:Since both sides test the same bit on the same
flagsvalue, semi-transparent pixels end up with alpha applied twice (RGB gets darker than intended). One of the two loops should be removed (keeping it in the worker avoids the extra pass over the transferred buffer).Related nit in the same worker function:
FPDF_RenderPageBitmapreceives masked flags ((flags & 0xffff) | FPDF_ANNOT), butFPDF_FFLDrawreceives the rawflagsincluding the non-PDFium 0x80000000 bit (negative as int32). It should probably receive the same masked value.