fromImage static method

Future<ColorPalette> fromImage(
  1. Image image, {
  2. int maxColors = 64,
  3. int desiredColors = 16,
  4. int maxSamplePixels = 128 * 128,
  5. Rect? region,
  6. PaletteWeights weights = const PaletteWeights(),
})

Extracts a ColorPalette from a source image, sampling pixels to keep the work bounded.

maxSamplePixels caps how many pixels are analyzed: larger images are subsampled with a uniform stride, which preserves the dominant colors while keeping extraction fast. region limits sampling to a normalized sub-rectangle of the image (values 0..1), mirroring the region support of the original palette_generator.

Implementation

static Future<ColorPalette> fromImage(
  ui.Image image, {
  int maxColors = 64,
  int desiredColors = 16,
  int maxSamplePixels = 128 * 128,
  Rect? region,
  PaletteWeights weights = const PaletteWeights(),
}) async {
  final width = image.width;
  final height = image.height;
  if (width <= 0 || height <= 0) {
    throw ArgumentError.value(image, 'image', 'must have non-empty bounds');
  }

  final byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
  if (byteData == null) {
    throw StateError('Failed to read pixel data from the source image');
  }
  final bytes = byteData.buffer.asUint8List();

  // Normalized sampling region (defaults to the full image).
  final rect = (region ?? Rect.zero).normalize();
  final x0 = (rect.left * width).floor().clamp(0, width - 1);
  final x1 = (rect.right * width).ceil().clamp(1, width);
  final y0 = (rect.top * height).floor().clamp(0, height - 1);
  final y1 = (rect.bottom * height).ceil().clamp(1, height);
  final regionWidth = x1 - x0;
  final regionHeight = y1 - y0;
  final regionPixelCount = regionWidth * regionHeight;

  // Uniform stride so that the number of sampled pixels stays close to
  // maxSamplePixels without ever exceeding the actual region size.
  final stride = regionPixelCount <= maxSamplePixels
      ? 1
      : (regionPixelCount / maxSamplePixels).ceil();

  final pixels = <int>[];
  for (var y = y0; y < y1; y += stride) {
    for (var x = x0; x < x1; x += stride) {
      final offset = (y * width + x) * 4;
      final r = bytes[offset];
      final g = bytes[offset + 1];
      final b = bytes[offset + 2];
      pixels.add(0xFF000000 | (r << 16) | (g << 8) | b);
    }
  }

  return extract(
    pixels,
    maxColors: maxColors,
    desiredColors: desiredColors,
    weights: weights,
  );
}