blob: 2b800ba2769644b63a5d5343d3e718f47af06b6a [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ffi';
import 'dart:js_interop';
import 'dart:typed_data';
import 'package:ui/src/engine.dart';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;
class SkwasmImage extends SkwasmObjectWrapper<RawImage> implements ui.Image {
SkwasmImage(ImageHandle handle) : super(handle, _registry)
{
ui.Image.onCreate?.call(this);
}
factory SkwasmImage.fromPixels(
Uint8List pixels,
int width,
int height,
ui.PixelFormat format, {
int? rowBytes,
}) {
final SkDataHandle dataHandle = skDataCreate(pixels.length);
final Pointer<Uint8> dataPointer = skDataGetPointer(dataHandle).cast<Uint8>();
for (int i = 0; i < pixels.length; i++) {
dataPointer[i] = pixels[i];
}
final ImageHandle imageHandle = imageCreateFromPixels(
dataHandle,
width,
height,
format.index,
rowBytes ?? 4 * width,
);
skDataDispose(dataHandle);
return SkwasmImage(imageHandle);
}
static final SkwasmFinalizationRegistry<RawImage> _registry =
SkwasmFinalizationRegistry<RawImage>(imageDispose);
@override
void dispose() {
super.dispose();
ui.Image.onDispose?.call(this);
}
@override
int get width => imageGetWidth(handle);
@override
int get height => imageGetHeight(handle);
@override
Future<ByteData?> toByteData(
{ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) async {
if (format == ui.ImageByteFormat.png) {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.drawImage(this, ui.Offset.zero, ui.Paint());
final DomImageBitmap bitmap =
(await (renderer as SkwasmRenderer).surface.renderPictures(
<SkwasmPicture>[recorder.endRecording() as SkwasmPicture],
)).imageBitmaps.single;
final DomOffscreenCanvas offscreenCanvas =
createDomOffscreenCanvas(bitmap.width.toDartInt, bitmap.height.toDartInt);
final DomCanvasRenderingContextBitmapRenderer context =
offscreenCanvas.getContext('bitmaprenderer')! as DomCanvasRenderingContextBitmapRenderer;
context.transferFromImageBitmap(bitmap);
final DomBlob blob = await offscreenCanvas.convertToBlob();
final JSArrayBuffer arrayBuffer = (await blob.arrayBuffer().toDart)! as JSArrayBuffer;
// Zero out the contents of the canvas so that resources can be reclaimed
// by the browser.
context.transferFromImageBitmap(null);
return ByteData.view(arrayBuffer.toDart);
} else {
return (renderer as SkwasmRenderer).surface.rasterizeImage(this, format);
}
}
@override
ui.ColorSpace get colorSpace => ui.ColorSpace.sRGB;
@override
SkwasmImage clone() {
imageRef(handle);
return SkwasmImage(handle);
}
@override
bool isCloneOf(ui.Image other) => other is SkwasmImage && handle == other.handle;
@override
List<StackTrace>? debugGetOpenHandleStackTraces() => null;
@override
String toString() => '[$width\u00D7$height]';
}