Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
8c855dba
E
engine
项目概览
sxychenjing
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
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,发现更多精彩内容 >>
未验证
提交
8c855dba
编写于
1月 15, 2020
作者:
Y
Yegor
提交者:
GitHub
1月 15, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix text blurriness in HTML-rendered text (#15649)
* fix text blurriness in HTML-rendered text
上级
09d892b3
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
454 addition
and
101 deletion
+454
-101
lib/web_ui/dev/goldens.dart
lib/web_ui/dev/goldens.dart
+66
-25
lib/web_ui/dev/goldens_lock.yaml
lib/web_ui/dev/goldens_lock.yaml
+1
-1
lib/web_ui/dev/test_platform.dart
lib/web_ui/dev/test_platform.dart
+10
-6
lib/web_ui/lib/src/engine/bitmap_canvas.dart
lib/web_ui/lib/src/engine/bitmap_canvas.dart
+23
-22
lib/web_ui/lib/src/engine/engine_canvas.dart
lib/web_ui/lib/src/engine/engine_canvas.dart
+4
-4
lib/web_ui/lib/src/engine/surface/clip.dart
lib/web_ui/lib/src/engine/surface/clip.dart
+32
-17
lib/web_ui/lib/src/engine/util.dart
lib/web_ui/lib/src/engine/util.dart
+82
-6
lib/web_ui/test/engine/util_test.dart
lib/web_ui/test/engine/util_test.dart
+30
-0
lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart
lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart
+72
-9
lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart
..._ui/test/golden_tests/engine/compositing_golden_test.dart
+104
-9
web_sdk/web_engine_tester/lib/golden_tester.dart
web_sdk/web_engine_tester/lib/golden_tester.dart
+30
-2
未找到文件。
lib/web_ui/dev/goldens.dart
浏览文件 @
8c855dba
...
...
@@ -3,18 +3,33 @@
// found in the LICENSE file.
import
'dart:io'
as
io
;
import
'package:image/image.dart'
;
import
'package:meta/meta.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:yaml/yaml.dart'
;
import
'environment.dart'
;
import
'utils.dart'
;
/// How to compares pixels within the image.
///
/// Keep this enum in sync with the one defined in `golden_tester.dart`.
enum
PixelComparison
{
/// Allows minor blur and anti-aliasing differences by comparing a 3x3 grid
/// surrounding the pixel rather than direct 1:1 comparison.
fuzzy
,
/// Compares one pixel at a time.
///
/// Anti-aliasing or blur will result in higher diff rate.
precise
,
}
void
main
(
List
<
String
>
args
)
{
final
io
.
File
fileA
=
io
.
File
(
args
[
0
]);
final
io
.
File
fileB
=
io
.
File
(
args
[
1
]);
final
Image
imageA
=
decodeNamedImage
(
fileA
.
readAsBytesSync
(),
'a.png'
);
final
Image
imageB
=
decodeNamedImage
(
fileB
.
readAsBytesSync
(),
'b.png'
);
final
ImageDiff
diff
=
ImageDiff
(
golden:
imageA
,
other:
imageB
);
final
ImageDiff
diff
=
ImageDiff
(
golden:
imageA
,
other:
imageB
,
pixelComparison:
PixelComparison
.
fuzzy
);
print
(
'Diff:
${(diff.rate * 100).toStringAsFixed(4)}
%'
);
}
...
...
@@ -28,6 +43,9 @@ class ImageDiff {
/// The image being compared
final
Image
other
;
/// Algorithm used for comparing pixels.
final
PixelComparison
pixelComparison
;
/// The output of the comparison
/// Pixels in the output image can have 3 different colors depending on the comparison
/// between golden pixels and other pixels:
...
...
@@ -40,7 +58,11 @@ class ImageDiff {
/// This gets set to 1 (100% difference) when golden and other aren't the same size.
double
get
rate
=>
_wrongPixels
/
_pixelCount
;
ImageDiff
({
Image
this
.
golden
,
Image
this
.
other
})
{
ImageDiff
({
@required
this
.
golden
,
@required
this
.
other
,
@required
this
.
pixelComparison
,
})
{
_computeDiff
();
}
...
...
@@ -84,26 +106,44 @@ class ImageDiff {
return
values
.
reduce
((
a
,
b
)
=>
a
+
b
)
~/
values
.
length
;
}
/// Reads the RGBA values of the average of the 3x3 box of pixels centered at [x] and [y].
static
List
<
int
>
_getFuzzyRgb
(
Image
image
,
int
x
,
int
y
)
{
final
List
<
int
>
pixels
=
<
int
>[
_reflectedPixel
(
image
,
x
-
1
,
y
-
1
),
_reflectedPixel
(
image
,
x
-
1
,
y
),
_reflectedPixel
(
image
,
x
-
1
,
y
+
1
),
_reflectedPixel
(
image
,
x
,
y
-
1
),
_reflectedPixel
(
image
,
x
,
y
),
_reflectedPixel
(
image
,
x
,
y
+
1
),
_reflectedPixel
(
image
,
x
+
1
,
y
-
1
),
_reflectedPixel
(
image
,
x
+
1
,
y
),
_reflectedPixel
(
image
,
x
+
1
,
y
+
1
),
];
return
<
int
>[
_average
(
pixels
.
map
((
p
)
=>
getRed
(
p
))),
_average
(
pixels
.
map
((
p
)
=>
getGreen
(
p
))),
_average
(
pixels
.
map
((
p
)
=>
getBlue
(
p
))),
];
/// The value of the pixel at [x] and [y] coordinates.
///
/// If [pixelComparison] is [PixelComparison.precise], reads the RGB value of
/// the pixel.
///
/// If [pixelComparison] is [PixelComparison.fuzzy], reads the RGB values of
/// the average of the 3x3 box of pixels centered at [x] and [y].
List
<
int
>
_getPixelRgbForComparison
(
Image
image
,
int
x
,
int
y
)
{
switch
(
pixelComparison
)
{
case
PixelComparison
.
fuzzy
:
final
List
<
int
>
pixels
=
<
int
>[
_reflectedPixel
(
image
,
x
-
1
,
y
-
1
),
_reflectedPixel
(
image
,
x
-
1
,
y
),
_reflectedPixel
(
image
,
x
-
1
,
y
+
1
),
_reflectedPixel
(
image
,
x
,
y
-
1
),
_reflectedPixel
(
image
,
x
,
y
),
_reflectedPixel
(
image
,
x
,
y
+
1
),
_reflectedPixel
(
image
,
x
+
1
,
y
-
1
),
_reflectedPixel
(
image
,
x
+
1
,
y
),
_reflectedPixel
(
image
,
x
+
1
,
y
+
1
),
];
return
<
int
>[
_average
(
pixels
.
map
((
p
)
=>
getRed
(
p
))),
_average
(
pixels
.
map
((
p
)
=>
getGreen
(
p
))),
_average
(
pixels
.
map
((
p
)
=>
getBlue
(
p
))),
];
case
PixelComparison
.
precise
:
final
int
pixel
=
image
.
getPixel
(
x
,
y
);
return
<
int
>[
getRed
(
pixel
),
getGreen
(
pixel
),
getBlue
(
pixel
),
];
default
:
throw
'Unrecognized pixel comparison value:
${pixelComparison}
'
;
}
}
void
_computeDiff
()
{
...
...
@@ -117,10 +157,11 @@ class ImageDiff {
for
(
int
y
=
0
;
y
<
goldenHeight
;
y
++)
{
for
(
int
x
=
0
;
x
<
goldenWidth
;
x
++)
{
final
bool
isExactlySame
=
golden
.
getPixel
(
x
,
y
)
==
other
.
getPixel
(
x
,
y
);
final
List
<
int
>
goldenPixel
=
_get
FuzzyRgb
(
golden
,
x
,
y
);
final
List
<
int
>
otherPixel
=
_get
FuzzyRgb
(
other
,
x
,
y
);
final
List
<
int
>
goldenPixel
=
_get
PixelRgbForComparison
(
golden
,
x
,
y
);
final
List
<
int
>
otherPixel
=
_get
PixelRgbForComparison
(
other
,
x
,
y
);
final
double
colorDistance
=
Color
.
distance
(
goldenPixel
,
otherPixel
,
false
)
/
_maxTheoreticalColorDistance
;
if
(
isExactlySame
||
colorDistance
<
_kColorDistanceThreshold
)
{
final
bool
isFuzzySame
=
colorDistance
<
_kColorDistanceThreshold
;
if
(
isExactlySame
||
isFuzzySame
)
{
diff
.
setPixel
(
x
,
y
,
_colorOk
);
}
else
{
final
int
goldenLuminance
=
getLuminanceRgb
(
goldenPixel
[
0
],
goldenPixel
[
1
],
goldenPixel
[
2
]);
...
...
lib/web_ui/dev/goldens_lock.yaml
浏览文件 @
8c855dba
repository
:
https://github.com/flutter/goldens.git
revision
:
c0032eeb9f9f064234991b8b5ddc15f714a53cf5
revision
:
b20fee88d8917af269e8073910c31aa373f8a188
lib/web_ui/dev/test_platform.dart
浏览文件 @
8c855dba
...
...
@@ -154,16 +154,18 @@ class BrowserPlatform extends PlatformPlugin {
final
Map
<
String
,
dynamic
>
requestData
=
json
.
decode
(
payload
);
final
String
filename
=
requestData
[
'filename'
];
final
bool
write
=
requestData
[
'write'
];
final
double
maxDiffRate
=
requestData
[
'maxdiffrate'
];
final
double
maxDiffRate
=
requestData
.
containsKey
(
'maxdiffrate'
)
?
requestData
[
'maxdiffrate'
].
toDouble
()
// can be parsed as either int or double
:
kMaxDiffRateFailure
;
final
Map
<
String
,
dynamic
>
region
=
requestData
[
'region'
];
final
String
result
=
await
_diffScreenshot
(
filename
,
write
,
maxDiffRate
??
kMaxDiffRateFailure
,
regi
on
);
final
PixelComparison
pixelComparison
=
PixelComparison
.
values
.
firstWhere
((
value
)
=>
value
.
toString
()
==
requestData
[
'pixelComparison'
]);
final
String
result
=
await
_diffScreenshot
(
filename
,
write
,
maxDiffRate
,
region
,
pixelComparis
on
);
return
shelf
.
Response
.
ok
(
json
.
encode
(
result
));
}
Future
<
String
>
_diffScreenshot
(
String
filename
,
bool
write
,
double
maxDiffRateFailure
,
[
Map
<
String
,
dynamic
>
region
]
)
async
{
Map
<
String
,
dynamic
>
region
,
PixelComparison
pixelComparison
)
async
{
if
(
doUpdateScreenshotGoldens
)
{
write
=
true
;
}
...
...
@@ -246,8 +248,10 @@ To automatically create this file call matchGoldenFile('$filename', write: true)
}
ImageDiff
diff
=
ImageDiff
(
golden:
decodeNamedImage
(
file
.
readAsBytesSync
(),
filename
),
other:
screenshot
);
golden:
decodeNamedImage
(
file
.
readAsBytesSync
(),
filename
),
other:
screenshot
,
pixelComparison:
pixelComparison
,
);
if
(
diff
.
rate
>
0
)
{
// Images are different, so produce some debug info
...
...
lib/web_ui/lib/src/engine/bitmap_canvas.dart
浏览文件 @
8c855dba
...
...
@@ -85,6 +85,13 @@ class BitmapCanvas extends EngineCanvas {
// with Widgets but CustomPainter(s) can hit this code path.
bool
_childOverdraw
=
false
;
/// Forces text to be drawn using HTML rather than bitmap.
///
/// Use this for tests only.
set
debugChildOverdraw
(
bool
value
)
{
_childOverdraw
=
value
;
}
/// Allocates a canvas with enough memory to paint a picture within the given
/// [bounds].
///
...
...
@@ -506,11 +513,10 @@ class BitmapCanvas extends EngineCanvas {
_children
.
add
(
clipElement
);
}
}
else
{
final
String
cssTransform
=
matrix4ToCssTransform3d
(
transformWithOffset
(
_canvasPool
.
currentTransform
,
offset
));
paragraphElement
.
style
..
transformOrigin
=
'0 0 0'
..
transform
=
cssTransform
;
setElementTransform
(
paragraphElement
,
transformWithOffset
(
_canvasPool
.
currentTransform
,
offset
).
storage
,
);
rootElement
.
append
(
paragraphElement
);
}
_children
.
add
(
paragraphElement
);
...
...
@@ -680,6 +686,7 @@ List<html.Element> _clipContent(List<_SaveClipEntry> clipStack,
for
(
int
clipIndex
=
0
;
clipIndex
<
len
;
clipIndex
++)
{
final
_SaveClipEntry
entry
=
clipStack
[
clipIndex
];
final
html
.
HtmlElement
newElement
=
html
.
DivElement
();
newElement
.
style
.
position
=
'absolute'
;
if
(
root
==
null
)
{
root
=
newElement
;
}
else
{
...
...
@@ -695,10 +702,9 @@ List<html.Element> _clipContent(List<_SaveClipEntry> clipStack,
..
translate
(
clipOffsetX
,
clipOffsetY
);
curElement
.
style
..
overflow
=
'hidden'
..
transform
=
matrix4ToCssTransform3d
(
newClipTransform
)
..
transformOrigin
=
'0 0 0'
..
width
=
'
${rect.right - clipOffsetX}
px'
..
height
=
'
${rect.bottom - clipOffsetY}
px'
;
setElementTransform
(
curElement
,
newClipTransform
.
storage
);
}
else
if
(
entry
.
rrect
!=
null
)
{
final
ui
.
RRect
roundRect
=
entry
.
rrect
;
final
String
borderRadius
=
...
...
@@ -711,10 +717,9 @@ List<html.Element> _clipContent(List<_SaveClipEntry> clipStack,
curElement
.
style
..
borderRadius
=
borderRadius
..
overflow
=
'hidden'
..
transform
=
matrix4ToCssTransform3d
(
newClipTransform
)
..
transformOrigin
=
'0 0 0'
..
width
=
'
${roundRect.right - clipOffsetX}
px'
..
height
=
'
${roundRect.bottom - clipOffsetY}
px'
;
setElementTransform
(
curElement
,
newClipTransform
.
storage
);
}
else
if
(
entry
.
path
!=
null
)
{
curElement
.
style
.
transform
=
matrix4ToCssTransform
(
newClipTransform
);
final
String
svgClipPath
=
_pathToSvgClipPath
(
entry
.
path
);
...
...
@@ -731,28 +736,24 @@ List<html.Element> _clipContent(List<_SaveClipEntry> clipStack,
// TODO(flutter_web): When we have more than a single clip element,
// reduce number of div nodes by merging (multiplying transforms).
final
html
.
Element
reverseTransformDiv
=
html
.
DivElement
();
reverseTransformDiv
.
style
..
transform
=
_cssTransformAtOffset
(
newClipTransform
.
clone
()..
invert
(),
0
,
0
)
..
transformOrigin
=
'0 0 0'
;
reverseTransformDiv
.
style
.
position
=
'absolute'
;
setElementTransform
(
reverseTransformDiv
,
(
newClipTransform
.
clone
()..
invert
()).
storage
,
);
curElement
.
append
(
reverseTransformDiv
);
curElement
=
reverseTransformDiv
;
}
root
.
style
.
position
=
'absolute'
;
domRenderer
.
append
(
curElement
,
content
);
content
.
style
..
transformOrigin
=
'0 0 0'
..
transform
=
_cssTransformAtOffset
(
currentTransform
,
offset
.
dx
,
offset
.
dy
);
setElementTransform
(
content
,
transformWithOffset
(
currentTransform
,
offset
).
storage
,
);
return
<
html
.
Element
>[
root
]..
addAll
(
clipDefs
);
}
String
_cssTransformAtOffset
(
Matrix4
transform
,
double
offsetX
,
double
offsetY
)
{
return
matrix4ToCssTransform3d
(
transformWithOffset
(
transform
,
ui
.
Offset
(
offsetX
,
offsetY
)));
}
String
_maskFilterToCss
(
ui
.
MaskFilter
maskFilter
)
{
if
(
maskFilter
==
null
)
return
'none'
;
return
'blur(
${maskFilter.webOnlySigma}
px)'
;
...
...
lib/web_ui/lib/src/engine/engine_canvas.dart
浏览文件 @
8c855dba
...
...
@@ -262,10 +262,10 @@ html.Element _drawParagraphElement(
..
width
=
'
${paragraph.width}
px'
;
if
(
transform
!=
null
)
{
paragraphStyle
..
transformOrigin
=
'0 0 0'
..
transform
=
matrix4ToCssTransform3d
(
transformWithOffset
(
transform
,
offset
)
);
setElementTransform
(
paragraphElement
,
transformWithOffset
(
transform
,
offset
).
storage
,
);
}
final
ParagraphGeometricStyle
style
=
paragraph
.
_geometricStyle
;
...
...
lib/web_ui/lib/src/engine/surface/clip.dart
浏览文件 @
8c855dba
...
...
@@ -79,15 +79,17 @@ class PersistedClipRect extends PersistedContainerSurface
@override
void
apply
()
{
rootElement
.
style
..
transform
=
'translate(
${rect.left}
px,
${rect.top}
px)'
..
left
=
'
${rect.left}
px'
..
top
=
'
${rect.top}
px'
..
width
=
'
${rect.right - rect.left}
px'
..
height
=
'
${rect.bottom - rect.top}
px'
;
// Translate the child container in the opposite direction to compensate for
// the shift in the coordinate system introduced by the translation of the
// rootElement. Clipping in Flutter has no effect on the coordinate system.
childContainer
.
style
.
transform
=
'translate(
${-rect.left}
px,
${-rect.top}
px)'
;
childContainer
.
style
..
left
=
'
${-rect.left}
px'
..
top
=
'
${-rect.top}
px'
;
}
@override
...
...
@@ -126,7 +128,8 @@ class PersistedClipRRect extends PersistedContainerSurface
@override
void
apply
()
{
rootElement
.
style
..
transform
=
'translate(
${rrect.left}
px,
${rrect.top}
px)'
..
left
=
'
${rrect.left}
px'
..
top
=
'
${rrect.top}
px'
..
width
=
'
${rrect.width}
px'
..
height
=
'
${rrect.height}
px'
..
borderTopLeftRadius
=
'
${rrect.tlRadiusX}
px'
...
...
@@ -137,8 +140,9 @@ class PersistedClipRRect extends PersistedContainerSurface
// Translate the child container in the opposite direction to compensate for
// the shift in the coordinate system introduced by the translation of the
// rootElement. Clipping in Flutter has no effect on the coordinate system.
childContainer
.
style
.
transform
=
'translate(
${-rrect.left}
px,
${-rrect.top}
px)'
;
childContainer
.
style
..
left
=
'
${-rrect.left}
px'
..
top
=
'
${-rrect.top}
px'
;
}
@override
...
...
@@ -218,12 +222,14 @@ class PersistedPhysicalShape extends PersistedContainerSurface
'
${roundRect.brRadiusX}
px
${roundRect.blRadiusX}
px'
;
final
html
.
CssStyleDeclaration
style
=
rootElement
.
style
;
style
..
transform
=
'translate(
${roundRect.left}
px,
${roundRect.top}
px)'
..
left
=
'
${roundRect.left}
px'
..
top
=
'
${roundRect.top}
px'
..
width
=
'
${roundRect.width}
px'
..
height
=
'
${roundRect.height}
px'
..
borderRadius
=
borderRadius
;
childContainer
.
style
.
transform
=
'translate(
${-roundRect.left}
px,
${-roundRect.top}
px)'
;
childContainer
.
style
..
left
=
'
${-roundRect.left}
px'
..
top
=
'
${-roundRect.top}
px'
;
if
(
clipBehavior
!=
ui
.
Clip
.
none
)
{
style
.
overflow
=
'hidden'
;
}
...
...
@@ -233,12 +239,14 @@ class PersistedPhysicalShape extends PersistedContainerSurface
if
(
rect
!=
null
)
{
final
html
.
CssStyleDeclaration
style
=
rootElement
.
style
;
style
..
transform
=
'translate(
${rect.left}
px,
${rect.top}
px)'
..
left
=
'
${rect.left}
px'
..
top
=
'
${rect.top}
px'
..
width
=
'
${rect.width}
px'
..
height
=
'
${rect.height}
px'
..
borderRadius
=
''
;
childContainer
.
style
.
transform
=
'translate(
${-rect.left}
px,
${-rect.top}
px)'
;
childContainer
.
style
..
left
=
'
${-rect.left}
px'
..
top
=
'
${-rect.top}
px'
;
if
(
clipBehavior
!=
ui
.
Clip
.
none
)
{
style
.
overflow
=
'hidden'
;
}
...
...
@@ -254,11 +262,14 @@ class PersistedPhysicalShape extends PersistedContainerSurface
final
double
left
=
ellipse
.
x
-
rx
;
final
double
top
=
ellipse
.
y
-
ry
;
style
..
transform
=
'translate(
${left}
px,
${top}
px)'
..
left
=
'
${left}
px'
..
top
=
'
${top}
px'
..
width
=
'
${rx * 2}
px'
..
height
=
'
${ry * 2}
px'
..
borderRadius
=
borderRadius
;
childContainer
.
style
.
transform
=
'translate(
${-left}
px,
${-top}
px)'
;
childContainer
.
style
..
left
=
'
${-left}
px'
..
top
=
'
${-top}
px'
;
if
(
clipBehavior
!=
ui
.
Clip
.
none
)
{
style
.
overflow
=
'hidden'
;
}
...
...
@@ -281,12 +292,14 @@ class PersistedPhysicalShape extends PersistedContainerSurface
final
html
.
CssStyleDeclaration
rootElementStyle
=
rootElement
.
style
;
rootElementStyle
..
overflow
=
''
..
transform
=
'translate(
${bounds.left}
px,
${bounds.top}
px)'
..
left
=
'
${bounds.left}
px'
..
top
=
'
${bounds.top}
px'
..
width
=
'
${bounds.width}
px'
..
height
=
'
${bounds.height}
px'
..
borderRadius
=
''
;
childContainer
.
style
.
transform
=
'translate(
${-bounds.left}
px,
${-bounds.top}
px)'
;
childContainer
.
style
..
left
=
'-
${bounds.left}
px'
..
top
=
'-
${bounds.top}
px'
;
}
@override
...
...
@@ -305,6 +318,8 @@ class PersistedPhysicalShape extends PersistedContainerSurface
// rect/rrect and arbitrary path.
final
html
.
CssStyleDeclaration
style
=
rootElement
.
style
;
style
.
transform
=
''
;
style
.
left
=
''
;
style
.
top
=
''
;
style
.
borderRadius
=
''
;
domRenderer
.
setElementStyle
(
rootElement
,
'clip-path'
,
''
);
domRenderer
.
setElementStyle
(
rootElement
,
'-webkit-clip-path'
,
''
);
...
...
lib/web_ui/lib/src/engine/util.dart
浏览文件 @
8c855dba
...
...
@@ -59,11 +59,71 @@ String matrix4ToCssTransform3d(Matrix4 matrix) {
return
float64ListToCssTransform3d
(
matrix
.
storage
);
}
/// Returns `true` is the [matrix] describes an identity transformation.
bool
isIdentityFloat64ListTransform
(
Float64List
matrix
)
{
/// Applies a transform to the [element].
///
/// There are several ways to transform an element. This function chooses
/// between CSS "transform", "left", "top" or no transform, depending on the
/// [matrix4] and the current device's screen properties. This function
/// attempts to avoid issues with text blurriness on low pixel density screens.
///
/// See also:
/// * https://github.com/flutter/flutter/issues/32274
/// * https://bugs.chromium.org/p/chromium/issues/detail?id=1040222
void
setElementTransform
(
html
.
Element
element
,
Float64List
matrix4
)
{
final
TransformKind
transformKind
=
transformKindOf
(
matrix4
);
// On low device-pixel ratio screens using CSS "transform" causes text blurriness
// at least on Blink browsers. We therefore prefer using CSS "left" and "top" instead.
final
bool
isHighDevicePixelRatioScreen
=
EngineWindow
.
browserDevicePixelRatio
>
1.0
;
if
(
transformKind
==
TransformKind
.
complex
||
isHighDevicePixelRatioScreen
)
{
final
String
cssTransform
=
float64ListToCssTransform3d
(
matrix4
);
element
.
style
..
transformOrigin
=
'0 0 0'
..
transform
=
cssTransform
..
top
=
null
..
left
=
null
;
}
else
if
(
transformKind
==
TransformKind
.
translation2d
)
{
final
double
ty
=
matrix4
[
13
];
final
double
tx
=
matrix4
[
12
];
element
.
style
..
transformOrigin
=
null
..
transform
=
null
..
left
=
'
${tx}
px'
..
top
=
'
${ty}
px'
;
}
else
{
assert
(
transformKind
==
TransformKind
.
identity
);
element
.
style
..
transformOrigin
=
null
..
transform
=
null
..
left
=
null
..
top
=
null
;
}
}
/// The kind of effect a transform matrix performs.
enum
TransformKind
{
/// No effect.
identity
,
/// A translation along either X or Y axes, or both.
translation2d
,
/// All other kinds of transforms.
complex
,
}
/// Detects the kind of transform the [matrix] performs.
TransformKind
transformKindOf
(
Float64List
matrix
)
{
assert
(
matrix
.
length
==
16
);
final
Float64List
m
=
matrix
;
return
m
[
0
]
==
1.0
&&
final
double
ty
=
m
[
13
];
final
double
tx
=
m
[
12
];
// If matrix contains scaling, rotation, z translation or
// perspective transform, it is not considered simple.
final
bool
isSimpleTransform
=
m
[
0
]
==
1.0
&&
m
[
1
]
==
0.0
&&
m
[
2
]
==
0.0
&&
m
[
3
]
==
0.0
&&
...
...
@@ -75,10 +135,26 @@ bool isIdentityFloat64ListTransform(Float64List matrix) {
m
[
9
]
==
0.0
&&
m
[
10
]
==
1.0
&&
m
[
11
]
==
0.0
&&
m
[
12
]
==
0.0
&&
m
[
13
]
==
0.0
&&
m
[
14
]
==
0.0
&&
// m[12] - x translation is simple
// m[13] - y translation is simple
m
[
14
]
==
0.0
&&
// z translation is NOT simple
m
[
15
]
==
1.0
;
if
(!
isSimpleTransform
)
{
return
TransformKind
.
complex
;
}
if
(
ty
!=
0.0
||
tx
!=
0.0
)
{
return
TransformKind
.
translation2d
;
}
return
TransformKind
.
identity
;
}
/// Returns `true` is the [matrix] describes an identity transformation.
bool
isIdentityFloat64ListTransform
(
Float64List
matrix
)
{
assert
(
matrix
.
length
==
16
);
return
transformKindOf
(
matrix
)
==
TransformKind
.
identity
;
}
/// Converts [matrix] to CSS transform value.
...
...
lib/web_ui/test/engine/util_test.dart
0 → 100644
浏览文件 @
8c855dba
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:typed_data'
;
import
'package:ui/src/engine.dart'
;
import
'package:test/test.dart'
;
final
Float64List
identityTransform
=
Matrix4
.
identity
().
storage
;
final
Float64List
xTranslation
=
(
Matrix4
.
identity
()..
translate
(
10
)).
storage
;
final
Float64List
yTranslation
=
(
Matrix4
.
identity
()..
translate
(
0
,
10
)).
storage
;
final
Float64List
zTranslation
=
(
Matrix4
.
identity
()..
translate
(
0
,
0
,
10
)).
storage
;
void
main
(
)
{
test
(
'transformKindOf and isIdentityFloat64ListTransform identify matrix kind'
,
()
{
expect
(
transformKindOf
(
identityTransform
),
TransformKind
.
identity
);
expect
(
isIdentityFloat64ListTransform
(
identityTransform
),
isTrue
);
expect
(
transformKindOf
(
xTranslation
),
TransformKind
.
translation2d
);
expect
(
isIdentityFloat64ListTransform
(
xTranslation
),
isFalse
);
expect
(
transformKindOf
(
yTranslation
),
TransformKind
.
translation2d
);
expect
(
isIdentityFloat64ListTransform
(
yTranslation
),
isFalse
);
expect
(
transformKindOf
(
zTranslation
),
TransformKind
.
complex
);
expect
(
isIdentityFloat64ListTransform
(
zTranslation
),
isFalse
);
});
}
lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart
浏览文件 @
8c855dba
...
...
@@ -11,17 +11,25 @@ import 'package:test/test.dart';
import
'package:web_engine_tester/golden_tester.dart'
;
void
main
(
)
async
{
final
Rect
region
=
Rect
.
fromLTWH
(
8
,
8
,
500
,
100
);
// Compensate for old scuba tester padding
final
Rect
region
=
Rect
.
fromLTWH
(
0
,
0
,
500
,
100
);
BitmapCanvas
canvas
;
setUp
(()
{
html
.
document
.
body
.
style
.
transform
=
'translate(10px, 10px)'
;
void
appendToScene
()
{
// Create a <flt-scene> element to make sure our CSS reset applies correctly.
final
html
.
Element
testScene
=
html
.
Element
.
tag
(
'flt-scene'
);
testScene
.
append
(
canvas
.
rootElement
);
html
.
document
.
querySelector
(
'flt-scene-host'
).
append
(
testScene
);
}
setUp
(()
async
{
await
webOnlyInitializePlatform
();
webOnlyFontCollection
.
debugRegisterTestFonts
();
await
webOnlyFontCollection
.
ensureFontsLoaded
();
});
tearDown
(()
{
html
.
document
.
body
.
style
.
transform
=
'none'
;
canvas
.
rootElement
.
remove
();
html
.
document
.
querySelector
(
'flt-scene'
).
remove
();
});
/// Draws several lines, some aligned precisely with the pixel grid, and some
...
...
@@ -40,6 +48,8 @@ void main() async {
final
SurfacePaintData
fillPaint
=
(
SurfacePaint
()..
style
=
PaintingStyle
.
fill
).
paintData
;
canvas
.
translate
(
10
,
10
);
canvas
.
drawRect
(
const
Rect
.
fromLTWH
(
0
,
0
,
40
,
40
),
linePaint
,
...
...
@@ -66,7 +76,7 @@ void main() async {
drawMisalignedLines
(
canvas
);
html
.
document
.
body
.
append
(
canvas
.
rootElement
);
appendToScene
(
);
await
matchGoldenFile
(
'misaligned_pixels_in_canvas_test.png'
,
region:
region
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
...
...
@@ -81,7 +91,7 @@ void main() async {
drawMisalignedLines
(
canvas
);
html
.
document
.
body
.
append
(
canvas
.
rootElement
);
appendToScene
(
);
await
matchGoldenFile
(
'misaligned_canvas_test.png'
,
region:
region
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
...
...
@@ -92,7 +102,7 @@ void main() async {
canvas
.
translate
(
25
,
25
);
canvas
.
drawColor
(
const
Color
.
fromRGBO
(
0
,
255
,
0
,
1.0
),
BlendMode
.
src
);
html
.
document
.
body
.
append
(
canvas
.
rootElement
);
appendToScene
(
);
await
matchGoldenFile
(
'bitmap_canvas_fills_color_when_transformed.png'
,
region:
region
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
...
...
@@ -105,8 +115,61 @@ void main() async {
..
color
=
const
Color
.
fromRGBO
(
0
,
255
,
0
,
1.0
)
..
style
=
PaintingStyle
.
fill
);
html
.
document
.
body
.
append
(
canvas
.
rootElement
);
appendToScene
(
);
await
matchGoldenFile
(
'bitmap_canvas_fills_paint_when_transformed.png'
,
region:
region
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
// This test reproduces text blurriness when two pieces of text appear inside
// two nested clips:
//
// ┌───────────────────────┐
// │ text in outer clip │
// │ ┌────────────────────┐│
// │ │ text in inner clip ││
// │ └────────────────────┘│
// └───────────────────────┘
//
// This test clips using canvas. See a similar test in `compositing_golden_test.dart`,
// which clips using layers.
//
// More details: https://github.com/flutter/flutter/issues/32274
test
(
'renders clipped DOM text with high quality'
,
()
async
{
final
Paragraph
paragraph
=
(
ParagraphBuilder
(
ParagraphStyle
(
fontFamily:
'Roboto'
))..
addText
(
'Am I blurry?'
)).
build
();
paragraph
.
layout
(
const
ParagraphConstraints
(
width:
1000
));
final
Rect
canvasSize
=
Rect
.
fromLTRB
(
0
,
0
,
paragraph
.
maxIntrinsicWidth
+
16
,
2
*
paragraph
.
height
+
32
,
);
final
Rect
outerClip
=
Rect
.
fromLTRB
(
0.5
,
0.5
,
canvasSize
.
right
,
canvasSize
.
bottom
);
final
Rect
innerClip
=
Rect
.
fromLTRB
(
0.5
,
canvasSize
.
bottom
/
2
+
0.5
,
canvasSize
.
right
,
canvasSize
.
bottom
);
canvas
=
BitmapCanvas
(
canvasSize
);
canvas
.
debugChildOverdraw
=
true
;
canvas
.
clipRect
(
outerClip
);
canvas
.
drawParagraph
(
paragraph
,
const
Offset
(
8.5
,
8.5
));
canvas
.
clipRect
(
innerClip
);
canvas
.
drawParagraph
(
paragraph
,
Offset
(
8.5
,
8.5
+
innerClip
.
top
));
expect
(
canvas
.
rootElement
.
querySelectorAll
(
'p'
).
map
<
String
>((
e
)
=>
e
.
innerText
).
toList
(),
<
String
>[
'Am I blurry?'
,
'Am I blurry?'
],
reason:
'Expected to render text using HTML'
,
);
appendToScene
();
await
matchGoldenFile
(
'bitmap_canvas_draws_high_quality_text.png'
,
region:
canvasSize
,
maxDiffRate:
0.0
,
pixelComparison:
PixelComparison
.
precise
,
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)),
testOn:
'chrome'
);
}
lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart
浏览文件 @
8c855dba
...
...
@@ -15,13 +15,16 @@ import 'package:web_engine_tester/golden_tester.dart';
final
Rect
region
=
Rect
.
fromLTWH
(
0
,
0
,
500
,
100
);
void
main
(
)
async
{
debugShowClipLayers
=
true
;
setUp
(()
{
setUp
(()
async
{
debugShowClipLayers
=
true
;
SurfaceSceneBuilder
.
debugForgetFrameScene
();
for
(
html
.
Node
scene
in
html
.
document
.
querySelectorAll
(
'flt-scene'
))
{
scene
.
remove
();
}
await
webOnlyInitializePlatform
();
webOnlyFontCollection
.
debugRegisterTestFonts
();
await
webOnlyFontCollection
.
ensureFontsLoaded
();
});
test
(
'pushClipRect'
,
()
async
{
...
...
@@ -54,7 +57,8 @@ void main() async {
html
.
document
.
body
.
append
(
builder
.
build
().
webOnlyRootElement
);
await
matchGoldenFile
(
'compositing_clip_rect_with_offset_and_transform.png'
,
region:
region
);
await
matchGoldenFile
(
'compositing_clip_rect_with_offset_and_transform.png'
,
region:
region
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
test
(
'pushClipRRect'
,
()
async
{
...
...
@@ -95,7 +99,8 @@ void main() async {
html
.
document
.
body
.
append
(
builder
.
build
().
webOnlyRootElement
);
await
matchGoldenFile
(
'compositing_shifted_physical_shape_clip.png'
,
region:
region
);
await
matchGoldenFile
(
'compositing_shifted_physical_shape_clip.png'
,
region:
region
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
test
(
'pushImageFilter'
,
()
async
{
...
...
@@ -235,7 +240,8 @@ void _testCullRectComputation() {
builder
.
pop
();
// pushClipRect
html
.
document
.
body
.
append
(
builder
.
build
().
webOnlyRootElement
);
await
matchGoldenFile
(
'compositing_cull_rect_fills_layer_clip.png'
,
region:
region
);
await
matchGoldenFile
(
'compositing_cull_rect_fills_layer_clip.png'
,
region:
region
);
final
PersistedStandardPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
40
,
40
,
70
,
70
));
...
...
@@ -263,7 +269,9 @@ void _testCullRectComputation() {
builder
.
pop
();
// pushClipRect
html
.
document
.
body
.
append
(
builder
.
build
().
webOnlyRootElement
);
await
matchGoldenFile
(
'compositing_cull_rect_intersects_clip_and_paint_bounds.png'
,
region:
region
);
await
matchGoldenFile
(
'compositing_cull_rect_intersects_clip_and_paint_bounds.png'
,
region:
region
);
final
PersistedStandardPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
const
Rect
.
fromLTRB
(
50
,
40
,
70
,
70
));
...
...
@@ -293,7 +301,8 @@ void _testCullRectComputation() {
builder
.
pop
();
// pushClipRect
html
.
document
.
body
.
append
(
builder
.
build
().
webOnlyRootElement
);
await
matchGoldenFile
(
'compositing_cull_rect_offset_inside_layer_clip.png'
,
region:
region
);
await
matchGoldenFile
(
'compositing_cull_rect_offset_inside_layer_clip.png'
,
region:
region
);
final
PersistedStandardPicture
picture
=
enumeratePictures
().
single
;
expect
(
picture
.
optimalLocalCullRect
,
...
...
@@ -493,7 +502,8 @@ void _testCullRectComputation() {
await
matchGoldenFile
(
'compositing_3d_rotate1.png'
,
region:
region
);
final
PersistedStandardPicture
picture
=
enumeratePictures
().
single
;
// ignore: unused_local_variable
// ignore: unused_local_variable
final
PersistedStandardPicture
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(
...
...
@@ -504,6 +514,91 @@ void _testCullRectComputation() {
// -140, -140, screenWidth - 360.0, screenHeight + 40.0)),
// );
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)));
// This test reproduces text blurriness when two pieces of text appear inside
// two nested clips:
//
// ┌───────────────────────┐
// │ text in outer clip │
// │ ┌────────────────────┐│
// │ │ text in inner clip ││
// │ └────────────────────┘│
// └───────────────────────┘
//
// This test clips using layers. See a similar test in `canvas_golden_test.dart`,
// which clips using canvas.
//
// More details: https://github.com/flutter/flutter/issues/32274
test
(
'renders clipped text with high quality'
,
()
async
{
// To reproduce blurriness we need real clipping.
debugShowClipLayers
=
false
;
final
Paragraph
paragraph
=
(
ParagraphBuilder
(
ParagraphStyle
(
fontFamily:
'Roboto'
))..
addText
(
'Am I blurry?'
)).
build
();
paragraph
.
layout
(
const
ParagraphConstraints
(
width:
1000
));
final
Rect
canvasSize
=
Rect
.
fromLTRB
(
0
,
0
,
paragraph
.
maxIntrinsicWidth
+
16
,
2
*
paragraph
.
height
+
32
,
);
final
Rect
outerClip
=
Rect
.
fromLTRB
(
0.5
,
0.5
,
canvasSize
.
right
,
canvasSize
.
bottom
);
final
Rect
innerClip
=
Rect
.
fromLTRB
(
0.5
,
canvasSize
.
bottom
/
2
+
0.5
,
canvasSize
.
right
,
canvasSize
.
bottom
);
final
SurfaceSceneBuilder
builder
=
SurfaceSceneBuilder
();
builder
.
pushClipRect
(
outerClip
);
{
final
EnginePictureRecorder
recorder
=
PictureRecorder
();
final
RecordingCanvas
canvas
=
recorder
.
beginRecording
(
outerClip
);
canvas
.
drawParagraph
(
paragraph
,
const
Offset
(
8.5
,
8.5
));
final
Picture
picture
=
recorder
.
endRecording
();
expect
(
canvas
.
hasArbitraryPaint
,
false
);
builder
.
addPicture
(
Offset
.
zero
,
picture
,
);
}
builder
.
pushClipRect
(
innerClip
);
{
final
EnginePictureRecorder
recorder
=
PictureRecorder
();
final
RecordingCanvas
canvas
=
recorder
.
beginRecording
(
innerClip
);
canvas
.
drawParagraph
(
paragraph
,
Offset
(
8.5
,
8.5
+
innerClip
.
top
));
final
Picture
picture
=
recorder
.
endRecording
();
expect
(
canvas
.
hasArbitraryPaint
,
false
);
builder
.
addPicture
(
Offset
.
zero
,
picture
,
);
}
builder
.
pop
();
// inner clip
builder
.
pop
();
// outer clip
final
html
.
Element
sceneElement
=
builder
.
build
().
webOnlyRootElement
;
expect
(
sceneElement
.
querySelectorAll
(
'p'
).
map
<
String
>((
e
)
=>
e
.
innerText
).
toList
(),
<
String
>[
'Am I blurry?'
,
'Am I blurry?'
],
reason:
'Expected to render text using HTML'
,
);
html
.
document
.
body
.
append
(
sceneElement
);
await
matchGoldenFile
(
'compositing_draw_high_quality_text.png'
,
region:
canvasSize
,
maxDiffRate:
0.0
,
pixelComparison:
PixelComparison
.
precise
,
);
},
timeout:
const
Timeout
(
Duration
(
seconds:
10
)),
testOn:
'chrome'
,
);
}
void
_drawTestPicture
(
SceneBuilder
builder
)
{
...
...
web_sdk/web_engine_tester/lib/golden_tester.dart
浏览文件 @
8c855dba
...
...
@@ -20,9 +20,36 @@ Future<dynamic> _callScreenshotServer(dynamic requestData) async {
return
json
.
decode
(
request
.
responseText
);
}
/// How to compare pixels within the image.
///
/// Keep this enum in sync with the one defined in `goldens.dart`.
enum
PixelComparison
{
/// Allows minor blur and anti-aliasing differences by comparing a 3x3 grid
/// surrounding the pixel rather than direct 1:1 comparison.
fuzzy
,
/// Compares one pixel at a time.
///
/// Anti-aliasing or blur will result in higher diff rate.
precise
,
}
/// Attempts to match the current browser state with the screenshot [filename].
///
/// If [write] is true, will overwrite the golden file and fail the test. Use
/// it to update golden files.
///
/// If [region] is not null, the golden will only include the part contained by
/// the rectangle.
///
/// [maxDiffRate] specifies the tolerance to the number of non-matching pixels
/// before the test is considered as failing. If [maxDiffRate] is null, applies
/// a default value defined in `test_platform.dart`.
///
/// [pixelComparison] determines the algorithm used to compare pixels. Uses
/// fuzzy comparison by default.
Future
<
void
>
matchGoldenFile
(
String
filename
,
{
bool
write
=
false
,
Rect
region
=
null
,
double
maxDiffRate
=
null
})
async
{
{
bool
write
=
false
,
Rect
region
=
null
,
double
maxDiffRate
=
null
,
PixelComparison
pixelComparison
=
PixelComparison
.
fuzzy
})
async
{
Map
<
String
,
dynamic
>
serverParams
=
<
String
,
dynamic
>{
'filename'
:
filename
,
'write'
:
write
,
...
...
@@ -33,7 +60,8 @@ Future<void> matchGoldenFile(String filename,
'y'
:
region
.
top
,
'width'
:
region
.
width
,
'height'
:
region
.
height
}
},
'pixelComparison'
:
pixelComparison
.
toString
(),
};
if
(
maxDiffRate
!=
null
)
{
serverParams
[
'maxdiffrate'
]
=
maxDiffRate
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录