mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Draw images directly into the canvas when compositing for data purposes (flutter/engine#36058)
This commit is contained in:
parent
3508545994
commit
3699be7e55
@ -398,7 +398,13 @@ class CanvasPool extends _SaveStackTracking {
|
||||
|
||||
/// Returns a "data://" URI containing a representation of the image in this
|
||||
/// canvas in PNG format.
|
||||
String toDataUrl() => _canvas?.toDataURL() ?? '';
|
||||
String toDataUrl() {
|
||||
if (_canvas == null) {
|
||||
_createCanvas();
|
||||
}
|
||||
return _canvas!.toDataURL();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void save() {
|
||||
@ -773,6 +779,10 @@ class CanvasPool extends _SaveStackTracking {
|
||||
contextHandle.paintPath(style, path.fillType);
|
||||
}
|
||||
|
||||
void drawImage(DomHTMLImageElement element, ui.Offset p) {
|
||||
context.drawImage(element, p.dx, p.dy);
|
||||
}
|
||||
|
||||
/// Draws a shadow for a Path representing the given material elevation.
|
||||
void drawShadow(ui.Path path, ui.Color color, double elevation,
|
||||
bool transparentOccluder) {
|
||||
|
||||
@ -366,27 +366,35 @@ class BitmapCanvas extends EngineCanvas {
|
||||
/// - Pictures typically have large rect/rounded rectangles as background
|
||||
/// prefer DOM if canvas has not been allocated yet.
|
||||
///
|
||||
bool _useDomForRenderingFill(SurfacePaintData paint) =>
|
||||
_renderStrategy.isInsideSvgFilterTree ||
|
||||
(_preserveImageData == false && _contains3dTransform) ||
|
||||
bool _useDomForRenderingFill(SurfacePaintData paint) {
|
||||
if (_preserveImageData) {
|
||||
return false;
|
||||
}
|
||||
return _renderStrategy.isInsideSvgFilterTree ||
|
||||
_contains3dTransform ||
|
||||
(_childOverdraw &&
|
||||
!_canvasPool.hasCanvas &&
|
||||
paint.maskFilter == null &&
|
||||
paint.shader == null &&
|
||||
paint.style != ui.PaintingStyle.stroke);
|
||||
}
|
||||
|
||||
/// Same as [_useDomForRenderingFill] but allows stroke as well.
|
||||
///
|
||||
/// DOM canvas is generated for simple strokes using borders.
|
||||
bool _useDomForRenderingFillAndStroke(SurfacePaintData paint) =>
|
||||
_renderStrategy.isInsideSvgFilterTree ||
|
||||
(_preserveImageData == false && _contains3dTransform) ||
|
||||
bool _useDomForRenderingFillAndStroke(SurfacePaintData paint) {
|
||||
if (_preserveImageData) {
|
||||
return false;
|
||||
}
|
||||
return _renderStrategy.isInsideSvgFilterTree ||
|
||||
_contains3dTransform ||
|
||||
((_childOverdraw ||
|
||||
_renderStrategy.hasImageElements ||
|
||||
_renderStrategy.hasParagraphs) &&
|
||||
!_canvasPool.hasCanvas &&
|
||||
paint.maskFilter == null &&
|
||||
paint.shader == null);
|
||||
}
|
||||
|
||||
@override
|
||||
void drawColor(ui.Color color, ui.BlendMode blendMode) {
|
||||
@ -626,7 +634,9 @@ class BitmapCanvas extends EngineCanvas {
|
||||
_applyTargetSize(
|
||||
imageElement, image.width.toDouble(), image.height.toDouble());
|
||||
}
|
||||
_closeCanvas();
|
||||
if (!_preserveImageData) {
|
||||
_closeCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
DomHTMLImageElement _reuseOrCreateImage(HtmlImage htmlImage) {
|
||||
@ -668,26 +678,40 @@ class BitmapCanvas extends EngineCanvas {
|
||||
imgElement = _reuseOrCreateImage(htmlImage);
|
||||
}
|
||||
imgElement.style.mixBlendMode = blendModeToCssMixBlendMode(blendMode) ?? '';
|
||||
if (_canvasPool.isClipped) {
|
||||
// Reset width/height since they may have been previously set.
|
||||
imgElement.style..removeProperty('width')..removeProperty('height');
|
||||
final List<DomElement> clipElements = _clipContent(
|
||||
_canvasPool.clipStack!, imgElement, p, _canvasPool.currentTransform);
|
||||
for (final DomElement clipElement in clipElements) {
|
||||
rootElement.append(clipElement);
|
||||
_children.add(clipElement);
|
||||
}
|
||||
if (_preserveImageData && imgElement is DomHTMLImageElement) {
|
||||
// If we're preserving image data, we have to actually draw the image
|
||||
// element onto the canvas.
|
||||
// TODO(jacksongardner): Make this actually work with color filters.
|
||||
setUpPaint(paint, null);
|
||||
_canvasPool.drawImage(imgElement, p);
|
||||
tearDownPaint();
|
||||
} else {
|
||||
final String cssTransform = float64ListToCssTransform(
|
||||
transformWithOffset(_canvasPool.currentTransform, p).storage);
|
||||
imgElement.style
|
||||
..transformOrigin = '0 0 0'
|
||||
..transform = cssTransform
|
||||
if (_canvasPool.isClipped) {
|
||||
// Reset width/height since they may have been previously set.
|
||||
..removeProperty('width')
|
||||
..removeProperty('height');
|
||||
rootElement.append(imgElement);
|
||||
_children.add(imgElement);
|
||||
imgElement.style
|
||||
..removeProperty('width')
|
||||
..removeProperty('height');
|
||||
final List<DomElement> clipElements = _clipContent(
|
||||
_canvasPool.clipStack!,
|
||||
imgElement,
|
||||
p,
|
||||
_canvasPool.currentTransform);
|
||||
for (final DomElement clipElement in clipElements) {
|
||||
rootElement.append(clipElement);
|
||||
_children.add(clipElement);
|
||||
}
|
||||
} else {
|
||||
final String cssTransform = float64ListToCssTransform(
|
||||
transformWithOffset(_canvasPool.currentTransform, p).storage);
|
||||
imgElement.style
|
||||
..transformOrigin = '0 0 0'
|
||||
..transform = cssTransform
|
||||
// Reset width/height since they may have been previously set.
|
||||
..removeProperty('width')
|
||||
..removeProperty('height');
|
||||
rootElement.append(imgElement);
|
||||
_children.add(imgElement);
|
||||
}
|
||||
}
|
||||
return imgElement;
|
||||
}
|
||||
|
||||
@ -2,8 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:js_util' as js_util;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:test/test.dart';
|
||||
@ -36,6 +38,28 @@ Future<void> testMain() async {
|
||||
region: const Rect.fromLTWH(0, 0, 500, 500));
|
||||
});
|
||||
|
||||
test('Images from raw data are composited when picture is roundtripped through toImage', () async {
|
||||
final Uint8List imageData = base64Decode(base64PngData);
|
||||
final Codec codec = await instantiateImageCodec(imageData);
|
||||
final FrameInfo frameInfo = await codec.getNextFrame();
|
||||
|
||||
const Rect bounds = Rect.fromLTRB(0, 0, 400, 300);
|
||||
final EnginePictureRecorder recorder = EnginePictureRecorder();
|
||||
final RecordingCanvas scratchCanvas = recorder.beginRecording(bounds);
|
||||
scratchCanvas.save();
|
||||
scratchCanvas.drawImage(frameInfo.image, Offset.zero, SurfacePaint());
|
||||
scratchCanvas.restore();
|
||||
final Picture picture = recorder.endRecording();
|
||||
final Image image = await picture.toImage(400, 300);
|
||||
|
||||
final RecordingCanvas rc = RecordingCanvas(bounds);
|
||||
rc.save();
|
||||
rc.drawImage(image, Offset.zero, SurfacePaint());
|
||||
rc.restore();
|
||||
await canvasScreenshot(rc, 'draw_raw_image',
|
||||
region: const Rect.fromLTWH(0, 0, 500, 500));
|
||||
});
|
||||
|
||||
test('Paints image with transform', () async {
|
||||
final RecordingCanvas rc =
|
||||
RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300));
|
||||
@ -592,9 +616,8 @@ Future<void> testMain() async {
|
||||
setupPerspective: true);
|
||||
});
|
||||
}
|
||||
|
||||
// 9 slice test image that has a shiny/glass look.
|
||||
const String base64ImageData = 'data:image/png;base64,iVBORw0KGgoAAAANSUh'
|
||||
const String base64PngData = 'iVBORw0KGgoAAAANSUh'
|
||||
'EUgAAADwAAAA8CAYAAAA6/NlyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPo'
|
||||
'AAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAApGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQA'
|
||||
'AARoABQAAAAEAAABKARsABQAAAAEAAABSATEAAgAAACAAAABah2kABAAAAAEAAAB6AAAAAAAA'
|
||||
@ -721,11 +744,12 @@ const String base64ImageData = 'data:image/png;base64,iVBORw0KGgoAAAANSUh'
|
||||
'Z2x0yEXi0PeqFO87AiFGCkemvOKYkSF/6yS1vnLOWTaHN/VcmnSWt0eHThELBHBiCGisGra'
|
||||
'SI5l6R3qD0q05ZR4TNVHES7bnltEgcg6JF3uVlyFsBpdB+lzgdRTMHeejneZR0H1BrnKECH'
|
||||
'7GyVy1BAmcsr17WxYs78QNqQF4bppFXqX9BBqIrzmcExwueASjAFzlaWncpqEpJCXVc7wv'
|
||||
'Nj7eT/BbztCaofk+k0AAAAAElFTkSuQmCC';
|
||||
'Nj7eT/BbztCaofk+k0AAAAAyBMj8AAAAAElFTkSuQmCC';
|
||||
const String base64ImageUrl = 'data:image/png;base64,$base64PngData';
|
||||
|
||||
HtmlImage createNineSliceImage() {
|
||||
return HtmlImage(
|
||||
createDomHTMLImageElement()..src = base64ImageData,
|
||||
createDomHTMLImageElement()..src = base64ImageUrl,
|
||||
60,
|
||||
60,
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user