Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[canvaskit] Refactor HtmlImageCodec to generalize to different renderers #52905

Merged
merged 5 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -43279,6 +43279,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/clip.dart + ../../../flu
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/color_filter.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/dom_canvas.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/image.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/image_filter.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/offset.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/opacity.dart + ../../../flutter/LICENSE
Expand Down Expand Up @@ -43311,7 +43312,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.d
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html_image_element_codec.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/image_decoder.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/initialization.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_app.dart + ../../../flutter/LICENSE
Expand All @@ -43328,7 +43329,6 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart + ../
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/noto_font.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/noto_font_encoding.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/onscreen_logging.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher/app_lifecycle_state.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart + ../../../flutter/LICENSE
Expand Down Expand Up @@ -46156,6 +46156,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/clip.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/color_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/dom_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/image.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/image_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/offset.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/opacity.dart
Expand Down Expand Up @@ -46188,7 +46189,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dar
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_element_codec.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/image_decoder.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/initialization.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_app.dart
Expand All @@ -46205,7 +46206,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/noto_font.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/noto_font_encoding.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/onscreen_logging.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher/app_lifecycle_state.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart
Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export 'engine/html/clip.dart';
export 'engine/html/color_filter.dart';
export 'engine/html/debug_canvas_reuse_overlay.dart';
export 'engine/html/dom_canvas.dart';
export 'engine/html/image.dart';
export 'engine/html/image_filter.dart';
export 'engine/html/offset.dart';
export 'engine/html/opacity.dart';
Expand Down Expand Up @@ -105,7 +106,7 @@ export 'engine/html/shaders/vertex_shaders.dart';
export 'engine/html/surface.dart';
export 'engine/html/surface_stats.dart';
export 'engine/html/transform.dart';
export 'engine/html_image_codec.dart';
export 'engine/html_image_element_codec.dart';
export 'engine/image_decoder.dart';
export 'engine/initialization.dart';
export 'engine/js_interop/js_app.dart';
Expand All @@ -122,7 +123,6 @@ export 'engine/navigation/history.dart';
export 'engine/noto_font.dart';
export 'engine/noto_font_encoding.dart';
export 'engine/onscreen_logging.dart';
export 'engine/picture.dart';
export 'engine/platform_dispatcher.dart';
export 'engine/platform_dispatcher/app_lifecycle_state.dart';
export 'engine/platform_dispatcher/view_focus_binding.dart';
Expand Down
130 changes: 74 additions & 56 deletions lib/web_ui/lib/src/engine/html/bitmap_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import '../display.dart';
import '../dom.dart';
import '../engine_canvas.dart';
import '../frame_reference.dart';
import '../html_image_codec.dart';
import '../text/canvas_paragraph.dart';
import '../util.dart';
import '../vector_math.dart';
import 'clip.dart';
import 'color_filter.dart';
import 'dom_canvas.dart';
import 'image.dart';
import 'painting.dart';
import 'path/path.dart';
import 'recording_canvas.dart';
Expand Down Expand Up @@ -207,7 +207,8 @@ class BitmapCanvas extends EngineCanvas {

static int heightToPhysical(double height) {
final double boundsHeight = height + 1;
return (boundsHeight * EngineFlutterDisplay.instance.browserDevicePixelRatio)
return (boundsHeight *
EngineFlutterDisplay.instance.browserDevicePixelRatio)
.ceil() +
2 * kPaddingPixels;
}
Expand Down Expand Up @@ -252,7 +253,8 @@ class BitmapCanvas extends EngineCanvas {
/// * [PersistedPicture._recycleCanvas] which also uses this method
/// for the same reason.
bool isReusable() {
return _devicePixelRatio == EngineFlutterDisplay.instance.browserDevicePixelRatio;
return _devicePixelRatio ==
EngineFlutterDisplay.instance.browserDevicePixelRatio;
}

/// Returns a "data://" URI containing a representation of the image in this
Expand Down Expand Up @@ -365,12 +367,12 @@ class BitmapCanvas extends EngineCanvas {
return false;
}
return _renderStrategy.isInsideSvgFilterTree ||
_contains3dTransform ||
(_childOverdraw &&
!_canvasPool.hasCanvas &&
paint.maskFilter == null &&
paint.shader == null &&
paint.style != ui.PaintingStyle.stroke);
_contains3dTransform ||
(_childOverdraw &&
!_canvasPool.hasCanvas &&
paint.maskFilter == null &&
paint.shader == null &&
paint.style != ui.PaintingStyle.stroke);
}

/// Same as [_useDomForRenderingFill] but allows stroke as well.
Expand All @@ -381,13 +383,13 @@ class BitmapCanvas extends EngineCanvas {
return false;
}
return _renderStrategy.isInsideSvgFilterTree ||
_contains3dTransform ||
((_childOverdraw ||
_renderStrategy.hasImageElements ||
_renderStrategy.hasParagraphs) &&
!_canvasPool.hasCanvas &&
paint.maskFilter == null &&
paint.shader == null);
_contains3dTransform ||
((_childOverdraw ||
_renderStrategy.hasImageElements ||
_renderStrategy.hasParagraphs) &&
!_canvasPool.hasCanvas &&
paint.maskFilter == null &&
paint.shader == null);
}

@override
Expand Down Expand Up @@ -512,7 +514,8 @@ class BitmapCanvas extends EngineCanvas {
@override
void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) {
if (_useDomForRenderingFillAndStroke(paint)) {
final ui.Rect rect = adjustRectForDom(ui.Rect.fromCircle(center: c, radius: radius), paint);
final ui.Rect rect = adjustRectForDom(
ui.Rect.fromCircle(center: c, radius: radius), paint);
final DomHTMLElement element = buildDrawRectElement(
rect, paint, 'draw-circle', _canvasPool.currentTransform);
_drawElement(element, rect.topLeft, paint);
Expand Down Expand Up @@ -572,7 +575,8 @@ class BitmapCanvas extends EngineCanvas {
final bool isStroke = paint.style == ui.PaintingStyle.stroke;
final String cssColor = colorValueToCssString(paint.color);
final double sigma = paint.maskFilter!.webOnlySigma;
if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && !isStroke) {
if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit &&
!isStroke) {
// A bug in webkit leaves artifacts when this element is animated
// with filter: blur, we use boxShadow instead.
element.style.boxShadow = '0px 0px ${sigma * 2.0}px $cssColor';
Expand Down Expand Up @@ -625,7 +629,8 @@ class BitmapCanvas extends EngineCanvas {
ui.Image image, ui.Offset p, SurfacePaintData paint) {
final HtmlImage htmlImage = image as HtmlImage;
final ui.BlendMode? blendMode = paint.blendMode;
final EngineHtmlColorFilter? colorFilter = createHtmlColorFilter(paint.colorFilter);
final EngineHtmlColorFilter? colorFilter =
createHtmlColorFilter(paint.colorFilter);
DomHTMLElement imgElement;
if (colorFilter is ModeHtmlColorFilter) {
imgElement = _createImageElementWithBlend(
Expand Down Expand Up @@ -748,8 +753,7 @@ class BitmapCanvas extends EngineCanvas {
targetWidth *= image.width / src.width;
targetHeight *= image.height / src.height;
}
_applyTargetSize(
imgElement as DomHTMLElement, targetWidth, targetHeight);
_applyTargetSize(imgElement as DomHTMLElement, targetWidth, targetHeight);
if (requiresClipping) {
restore();
}
Expand Down Expand Up @@ -831,7 +835,8 @@ class BitmapCanvas extends EngineCanvas {
ui.BlendMode colorFilterBlendMode,
SurfacePaintData paint) {
// For srcIn blendMode, we use an svg filter to apply to image element.
final SvgFilter svgFilter = svgFilterFromBlendMode(filterColor, colorFilterBlendMode);
final SvgFilter svgFilter =
svgFilterFromBlendMode(filterColor, colorFilterBlendMode);
rootElement.append(svgFilter.element);
_children.add(svgFilter.element);
final DomHTMLElement imgElement = _reuseOrCreateImage(image);
Expand Down Expand Up @@ -893,7 +898,8 @@ class BitmapCanvas extends EngineCanvas {
///
/// The text is drawn starting at coordinates ([x], [y]). It uses the current
/// font set by the most recent call to [setCssFont].
void drawText(String text, double x, double y, {ui.PaintingStyle? style, List<ui.Shadow>? shadows}) {
void drawText(String text, double x, double y,
{ui.PaintingStyle? style, List<ui.Shadow>? shadows}) {
final DomCanvasRenderingContext2D ctx = _canvasPool.context;
if (shadows != null) {
ctx.save();
Expand Down Expand Up @@ -932,27 +938,23 @@ class BitmapCanvas extends EngineCanvas {
// Cannot composite if the paragraph cannot be drawn into bitmap canvas
// in the first place.
paragraph.canDrawOnCanvas &&
// Cannot composite if there's no bitmap canvas to composite into.
// Creating a new bitmap canvas just to draw text doesn't make sense.
_canvasPool.hasCanvas &&
!_childOverdraw &&
// Bitmap canvas introduces correctness issues in the presence of SVG
// filters, so prefer plain HTML in this case.
!_renderStrategy.isInsideSvgFilterTree;
// Cannot composite if there's no bitmap canvas to composite into.
// Creating a new bitmap canvas just to draw text doesn't make sense.
_canvasPool.hasCanvas &&
!_childOverdraw &&
// Bitmap canvas introduces correctness issues in the presence of SVG
// filters, so prefer plain HTML in this case.
!_renderStrategy.isInsideSvgFilterTree;

if (canCompositeIntoBitmapCanvas) {
paragraph.paint(this, offset);
return;
}

final DomElement paragraphElement =
drawParagraphElement(paragraph, offset);
final DomElement paragraphElement = drawParagraphElement(paragraph, offset);
if (_canvasPool.isClipped) {
final List<DomElement> clipElements = _clipContent(
_canvasPool.clipStack!,
paragraphElement,
offset,
_canvasPool.currentTransform);
final List<DomElement> clipElements = _clipContent(_canvasPool.clipStack!,
paragraphElement, offset, _canvasPool.currentTransform);
for (final DomElement clipElement in clipElements) {
rootElement.append(clipElement);
_children.add(clipElement);
Expand Down Expand Up @@ -1050,7 +1052,8 @@ class BitmapCanvas extends EngineCanvas {
void endOfPaint() {
_canvasPool.endOfPaint();
_elementCache?.commitFrame();
if (_contains3dTransform && ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) {
if (_contains3dTransform &&
ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) {
// Copy the children list to avoid concurrent modification.
final List<DomElement> children = rootElement.children.toList();
for (final DomElement element in children) {
Expand Down Expand Up @@ -1080,10 +1083,12 @@ class BitmapCanvas extends EngineCanvas {
final double width = ui.window.physicalSize.width * dpr;
final double height = ui.window.physicalSize.height * dpr;
final Vector3 topLeft = inverted.perspectiveTransform(x: 0, y: 0, z: 0);
final Vector3 topRight = inverted.perspectiveTransform(x: width, y: 0, z: 0);
final Vector3 topRight =
inverted.perspectiveTransform(x: width, y: 0, z: 0);
final Vector3 bottomRight =
inverted.perspectiveTransform(x: width, y: height, z: 0);
final Vector3 bottomLeft = inverted.perspectiveTransform(x: 0, y: height, z: 0);
final Vector3 bottomLeft =
inverted.perspectiveTransform(x: 0, y: height, z: 0);
return ui.Rect.fromLTRB(
math.min(topLeft.x,
math.min(topRight.x, math.min(bottomRight.x, bottomLeft.x))),
Expand Down Expand Up @@ -1254,13 +1259,17 @@ SvgBlendMode? blendModeToSvgEnum(ui.BlendMode? blendMode) {
case ui.BlendMode.srcATop:
return const SvgBlendMode(kCompositeSourceAtop, SVG_FEBLEND_MODE_UNKNOWN);
case ui.BlendMode.dstOver:
return const SvgBlendMode(kCompositeDestinationOver, SVG_FEBLEND_MODE_UNKNOWN);
return const SvgBlendMode(
kCompositeDestinationOver, SVG_FEBLEND_MODE_UNKNOWN);
case ui.BlendMode.dstIn:
return const SvgBlendMode(kCompositeDestinationIn, SVG_FEBLEND_MODE_UNKNOWN);
return const SvgBlendMode(
kCompositeDestinationIn, SVG_FEBLEND_MODE_UNKNOWN);
case ui.BlendMode.dstOut:
return const SvgBlendMode(kCompositeDestinationOut, SVG_FEBLEND_MODE_UNKNOWN);
return const SvgBlendMode(
kCompositeDestinationOut, SVG_FEBLEND_MODE_UNKNOWN);
case ui.BlendMode.dstATop:
return const SvgBlendMode(kCompositeDestinationAtop, SVG_FEBLEND_MODE_UNKNOWN);
return const SvgBlendMode(
kCompositeDestinationAtop, SVG_FEBLEND_MODE_UNKNOWN);
case ui.BlendMode.plus:
return const SvgBlendMode(kCompositeLighter, SVG_FEBLEND_MODE_UNKNOWN);
case ui.BlendMode.src:
Expand All @@ -1271,7 +1280,8 @@ SvgBlendMode? blendModeToSvgEnum(ui.BlendMode? blendMode) {
// Falling back to multiply, ignoring alpha channel.
// TODO(ferhat): only used for debug, find better fallback for web.
case ui.BlendMode.modulate:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_MULTIPLY);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_MULTIPLY);
case ui.BlendMode.screen:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_SCREEN);
case ui.BlendMode.overlay:
Expand All @@ -1281,32 +1291,40 @@ SvgBlendMode? blendModeToSvgEnum(ui.BlendMode? blendMode) {
case ui.BlendMode.lighten:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_LIGHTEN);
case ui.BlendMode.colorDodge:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_DODGE);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_DODGE);
case ui.BlendMode.colorBurn:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_BURN);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_BURN);
case ui.BlendMode.hardLight:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_HARD_LIGHT);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_HARD_LIGHT);
case ui.BlendMode.softLight:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_SOFT_LIGHT);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_SOFT_LIGHT);
case ui.BlendMode.difference:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_DIFFERENCE);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_DIFFERENCE);
case ui.BlendMode.exclusion:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_EXCLUSION);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_EXCLUSION);
case ui.BlendMode.hue:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_HUE);
case ui.BlendMode.saturation:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_SATURATION);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_SATURATION);
case ui.BlendMode.color:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR);
case ui.BlendMode.luminosity:
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_LUMINOSITY);
return const SvgBlendMode(
kCompositeSourceOver, SVG_FEBLEND_MODE_LUMINOSITY);
default:
assert(
false,
'Flutter Web does not support the blend mode: $blendMode',
);

return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_NORMAL);
return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_NORMAL);
}
}

Expand Down Expand Up @@ -1344,8 +1362,8 @@ String stringForStrokeJoin(ui.StrokeJoin strokeJoin) {
/// overflow:hidden with bounds to clip child or sets a clip-path to clip
/// it's contents. The clipping rectangles are nested and returned together
/// with a list of svg elements that provide clip-paths.
List<DomElement> _clipContent(List<SaveClipEntry> clipStack,
DomElement content, ui.Offset offset, Matrix4 currentTransform) {
List<DomElement> _clipContent(List<SaveClipEntry> clipStack, DomElement content,
ui.Offset offset, Matrix4 currentTransform) {
DomElement? root, curElement;
final List<DomElement> clipDefs = <DomElement>[];
final int len = clipStack.length;
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/html/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import 'dart:typed_data';

import 'package:ui/ui.dart' as ui;

import '../picture.dart';
import '../util.dart';
import '../validators.dart';
import '../vector_math.dart';
import 'painting.dart';
import 'picture.dart';
import 'recording_canvas.dart';
import 'render_vertices.dart';

Expand Down
Loading