libjpeg_turbo_dart 1.1.0 copy "libjpeg_turbo_dart: ^1.1.0" to clipboard
libjpeg_turbo_dart: ^1.1.0 copied to clipboard

Pure-Dart port of libjpeg-turbo: JPEG decode, encode, and lossless transforms with output verified byte-for-byte against the C library. On web, requires WebAssembly.

example/example.dart

// Encode and decode a JPEG entirely in memory.
//
// This example touches no files, so it runs on every supported platform,
// including Flutter web built with `flutter build web --wasm`.
//
// Run it with:  dart run example/example.dart

import 'dart:typed_data';

import 'package:libjpeg_turbo_dart/libjpeg_turbo_dart.dart';

const int width = 256;
const int height = 192;

/// Build an interleaved RGB test image (a smooth gradient plus a grid).
Uint8List buildRgbImage() {
  final pixels = Uint8List(width * height * 3);
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      final i = (y * width + x) * 3;
      final onGrid = x % 32 == 0 || y % 32 == 0;
      pixels[i + 0] = onGrid ? 255 : x;
      pixels[i + 1] = onGrid ? 255 : y * 255 ~/ (height - 1);
      pixels[i + 2] = onGrid ? 255 : 128;
    }
  }
  return pixels;
}

/// Compress interleaved RGB to a JPEG at the given quality.
Uint8List encode(Uint8List rgb, {int quality = 90}) {
  final cinfo = JpegCompress();
  cinfo.err = jpegStdError();
  jpegCreateCompress(cinfo);
  try {
    // Destination buffer; it grows automatically if the image needs more.
    jpegMemDest(cinfo, 1 << 16);

    cinfo.imageWidth = width;
    cinfo.imageHeight = height;
    cinfo.inputComponents = 3;
    cinfo.inColorSpace = JColorSpace.rgb;
    jpegSetDefaults(cinfo);
    jpegSetQuality(cinfo, quality, false);

    jpegStartCompress(cinfo, true);
    final row = Uint8List(width * 3);
    while (cinfo.nextScanline < cinfo.imageHeight) {
      final start = cinfo.nextScanline * width * 3;
      row.setRange(0, width * 3, rgb, start);
      jpegWriteScanlines(cinfo, [row], 1);
    }
    jpegFinishCompress(cinfo);
    return jpegMemDestGetBuffer(cinfo);
  } finally {
    jpegDestroyCompress(cinfo);
  }
}

/// Decompress a JPEG to interleaved RGB.
({Uint8List pixels, int width, int height, int components}) decode(
  Uint8List jpeg,
) {
  final cinfo = JpegDecompress();
  cinfo.err = jpegStdError();
  jpegCreateDecompress(cinfo, jpegLibVersion, 0);
  try {
    jpegMemSrc(cinfo, jpeg);
    jpegReadHeader(cinfo, true);
    jpegStartDecompress(cinfo);

    final w = cinfo.outputWidth;
    final h = cinfo.outputHeight;
    final nc = cinfo.outputComponents;
    final pixels = Uint8List(w * h * nc);
    // Uint8List rows take a fast path in the color converters.
    final row = Uint8List(w * nc);
    while (cinfo.outputScanline < h) {
      final y = cinfo.outputScanline;
      jpegReadScanlines(cinfo, [row], 1);
      pixels.setRange(y * w * nc, (y + 1) * w * nc, row);
    }
    jpegFinishDecompress(cinfo);
    return (pixels: pixels, width: w, height: h, components: nc);
  } finally {
    jpegDestroyDecompress(cinfo);
  }
}

void main() {
  final original = buildRgbImage();
  print('original:  $width x $height RGB, ${original.length} bytes');

  for (final quality in [50, 90]) {
    final jpeg = encode(original, quality: quality);
    final decoded = decode(jpeg);

    // Mean absolute error against the source pixels, as a sanity check that
    // the round trip actually reproduces the image.
    int total = 0;
    for (int i = 0; i < original.length; i++) {
      total += (original[i] - decoded.pixels[i]).abs();
    }
    final mae = total / original.length;
    final ratio = original.length / jpeg.length;

    print(
      'quality $quality:  ${jpeg.length} bytes '
      '(${ratio.toStringAsFixed(1)}x smaller), '
      'decoded ${decoded.width}x${decoded.height}'
      ' x${decoded.components}, mean error ${mae.toStringAsFixed(2)}',
    );
  }

  // Errors in the JPEG data surface as catchable JpegError exceptions.
  try {
    decode(Uint8List.fromList([0x00, 0x01, 0x02, 0x03]));
  } on JpegError catch (e) {
    print('rejected non-JPEG input as expected: ${e.message}');
  }
}
1
likes
140
points
11
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Pure-Dart port of libjpeg-turbo: JPEG decode, encode, and lossless transforms with output verified byte-for-byte against the C library. On web, requires WebAssembly.

Repository (GitHub)
View/report issues

Topics

#jpeg #image #codec #compression #jpegtran

License

unknown (license)

More

Packages that depend on libjpeg_turbo_dart