Function tryRgbHistogram256WithValidity

Build an RGB histogram using an explicit validity/NoData mask.

RgbHistogramError tryRgbHistogram256WithValidity(
  scope raster.view.RasterView!(ubyte) rgb,
  raster.region.Region2D roi,
  scope raster.view.RasterView!(ubyte) validity,
  ref RgbHistogram256 published
) @safe;

Unlike the selection-mask overload, zero means that the source sample is not valid and must not contribute to statistics; nonzero means valid. The mask does not alter pixels and is not an editing-selection or alpha policy. The caller is responsible for constructing it from source-specific NoData rules.

Example

Count two stored RGB pixels, then include only the first using a binary mask. Allocation here belongs to the consumer, not the histogram.

import core.stdc.stdlib : malloc;
import raster : OwnedByteResource, PlaneByteLayout, RasterLease,
    tryAdoptMallocResource, tryImportOwnedRaster;

// The consumer creates a retained, interleaved two-pixel RGB raster.
auto bytes = cast(ubyte*)malloc(6);
assert(bytes !is null);
bytes[0 .. 6] = [cast(ubyte)0, 64, 255, 128, 192, 17];
OwnedByteResource owned;
assert(tryAdoptMallocResource(bytes, 6, owned));
const PlaneByteLayout[3] channels = [
    PlaneByteLayout(0, 6, 3), PlaneByteLayout(1, 6, 3),
    PlaneByteLayout(2, 6, 3)
];
RasterLease!ubyte image;
assert(tryImportOwnedRaster!ubyte(owned, channels[],
    Region2D(0, 0, 2, 1), image).ok);

RgbHistogram256 result;
const roi = Region2D(0, 0, 2, 1);
assert(tryRgbHistogram256(image.view(), roi, result) ==
    RgbHistogramError.none);
assert(result.includedPixels == 2 && result.excludedPixels == 0);
assert(result.bins[0][0] == 1 && result.bins[0][128] == 1);
assert(result.minimum == [0, 64, 17]);
assert(result.maximum == [128, 192, 255]);
assert(result.mean == [64.0, 128.0, 136.0]);

auto maskBytes = cast(ubyte*)malloc(2);
assert(maskBytes !is null);
maskBytes[0 .. 2] = [cast(ubyte)1, 0];
OwnedByteResource maskOwned;
assert(tryAdoptMallocResource(maskBytes, 2, maskOwned));
const PlaneByteLayout[1] maskPlane = [PlaneByteLayout(0, 2, 1)];
RasterLease!ubyte mask;
assert(tryImportOwnedRaster!ubyte(maskOwned, maskPlane[], roi, mask).ok);
assert(tryRgbHistogram256(image.view(), roi, mask.view(), result) ==
    RgbHistogramError.none);
assert(result.includedPixels == 1 && result.excludedPixels == 1);
assert(result.mean == [0.0, 64.0, 255.0]); // mask byte 1 includes once

const previous = result;
assert(tryRgbHistogram256(image.view(), Region2D(2, 0, 1, 1), result)
    == RgbHistogramError.invalidRegion);
assert(result == previous); // returned failure preserves publication