compress method

  1. @override
Future<Uint8List> compress(
  1. Uint8List data,
  2. CompressOptions options
)
override

Compresses image data in memory.

Takes raw image data as Uint8List and applies compression according to options.

Returns compressed image data as Uint8List.

Throws ImageCompressException if compression fails.

Implementation

@override
Future<Uint8List> compress(Uint8List data, CompressOptions options) async {
  try {
    // Detect image format by magic bytes
    final format = _detectFormat(data);
    final mimeType = _getMimeType(format);

    // Create blob from Uint8List
    final blob = web.Blob(
      [data.buffer.toJS].toJS,
      web.BlobPropertyBag(type: mimeType),
    );
    final url = web.URL.createObjectURL(blob);

    try {
      // Load image
      final img = web.HTMLImageElement();
      final completer = Completer<void>();

      img.addEventListener(
        'load',
        (web.Event event) {
          completer.complete();
        }.toJS,
      );

      img.addEventListener(
        'error',
        (web.Event event) {
          completer.completeError(
            ImageCompressException('Failed to load image', 'INVALID_IMAGE'),
          );
        }.toJS,
      );

      img.src = url;
      await completer.future;

      // Calculate target dimensions
      final dimensions = _calculateDimensions(
        img.width,
        img.height,
        options.maxWidth,
        options.maxHeight,
      );

      // Create canvas and draw resized image
      final canvas = web.HTMLCanvasElement();
      canvas.width = dimensions.width;
      canvas.height = dimensions.height;

      final ctx = canvas.getContext('2d') as web.CanvasRenderingContext2D;
      ctx.drawImage(
        img,
        0,
        0,
        dimensions.width.toDouble(),
        dimensions.height.toDouble(),
      );

      // Convert to blob
      final resultCompleter = Completer<Uint8List>();

      // Determine quality parameter based on format
      final qualityParam = _shouldApplyQuality(format)
          ? (options.quality / 100.0).toJS
          : null;

      canvas.toBlob(
        ((web.Blob? blob) {
          if (blob == null) {
            resultCompleter.completeError(
              ImageCompressException(
                'Failed to create blob',
                'COMPRESSION_FAILED',
              ),
            );
            return;
          }

          final reader = web.FileReader();
          reader.addEventListener(
            'loadend',
            (web.Event event) {
              final result = reader.result;
              if (result != null) {
                final jsArrayBuffer = result as JSArrayBuffer;
                final uint8List = jsArrayBuffer.toDart.asUint8List();
                resultCompleter.complete(uint8List);
              } else {
                resultCompleter.completeError(
                  ImageCompressException(
                    'Failed to read blob',
                    'COMPRESSION_FAILED',
                  ),
                );
              }
            }.toJS,
          );

          reader.addEventListener(
            'error',
            (web.Event event) {
              resultCompleter.completeError(
                ImageCompressException(
                  'Failed to read blob',
                  'COMPRESSION_FAILED',
                ),
              );
            }.toJS,
          );

          reader.readAsArrayBuffer(blob);
        }).toJS,
        mimeType,
        qualityParam,
      );

      return await resultCompleter.future;
    } finally {
      web.URL.revokeObjectURL(url);
    }
  } catch (e) {
    // Graceful error handling: log error and return original data
    if (e is ImageCompressException) {
      debugPrint('Web compression failed: ${e.message} (${e.code})');
    } else {
      debugPrint('Web compression failed with unexpected error: $e');
    }
    return data;
  }
}