Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
weixin_43355755
engine
提交
5c8a6260
E
engine
项目概览
weixin_43355755
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
5c8a6260
编写于
8月 12, 2020
作者:
Y
Yegor
提交者:
GitHub
8月 12, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Delete CSS paint code (#20426)
* Delete CSS paint code
上级
2ac5b346
变更
20
隐藏空白更改
内联
并排
Showing
20 changed file
with
347 addition
and
2113 deletion
+347
-2113
ci/licenses_golden/licenses_flutter
ci/licenses_golden/licenses_flutter
+0
-2
lib/web_ui/lib/assets/houdini_painter.js
lib/web_ui/lib/assets/houdini_painter.js
+0
-1069
lib/web_ui/lib/src/engine.dart
lib/web_ui/lib/src/engine.dart
+0
-1
lib/web_ui/lib/src/engine/bitmap_canvas.dart
lib/web_ui/lib/src/engine/bitmap_canvas.dart
+2
-2
lib/web_ui/lib/src/engine/color_filter.dart
lib/web_ui/lib/src/engine/color_filter.dart
+0
-4
lib/web_ui/lib/src/engine/engine_canvas.dart
lib/web_ui/lib/src/engine/engine_canvas.dart
+124
-0
lib/web_ui/lib/src/engine/houdini_canvas.dart
lib/web_ui/lib/src/engine/houdini_canvas.dart
+0
-370
lib/web_ui/lib/src/engine/rrect_renderer.dart
lib/web_ui/lib/src/engine/rrect_renderer.dart
+0
-1
lib/web_ui/lib/src/engine/surface/path/path.dart
lib/web_ui/lib/src/engine/surface/path/path.dart
+0
-6
lib/web_ui/lib/src/engine/surface/picture.dart
lib/web_ui/lib/src/engine/surface/picture.dart
+196
-287
lib/web_ui/lib/src/engine/surface/recording_canvas.dart
lib/web_ui/lib/src/engine/surface/recording_canvas.dart
+8
-311
lib/web_ui/lib/src/engine/surface/scene_builder.dart
lib/web_ui/lib/src/engine/surface/scene_builder.dart
+1
-1
lib/web_ui/lib/src/engine/surface/shader.dart
lib/web_ui/lib/src/engine/surface/shader.dart
+0
-22
lib/web_ui/lib/src/engine/surface/surface_stats.dart
lib/web_ui/lib/src/engine/surface/surface_stats.dart
+1
-1
lib/web_ui/lib/src/ui/painting.dart
lib/web_ui/lib/src/ui/painting.dart
+0
-8
lib/web_ui/test/canvas_test.dart
lib/web_ui/test/canvas_test.dart
+0
-1
lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart
..._ui/test/golden_tests/engine/compositing_golden_test.dart
+12
-12
lib/web_ui/test/golden_tests/engine/scuba.dart
lib/web_ui/test/golden_tests/engine/scuba.dart
+1
-13
lib/web_ui/test/golden_tests/engine/text_placeholders_test.dart
...b_ui/test/golden_tests/engine/text_placeholders_test.dart
+1
-1
lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart
...b_ui/test/golden_tests/engine/text_style_golden_test.dart
+1
-1
未找到文件。
ci/licenses_golden/licenses_flutter
浏览文件 @
5c8a6260
...
...
@@ -419,7 +419,6 @@ FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc
FILE: ../../../flutter/lib/ui/window/viewport_metrics.h
FILE: ../../../flutter/lib/ui/window/window.cc
FILE: ../../../flutter/lib/ui/window/window.h
FILE: ../../../flutter/lib/web_ui/lib/assets/houdini_painter.js
FILE: ../../../flutter/lib/web_ui/lib/src/engine.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/alarm_clock.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/assets.dart
...
...
@@ -463,7 +462,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_renderer.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/houdini_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart
...
...
lib/web_ui/lib/assets/houdini_painter.js
已删除
100644 → 0
浏览文件 @
2ac5b346
// 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.
// TODO(yjbanov): Consider the following optimizations:
// - Switch from JSON to typed arrays. See:
// https://github.com/w3c/css-houdini-drafts/issues/136
// - When there is no DOM-rendered content, then clipping in the canvas is more
// efficient than DOM-rendered clipping.
// - When DOM-rendered clip is the only option, then clipping _again_ in the
// canvas is superfluous.
// - When transform is a 2D transform and there is no DOM-rendered content, then
// canvas transform is more efficient than DOM-rendered transform.
// - If a transform must be DOM-rendered, then clipping in the canvas _again_ is
// superfluous.
/**
* Applies paint commands to CSS Paint API (a.k.a. Houdini).
*
* This painter is driven by houdini_canvas.dart. This painter and the
* HoudiniCanvas class must be kept in sync with each other.
*/
class
FlutterPainter
{
/**
* Properties used by this painter.
*
* @return {string[]} list of CSS properties this painter depends on.
*/
static
get
inputProperties
()
{
return
[
'
--flt
'
];
}
/**
* Implements the painter interface.
*/
paint
(
ctx
,
geom
,
properties
)
{
let
fltProp
=
properties
.
get
(
'
--flt
'
).
toString
();
if
(
!
fltProp
)
{
// Nothing to paint.
return
;
}
const
commands
=
JSON
.
parse
(
fltProp
);
for
(
let
i
=
0
;
i
<
commands
.
length
;
i
++
)
{
let
command
=
commands
[
i
];
// TODO(yjbanov): we should probably move command identifiers into an enum
switch
(
command
[
0
])
{
case
1
:
this
.
_save
(
ctx
,
geom
,
command
);
break
;
case
2
:
this
.
_restore
(
ctx
,
geom
,
command
);
break
;
case
3
:
this
.
_translate
(
ctx
,
geom
,
command
);
break
;
case
4
:
this
.
_scale
(
ctx
,
geom
,
command
);
break
;
case
5
:
this
.
_rotate
(
ctx
,
geom
,
command
);
break
;
// Skip case 6: implemented in the DOM for now.
case
7
:
this
.
_skew
(
ctx
,
geom
,
command
);
break
;
case
8
:
this
.
_clipRect
(
ctx
,
geom
,
command
);
break
;
case
9
:
this
.
_clipRRect
(
ctx
,
geom
,
command
);
break
;
case
10
:
this
.
_clipPath
(
ctx
,
geom
,
command
);
break
;
case
11
:
this
.
_drawColor
(
ctx
,
geom
,
command
);
break
;
case
12
:
this
.
_drawLine
(
ctx
,
geom
,
command
);
break
;
case
13
:
this
.
_drawPaint
(
ctx
,
geom
,
command
);
break
;
case
14
:
this
.
_drawRect
(
ctx
,
geom
,
command
);
break
;
case
15
:
this
.
_drawRRect
(
ctx
,
geom
,
command
);
break
;
case
16
:
this
.
_drawDRRect
(
ctx
,
geom
,
command
);
break
;
case
17
:
this
.
_drawOval
(
ctx
,
geom
,
command
);
break
;
case
18
:
this
.
_drawCircle
(
ctx
,
geom
,
command
);
break
;
case
19
:
this
.
_drawPath
(
ctx
,
geom
,
command
);
break
;
case
20
:
this
.
_drawShadow
(
ctx
,
geom
,
command
);
break
;
default
:
throw
new
Error
(
`Unsupported command ID:
${
command
[
0
]}
`
);
}
}
}
_applyPaint
(
ctx
,
paint
)
{
let
blendMode
=
_stringForBlendMode
(
paint
.
blendMode
);
ctx
.
globalCompositeOperation
=
blendMode
?
blendMode
:
'
source-over
'
;
ctx
.
lineWidth
=
paint
.
strokeWidth
?
paint
.
strokeWidth
:
1.0
;
let
strokeCap
=
_stringForStrokeCap
(
paint
.
strokeCap
);
ctx
.
lineCap
=
strokeCap
?
strokeCap
:
'
butt
'
;
if
(
paint
.
shader
!=
null
)
{
let
paintStyle
=
paint
.
shader
.
createPaintStyle
(
ctx
);
ctx
.
fillStyle
=
paintStyle
;
ctx
.
strokeStyle
=
paintStyle
;
}
else
if
(
paint
.
color
!=
null
)
{
let
colorString
=
paint
.
color
;
ctx
.
fillStyle
=
colorString
;
ctx
.
strokeStyle
=
colorString
;
}
if
(
paint
.
maskFilter
!=
null
)
{
ctx
.
filter
=
`blur(
${
paint
.
maskFilter
[
1
]}
px)`
;
}
}
_strokeOrFill
(
ctx
,
paint
,
resetPaint
)
{
switch
(
paint
.
style
)
{
case
PaintingStyle
.
stroke
:
ctx
.
stroke
();
break
;
case
PaintingStyle
.
fill
:
default
:
ctx
.
fill
();
break
;
}
if
(
resetPaint
)
{
this
.
_resetPaint
(
ctx
);
}
}
_resetPaint
(
ctx
)
{
ctx
.
globalCompositeOperation
=
'
source-over
'
;
ctx
.
lineWidth
=
1.0
;
ctx
.
lineCap
=
'
butt
'
;
ctx
.
filter
=
'
none
'
;
ctx
.
fillStyle
=
null
;
ctx
.
strokeStyle
=
null
;
}
_save
(
ctx
,
geom
,
command
)
{
ctx
.
save
();
}
_restore
(
ctx
,
geom
,
command
)
{
ctx
.
restore
();
}
_translate
(
ctx
,
geom
,
command
)
{
ctx
.
translate
(
command
[
1
],
command
[
2
]);
}
_scale
(
ctx
,
geom
,
command
)
{
ctx
.
translate
(
command
[
1
],
command
[
2
]);
}
_rotate
(
ctx
,
geom
,
command
)
{
ctx
.
rotate
(
command
[
1
]);
}
_skew
(
ctx
,
geom
,
command
)
{
ctx
.
translate
(
command
[
1
],
command
[
2
]);
}
_drawRect
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
rect
=
scanner
.
scanRect
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
ctx
.
beginPath
();
ctx
.
rect
(
rect
.
left
,
rect
.
top
,
rect
.
width
(),
rect
.
height
());
this
.
_strokeOrFill
(
ctx
,
paint
,
true
);
}
_drawRRect
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
rrect
=
scanner
.
scanRRect
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
this
.
_drawRRectPath
(
ctx
,
rrect
,
true
);
this
.
_strokeOrFill
(
ctx
,
paint
,
true
);
}
_drawDRRect
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
outer
=
scanner
.
scanRRect
();
let
inner
=
scanner
.
scanRRect
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
this
.
_drawRRectPath
(
ctx
,
outer
,
true
);
this
.
_drawRRectPathReverse
(
ctx
,
inner
,
false
);
this
.
_strokeOrFill
(
ctx
,
paint
,
true
);
}
_drawRRectPath
(
ctx
,
rrect
,
startNewPath
)
{
// TODO(mdebbar): there's a bug in this code, it doesn't correctly handle
// the case when the radius is greater than the width of the
// rect. When we fix that in BitmapCanvas, we need to fix it
// here too.
// To draw the rounded rectangle, perform the following 8 steps:
// 1. draw the line for the top
// 2. draw the arc for the top-right corner
// 3. draw the line for the right side
// 4. draw the arc for the bottom-right corner
// 5. draw the line for the bottom of the rectangle
// 6. draw the arc for the bottom-left corner
// 7. draw the line for the left side
// 8. draw the arc for the top-left corner
//
// After drawing, the current point will be the left side of the top of the
// rounded rectangle (after the corner).
// TODO(het): Confirm that this is the end point in Flutter for RRect
if
(
startNewPath
)
{
ctx
.
beginPath
();
}
ctx
.
moveTo
(
rrect
.
left
+
rrect
.
trRadiusX
,
rrect
.
top
);
// Top side and top-right corner
ctx
.
lineTo
(
rrect
.
right
-
rrect
.
trRadiusX
,
rrect
.
top
);
ctx
.
ellipse
(
rrect
.
right
-
rrect
.
trRadiusX
,
rrect
.
top
+
rrect
.
trRadiusY
,
rrect
.
trRadiusX
,
rrect
.
trRadiusY
,
0
,
1.5
*
Math
.
PI
,
2.0
*
Math
.
PI
,
false
,
);
// Right side and bottom-right corner
ctx
.
lineTo
(
rrect
.
right
,
rrect
.
bottom
-
rrect
.
brRadiusY
);
ctx
.
ellipse
(
rrect
.
right
-
rrect
.
brRadiusX
,
rrect
.
bottom
-
rrect
.
brRadiusY
,
rrect
.
brRadiusX
,
rrect
.
brRadiusY
,
0
,
0
,
0.5
*
Math
.
PI
,
false
,
);
// Bottom side and bottom-left corner
ctx
.
lineTo
(
rrect
.
left
+
rrect
.
blRadiusX
,
rrect
.
bottom
);
ctx
.
ellipse
(
rrect
.
left
+
rrect
.
blRadiusX
,
rrect
.
bottom
-
rrect
.
blRadiusY
,
rrect
.
blRadiusX
,
rrect
.
blRadiusY
,
0
,
0.5
*
Math
.
PI
,
Math
.
PI
,
false
,
);
// Left side and top-left corner
ctx
.
lineTo
(
rrect
.
left
,
rrect
.
top
+
rrect
.
tlRadiusY
);
ctx
.
ellipse
(
rrect
.
left
+
rrect
.
tlRadiusX
,
rrect
.
top
+
rrect
.
tlRadiusY
,
rrect
.
tlRadiusX
,
rrect
.
tlRadiusY
,
0
,
Math
.
PI
,
1.5
*
Math
.
PI
,
false
,
);
}
_drawRRectPathReverse
(
ctx
,
rrect
,
startNewPath
)
{
// Draw the rounded rectangle, counterclockwise.
ctx
.
moveTo
(
rrect
.
right
-
rrect
.
trRadiusX
,
rrect
.
top
);
if
(
startNewPath
)
{
ctx
.
beginPath
();
}
// Top side and top-left corner
ctx
.
lineTo
(
rrect
.
left
+
rrect
.
tlRadiusX
,
rrect
.
top
);
ctx
.
ellipse
(
rrect
.
left
+
rrect
.
tlRadiusX
,
rrect
.
top
+
rrect
.
tlRadiusY
,
rrect
.
tlRadiusX
,
rrect
.
tlRadiusY
,
0
,
1.5
*
Math
.
PI
,
Math
.
PI
,
true
,
);
// Left side and bottom-left corner
ctx
.
lineTo
(
rrect
.
left
,
rrect
.
bottom
-
rrect
.
blRadiusY
);
ctx
.
ellipse
(
rrect
.
left
+
rrect
.
blRadiusX
,
rrect
.
bottom
-
rrect
.
blRadiusY
,
rrect
.
blRadiusX
,
rrect
.
blRadiusY
,
0
,
Math
.
PI
,
0.5
*
Math
.
PI
,
true
,
);
// Bottom side and bottom-right corner
ctx
.
lineTo
(
rrect
.
right
-
rrect
.
brRadiusX
,
rrect
.
bottom
);
ctx
.
ellipse
(
rrect
.
right
-
rrect
.
brRadiusX
,
rrect
.
bottom
-
rrect
.
brRadiusY
,
rrect
.
brRadiusX
,
rrect
.
brRadiusY
,
0
,
0.5
*
Math
.
PI
,
0
,
true
,
);
// Right side and top-right corner
ctx
.
lineTo
(
rrect
.
right
,
rrect
.
top
+
rrect
.
trRadiusY
);
ctx
.
ellipse
(
rrect
.
right
-
rrect
.
trRadiusX
,
rrect
.
top
+
rrect
.
trRadiusY
,
rrect
.
trRadiusX
,
rrect
.
trRadiusY
,
0
,
0
,
1.5
*
Math
.
PI
,
true
,
);
}
_clipRect
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
rect
=
scanner
.
scanRect
();
ctx
.
beginPath
();
ctx
.
rect
(
rect
.
left
,
rect
.
top
,
rect
.
width
(),
rect
.
height
());
ctx
.
clip
();
}
_clipRRect
(
ctx
,
geom
,
command
)
{
let
path
=
new
Path
([]);
let
commands
=
[
new
RRectCommand
(
command
[
1
])];
path
.
subpaths
.
push
(
new
Subpath
(
commands
));
this
.
_runPath
(
ctx
,
path
);
ctx
.
clip
();
}
_clipPath
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
path
=
scanner
.
scanPath
();
this
.
_runPath
(
ctx
,
path
);
ctx
.
clip
();
}
_drawCircle
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
dx
=
scanner
.
scanNumber
();
let
dy
=
scanner
.
scanNumber
();
let
radius
=
scanner
.
scanNumber
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
ctx
.
beginPath
();
ctx
.
ellipse
(
dx
,
dy
,
radius
,
radius
,
0
,
0
,
2.0
*
Math
.
PI
,
false
);
this
.
_strokeOrFill
(
ctx
,
paint
,
true
);
}
_drawOval
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
rect
=
scanner
.
scanRect
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
ctx
.
beginPath
();
ctx
.
ellipse
(
(
rect
.
left
+
rect
.
right
)
/
2
,
(
rect
.
top
+
rect
.
bottom
)
/
2
,
rect
.
width
/
2
,
rect
.
height
/
2
,
0
,
0
,
2.0
*
Math
.
PI
,
false
);
this
.
_strokeOrFill
(
ctx
,
paint
,
true
);
}
_drawPath
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
path
=
scanner
.
scanPath
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
this
.
_runPath
(
ctx
,
path
);
this
.
_strokeOrFill
(
ctx
,
paint
,
true
);
}
_drawShadow
(
ctx
,
geom
,
command
)
{
// TODO: this is mostly a stub; implement properly.
let
scanner
=
_scanCommand
(
command
);
let
path
=
scanner
.
scanPath
();
let
color
=
scanner
.
scanArray
();
let
elevation
=
scanner
.
scanNumber
();
let
transparentOccluder
=
scanner
.
scanBool
();
let
shadows
=
_computeShadowsForElevation
(
elevation
,
color
);
for
(
let
i
=
0
;
i
<
shadows
.
length
;
i
++
)
{
let
shadow
=
shadows
[
i
];
let
paint
=
new
Paint
(
null
,
// blendMode
PaintingStyle
.
fill
,
// style
1.0
,
// strokeWidth
null
,
// strokeCap
true
,
// isAntialias
shadow
.
color
,
// color
null
,
// shader
[
BlurStyle
.
normal
,
shadow
.
blur
],
// maskFilter
null
,
// filterQuality
null
// colorFilter
);
ctx
.
save
();
ctx
.
translate
(
shadow
.
offsetX
,
shadow
.
offsetY
);
this
.
_applyPaint
(
ctx
,
paint
);
this
.
_runPath
(
ctx
,
path
,
true
);
this
.
_strokeOrFill
(
ctx
,
paint
,
false
);
ctx
.
restore
();
}
this
.
_resetPaint
(
ctx
);
}
_runPath
(
ctx
,
path
)
{
ctx
.
beginPath
();
for
(
let
i
=
0
;
i
<
path
.
subpaths
.
length
;
i
++
)
{
let
subpath
=
path
.
subpaths
[
i
];
for
(
let
j
=
0
;
j
<
subpath
.
commands
.
length
;
j
++
)
{
let
command
=
subpath
.
commands
[
j
];
switch
(
command
.
type
())
{
case
PathCommandType
.
bezierCurveTo
:
ctx
.
bezierCurveTo
(
command
.
x1
,
command
.
y1
,
command
.
x2
,
command
.
y2
,
command
.
x3
,
command
.
y3
);
break
;
case
PathCommandType
.
close
:
ctx
.
closePath
();
break
;
case
PathCommandType
.
ellipse
:
ctx
.
ellipse
(
command
.
x
,
command
.
y
,
command
.
radiusX
,
command
.
radiusY
,
command
.
rotation
,
command
.
startAngle
,
command
.
endAngle
,
command
.
anticlockwise
);
break
;
case
PathCommandType
.
lineTo
:
ctx
.
lineTo
(
command
.
x
,
command
.
y
);
break
;
case
PathCommandType
.
moveTo
:
ctx
.
moveTo
(
command
.
x
,
command
.
y
);
break
;
case
PathCommandType
.
rrect
:
this
.
_drawRRectPath
(
ctx
,
command
.
rrect
,
false
);
break
;
case
PathCommandType
.
rect
:
ctx
.
rect
(
command
.
x
,
command
.
y
,
command
.
width
,
command
.
height
);
break
;
case
PathCommandType
.
quadraticCurveTo
:
ctx
.
quadraticCurveTo
(
command
.
x1
,
command
.
y1
,
command
.
x2
,
command
.
y2
);
break
;
default
:
throw
new
Error
(
`Unknown path command
${
command
.
type
()}
`
);
}
}
}
}
_drawColor
(
ctx
,
geom
,
command
)
{
ctx
.
globalCompositeOperation
=
_stringForBlendMode
(
command
[
2
]);
ctx
.
fillStyle
=
command
[
1
];
// Fill a virtually infinite rect with the color.
//
// We can't use (0, 0, width, height) because the current transform can
// cause it to not fill the entire clip.
ctx
.
fillRect
(
-
10000
,
-
10000
,
20000
,
20000
);
this
.
_resetPaint
(
ctx
);
}
_drawLine
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
p1dx
=
scanner
.
scanNumber
();
let
p1dy
=
scanner
.
scanNumber
();
let
p2dx
=
scanner
.
scanNumber
();
let
p2dy
=
scanner
.
scanNumber
();
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
ctx
.
beginPath
();
ctx
.
moveTo
(
p1dx
,
p1dy
);
ctx
.
lineTo
(
p2dx
,
p2dy
);
ctx
.
stroke
();
this
.
_resetPaint
(
ctx
);
}
_drawPaint
(
ctx
,
geom
,
command
)
{
let
scanner
=
_scanCommand
(
command
);
let
paint
=
scanner
.
scanPaint
();
this
.
_applyPaint
(
ctx
,
paint
);
ctx
.
beginPath
();
// Fill a virtually infinite rect with the color.
//
// We can't use (0, 0, width, height) because the current transform can
// cause it to not fill the entire clip.
ctx
.
fillRect
(
-
10000
,
-
10000
,
20000
,
20000
);
this
.
_resetPaint
(
ctx
);
}
}
function
_scanCommand
(
command
)
{
return
new
CommandScanner
(
command
);
}
const
PaintingStyle
=
{
fill
:
0
,
stroke
:
1
,
};
/// A singleton used to parse serialized commands.
class
CommandScanner
{
constructor
(
command
)
{
// Skip the first element, which is always the command ID.
this
.
index
=
1
;
this
.
command
=
command
;
}
scanRect
()
{
let
rect
=
this
.
command
[
this
.
index
++
];
return
new
Rect
(
rect
[
0
],
rect
[
1
],
rect
[
2
],
rect
[
3
]);
}
scanRRect
()
{
let
rrect
=
this
.
command
[
this
.
index
++
];
return
new
RRect
(
rrect
[
0
],
rrect
[
1
],
rrect
[
2
],
rrect
[
3
],
rrect
[
4
],
rrect
[
5
],
rrect
[
6
],
rrect
[
7
],
rrect
[
8
],
rrect
[
9
],
rrect
[
10
],
rrect
[
11
]);
}
scanPaint
()
{
let
paint
=
this
.
command
[
this
.
index
++
];
return
new
Paint
(
paint
[
0
],
paint
[
1
],
paint
[
2
],
paint
[
3
],
paint
[
4
],
paint
[
5
],
paint
[
6
],
paint
[
7
],
paint
[
8
],
paint
[
9
]);
}
scanNumber
()
{
return
this
.
command
[
this
.
index
++
];
}
scanString
()
{
return
this
.
command
[
this
.
index
++
];
}
scanBool
()
{
return
this
.
command
[
this
.
index
++
];
}
scanPath
()
{
let
subpaths
=
this
.
command
[
this
.
index
++
];
return
new
Path
(
subpaths
);
}
scanArray
()
{
return
this
.
command
[
this
.
index
++
];
}
}
class
Rect
{
constructor
(
left
,
top
,
right
,
bottom
)
{
this
.
left
=
left
;
this
.
top
=
top
;
this
.
right
=
right
;
this
.
bottom
=
bottom
;
}
width
()
{
return
this
.
right
-
this
.
left
;
}
height
()
{
return
this
.
bottom
-
this
.
top
;
}
}
class
RRect
{
constructor
(
left
,
top
,
right
,
bottom
,
tlRadiusX
,
tlRadiusY
,
trRadiusX
,
trRadiusY
,
brRadiusX
,
brRadiusY
,
blRadiusX
,
blRadiusY
)
{
this
.
left
=
left
;
this
.
top
=
top
;
this
.
right
=
right
;
this
.
bottom
=
bottom
;
this
.
tlRadiusX
=
tlRadiusX
;
this
.
tlRadiusY
=
tlRadiusY
;
this
.
trRadiusX
=
trRadiusX
;
this
.
trRadiusY
=
trRadiusY
;
this
.
brRadiusX
=
brRadiusX
;
this
.
brRadiusY
=
brRadiusY
;
this
.
blRadiusX
=
blRadiusX
;
this
.
blRadiusY
=
blRadiusY
;
}
tallMiddleRect
()
{
let
leftRadius
=
Math
.
max
(
this
.
blRadiusX
,
this
.
tlRadiusX
);
let
rightRadius
=
Math
.
max
(
this
.
trRadiusX
,
this
.
brRadiusX
);
return
new
Rect
(
this
.
left
+
leftRadius
,
this
.
top
,
this
.
right
-
rightRadius
,
this
.
bottom
);
}
middleRect
()
{
let
leftRadius
=
Math
.
max
(
this
.
blRadiusX
,
this
.
tlRadiusX
);
let
topRadius
=
Math
.
max
(
this
.
tlRadiusY
,
this
.
trRadiusY
);
let
rightRadius
=
Math
.
max
(
this
.
trRadiusX
,
this
.
brRadiusX
);
let
bottomRadius
=
Math
.
max
(
this
.
brRadiusY
,
this
.
blRadiusY
);
return
new
Rect
(
this
.
left
+
leftRadius
,
this
.
top
+
topRadius
,
this
.
right
-
rightRadius
,
this
.
bottom
-
bottomRadius
);
}
wideMiddleRect
()
{
let
topRadius
=
Math
.
max
(
this
.
tlRadiusY
,
this
.
trRadiusY
);
let
bottomRadius
=
Math
.
max
(
this
.
brRadiusY
,
this
.
blRadiusY
);
return
new
Rect
(
this
.
left
,
this
.
top
+
topRadius
,
this
.
right
,
this
.
bottom
-
bottomRadius
);
}
}
class
Paint
{
constructor
(
blendMode
,
style
,
strokeWidth
,
strokeCap
,
isAntialias
,
color
,
shader
,
maskFilter
,
filterQuality
,
colorFilter
)
{
this
.
blendMode
=
blendMode
;
this
.
style
=
style
;
this
.
strokeWidth
=
strokeWidth
;
this
.
strokeCap
=
strokeCap
;
this
.
isAntialias
=
isAntialias
;
this
.
color
=
color
;
this
.
shader
=
_deserializeShader
(
shader
);
// TODO: deserialize
this
.
maskFilter
=
maskFilter
;
this
.
filterQuality
=
filterQuality
;
this
.
colorFilter
=
colorFilter
;
// TODO: deserialize
}
}
function
_deserializeShader
(
data
)
{
if
(
!
data
)
{
return
null
;
}
switch
(
data
[
0
])
{
case
1
:
return
new
GradientLinear
(
data
);
default
:
throw
new
Error
(
`Shader type not supported:
${
data
}
`
);
}
}
class
GradientLinear
{
constructor
(
data
)
{
this
.
fromX
=
data
[
1
];
this
.
fromY
=
data
[
2
];
this
.
toX
=
data
[
3
];
this
.
toY
=
data
[
4
];
this
.
colors
=
data
[
5
];
this
.
colorStops
=
data
[
6
];
this
.
tileMode
=
data
[
7
];
}
createPaintStyle
(
ctx
)
{
let
gradient
=
ctx
.
createLinearGradient
(
this
.
fromX
,
this
.
fromY
,
this
.
toX
,
this
.
toY
);
if
(
this
.
colorStops
==
null
)
{
gradient
.
addColorStop
(
0
,
this
.
colors
[
0
]);
gradient
.
addColorStop
(
1
,
this
.
colors
[
1
]);
return
gradient
;
}
for
(
let
i
=
0
;
i
<
this
.
colors
.
length
;
i
++
)
{
gradient
.
addColorStop
(
this
.
colorStops
[
i
],
this
.
colors
[
i
]);
}
return
gradient
;
}
}
const
BlendMode
=
{
clear
:
0
,
src
:
1
,
dst
:
2
,
srcOver
:
3
,
dstOver
:
4
,
srcIn
:
5
,
dstIn
:
6
,
srcOut
:
7
,
dstOut
:
8
,
srcATop
:
9
,
dstATop
:
10
,
xor
:
11
,
plus
:
12
,
modulate
:
13
,
screen
:
14
,
overlay
:
15
,
darken
:
16
,
lighten
:
17
,
colorDodge
:
18
,
colorBurn
:
19
,
hardLight
:
20
,
softLight
:
21
,
difference
:
22
,
exclusion
:
23
,
multiply
:
24
,
hue
:
25
,
saturation
:
26
,
color
:
27
,
luminosity
:
28
,
};
function
_stringForBlendMode
(
blendMode
)
{
if
(
blendMode
==
null
)
return
null
;
switch
(
blendMode
)
{
case
BlendMode
.
srcOver
:
return
'
source-over
'
;
case
BlendMode
.
srcIn
:
return
'
source-in
'
;
case
BlendMode
.
srcOut
:
return
'
source-out
'
;
case
BlendMode
.
srcATop
:
return
'
source-atop
'
;
case
BlendMode
.
dstOver
:
return
'
destination-over
'
;
case
BlendMode
.
dstIn
:
return
'
destination-in
'
;
case
BlendMode
.
dstOut
:
return
'
destination-out
'
;
case
BlendMode
.
dstATop
:
return
'
destination-atop
'
;
case
BlendMode
.
plus
:
return
'
lighten
'
;
case
BlendMode
.
src
:
return
'
copy
'
;
case
BlendMode
.
xor
:
return
'
xor
'
;
case
BlendMode
.
multiply
:
// Falling back to multiply, ignoring alpha channel.
// TODO(flutter_web): only used for debug, find better fallback for web.
case
BlendMode
.
modulate
:
return
'
multiply
'
;
case
BlendMode
.
screen
:
return
'
screen
'
;
case
BlendMode
.
overlay
:
return
'
overlay
'
;
case
BlendMode
.
darken
:
return
'
darken
'
;
case
BlendMode
.
lighten
:
return
'
lighten
'
;
case
BlendMode
.
colorDodge
:
return
'
color-dodge
'
;
case
BlendMode
.
colorBurn
:
return
'
color-burn
'
;
case
BlendMode
.
hardLight
:
return
'
hard-light
'
;
case
BlendMode
.
softLight
:
return
'
soft-light
'
;
case
BlendMode
.
difference
:
return
'
difference
'
;
case
BlendMode
.
exclusion
:
return
'
exclusion
'
;
case
BlendMode
.
hue
:
return
'
hue
'
;
case
BlendMode
.
saturation
:
return
'
saturation
'
;
case
BlendMode
.
color
:
return
'
color
'
;
case
BlendMode
.
luminosity
:
return
'
luminosity
'
;
default
:
throw
new
Error
(
'
Flutter web does not support the blend mode: $blendMode
'
);
}
}
const
StrokeCap
=
{
butt
:
0
,
round
:
1
,
square
:
2
,
};
function
_stringForStrokeCap
(
strokeCap
)
{
if
(
strokeCap
==
null
)
return
null
;
switch
(
strokeCap
)
{
case
StrokeCap
.
butt
:
return
'
butt
'
;
case
StrokeCap
.
round
:
return
'
round
'
;
case
StrokeCap
.
square
:
default
:
return
'
square
'
;
}
}
class
Path
{
constructor
(
serializedSubpaths
)
{
this
.
subpaths
=
[];
for
(
let
i
=
0
;
i
<
serializedSubpaths
.
length
;
i
++
)
{
let
subpath
=
serializedSubpaths
[
i
];
let
pathCommands
=
[];
for
(
let
j
=
0
;
j
<
subpath
.
length
;
j
++
)
{
let
pathCommand
=
subpath
[
j
];
switch
(
pathCommand
[
0
])
{
case
1
:
pathCommands
.
push
(
new
MoveTo
(
pathCommand
));
break
;
case
2
:
pathCommands
.
push
(
new
LineTo
(
pathCommand
));
break
;
case
3
:
pathCommands
.
push
(
new
Ellipse
(
pathCommand
));
break
;
case
4
:
pathCommands
.
push
(
new
QuadraticCurveTo
(
pathCommand
));
break
;
case
5
:
pathCommands
.
push
(
new
BezierCurveTo
(
pathCommand
));
break
;
case
6
:
pathCommands
.
push
(
new
RectCommand
(
pathCommand
));
break
;
case
7
:
pathCommands
.
push
(
new
RRectCommand
(
pathCommand
));
break
;
case
8
:
pathCommands
.
push
(
new
CloseCommand
());
break
;
default
:
throw
new
Error
(
`Unsupported path command:
${
pathCommand
}
`
);
}
}
this
.
subpaths
.
push
(
new
Subpath
(
pathCommands
));
}
}
}
class
Subpath
{
constructor
(
commands
)
{
this
.
commands
=
commands
;
}
}
class
MoveTo
{
constructor
(
data
)
{
this
.
x
=
data
[
1
];
this
.
y
=
data
[
2
];
}
type
()
{
return
PathCommandType
.
moveTo
;
}
}
class
LineTo
{
constructor
(
data
)
{
this
.
x
=
data
[
1
];
this
.
y
=
data
[
2
];
}
type
()
{
return
PathCommandType
.
lineTo
;
}
}
class
Ellipse
{
constructor
(
data
)
{
this
.
x
=
data
[
1
];
this
.
y
=
data
[
2
];
this
.
radiusX
=
data
[
3
];
this
.
radiusY
=
data
[
4
];
this
.
rotation
=
data
[
5
];
this
.
startAngle
=
data
[
6
];
this
.
endAngle
=
data
[
7
];
this
.
anticlockwise
=
data
[
8
];
}
type
()
{
return
PathCommandType
.
ellipse
;
}
}
class
QuadraticCurveTo
{
constructor
(
data
)
{
this
.
x1
=
data
[
1
];
this
.
y1
=
data
[
2
];
this
.
x2
=
data
[
3
];
this
.
y2
=
data
[
4
];
}
type
()
{
return
PathCommandType
.
quadraticCurveTo
;
}
}
class
BezierCurveTo
{
constructor
(
data
)
{
this
.
x1
=
data
[
1
];
this
.
y1
=
data
[
2
];
this
.
x2
=
data
[
3
];
this
.
y2
=
data
[
4
];
this
.
x3
=
data
[
5
];
this
.
y3
=
data
[
6
];
}
type
()
{
return
PathCommandType
.
bezierCurveTo
;
}
}
class
RectCommand
{
constructor
(
data
)
{
this
.
x
=
data
[
1
];
this
.
y
=
data
[
2
];
this
.
width
=
data
[
3
];
this
.
height
=
data
[
4
];
}
type
()
{
return
PathCommandType
.
rect
;
}
}
class
RRectCommand
{
constructor
(
data
)
{
let
scanner
=
_scanCommand
(
data
);
this
.
rrect
=
scanner
.
scanRRect
();
}
type
()
{
return
PathCommandType
.
rrect
;
}
}
class
CloseCommand
{
type
()
{
return
PathCommandType
.
close
;
}
}
class
CanvasShadow
{
constructor
(
offsetX
,
offsetY
,
blur
,
spread
,
color
)
{
this
.
offsetX
=
offsetX
;
this
.
offsetY
=
offsetY
;
this
.
blur
=
blur
;
this
.
spread
=
spread
;
this
.
color
=
color
;
}
}
const
_noShadows
=
[];
function
_computeShadowsForElevation
(
elevation
,
color
)
{
if
(
elevation
<=
0.0
)
{
return
_noShadows
;
}
else
if
(
elevation
<=
1.0
)
{
return
_computeShadowElevation
(
2
,
color
);
}
else
if
(
elevation
<=
2.0
)
{
return
_computeShadowElevation
(
4
,
color
);
}
else
if
(
elevation
<=
3.0
)
{
return
_computeShadowElevation
(
6
,
color
);
}
else
if
(
elevation
<=
4.0
)
{
return
_computeShadowElevation
(
8
,
color
);
}
else
if
(
elevation
<=
5.0
)
{
return
_computeShadowElevation
(
16
,
color
);
}
else
{
return
_computeShadowElevation
(
24
,
color
);
}
}
function
_computeShadowElevation
(
dp
,
color
)
{
// TODO(yjbanov): multiple shadows are very expensive. Find a more efficient
// method to render them.
let
red
=
color
[
1
];
let
green
=
color
[
2
];
let
blue
=
color
[
3
];
// let penumbraColor = `rgba(${red}, ${green}, ${blue}, 0.14)`;
// let ambientShadowColor = `rgba(${red}, ${green}, ${blue}, 0.12)`;
let
umbraColor
=
`rgba(
${
red
}
,
${
green
}
,
${
blue
}
, 0.2)`
;
let
result
=
[];
if
(
dp
===
2
)
{
// result.push(new CanvasShadow(0.0, 2.0, 1.0, 0.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 3.0, 0.5, -2.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
1.0
,
2.5
,
0.0
,
umbraColor
));
}
else
if
(
dp
===
3
)
{
// result.push(new CanvasShadow(0.0, 1.5, 4.0, 0.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 3.0, 1.5, -2.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
1.0
,
4.0
,
0.0
,
umbraColor
));
}
else
if
(
dp
===
4
)
{
// result.push(new CanvasShadow(0.0, 4.0, 2.5, 0.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 1.0, 5.0, 0.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
2.0
,
2.0
,
-
1.0
,
umbraColor
));
}
else
if
(
dp
===
6
)
{
// result.push(new CanvasShadow(0.0, 6.0, 5.0, 0.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 1.0, 9.0, 0.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
3.0
,
2.5
,
-
1.0
,
umbraColor
));
}
else
if
(
dp
===
8
)
{
// result.push(new CanvasShadow(0.0, 4.0, 10.0, 1.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 3.0, 7.0, 2.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
5.0
,
2.5
,
-
3.0
,
umbraColor
));
}
else
if
(
dp
===
12
)
{
// result.push(new CanvasShadow(0.0, 12.0, 8.5, 2.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 5.0, 11.0, 4.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
7.0
,
4.0
,
-
4.0
,
umbraColor
));
}
else
if
(
dp
===
16
)
{
// result.push(new CanvasShadow(0.0, 16.0, 12.0, 2.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 6.0, 15.0, 5.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
0.0
,
5.0
,
-
5.0
,
umbraColor
));
}
else
{
// result.push(new CanvasShadow(0.0, 24.0, 18.0, 3.0, penumbraColor));
// result.push(new CanvasShadow(0.0, 9.0, 23.0, 8.0, ambientShadowColor));
result
.
push
(
new
CanvasShadow
(
0.0
,
11.0
,
7.5
,
-
7.0
,
umbraColor
));
}
return
result
;
}
const
PathCommandType
=
{
moveTo
:
0
,
lineTo
:
1
,
ellipse
:
2
,
close
:
3
,
quadraticCurveTo
:
4
,
bezierCurveTo
:
5
,
rect
:
6
,
rrect
:
7
,
};
const
TileMode
=
{
clamp
:
0
,
repeated
:
1
,
};
const
BlurStyle
=
{
normal
:
0
,
solid
:
1
,
outer
:
2
,
inner
:
3
,
};
/// This makes the painter available as "background-image: paint(flt)".
registerPaint
(
'
flt
'
,
FlutterPainter
);
lib/web_ui/lib/src/engine.dart
浏览文件 @
5c8a6260
...
...
@@ -64,7 +64,6 @@ part 'engine/dom_renderer.dart';
part
'engine/engine_canvas.dart'
;
part
'engine/frame_reference.dart'
;
part
'engine/history.dart'
;
part
'engine/houdini_canvas.dart'
;
part
'engine/html_image_codec.dart'
;
part
'engine/keyboard.dart'
;
part
'engine/mouse_cursor.dart'
;
...
...
lib/web_ui/lib/src/engine/bitmap_canvas.dart
浏览文件 @
5c8a6260
...
...
@@ -193,9 +193,9 @@ class BitmapCanvas extends EngineCanvas {
///
/// See also:
///
/// * [Persisted
Standard
Picture._applyBitmapPaint] which uses this method to
/// * [PersistedPicture._applyBitmapPaint] which uses this method to
/// decide whether to reuse this canvas or not.
/// * [Persisted
Standard
Picture._recycleCanvas] which also uses this method
/// * [PersistedPicture._recycleCanvas] which also uses this method
/// for the same reason.
bool
isReusable
()
{
return
_devicePixelRatio
==
EngineWindow
.
browserDevicePixelRatio
;
...
...
lib/web_ui/lib/src/engine/color_filter.dart
浏览文件 @
5c8a6260
...
...
@@ -169,8 +169,4 @@ class EngineColorFilter implements ui.ColorFilter {
return
'Unknown ColorFilter type. This is an error. If you
\'
re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.'
;
}
}
List
<
dynamic
>
webOnlySerializeToCssPaint
()
{
throw
UnsupportedError
(
'ColorFilter for CSS paint not yet supported'
);
}
}
lib/web_ui/lib/src/engine/engine_canvas.dart
浏览文件 @
5c8a6260
...
...
@@ -282,3 +282,127 @@ html.Element _drawParagraphElement(
}
return
paragraphElement
;
}
class
_SaveElementStackEntry
{
_SaveElementStackEntry
({
required
this
.
savedElement
,
required
this
.
transform
,
});
final
html
.
Element
savedElement
;
final
Matrix4
transform
;
}
/// Provides save stack tracking functionality to implementations of
/// [EngineCanvas].
mixin
SaveElementStackTracking
on
EngineCanvas
{
static
final
Vector3
_unitZ
=
Vector3
(
0.0
,
0.0
,
1.0
);
final
List
<
_SaveElementStackEntry
>
_saveStack
=
<
_SaveElementStackEntry
>[];
/// The element at the top of the element stack, or [rootElement] if the stack
/// is empty.
html
.
Element
get
currentElement
=>
_elementStack
.
isEmpty
?
rootElement
:
_elementStack
.
last
;
/// The stack that maintains the DOM elements used to express certain paint
/// operations, such as clips.
final
List
<
html
.
Element
>
_elementStack
=
<
html
.
Element
>[];
/// Pushes the [element] onto the element stack for the purposes of applying
/// a paint effect using a DOM element, e.g. for clipping.
///
/// The [restore] method automatically pops the element off the stack.
void
pushElement
(
html
.
Element
element
)
{
_elementStack
.
add
(
element
);
}
/// Empties the save stack and the element stack, and resets the transform
/// and clip parameters.
///
/// Classes that override this method must call `super.clear()`.
@override
void
clear
()
{
_saveStack
.
clear
();
_elementStack
.
clear
();
_currentTransform
=
Matrix4
.
identity
();
}
/// The current transformation matrix.
Matrix4
get
currentTransform
=>
_currentTransform
;
Matrix4
_currentTransform
=
Matrix4
.
identity
();
/// Saves current clip and transform on the save stack.
///
/// Classes that override this method must call `super.save()`.
@override
void
save
()
{
_saveStack
.
add
(
_SaveElementStackEntry
(
savedElement:
currentElement
,
transform:
_currentTransform
.
clone
(),
));
}
/// Restores current clip and transform from the save stack.
///
/// Classes that override this method must call `super.restore()`.
@override
void
restore
()
{
if
(
_saveStack
.
isEmpty
)
{
return
;
}
final
_SaveElementStackEntry
entry
=
_saveStack
.
removeLast
();
_currentTransform
=
entry
.
transform
;
// Pop out of any clips.
while
(
currentElement
!=
entry
.
savedElement
)
{
_elementStack
.
removeLast
();
}
}
/// Multiplies the [currentTransform] matrix by a translation.
///
/// Classes that override this method must call `super.translate()`.
@override
void
translate
(
double
dx
,
double
dy
)
{
_currentTransform
.
translate
(
dx
,
dy
);
}
/// Scales the [currentTransform] matrix.
///
/// Classes that override this method must call `super.scale()`.
@override
void
scale
(
double
sx
,
double
sy
)
{
_currentTransform
.
scale
(
sx
,
sy
);
}
/// Rotates the [currentTransform] matrix.
///
/// Classes that override this method must call `super.rotate()`.
@override
void
rotate
(
double
radians
)
{
_currentTransform
.
rotate
(
_unitZ
,
radians
);
}
/// Skews the [currentTransform] matrix.
///
/// Classes that override this method must call `super.skew()`.
@override
void
skew
(
double
sx
,
double
sy
)
{
// DO NOT USE Matrix4.skew(sx, sy)! It treats sx and sy values as radians,
// but in our case they are transform matrix values.
final
Matrix4
skewMatrix
=
Matrix4
.
identity
();
final
Float32List
storage
=
skewMatrix
.
storage
;
storage
[
1
]
=
sy
;
storage
[
4
]
=
sx
;
_currentTransform
.
multiply
(
skewMatrix
);
}
/// Multiplies the [currentTransform] matrix by another matrix.
///
/// Classes that override this method must call `super.transform()`.
@override
void
transform
(
Float32List
matrix4
)
{
_currentTransform
.
multiply
(
Matrix4
.
fromFloat32List
(
matrix4
));
}
}
lib/web_ui/lib/src/engine/houdini_canvas.dart
已删除
100644 → 0
浏览文件 @
2ac5b346
// 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.
// TODO(yjbanov): optimization opportunities (see also houdini_painter.js)
// - collapse non-drawing paint operations
// - avoid producing DOM-based clips if there is no text
// - evaluate using stylesheets for static CSS properties
// - evaluate reusing houdini canvases
// @dart = 2.10
part of
engine
;
/// A canvas that renders to a combination of HTML DOM and CSS Custom Paint API.
///
/// This canvas produces paint commands for houdini_painter.js to apply. This
/// class must be kept in sync with houdini_painter.js.
class
HoudiniCanvas
extends
EngineCanvas
with
SaveElementStackTracking
{
@override
final
html
.
Element
rootElement
=
html
.
Element
.
tag
(
'flt-houdini'
);
/// The rectangle positioned relative to the parent layer's coordinate system
/// where this canvas paints.
///
/// Painting outside the bounds of this rectangle is cropped.
final
ui
.
Rect
?
bounds
;
HoudiniCanvas
(
this
.
bounds
)
{
// TODO(yjbanov): would it be faster to specify static values in a
// stylesheet and let the browser apply them?
rootElement
.
style
..
position
=
'absolute'
..
top
=
'0'
..
left
=
'0'
..
width
=
'
${bounds!.size.width}
px'
..
height
=
'
${bounds!.size.height}
px'
..
backgroundImage
=
'paint(flt)'
;
}
/// Prepare to reuse this canvas by clearing it's current contents.
@override
void
clear
()
{
super
.
clear
();
_serializedCommands
=
<
List
<
dynamic
>>[];
// TODO(yjbanov): we should measure if reusing old elements is beneficial.
domRenderer
.
clearDom
(
rootElement
);
}
/// Paint commands serialized for sending to the CSS custom painter.
List
<
List
<
dynamic
>>
_serializedCommands
=
<
List
<
dynamic
>>[];
void
apply
(
PaintCommand
command
)
{
// Some commands are applied purely in HTML DOM and do not need to be
// serialized.
if
(
command
is
!
PaintDrawParagraph
&&
command
is
!
PaintDrawImageRect
&&
command
is
!
PaintTransform
)
{
command
.
serializeToCssPaint
(
_serializedCommands
);
}
command
.
apply
(
this
);
}
/// Sends the paint commands to the CSS custom painter for painting.
void
commit
()
{
if
(
_serializedCommands
.
isNotEmpty
)
{
rootElement
.
style
.
setProperty
(
'--flt'
,
json
.
encode
(
_serializedCommands
));
}
else
{
rootElement
.
style
.
removeProperty
(
'--flt'
);
}
}
@override
void
clipRect
(
ui
.
Rect
rect
)
{
final
html
.
Element
clip
=
html
.
Element
.
tag
(
'flt-clip-rect'
);
final
String
cssTransform
=
matrix4ToCssTransform
(
transformWithOffset
(
currentTransform
,
ui
.
Offset
(
rect
.
left
,
rect
.
top
)));
clip
.
style
..
overflow
=
'hidden'
..
position
=
'absolute'
..
transform
=
cssTransform
..
width
=
'
${rect.width}
px'
..
height
=
'
${rect.height}
px'
;
// The clipping element will translate the coordinate system as well, which
// is not what a clip should do. To offset that we translate in the opposite
// direction.
super
.
translate
(-
rect
.
left
,
-
rect
.
top
);
currentElement
.
append
(
clip
);
pushElement
(
clip
);
}
@override
void
clipRRect
(
ui
.
RRect
rrect
)
{
final
ui
.
Rect
outer
=
rrect
.
outerRect
;
if
(
rrect
.
isRect
)
{
clipRect
(
outer
);
return
;
}
final
html
.
Element
clip
=
html
.
Element
.
tag
(
'flt-clip-rrect'
);
final
html
.
CssStyleDeclaration
style
=
clip
.
style
;
style
..
overflow
=
'hidden'
..
position
=
'absolute'
..
transform
=
'translate(
${outer.left}
px,
${outer.right}
px)'
..
width
=
'
${outer.width}
px'
..
height
=
'
${outer.height}
px'
;
if
(
rrect
.
tlRadiusY
==
rrect
.
tlRadiusX
)
{
style
.
borderTopLeftRadius
=
'
${rrect.tlRadiusX}
px'
;
}
else
{
style
.
borderTopLeftRadius
=
'
${rrect.tlRadiusX}
px
${rrect.tlRadiusY}
px'
;
}
if
(
rrect
.
trRadiusY
==
rrect
.
trRadiusX
)
{
style
.
borderTopRightRadius
=
'
${rrect.trRadiusX}
px'
;
}
else
{
style
.
borderTopRightRadius
=
'
${rrect.trRadiusX}
px
${rrect.trRadiusY}
px'
;
}
if
(
rrect
.
brRadiusY
==
rrect
.
brRadiusX
)
{
style
.
borderBottomRightRadius
=
'
${rrect.brRadiusX}
px'
;
}
else
{
style
.
borderBottomRightRadius
=
'
${rrect.brRadiusX}
px
${rrect.brRadiusY}
px'
;
}
if
(
rrect
.
blRadiusY
==
rrect
.
blRadiusX
)
{
style
.
borderBottomLeftRadius
=
'
${rrect.blRadiusX}
px'
;
}
else
{
style
.
borderBottomLeftRadius
=
'
${rrect.blRadiusX}
px
${rrect.blRadiusY}
px'
;
}
// The clipping element will translate the coordinate system as well, which
// is not what a clip should do. To offset that we translate in the opposite
// direction.
super
.
translate
(-
rrect
.
left
,
-
rrect
.
top
);
currentElement
.
append
(
clip
);
pushElement
(
clip
);
}
@override
void
clipPath
(
ui
.
Path
path
)
{
// TODO(yjbanov): implement.
}
@override
void
drawColor
(
ui
.
Color
color
,
ui
.
BlendMode
blendMode
)
{
// Drawn using CSS Paint.
}
@override
void
drawLine
(
ui
.
Offset
p1
,
ui
.
Offset
p2
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawPaint
(
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawRect
(
ui
.
Rect
rect
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawRRect
(
ui
.
RRect
rrect
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawDRRect
(
ui
.
RRect
outer
,
ui
.
RRect
inner
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawOval
(
ui
.
Rect
rect
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawCircle
(
ui
.
Offset
c
,
double
radius
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawPath
(
ui
.
Path
path
,
SurfacePaintData
paint
)
{
// Drawn using CSS Paint.
}
@override
void
drawShadow
(
ui
.
Path
path
,
ui
.
Color
color
,
double
elevation
,
bool
transparentOccluder
)
{
// Drawn using CSS Paint.
}
@override
void
drawImage
(
ui
.
Image
image
,
ui
.
Offset
p
,
SurfacePaintData
paint
)
{
// TODO(yjbanov): implement.
}
@override
void
drawImageRect
(
ui
.
Image
image
,
ui
.
Rect
src
,
ui
.
Rect
dst
,
SurfacePaintData
paint
)
{
// TODO(yjbanov): implement src rectangle
final
HtmlImage
htmlImage
=
image
as
HtmlImage
;
final
html
.
Element
imageBox
=
html
.
Element
.
tag
(
'flt-img'
);
final
String
cssTransform
=
matrix4ToCssTransform
(
transformWithOffset
(
currentTransform
,
ui
.
Offset
(
dst
.
left
,
dst
.
top
)));
imageBox
.
style
..
position
=
'absolute'
..
transformOrigin
=
'0 0 0'
..
width
=
'
${dst.width.toInt()}
px'
..
height
=
'
${dst.height.toInt()}
px'
..
transform
=
cssTransform
..
backgroundImage
=
'url(
${htmlImage.imgElement.src}
)'
..
backgroundRepeat
=
'norepeat'
..
backgroundSize
=
'
${dst.width}
px
${dst.height}
px'
;
currentElement
.
append
(
imageBox
);
}
@override
void
drawParagraph
(
ui
.
Paragraph
paragraph
,
ui
.
Offset
offset
)
{
final
html
.
Element
paragraphElement
=
_drawParagraphElement
(
paragraph
as
EngineParagraph
,
offset
,
transform:
currentTransform
);
currentElement
.
append
(
paragraphElement
);
}
@override
void
drawVertices
(
ui
.
Vertices
vertices
,
ui
.
BlendMode
blendMode
,
SurfacePaintData
paint
)
{
// TODO(flutter_web): implement.
}
@override
void
drawPoints
(
ui
.
PointMode
pointMode
,
Float32List
points
,
SurfacePaintData
paint
)
{
// TODO(flutter_web): implement.
}
@override
void
endOfPaint
()
{}
}
class
_SaveElementStackEntry
{
_SaveElementStackEntry
({
required
this
.
savedElement
,
required
this
.
transform
,
});
final
html
.
Element
savedElement
;
final
Matrix4
transform
;
}
/// Provides save stack tracking functionality to implementations of
/// [EngineCanvas].
mixin
SaveElementStackTracking
on
EngineCanvas
{
static
final
Vector3
_unitZ
=
Vector3
(
0.0
,
0.0
,
1.0
);
final
List
<
_SaveElementStackEntry
>
_saveStack
=
<
_SaveElementStackEntry
>[];
/// The element at the top of the element stack, or [rootElement] if the stack
/// is empty.
html
.
Element
get
currentElement
=>
_elementStack
.
isEmpty
?
rootElement
:
_elementStack
.
last
;
/// The stack that maintains the DOM elements used to express certain paint
/// operations, such as clips.
final
List
<
html
.
Element
>
_elementStack
=
<
html
.
Element
>[];
/// Pushes the [element] onto the element stack for the purposes of applying
/// a paint effect using a DOM element, e.g. for clipping.
///
/// The [restore] method automatically pops the element off the stack.
void
pushElement
(
html
.
Element
element
)
{
_elementStack
.
add
(
element
);
}
/// Empties the save stack and the element stack, and resets the transform
/// and clip parameters.
///
/// Classes that override this method must call `super.clear()`.
@override
void
clear
()
{
_saveStack
.
clear
();
_elementStack
.
clear
();
_currentTransform
=
Matrix4
.
identity
();
}
/// The current transformation matrix.
Matrix4
get
currentTransform
=>
_currentTransform
;
Matrix4
_currentTransform
=
Matrix4
.
identity
();
/// Saves current clip and transform on the save stack.
///
/// Classes that override this method must call `super.save()`.
@override
void
save
()
{
_saveStack
.
add
(
_SaveElementStackEntry
(
savedElement:
currentElement
,
transform:
_currentTransform
.
clone
(),
));
}
/// Restores current clip and transform from the save stack.
///
/// Classes that override this method must call `super.restore()`.
@override
void
restore
()
{
if
(
_saveStack
.
isEmpty
)
{
return
;
}
final
_SaveElementStackEntry
entry
=
_saveStack
.
removeLast
();
_currentTransform
=
entry
.
transform
;
// Pop out of any clips.
while
(
currentElement
!=
entry
.
savedElement
)
{
_elementStack
.
removeLast
();
}
}
/// Multiplies the [currentTransform] matrix by a translation.
///
/// Classes that override this method must call `super.translate()`.
@override
void
translate
(
double
dx
,
double
dy
)
{
_currentTransform
.
translate
(
dx
,
dy
);
}
/// Scales the [currentTransform] matrix.
///
/// Classes that override this method must call `super.scale()`.
@override
void
scale
(
double
sx
,
double
sy
)
{
_currentTransform
.
scale
(
sx
,
sy
);
}
/// Rotates the [currentTransform] matrix.
///
/// Classes that override this method must call `super.rotate()`.
@override
void
rotate
(
double
radians
)
{
_currentTransform
.
rotate
(
_unitZ
,
radians
);
}
/// Skews the [currentTransform] matrix.
///
/// Classes that override this method must call `super.skew()`.
@override
void
skew
(
double
sx
,
double
sy
)
{
// DO NOT USE Matrix4.skew(sx, sy)! It treats sx and sy values as radians,
// but in our case they are transform matrix values.
final
Matrix4
skewMatrix
=
Matrix4
.
identity
();
final
Float32List
storage
=
skewMatrix
.
storage
;
storage
[
1
]
=
sy
;
storage
[
4
]
=
sx
;
_currentTransform
.
multiply
(
skewMatrix
);
}
/// Multiplies the [currentTransform] matrix by another matrix.
///
/// Classes that override this method must call `super.transform()`.
@override
void
transform
(
Float32List
matrix4
)
{
_currentTransform
.
multiply
(
Matrix4
.
fromFloat32List
(
matrix4
));
}
}
lib/web_ui/lib/src/engine/rrect_renderer.dart
浏览文件 @
5c8a6260
...
...
@@ -7,7 +7,6 @@ part of engine;
/// Renders an RRect using path primitives.
abstract
class
_RRectRenderer
{
// TODO(mdebbar): Backport the overlapping corners fix to houdini_painter.js
// To draw the rounded rectangle, perform the following steps:
// 0. Ensure border radius don't overlap
// 1. Flip left,right top,bottom since web doesn't support flipped
...
...
lib/web_ui/lib/src/engine/surface/path/path.dart
浏览文件 @
5c8a6260
...
...
@@ -1542,12 +1542,6 @@ class SurfacePath implements ui.Path {
ui
.
Rect
?
get
webOnlyPathAsCircle
=>
pathRef
.
isOval
==
-
1
?
null
:
pathRef
.
getBounds
();
/// Serializes this path to a value that's sent to a CSS custom painter for
/// painting.
List
<
dynamic
>
webOnlySerializeToCssPaint
()
{
throw
UnimplementedError
();
}
/// Returns if Path is empty.
/// Empty Path may have FillType but has no points, verbs or weights.
/// Constructor, reset and rewind makes SkPath empty.
...
...
lib/web_ui/lib/src/engine/surface/picture.dart
浏览文件 @
5c8a6260
...
...
@@ -71,292 +71,9 @@ void _recycleCanvas(EngineCanvas? canvas) {
}
}
/// Signature of a function that instantiates a [PersistedPicture].
typedef
PersistedPictureFactory
=
PersistedPicture
Function
(
double
dx
,
double
dy
,
ui
.
Picture
picture
,
int
hints
,
);
/// Function used by the [SceneBuilder] to instantiate a picture layer.
PersistedPictureFactory
persistedPictureFactory
=
standardPictureFactory
;
/// Instantiates an implementation of a picture layer that uses DOM, CSS, and
/// 2D canvas for painting.
PersistedStandardPicture
standardPictureFactory
(
double
dx
,
double
dy
,
ui
.
Picture
picture
,
int
hints
)
{
return
PersistedStandardPicture
(
dx
,
dy
,
picture
,
hints
);
}
/// Instantiates an implementation of a picture layer that uses CSS Paint API
/// (part of Houdini) for painting.
PersistedHoudiniPicture
houdiniPictureFactory
(
double
dx
,
double
dy
,
ui
.
Picture
picture
,
int
hints
)
{
return
PersistedHoudiniPicture
(
dx
,
dy
,
picture
,
hints
);
}
class
PersistedHoudiniPicture
extends
PersistedPicture
{
PersistedHoudiniPicture
(
double
dx
,
double
dy
,
ui
.
Picture
picture
,
int
hints
)
:
super
(
dx
,
dy
,
picture
as
EnginePicture
,
hints
)
{
if
(!
_cssPainterRegistered
)
{
_registerCssPainter
();
}
}
static
bool
_cssPainterRegistered
=
false
;
@override
double
matchForUpdate
(
PersistedPicture
existingSurface
)
{
// Houdini is display list-based so all pictures are cheap to repaint.
// However, if the picture hasn't changed at all then it's completely
// free.
return
existingSurface
.
picture
==
picture
?
0.0
:
1.0
;
}
static
void
_registerCssPainter
()
{
_cssPainterRegistered
=
true
;
final
dynamic
css
=
js_util
.
getProperty
(
html
.
window
,
'CSS'
);
final
dynamic
paintWorklet
=
js_util
.
getProperty
(
css
,
'paintWorklet'
);
if
(
paintWorklet
==
null
)
{
html
.
window
.
console
.
warn
(
'WARNING: CSS.paintWorklet not available. Paint worklets are only '
'supported on sites served from https:// or http://localhost.'
);
return
;
}
js_util
.
callMethod
(
paintWorklet
,
'addModule'
,
<
dynamic
>[
'/packages/flutter_web_ui/assets/houdini_painter.js'
,
],
);
}
/// Houdini does not paint to bitmap.
@override
int
get
bitmapPixelCount
=>
0
;
@override
void
applyPaint
(
EngineCanvas
?
oldCanvas
)
{
_recycleCanvas
(
oldCanvas
);
final
HoudiniCanvas
canvas
=
HoudiniCanvas
(
_optimalLocalCullRect
);
_canvas
=
canvas
;
domRenderer
.
clearDom
(
rootElement
!);
rootElement
!.
append
(
_canvas
!.
rootElement
);
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
canvas
.
commit
();
}
}
class
PersistedStandardPicture
extends
PersistedPicture
{
PersistedStandardPicture
(
double
dx
,
double
dy
,
ui
.
Picture
picture
,
int
hints
)
:
super
(
dx
,
dy
,
picture
as
EnginePicture
,
hints
);
@override
double
matchForUpdate
(
PersistedStandardPicture
existingSurface
)
{
if
(
existingSurface
.
picture
==
picture
)
{
// Picture is the same, return perfect score.
return
0.0
;
}
if
(!
existingSurface
.
picture
.
recordingCanvas
!.
didDraw
)
{
// The previous surface didn't draw anything and therefore has no
// resources to reuse.
return
1.0
;
}
final
bool
didRequireBitmap
=
existingSurface
.
picture
.
recordingCanvas
!.
hasArbitraryPaint
;
final
bool
requiresBitmap
=
picture
.
recordingCanvas
!.
hasArbitraryPaint
;
if
(
didRequireBitmap
!=
requiresBitmap
)
{
// Switching canvas types is always expensive.
return
1.0
;
}
else
if
(!
requiresBitmap
)
{
// Currently DomCanvas is always expensive to repaint, as we always throw
// out all the DOM we rendered before. This may change in the future, at
// which point we may return other values here.
return
1.0
;
}
else
{
final
BitmapCanvas
?
oldCanvas
=
existingSurface
.
_canvas
as
BitmapCanvas
?;
if
(
oldCanvas
==
null
)
{
// We did not allocate a canvas last time. This can happen when the
// picture is completely clipped out of the view.
return
1.0
;
}
else
if
(!
oldCanvas
.
doesFitBounds
(
_exactLocalCullRect
!))
{
// The canvas needs to be resized before painting.
return
1.0
;
}
else
{
final
int
newPixelCount
=
BitmapCanvas
.
_widthToPhysical
(
_exactLocalCullRect
!.
width
)
*
BitmapCanvas
.
_heightToPhysical
(
_exactLocalCullRect
!.
height
);
final
int
oldPixelCount
=
oldCanvas
.
_widthInBitmapPixels
*
oldCanvas
.
_heightInBitmapPixels
;
if
(
oldPixelCount
==
0
)
{
return
1.0
;
}
final
double
pixelCountRatio
=
newPixelCount
/
oldPixelCount
;
assert
(
0
<=
pixelCountRatio
&&
pixelCountRatio
<=
1.0
,
'Invalid pixel count ratio
$pixelCountRatio
'
);
return
1.0
-
pixelCountRatio
;
}
}
}
@override
Matrix4
?
get
localTransformInverse
=>
null
;
@override
int
get
bitmapPixelCount
{
if
(
_canvas
is
!
BitmapCanvas
)
{
return
0
;
}
final
BitmapCanvas
bitmapCanvas
=
_canvas
as
BitmapCanvas
;
return
bitmapCanvas
.
bitmapPixelCount
;
}
@override
void
applyPaint
(
EngineCanvas
?
oldCanvas
)
{
if
(
picture
.
recordingCanvas
!.
hasArbitraryPaint
)
{
_applyBitmapPaint
(
oldCanvas
);
}
else
{
_applyDomPaint
(
oldCanvas
);
}
}
void
_applyDomPaint
(
EngineCanvas
?
oldCanvas
)
{
_recycleCanvas
(
oldCanvas
);
_canvas
=
DomCanvas
();
domRenderer
.
clearDom
(
rootElement
!);
rootElement
!.
append
(
_canvas
!.
rootElement
);
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
}
void
_applyBitmapPaint
(
EngineCanvas
?
oldCanvas
)
{
if
(
oldCanvas
is
BitmapCanvas
&&
oldCanvas
.
doesFitBounds
(
_optimalLocalCullRect
!)
&&
oldCanvas
.
isReusable
())
{
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
keptCount
++;
}
oldCanvas
.
bounds
=
_optimalLocalCullRect
!;
_canvas
=
oldCanvas
;
oldCanvas
.
setElementCache
(
_elementCache
);
_canvas
!.
clear
();
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
}
else
{
// We can't use the old canvas because the size has changed, so we put
// it in a cache for later reuse.
_recycleCanvas
(
oldCanvas
);
// We cannot paint immediately because not all canvases that we may be
// able to reuse have been released yet. So instead we enqueue this
// picture to be painted after the update cycle is done syncing the layer
// tree then reuse canvases that were freed up.
_paintQueue
.
add
(
_PaintRequest
(
canvasSize:
_optimalLocalCullRect
!.
size
,
paintCallback:
()
{
_canvas
=
_findOrCreateCanvas
(
_optimalLocalCullRect
!);
assert
(
_canvas
is
BitmapCanvas
&&
(
_canvas
as
BitmapCanvas
?)!.
_elementCache
==
_elementCache
);
if
(
_debugExplainSurfaceStats
)
{
final
BitmapCanvas
bitmapCanvas
=
_canvas
as
BitmapCanvas
;
_surfaceStatsFor
(
this
).
paintPixelCount
+=
bitmapCanvas
.
bitmapPixelCount
;
}
domRenderer
.
clearDom
(
rootElement
!);
rootElement
!.
append
(
_canvas
!.
rootElement
);
_canvas
!.
clear
();
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
},
));
}
}
/// Attempts to reuse a canvas from the [_recycledCanvases]. Allocates a new
/// one if unable to reuse.
///
/// The best recycled canvas is one that:
///
/// - Fits the requested [canvasSize]. This is a hard requirement. Otherwise
/// we risk clipping the picture.
/// - Is the smallest among all possible reusable canvases. This makes canvas
/// reuse more efficient.
/// - Contains no more than twice the number of requested pixels. This makes
/// sure we do not use too much memory for small canvases.
BitmapCanvas
_findOrCreateCanvas
(
ui
.
Rect
bounds
)
{
final
ui
.
Size
canvasSize
=
bounds
.
size
;
BitmapCanvas
?
bestRecycledCanvas
;
double
lastPixelCount
=
double
.
infinity
;
for
(
int
i
=
0
;
i
<
_recycledCanvases
.
length
;
i
++)
{
final
BitmapCanvas
candidate
=
_recycledCanvases
[
i
];
if
(!
candidate
.
isReusable
())
{
continue
;
}
final
ui
.
Size
candidateSize
=
candidate
.
size
;
final
double
candidatePixelCount
=
candidateSize
.
width
*
candidateSize
.
height
;
final
bool
fits
=
candidate
.
doesFitBounds
(
bounds
);
final
bool
isSmaller
=
candidatePixelCount
<
lastPixelCount
;
if
(
fits
&&
isSmaller
)
{
// [isTooSmall] is used to make sure that a small picture doesn't
// reuse and hold onto memory of a large canvas.
final
double
requestedPixelCount
=
bounds
.
width
*
bounds
.
height
;
final
bool
isTooSmall
=
isSmaller
&&
requestedPixelCount
>
1
&&
(
candidatePixelCount
/
requestedPixelCount
)
>
4
;
if
(!
isTooSmall
)
{
bestRecycledCanvas
=
candidate
;
lastPixelCount
=
candidatePixelCount
;
final
bool
fitsExactly
=
candidateSize
.
width
==
canvasSize
.
width
&&
candidateSize
.
height
==
canvasSize
.
height
;
if
(
fitsExactly
)
{
// No need to keep looking any more.
break
;
}
}
}
}
if
(
bestRecycledCanvas
!=
null
)
{
if
(
_debugExplainSurfaceStats
)
{
_surfaceStatsFor
(
this
).
reuseCanvasCount
++;
}
_recycledCanvases
.
remove
(
bestRecycledCanvas
);
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
inRecycleCount
=
_recycledCanvases
.
length
;
}
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
reusedCount
++;
}
bestRecycledCanvas
.
bounds
=
bounds
;
bestRecycledCanvas
.
setElementCache
(
_elementCache
);
return
bestRecycledCanvas
;
}
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
createdCount
++;
}
final
BitmapCanvas
canvas
=
BitmapCanvas
(
bounds
);
canvas
.
setElementCache
(
_elementCache
);
if
(
_debugExplainSurfaceStats
)
{
_surfaceStatsFor
(
this
)
..
allocateBitmapCanvasCount
+=
1
..
allocatedBitmapSizeInPixels
=
canvas
.
_widthInBitmapPixels
*
canvas
.
_heightInBitmapPixels
;
}
return
canvas
;
}
}
/// A surface that uses a combination of `<canvas>`, `<div>` and `<p>` elements
/// to draw shapes and text.
abstract
class
PersistedPicture
extends
PersistedLeafSurface
{
class
PersistedPicture
extends
PersistedLeafSurface
{
PersistedPicture
(
this
.
dx
,
this
.
dy
,
this
.
picture
,
this
.
hints
)
:
localPaintBounds
=
picture
.
recordingCanvas
!.
pictureBounds
;
...
...
@@ -553,7 +270,14 @@ abstract class PersistedPicture extends PersistedLeafSurface {
///
/// If the implementation does not paint onto a bitmap canvas, it should
/// return zero.
int
get
bitmapPixelCount
;
int
get
bitmapPixelCount
{
if
(
_canvas
is
!
BitmapCanvas
)
{
return
0
;
}
final
BitmapCanvas
bitmapCanvas
=
_canvas
as
BitmapCanvas
;
return
bitmapCanvas
.
bitmapPixelCount
;
}
void
_applyPaint
(
PersistedPicture
?
oldSurface
)
{
final
EngineCanvas
?
oldCanvas
=
oldSurface
?.
_canvas
;
...
...
@@ -575,8 +299,193 @@ abstract class PersistedPicture extends PersistedLeafSurface {
applyPaint
(
oldCanvas
);
}
/// Concrete implementations implement this method to do actual painting.
void
applyPaint
(
EngineCanvas
?
oldCanvas
);
@override
double
matchForUpdate
(
PersistedPicture
existingSurface
)
{
if
(
existingSurface
.
picture
==
picture
)
{
// Picture is the same, return perfect score.
return
0.0
;
}
if
(!
existingSurface
.
picture
.
recordingCanvas
!.
didDraw
)
{
// The previous surface didn't draw anything and therefore has no
// resources to reuse.
return
1.0
;
}
final
bool
didRequireBitmap
=
existingSurface
.
picture
.
recordingCanvas
!.
hasArbitraryPaint
;
final
bool
requiresBitmap
=
picture
.
recordingCanvas
!.
hasArbitraryPaint
;
if
(
didRequireBitmap
!=
requiresBitmap
)
{
// Switching canvas types is always expensive.
return
1.0
;
}
else
if
(!
requiresBitmap
)
{
// Currently DomCanvas is always expensive to repaint, as we always throw
// out all the DOM we rendered before. This may change in the future, at
// which point we may return other values here.
return
1.0
;
}
else
{
final
BitmapCanvas
?
oldCanvas
=
existingSurface
.
_canvas
as
BitmapCanvas
?;
if
(
oldCanvas
==
null
)
{
// We did not allocate a canvas last time. This can happen when the
// picture is completely clipped out of the view.
return
1.0
;
}
else
if
(!
oldCanvas
.
doesFitBounds
(
_exactLocalCullRect
!))
{
// The canvas needs to be resized before painting.
return
1.0
;
}
else
{
final
int
newPixelCount
=
BitmapCanvas
.
_widthToPhysical
(
_exactLocalCullRect
!.
width
)
*
BitmapCanvas
.
_heightToPhysical
(
_exactLocalCullRect
!.
height
);
final
int
oldPixelCount
=
oldCanvas
.
_widthInBitmapPixels
*
oldCanvas
.
_heightInBitmapPixels
;
if
(
oldPixelCount
==
0
)
{
return
1.0
;
}
final
double
pixelCountRatio
=
newPixelCount
/
oldPixelCount
;
assert
(
0
<=
pixelCountRatio
&&
pixelCountRatio
<=
1.0
,
'Invalid pixel count ratio
$pixelCountRatio
'
);
return
1.0
-
pixelCountRatio
;
}
}
}
@override
Matrix4
?
get
localTransformInverse
=>
null
;
void
applyPaint
(
EngineCanvas
?
oldCanvas
)
{
if
(
picture
.
recordingCanvas
!.
hasArbitraryPaint
)
{
_applyBitmapPaint
(
oldCanvas
);
}
else
{
_applyDomPaint
(
oldCanvas
);
}
}
void
_applyDomPaint
(
EngineCanvas
?
oldCanvas
)
{
_recycleCanvas
(
oldCanvas
);
_canvas
=
DomCanvas
();
domRenderer
.
clearDom
(
rootElement
!);
rootElement
!.
append
(
_canvas
!.
rootElement
);
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
}
void
_applyBitmapPaint
(
EngineCanvas
?
oldCanvas
)
{
if
(
oldCanvas
is
BitmapCanvas
&&
oldCanvas
.
doesFitBounds
(
_optimalLocalCullRect
!)
&&
oldCanvas
.
isReusable
())
{
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
keptCount
++;
}
oldCanvas
.
bounds
=
_optimalLocalCullRect
!;
_canvas
=
oldCanvas
;
oldCanvas
.
setElementCache
(
_elementCache
);
_canvas
!.
clear
();
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
}
else
{
// We can't use the old canvas because the size has changed, so we put
// it in a cache for later reuse.
_recycleCanvas
(
oldCanvas
);
// We cannot paint immediately because not all canvases that we may be
// able to reuse have been released yet. So instead we enqueue this
// picture to be painted after the update cycle is done syncing the layer
// tree then reuse canvases that were freed up.
_paintQueue
.
add
(
_PaintRequest
(
canvasSize:
_optimalLocalCullRect
!.
size
,
paintCallback:
()
{
_canvas
=
_findOrCreateCanvas
(
_optimalLocalCullRect
!);
assert
(
_canvas
is
BitmapCanvas
&&
(
_canvas
as
BitmapCanvas
?)!.
_elementCache
==
_elementCache
);
if
(
_debugExplainSurfaceStats
)
{
final
BitmapCanvas
bitmapCanvas
=
_canvas
as
BitmapCanvas
;
_surfaceStatsFor
(
this
).
paintPixelCount
+=
bitmapCanvas
.
bitmapPixelCount
;
}
domRenderer
.
clearDom
(
rootElement
!);
rootElement
!.
append
(
_canvas
!.
rootElement
);
_canvas
!.
clear
();
picture
.
recordingCanvas
!.
apply
(
_canvas
,
_optimalLocalCullRect
);
},
));
}
}
/// Attempts to reuse a canvas from the [_recycledCanvases]. Allocates a new
/// one if unable to reuse.
///
/// The best recycled canvas is one that:
///
/// - Fits the requested [canvasSize]. This is a hard requirement. Otherwise
/// we risk clipping the picture.
/// - Is the smallest among all possible reusable canvases. This makes canvas
/// reuse more efficient.
/// - Contains no more than twice the number of requested pixels. This makes
/// sure we do not use too much memory for small canvases.
BitmapCanvas
_findOrCreateCanvas
(
ui
.
Rect
bounds
)
{
final
ui
.
Size
canvasSize
=
bounds
.
size
;
BitmapCanvas
?
bestRecycledCanvas
;
double
lastPixelCount
=
double
.
infinity
;
for
(
int
i
=
0
;
i
<
_recycledCanvases
.
length
;
i
++)
{
final
BitmapCanvas
candidate
=
_recycledCanvases
[
i
];
if
(!
candidate
.
isReusable
())
{
continue
;
}
final
ui
.
Size
candidateSize
=
candidate
.
size
;
final
double
candidatePixelCount
=
candidateSize
.
width
*
candidateSize
.
height
;
final
bool
fits
=
candidate
.
doesFitBounds
(
bounds
);
final
bool
isSmaller
=
candidatePixelCount
<
lastPixelCount
;
if
(
fits
&&
isSmaller
)
{
// [isTooSmall] is used to make sure that a small picture doesn't
// reuse and hold onto memory of a large canvas.
final
double
requestedPixelCount
=
bounds
.
width
*
bounds
.
height
;
final
bool
isTooSmall
=
isSmaller
&&
requestedPixelCount
>
1
&&
(
candidatePixelCount
/
requestedPixelCount
)
>
4
;
if
(!
isTooSmall
)
{
bestRecycledCanvas
=
candidate
;
lastPixelCount
=
candidatePixelCount
;
final
bool
fitsExactly
=
candidateSize
.
width
==
canvasSize
.
width
&&
candidateSize
.
height
==
canvasSize
.
height
;
if
(
fitsExactly
)
{
// No need to keep looking any more.
break
;
}
}
}
}
if
(
bestRecycledCanvas
!=
null
)
{
if
(
_debugExplainSurfaceStats
)
{
_surfaceStatsFor
(
this
).
reuseCanvasCount
++;
}
_recycledCanvases
.
remove
(
bestRecycledCanvas
);
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
inRecycleCount
=
_recycledCanvases
.
length
;
}
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
reusedCount
++;
}
bestRecycledCanvas
.
bounds
=
bounds
;
bestRecycledCanvas
.
setElementCache
(
_elementCache
);
return
bestRecycledCanvas
;
}
if
(
_debugShowCanvasReuseStats
)
{
DebugCanvasReuseOverlay
.
instance
.
createdCount
++;
}
final
BitmapCanvas
canvas
=
BitmapCanvas
(
bounds
);
canvas
.
setElementCache
(
_elementCache
);
if
(
_debugExplainSurfaceStats
)
{
_surfaceStatsFor
(
this
)
..
allocateBitmapCanvasCount
+=
1
..
allocatedBitmapSizeInPixels
=
canvas
.
_widthInBitmapPixels
*
canvas
.
_heightInBitmapPixels
;
}
return
canvas
;
}
void
_applyTranslate
()
{
rootElement
!.
style
.
transform
=
'translate(
${dx}
px,
${dy}
px)'
;
...
...
lib/web_ui/lib/src/engine/surface/recording_canvas.dart
浏览文件 @
5c8a6260
...
...
@@ -608,8 +608,6 @@ abstract class PaintCommand {
const
PaintCommand
();
void
apply
(
EngineCanvas
?
canvas
);
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
);
}
/// A [PaintCommand] that affect pixels on the screen (unlike, for example, the
...
...
@@ -665,11 +663,6 @@ class PaintSave extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(
const
<
int
>[
1
]);
}
}
class
PaintRestore
extends
PaintCommand
{
...
...
@@ -688,11 +681,6 @@ class PaintRestore extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(
const
<
int
>[
2
]);
}
}
class
PaintTranslate
extends
PaintCommand
{
...
...
@@ -714,11 +702,6 @@ class PaintTranslate extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
num
>[
3
,
dx
,
dy
]);
}
}
class
PaintScale
extends
PaintCommand
{
...
...
@@ -740,11 +723,6 @@ class PaintScale extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
num
>[
4
,
sx
,
sy
]);
}
}
class
PaintRotate
extends
PaintCommand
{
...
...
@@ -765,11 +743,6 @@ class PaintRotate extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
num
>[
5
,
radians
]);
}
}
class
PaintTransform
extends
PaintCommand
{
...
...
@@ -790,11 +763,6 @@ class PaintTransform extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
6
]..
addAll
(
matrix4
));
}
}
class
PaintSkew
extends
PaintCommand
{
...
...
@@ -816,11 +784,6 @@ class PaintSkew extends PaintCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
num
>[
7
,
sx
,
sy
]);
}
}
class
PaintClipRect
extends
DrawCommand
{
...
...
@@ -841,11 +804,6 @@ class PaintClipRect extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
8
,
_serializeRectToCssPaint
(
rect
)]);
}
}
class
PaintClipRRect
extends
DrawCommand
{
...
...
@@ -866,14 +824,6 @@ class PaintClipRRect extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
9
,
_serializeRRectToCssPaint
(
rrect
),
]);
}
}
class
PaintClipPath
extends
DrawCommand
{
...
...
@@ -894,11 +844,6 @@ class PaintClipPath extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
10
,
path
.
webOnlySerializeToCssPaint
()]);
}
}
class
PaintDrawColor
extends
DrawCommand
{
...
...
@@ -920,12 +865,6 @@ class PaintDrawColor extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
11
,
colorToCssString
(
color
),
blendMode
.
index
]);
}
}
class
PaintDrawLine
extends
DrawCommand
{
...
...
@@ -948,18 +887,6 @@ class PaintDrawLine extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
12
,
p1
.
dx
,
p1
.
dy
,
p2
.
dx
,
p2
.
dy
,
_serializePaintToCssPaint
(
paint
)
]);
}
}
class
PaintDrawPaint
extends
DrawCommand
{
...
...
@@ -980,11 +907,6 @@ class PaintDrawPaint extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
13
,
_serializePaintToCssPaint
(
paint
)]);
}
}
class
PaintDrawVertices
extends
DrawCommand
{
...
...
@@ -1006,11 +928,6 @@ class PaintDrawVertices extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
throw
UnimplementedError
();
}
}
class
PaintDrawPoints
extends
DrawCommand
{
...
...
@@ -1032,11 +949,6 @@ class PaintDrawPoints extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
throw
UnimplementedError
();
}
}
class
PaintDrawRect
extends
DrawCommand
{
...
...
@@ -1058,15 +970,6 @@ class PaintDrawRect extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
14
,
_serializeRectToCssPaint
(
rect
),
_serializePaintToCssPaint
(
paint
)
]);
}
}
class
PaintDrawRRect
extends
DrawCommand
{
...
...
@@ -1088,15 +991,6 @@ class PaintDrawRRect extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
15
,
_serializeRRectToCssPaint
(
rrect
),
_serializePaintToCssPaint
(
paint
),
]);
}
}
class
PaintDrawDRRect
extends
DrawCommand
{
...
...
@@ -1125,16 +1019,6 @@ class PaintDrawDRRect extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
16
,
_serializeRRectToCssPaint
(
outer
),
_serializeRRectToCssPaint
(
inner
),
_serializePaintToCssPaint
(
paint
),
]);
}
}
class
PaintDrawOval
extends
DrawCommand
{
...
...
@@ -1156,15 +1040,6 @@ class PaintDrawOval extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
17
,
_serializeRectToCssPaint
(
rect
),
_serializePaintToCssPaint
(
paint
),
]);
}
}
class
PaintDrawCircle
extends
DrawCommand
{
...
...
@@ -1187,17 +1062,6 @@ class PaintDrawCircle extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
18
,
c
.
dx
,
c
.
dy
,
radius
,
_serializePaintToCssPaint
(
paint
),
]);
}
}
class
PaintDrawPath
extends
DrawCommand
{
...
...
@@ -1219,15 +1083,6 @@ class PaintDrawPath extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
19
,
path
.
webOnlySerializeToCssPaint
(),
_serializePaintToCssPaint
(
paint
),
]);
}
}
class
PaintDrawShadow
extends
DrawCommand
{
...
...
@@ -1252,22 +1107,6 @@ class PaintDrawShadow extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
serializedCommands
.
add
(<
dynamic
>[
20
,
path
.
webOnlySerializeToCssPaint
(),
<
dynamic
>[
color
.
alpha
,
color
.
red
,
color
.
green
,
color
.
blue
,
],
elevation
,
transparentOccluder
,
]);
}
}
class
PaintDrawImage
extends
DrawCommand
{
...
...
@@ -1290,13 +1129,6 @@ class PaintDrawImage extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
if
(
assertionsEnabled
)
{
throw
UnsupportedError
(
'drawImage not serializable'
);
}
}
}
class
PaintDrawImageRect
extends
DrawCommand
{
...
...
@@ -1320,13 +1152,6 @@ class PaintDrawImageRect extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
if
(
assertionsEnabled
)
{
throw
UnsupportedError
(
'drawImageRect not serializable'
);
}
}
}
class
PaintDrawParagraph
extends
DrawCommand
{
...
...
@@ -1348,55 +1173,6 @@ class PaintDrawParagraph extends DrawCommand {
return
super
.
toString
();
}
}
@override
void
serializeToCssPaint
(
List
<
List
<
dynamic
>>
serializedCommands
)
{
if
(
assertionsEnabled
)
{
throw
UnsupportedError
(
'drawParagraph not serializable'
);
}
}
}
List
<
dynamic
>
_serializePaintToCssPaint
(
SurfacePaintData
paint
)
{
final
EngineGradient
?
engineShader
=
paint
.
shader
as
EngineGradient
?;
return
<
dynamic
>[
paint
.
blendMode
?.
index
,
paint
.
style
?.
index
,
paint
.
strokeWidth
,
paint
.
strokeCap
?.
index
,
paint
.
isAntiAlias
,
colorToCssString
(
paint
.
color
),
engineShader
?.
webOnlySerializeToCssPaint
(),
paint
.
maskFilter
?.
webOnlySerializeToCssPaint
(),
paint
.
filterQuality
?.
index
,
paint
.
colorFilter
?.
webOnlySerializeToCssPaint
(),
];
}
List
<
dynamic
>
_serializeRectToCssPaint
(
ui
.
Rect
rect
)
{
return
<
dynamic
>[
rect
.
left
,
rect
.
top
,
rect
.
right
,
rect
.
bottom
,
];
}
List
<
dynamic
>
_serializeRRectToCssPaint
(
ui
.
RRect
rrect
)
{
return
<
dynamic
>[
rrect
.
left
,
rrect
.
top
,
rrect
.
right
,
rrect
.
bottom
,
rrect
.
tlRadiusX
,
rrect
.
tlRadiusY
,
rrect
.
trRadiusX
,
rrect
.
trRadiusY
,
rrect
.
brRadiusX
,
rrect
.
brRadiusY
,
rrect
.
blRadiusX
,
rrect
.
blRadiusY
,
];
}
class
Subpath
{
...
...
@@ -1421,14 +1197,6 @@ class Subpath {
return
result
;
}
List
<
dynamic
>
serializeToCssPaint
()
{
final
List
<
dynamic
>
serialization
=
<
dynamic
>[];
for
(
int
i
=
0
;
i
<
commands
.
length
;
i
++)
{
serialization
.
add
(
commands
[
i
].
serializeToCssPaint
());
}
return
serialization
;
}
@override
String
toString
()
{
if
(
assertionsEnabled
)
{
...
...
@@ -1439,26 +1207,11 @@ class Subpath {
}
}
/// ! Houdini implementation relies on indices here. Keep in sync.
class
PathCommandTypes
{
static
const
int
moveTo
=
0
;
static
const
int
lineTo
=
1
;
static
const
int
ellipse
=
2
;
static
const
int
close
=
3
;
static
const
int
quadraticCurveTo
=
4
;
static
const
int
bezierCurveTo
=
5
;
static
const
int
rect
=
6
;
static
const
int
rRect
=
7
;
}
abstract
class
PathCommand
{
final
int
type
;
const
PathCommand
(
this
.
type
);
const
PathCommand
();
PathCommand
shifted
(
ui
.
Offset
offset
);
List
<
dynamic
>
serializeToCssPaint
();
/// Transform the command and add to targetPath.
void
transform
(
Float32List
matrix4
,
SurfacePath
targetPath
);
...
...
@@ -1472,18 +1225,13 @@ class MoveTo extends PathCommand {
final
double
x
;
final
double
y
;
const
MoveTo
(
this
.
x
,
this
.
y
)
:
super
(
PathCommandTypes
.
moveTo
)
;
const
MoveTo
(
this
.
x
,
this
.
y
);
@override
MoveTo
shifted
(
ui
.
Offset
offset
)
{
return
MoveTo
(
x
+
offset
.
dx
,
y
+
offset
.
dy
);
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
1
,
x
,
y
];
}
@override
void
transform
(
Float32List
matrix4
,
ui
.
Path
targetPath
)
{
final
ui
.
Offset
offset
=
PathCommand
.
_transformOffset
(
x
,
y
,
matrix4
);
...
...
@@ -1504,18 +1252,13 @@ class LineTo extends PathCommand {
final
double
x
;
final
double
y
;
const
LineTo
(
this
.
x
,
this
.
y
)
:
super
(
PathCommandTypes
.
lineTo
)
;
const
LineTo
(
this
.
x
,
this
.
y
);
@override
LineTo
shifted
(
ui
.
Offset
offset
)
{
return
LineTo
(
x
+
offset
.
dx
,
y
+
offset
.
dy
);
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
2
,
x
,
y
];
}
@override
void
transform
(
Float32List
matrix4
,
ui
.
Path
targetPath
)
{
final
ui
.
Offset
offset
=
PathCommand
.
_transformOffset
(
x
,
y
,
matrix4
);
...
...
@@ -1543,8 +1286,7 @@ class Ellipse extends PathCommand {
final
bool
anticlockwise
;
const
Ellipse
(
this
.
x
,
this
.
y
,
this
.
radiusX
,
this
.
radiusY
,
this
.
rotation
,
this
.
startAngle
,
this
.
endAngle
,
this
.
anticlockwise
)
:
super
(
PathCommandTypes
.
ellipse
);
this
.
startAngle
,
this
.
endAngle
,
this
.
anticlockwise
);
@override
Ellipse
shifted
(
ui
.
Offset
offset
)
{
...
...
@@ -1552,21 +1294,6 @@ class Ellipse extends PathCommand {
startAngle
,
endAngle
,
anticlockwise
);
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
3
,
x
,
y
,
radiusX
,
radiusY
,
rotation
,
startAngle
,
endAngle
,
anticlockwise
,
];
}
@override
void
transform
(
Float32List
matrix4
,
SurfacePath
targetPath
)
{
final
ui
.
Path
bezierPath
=
ui
.
Path
();
...
...
@@ -1686,8 +1413,7 @@ class QuadraticCurveTo extends PathCommand {
final
double
x2
;
final
double
y2
;
const
QuadraticCurveTo
(
this
.
x1
,
this
.
y1
,
this
.
x2
,
this
.
y2
)
:
super
(
PathCommandTypes
.
quadraticCurveTo
);
const
QuadraticCurveTo
(
this
.
x1
,
this
.
y1
,
this
.
x2
,
this
.
y2
);
@override
QuadraticCurveTo
shifted
(
ui
.
Offset
offset
)
{
...
...
@@ -1695,11 +1421,6 @@ class QuadraticCurveTo extends PathCommand {
x1
+
offset
.
dx
,
y1
+
offset
.
dy
,
x2
+
offset
.
dx
,
y2
+
offset
.
dy
);
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
4
,
x1
,
y1
,
x2
,
y2
];
}
@override
void
transform
(
Float32List
matrix4
,
ui
.
Path
targetPath
)
{
final
double
m0
=
matrix4
[
0
];
...
...
@@ -1734,8 +1455,7 @@ class BezierCurveTo extends PathCommand {
final
double
x3
;
final
double
y3
;
const
BezierCurveTo
(
this
.
x1
,
this
.
y1
,
this
.
x2
,
this
.
y2
,
this
.
x3
,
this
.
y3
)
:
super
(
PathCommandTypes
.
bezierCurveTo
);
const
BezierCurveTo
(
this
.
x1
,
this
.
y1
,
this
.
x2
,
this
.
y2
,
this
.
x3
,
this
.
y3
);
@override
BezierCurveTo
shifted
(
ui
.
Offset
offset
)
{
...
...
@@ -1743,11 +1463,6 @@ class BezierCurveTo extends PathCommand {
y2
+
offset
.
dy
,
x3
+
offset
.
dx
,
y3
+
offset
.
dy
);
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
5
,
x1
,
y1
,
x2
,
y2
,
x3
,
y3
];
}
@override
void
transform
(
Float32List
matrix4
,
ui
.
Path
targetPath
)
{
final
double
s0
=
matrix4
[
0
];
...
...
@@ -1782,8 +1497,7 @@ class RectCommand extends PathCommand {
final
double
width
;
final
double
height
;
const
RectCommand
(
this
.
x
,
this
.
y
,
this
.
width
,
this
.
height
)
:
super
(
PathCommandTypes
.
rect
);
const
RectCommand
(
this
.
x
,
this
.
y
,
this
.
width
,
this
.
height
);
@override
RectCommand
shifted
(
ui
.
Offset
offset
)
{
...
...
@@ -1824,11 +1538,6 @@ class RectCommand extends PathCommand {
}
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
6
,
x
,
y
,
width
,
height
];
}
@override
String
toString
()
{
if
(
assertionsEnabled
)
{
...
...
@@ -1842,18 +1551,13 @@ class RectCommand extends PathCommand {
class
RRectCommand
extends
PathCommand
{
final
ui
.
RRect
rrect
;
const
RRectCommand
(
this
.
rrect
)
:
super
(
PathCommandTypes
.
rRect
)
;
const
RRectCommand
(
this
.
rrect
);
@override
RRectCommand
shifted
(
ui
.
Offset
offset
)
{
return
RRectCommand
(
rrect
.
shift
(
offset
));
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
7
,
_serializeRRectToCssPaint
(
rrect
)];
}
@override
void
transform
(
Float32List
matrix4
,
SurfacePath
targetPath
)
{
final
ui
.
Path
roundRectPath
=
ui
.
Path
();
...
...
@@ -1872,18 +1576,11 @@ class RRectCommand extends PathCommand {
}
class
CloseCommand
extends
PathCommand
{
const
CloseCommand
()
:
super
(
PathCommandTypes
.
close
);
@override
CloseCommand
shifted
(
ui
.
Offset
offset
)
{
return
this
;
}
@override
List
<
dynamic
>
serializeToCssPaint
()
{
return
<
dynamic
>[
8
];
}
@override
void
transform
(
Float32List
matrix4
,
ui
.
Path
targetPath
)
{
targetPath
.
close
();
...
...
lib/web_ui/lib/src/engine/surface/scene_builder.dart
浏览文件 @
5c8a6260
...
...
@@ -363,7 +363,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
if
(
willChangeHint
)
{
hints
|=
2
;
}
_addSurface
(
persistedPictureFactory
(
offset
.
dx
,
offset
.
dy
,
p
icture
,
hints
));
_addSurface
(
PersistedPicture
(
offset
.
dx
,
offset
.
dy
,
picture
as
EngineP
icture
,
hints
));
}
/// Adds a backend texture to the scene.
...
...
lib/web_ui/lib/src/engine/surface/shader.dart
浏览文件 @
5c8a6260
...
...
@@ -11,10 +11,6 @@ abstract class EngineGradient implements ui.Gradient {
/// Creates a fill style to be used in painting.
Object
createPaintStyle
(
html
.
CanvasRenderingContext2D
?
ctx
);
List
<
dynamic
>
webOnlySerializeToCssPaint
()
{
throw
UnsupportedError
(
'CSS paint not implemented for this shader type'
);
}
}
class
GradientSweep
extends
EngineGradient
{
...
...
@@ -100,24 +96,6 @@ class GradientLinear extends EngineGradient {
}
return
gradient
;
}
@override
List
<
dynamic
>
webOnlySerializeToCssPaint
()
{
final
List
<
dynamic
>
serializedColors
=
<
dynamic
>[];
for
(
int
i
=
0
;
i
<
colors
.
length
;
i
++)
{
serializedColors
.
add
(
colorToCssString
(
colors
[
i
]));
}
return
<
dynamic
>[
1
,
from
.
dx
,
from
.
dy
,
to
.
dx
,
to
.
dy
,
serializedColors
,
colorStops
,
tileMode
.
index
];
}
}
// TODO(flutter_web): For transforms and tile modes implement as webgl
...
...
lib/web_ui/lib/src/engine/surface/surface_stats.dart
浏览文件 @
5c8a6260
...
...
@@ -237,7 +237,7 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) {
elementReuseCount
+=
stats
.
reuseElementCount
;
totalAllocatedDomNodeCount
+=
stats
.
allocatedDomNodeCount
;
if
(
surface
is
Persisted
Standard
Picture
)
{
if
(
surface
is
PersistedPicture
)
{
pictureCount
+=
1
;
paintCount
+=
stats
.
paintCount
;
...
...
lib/web_ui/lib/src/ui/painting.dart
浏览文件 @
5c8a6260
...
...
@@ -1323,10 +1323,6 @@ abstract class ColorFilter {
/// to the RGB channels.
const
factory
ColorFilter
.
srgbToLinearGamma
()
=
engine
.
EngineColorFilter
.
srgbToLinearGamma
;
List
<
dynamic
>
webOnlySerializeToCssPaint
()
{
throw
UnsupportedError
(
'ColorFilter for CSS paint not yet supported'
);
}
}
/// Styles to use for blurs in [MaskFilter] objects.
...
...
@@ -1402,10 +1398,6 @@ class MaskFilter {
@override
int
get
hashCode
=>
hashValues
(
_style
,
_sigma
);
List
<
dynamic
>
webOnlySerializeToCssPaint
()
{
return
<
dynamic
>[
_style
.
index
,
_sigma
];
}
@override
String
toString
()
=>
'MaskFilter.blur(
$_style
,
${_sigma.toStringAsFixed(1)}
)'
;
}
...
...
lib/web_ui/test/canvas_test.dart
浏览文件 @
5c8a6260
...
...
@@ -26,7 +26,6 @@ void main() {
test
(
description
,
()
{
testFn
(
BitmapCanvas
(
canvasSize
));
testFn
(
DomCanvas
());
testFn
(
HoudiniCanvas
(
canvasSize
));
testFn
(
mockCanvas
=
MockEngineCanvas
());
if
(
whenDone
!=
null
)
{
whenDone
();
...
...
lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart
浏览文件 @
5c8a6260
...
...
@@ -133,7 +133,7 @@ void _testCullRectComputation() {
});
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
0
,
0
,
500
,
100
));
},
skip:
'''TODO(https://github.com/flutter/flutter/issues/40395)
Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''
);
...
...
@@ -147,7 +147,7 @@ void _testCullRectComputation() {
});
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
0
,
0
,
20
,
20
));
});
...
...
@@ -161,7 +161,7 @@ void _testCullRectComputation() {
});
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
Rect
.
zero
);
expect
(
picture
.
debugExactGlobalCullRect
,
Rect
.
zero
);
});
...
...
@@ -176,7 +176,7 @@ void _testCullRectComputation() {
});
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
40
,
40
,
60
,
60
));
});
...
...
@@ -195,7 +195,7 @@ void _testCullRectComputation() {
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
40
,
40
,
60
,
60
));
});
...
...
@@ -213,7 +213,7 @@ void _testCullRectComputation() {
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
debugExactGlobalCullRect
,
const
Rect
.
fromLTRB
(
0
,
70
,
20
,
100
));
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
0
,
-
20
,
20
,
10
));
...
...
@@ -244,7 +244,7 @@ void _testCullRectComputation() {
await
matchGoldenFile
(
'compositing_cull_rect_fills_layer_clip.png'
,
region:
region
);
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
40
,
40
,
70
,
70
));
});
...
...
@@ -274,7 +274,7 @@ void _testCullRectComputation() {
'compositing_cull_rect_intersects_clip_and_paint_bounds.png'
,
region:
region
);
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
50
,
40
,
70
,
70
));
});
...
...
@@ -305,7 +305,7 @@ void _testCullRectComputation() {
await
matchGoldenFile
(
'compositing_cull_rect_offset_inside_layer_clip.png'
,
region:
region
);
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(-
15.0
,
-
20.0
,
15.0
,
0.0
));
});
...
...
@@ -335,7 +335,7 @@ void _testCullRectComputation() {
builder
.
build
();
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
Rect
.
zero
);
expect
(
picture
.
debugExactGlobalCullRect
,
Rect
.
zero
);
});
...
...
@@ -378,7 +378,7 @@ void _testCullRectComputation() {
await
matchGoldenFile
(
'compositing_cull_rect_rotated.png'
,
region:
region
);
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
within
(
...
...
@@ -510,7 +510,7 @@ void _testCullRectComputation() {
await
matchGoldenFile
(
'compositing_3d_rotate1.png'
,
region:
region
);
// ignore: unused_local_variable
final
Persisted
Standard
Picture
picture
=
enumeratePictures
().
single
;
final
PersistedPicture
picture
=
enumeratePictures
().
single
;
// TODO(https://github.com/flutter/flutter/issues/40395):
// Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500.
// expect(
...
...
lib/web_ui/test/golden_tests/engine/scuba.dart
浏览文件 @
5c8a6260
...
...
@@ -91,7 +91,7 @@ typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);
/// Runs the given test [body] with each type of canvas.
void
testEachCanvas
(
String
description
,
CanvasTest
body
,
{
double
maxDiffRate
,
bool
bSkipHoudini
=
false
})
{
{
double
maxDiffRate
})
{
const
ui
.
Rect
bounds
=
ui
.
Rect
.
fromLTWH
(
0
,
0
,
600
,
800
);
test
(
'
$description
(bitmap)'
,
()
{
try
{
...
...
@@ -123,18 +123,6 @@ void testEachCanvas(String description, CanvasTest body,
TextMeasurementService
.
clearCache
();
}
});
if
(!
bSkipHoudini
)
{
test
(
'
$description
(houdini)'
,
()
{
try
{
TextMeasurementService
.
initialize
(
rulerCacheCapacity:
2
);
WebExperiments
.
instance
.
useCanvasText
=
false
;
return
body
(
HoudiniCanvas
(
bounds
));
}
finally
{
WebExperiments
.
instance
.
useCanvasText
=
null
;
TextMeasurementService
.
clearCache
();
}
});
}
}
final
ui
.
TextStyle
_defaultTextStyle
=
ui
.
TextStyle
(
...
...
lib/web_ui/test/golden_tests/engine/text_placeholders_test.dart
浏览文件 @
5c8a6260
...
...
@@ -30,7 +30,7 @@ void main() async {
recordingCanvas
.
endRecording
();
recordingCanvas
.
apply
(
canvas
,
screenRect
);
return
scuba
.
diffCanvasScreenshot
(
canvas
,
'text_with_placeholders'
);
}
,
bSkipHoudini:
true
);
});
}
const
Color
black
=
Color
(
0xFF000000
);
...
...
lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart
浏览文件 @
5c8a6260
...
...
@@ -220,7 +220,7 @@ void main() async {
testEachCanvas
(
'draws text with a shadow'
,
(
EngineCanvas
canvas
)
{
drawTextWithShadow
(
canvas
);
return
scuba
.
diffCanvasScreenshot
(
canvas
,
'text_shadow'
,
maxDiffRatePercent:
0.2
);
}
,
bSkipHoudini:
true
);
});
testEachCanvas
(
'Handles disabled strut style'
,
(
EngineCanvas
canvas
)
{
// Flutter uses [StrutStyle.disabled] for the [SelectableText] widget. This
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录