pdfImageStencilMask function
An explicit /Mask stencil stream (§8.9.6.3): 1-bit samples where 1
means "masked out" (transparent); /Decode 1 0 flips the polarity.
Implementation
PdfImageSoftMask? pdfImageStencilMask(
CosDocument cos,
CosDictionary dict, {
int? targetWidth,
int? targetHeight,
}) {
final mask = cos.resolve(dict['Mask']);
if (mask is! CosStream) return null;
try {
final declaredWidth = _intOf(cos.resolve(mask.dictionary['Width']));
final declaredHeight = _intOf(cos.resolve(mask.dictionary['Height']));
if (declaredWidth <= 0 || declaredHeight <= 0) return null;
final declaredBits =
_intOf(cos.resolve(mask.dictionary['BitsPerComponent']), fallback: 1);
if (declaredBits != 1) return null;
final decode = cos.resolve(mask.dictionary['Decode']);
final inverted = decode is CosArray &&
decode.length > 0 &&
_numOf(cos.resolve(decode[0])) == 1;
final samples = _maskSampleData(
cos, mask, declaredWidth, declaredHeight, declaredBits);
// A codec that hands back wider samples is not a stencil (§8.9.6.3
// requires 1 bit per sample), whatever the dictionary declared.
if (samples == null || samples.bits != 1) return null;
final data = samples.data;
final width = samples.width;
final height = samples.height;
final rowBytes = (width + 7) ~/ 8;
if (data.length < rowBytes * height) return null;
final alpha = Uint8List(width * height);
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
final bit = (data[y * rowBytes + (x >> 3)] >> (7 - (x & 7))) & 1;
final masked = inverted ? bit == 0 : bit == 1;
alpha[y * width + x] = masked ? 0 : 255;
}
}
return _targetSizedSoftMask(
PdfImageSoftMask(alpha, width, height),
targetWidth,
targetHeight,
);
} on Exception {
return null; // unsupported mask: leave the image opaque
}
}