Commit d1b5a7b7 authored by Chris Harrelson's avatar Chris Harrelson Committed by Commit Bot

Make FEDisplacementMap a pass-through filters if the origin is tainted.

To do this, taint the source graphic of a canvas input to filters if it
is cross-origin.

Bug: 778506
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I7e070c887dbb8bfce64f660790d58836f5d75762
Reviewed-on: https://chromium-review.googlesource.com/782649
Commit-Queue: Chris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarFredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#522143}
parent b76ab6a6
<!DOCTYPE html> <!DOCTYPE html>
<div style="width: 100px; height: 100px; background-color: green"></div> <div style="position: relative; top: 20px; left: 20px; width: 100px; height: 100px; background-color: green"></div>
<!DOCTYPE html>
<div style="width: 200px; height: 200px; background: black"></img>
<!DOCTYPE html>
<canvas id="canvas" width="200" height="200"></canvas>
<svg height="0">
<filter id="filter">
<feFlood fill-color="lime"/>
<feDisplacementMap in2="SourceGraphic" xChannelSelector="R" yChannelSelector="G" scale="100"/>
</filter>
</svg>
<script>
if (window.testRunner)
window.testRunner.waitUntilDone();
// Passes if the output is a black square.
var img = new Image();
// Use a cross-origin URL.
img.src = "http://localhost:8000/resources/square.png";
img.onload = function () {
var ctx = document.getElementById("canvas").getContext("2d");
ctx.filter = "url(#filter)";
ctx.drawImage(img, 0, 0);
window.testRunner.notifyDone();
}
</script>
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"paintInvalidations": [ "paintInvalidations": [
{ {
"object": "LayoutBlockFlow DIV class='box'", "object": "LayoutBlockFlow DIV class='box'",
"rect": [8, 0, 240, 240], "rect": [28, 20, 200, 200],
"reason": "style change" "reason": "style change"
} }
] ]
......
{
"layers": [
{
"name": "LayoutView #document",
"bounds": [800, 600],
"contentsOpaque": true,
"backgroundColor": "#FFFFFF",
"paintInvalidations": [
{
"object": "LayoutBlockFlow DIV class='box'",
"rect": [28, 20, 200, 200],
"reason": "style change"
}
]
}
],
"objectPaintInvalidations": [
{
"object": "LayoutBlockFlow DIV class='box'",
"reason": "style change"
}
]
}
<svg version="1.2" width="500" height="300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect fill="rgb(50%,50%,50%)" stroke="green" x="20" y="20" width="75" height="75"/>
<rect fill="rgb(50%,50%,50%)" stroke="green" x="186" y="20" width="54" height="75"/>
<rect fill="rgb(50%,50%,50%)" stroke="green" x="304" y="64" width="56" height="56"/>
<rect fill="rgb(50%,50%,50%)" stroke="green" x="44" y="164" width="76" height="76"/>
<rect fill="rgb(50%,50%,50%)" stroke="green" x="140" y="140" width="75" height="75"/>
<rect fill="rgb(50%,50%,50%)" stroke="green" x="287" y="167" width="73" height="73"/>
</svg>
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 500x300
LayoutSVGRoot {svg} at (0,0) size 500x300
LayoutSVGHiddenContainer {defs} at (0,0) size 0x0
LayoutSVGResourceFilter {filter} [id="oBBoBBWhiteScale"] [filterUnits=objectBoundingBox] [primitiveUnits=objectBoundingBox]
[feDisplacementMap scale="0.50" xChannelSelector="RED" yChannelSelector="GREEN"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
[feFlood flood-color="#FFFF00" flood-opacity="1.00"]
LayoutSVGResourceFilter {filter} [id="oBBoBBLight"] [filterUnits=objectBoundingBox] [primitiveUnits=objectBoundingBox]
[feDisplacementMap scale="1.00" xChannelSelector="BLUE" yChannelSelector="ALPHA"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
[feFlood flood-color="#000030" flood-opacity="0.75"]
LayoutSVGResourceFilter {filter} [id="oBBoBBDark"] [filterUnits=objectBoundingBox] [primitiveUnits=objectBoundingBox]
[feDisplacementMap scale="1.00" xChannelSelector="RED" yChannelSelector="BLUE"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
[feFlood flood-color="#40FF40" flood-opacity="1.00"]
LayoutSVGResourceFilter {filter} [id="USOUOBBBlackScale"] [filterUnits=userSpaceOnUse] [primitiveUnits=objectBoundingBox]
[feDisplacementMap scale="0.50" xChannelSelector="GREEN" yChannelSelector="BLUE"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
[feFlood flood-color="#FF0000" flood-opacity="1.00"]
LayoutSVGResourceFilter {filter} [id="OBBUSOUWhite"] [filterUnits=objectBoundingBox] [primitiveUnits=userSpaceOnUse]
[feDisplacementMap scale="50.00" xChannelSelector="RED" yChannelSelector="GREEN"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
[feFlood flood-color="#FFFF80" flood-opacity="1.00"]
LayoutSVGResourceFilter {filter} [id="USOUUSOUGrey"] [filterUnits=userSpaceOnUse] [primitiveUnits=userSpaceOnUse]
[feDisplacementMap scale="100.00" xChannelSelector="RED" yChannelSelector="GREEN"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
[feFlood flood-color="#808080" flood-opacity="1.00"]
LayoutSVGRect {rect} at (20,20) size 100x100 [x=20.00] [y=20.00] [width=100.00] [height=100.00]
[filter="oBBoBBWhiteScale"] LayoutSVGResourceFilter {filter} at (20,20) size 100x100
LayoutSVGRect {rect} at (20,20) size 75x75 [stroke={[type=SOLID] [color=#008000]}] [x=20.00] [y=20.00] [width=75.00] [height=75.00]
LayoutSVGRect {rect} at (140,20) size 100x100 [x=140.00] [y=20.00] [width=100.00] [height=100.00]
[filter="oBBoBBLight"] LayoutSVGResourceFilter {filter} at (140,20) size 100x100
LayoutSVGRect {rect} at (186,20) size 54x75 [stroke={[type=SOLID] [color=#008000]}] [x=186.00] [y=20.00] [width=54.00] [height=75.00]
LayoutSVGRect {rect} at (260,20) size 100x100 [x=260.00] [y=20.00] [width=100.00] [height=100.00]
[filter="oBBoBBDark"] LayoutSVGResourceFilter {filter} at (260,20) size 100x100
LayoutSVGRect {rect} at (304,64) size 56x56 [stroke={[type=SOLID] [color=#008000]}] [x=304.00] [y=64.00] [width=56.00] [height=56.00]
LayoutSVGRect {rect} at (20,140) size 100x100 [stroke={[type=SOLID] [color=#008000]}] [x=20.00] [y=140.00] [width=100.00] [height=100.00]
[filter="USOUOBBBlackScale"] LayoutSVGResourceFilter {filter} at (20,140) size 100x100
LayoutSVGRect {rect} at (44,164) size 76x76 [stroke={[type=SOLID] [color=#008000]}] [x=44.00] [y=164.00] [width=76.00] [height=76.00]
LayoutSVGRect {rect} at (140,140) size 100x100 [x=140.00] [y=140.00] [width=100.00] [height=100.00]
[filter="OBBUSOUWhite"] LayoutSVGResourceFilter {filter} at (140,140) size 100x100
LayoutSVGRect {rect} at (140,140) size 75x75 [stroke={[type=SOLID] [color=#008000]}] [x=140.00] [y=140.00] [width=75.00] [height=75.00]
LayoutSVGRect {rect} at (260,140) size 100x100 [x=260.00] [y=140.00] [width=100.00] [height=100.00]
[filter="USOUUSOUGrey"] LayoutSVGResourceFilter {filter} at (260,140) size 100x100
LayoutSVGRect {rect} at (287,167) size 73x73 [stroke={[type=SOLID] [color=#008000]}] [x=287.00] [y=167.00] [width=73.00] [height=73.00]
<!DOCTYPE html> <!DOCTYPE html>
<div style="width: 100px; height: 100px; background-color: green"></div> <div style="position: relative; left: 20px; top: 20px; width: 100px; height: 100px; background-color: green"></div>
...@@ -142,11 +142,14 @@ FilterEffectBuilder::FilterEffectBuilder(Node* target, ...@@ -142,11 +142,14 @@ FilterEffectBuilder::FilterEffectBuilder(Node* target,
stroke_flags_(stroke_flags) {} stroke_flags_(stroke_flags) {}
FilterEffect* FilterEffectBuilder::BuildFilterEffect( FilterEffect* FilterEffectBuilder::BuildFilterEffect(
const FilterOperations& operations) const { const FilterOperations& operations,
bool input_tainted) const {
// Create a parent filter for shorthand filters. These have already been // Create a parent filter for shorthand filters. These have already been
// scaled by the CSS code for page zoom, so scale is 1.0 here. // scaled by the CSS code for page zoom, so scale is 1.0 here.
Filter* parent_filter = Filter::Create(1.0f); Filter* parent_filter = Filter::Create(1.0f);
FilterEffect* previous_effect = parent_filter->GetSourceGraphic(); FilterEffect* previous_effect = parent_filter->GetSourceGraphic();
if (input_tainted)
previous_effect->SetOriginTainted();
for (FilterOperation* filter_operation : operations.Operations()) { for (FilterOperation* filter_operation : operations.Operations()) {
FilterEffect* effect = nullptr; FilterEffect* effect = nullptr;
switch (filter_operation->GetType()) { switch (filter_operation->GetType()) {
......
...@@ -62,7 +62,8 @@ class CORE_EXPORT FilterEffectBuilder final { ...@@ -62,7 +62,8 @@ class CORE_EXPORT FilterEffectBuilder final {
FilterEffect* previous_effect, FilterEffect* previous_effect,
SVGFilterGraphNodeMap* = nullptr) const; SVGFilterGraphNodeMap* = nullptr) const;
FilterEffect* BuildFilterEffect(const FilterOperations&) const; FilterEffect* BuildFilterEffect(const FilterOperations&,
bool input_tainted = false) const;
CompositorFilterOperations BuildFilterOperations( CompositorFilterOperations BuildFilterOperations(
const FilterOperations&) const; const FilterOperations&) const;
......
...@@ -195,8 +195,10 @@ void BaseRenderingContext2D::setStrokeStyle( ...@@ -195,8 +195,10 @@ void BaseRenderingContext2D::setStrokeStyle(
} else if (style.IsCanvasPattern()) { } else if (style.IsCanvasPattern()) {
CanvasPattern* canvas_pattern = style.GetAsCanvasPattern(); CanvasPattern* canvas_pattern = style.GetAsCanvasPattern();
if (OriginClean() && !canvas_pattern->OriginClean()) if (OriginClean() && !canvas_pattern->OriginClean()) {
SetOriginTainted(); SetOriginTainted();
ClearResolvedFilters();
}
canvas_style = CanvasStyle::CreateFromPattern(canvas_pattern); canvas_style = CanvasStyle::CreateFromPattern(canvas_pattern);
} }
...@@ -236,8 +238,10 @@ void BaseRenderingContext2D::setFillStyle( ...@@ -236,8 +238,10 @@ void BaseRenderingContext2D::setFillStyle(
} else if (style.IsCanvasPattern()) { } else if (style.IsCanvasPattern()) {
CanvasPattern* canvas_pattern = style.GetAsCanvasPattern(); CanvasPattern* canvas_pattern = style.GetAsCanvasPattern();
if (OriginClean() && !canvas_pattern->OriginClean()) if (OriginClean() && !canvas_pattern->OriginClean()) {
SetOriginTainted(); SetOriginTainted();
ClearResolvedFilters();
}
if (canvas_pattern->GetPattern()->IsTextureBacked()) if (canvas_pattern->GetPattern()->IsTextureBacked())
DisableDeferral(kDisableDeferralReasonUsingTextureBackedPattern); DisableDeferral(kDisableDeferralReasonUsingTextureBackedPattern);
canvas_style = CanvasStyle::CreateFromPattern(canvas_pattern); canvas_style = CanvasStyle::CreateFromPattern(canvas_pattern);
...@@ -1103,6 +1107,11 @@ bool ShouldDisableDeferral(CanvasImageSource* image_source, ...@@ -1103,6 +1107,11 @@ bool ShouldDisableDeferral(CanvasImageSource* image_source,
return false; return false;
} }
void BaseRenderingContext2D::ClearResolvedFilters() {
for (auto& state : state_stack_)
state->ClearResolvedFilter();
}
void BaseRenderingContext2D::drawImage(ScriptState* script_state, void BaseRenderingContext2D::drawImage(ScriptState* script_state,
CanvasImageSource* image_source, CanvasImageSource* image_source,
double sx, double sx,
...@@ -1265,6 +1274,12 @@ void BaseRenderingContext2D::drawImage(ScriptState* script_state, ...@@ -1265,6 +1274,12 @@ void BaseRenderingContext2D::drawImage(ScriptState* script_state,
ValidateStateStack(); ValidateStateStack();
if (OriginClean() &&
WouldTaintOrigin(image_source, ExecutionContext::From(script_state))) {
SetOriginTainted();
ClearResolvedFilters();
}
Draw( Draw(
[this, &image_source, &image, &src_rect, dst_rect]( [this, &image_source, &image, &src_rect, dst_rect](
PaintCanvas* c, const PaintFlags* flags) // draw lambda PaintCanvas* c, const PaintFlags* flags) // draw lambda
...@@ -1281,10 +1296,6 @@ void BaseRenderingContext2D::drawImage(ScriptState* script_state, ...@@ -1281,10 +1296,6 @@ void BaseRenderingContext2D::drawImage(ScriptState* script_state,
ValidateStateStack(); ValidateStateStack();
if (OriginClean() &&
WouldTaintOrigin(image_source, ExecutionContext::From(script_state)))
SetOriginTainted();
if (!IsPaint2D()) { if (!IsPaint2D()) {
DCHECK(start_time); DCHECK(start_time);
timer->Count((WTF::MonotonicallyIncreasingTime() - start_time) * timer->Count((WTF::MonotonicallyIncreasingTime() - start_time) *
......
...@@ -410,6 +410,7 @@ class MODULES_EXPORT BaseRenderingContext2D : public GarbageCollectedMixin, ...@@ -410,6 +410,7 @@ class MODULES_EXPORT BaseRenderingContext2D : public GarbageCollectedMixin,
void ClearCanvas(); void ClearCanvas();
bool RectContainsTransformedRect(const FloatRect&, const SkIRect&) const; bool RectContainsTransformedRect(const FloatRect&, const SkIRect&) const;
void ClearResolvedFilters();
ImageDataColorSettings GetColorSettingsAsImageDataColorSettings() const; ImageDataColorSettings GetColorSettingsAsImageDataColorSettings() const;
}; };
......
...@@ -591,7 +591,7 @@ bool CanvasRenderingContext2D::OriginClean() const { ...@@ -591,7 +591,7 @@ bool CanvasRenderingContext2D::OriginClean() const {
} }
void CanvasRenderingContext2D::SetOriginTainted() { void CanvasRenderingContext2D::SetOriginTainted() {
return Host()->SetOriginTainted(); Host()->SetOriginTainted();
} }
int CanvasRenderingContext2D::Width() const { int CanvasRenderingContext2D::Width() const {
......
...@@ -277,7 +277,8 @@ void CanvasRenderingContext2DState::ResetTransform() { ...@@ -277,7 +277,8 @@ void CanvasRenderingContext2DState::ResetTransform() {
} }
sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilterForOffscreenCanvas( sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilterForOffscreenCanvas(
IntSize canvas_size) const { IntSize canvas_size,
BaseRenderingContext2D* context) const {
if (!filter_value_) if (!filter_value_)
return nullptr; return nullptr;
...@@ -301,9 +302,10 @@ sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilterForOffscreenCanvas( ...@@ -301,9 +302,10 @@ sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilterForOffscreenCanvas(
1.0f, // Deliberately ignore zoom on the canvas element. 1.0f, // Deliberately ignore zoom on the canvas element.
&fill_flags_for_filter, &stroke_flags_for_filter); &fill_flags_for_filter, &stroke_flags_for_filter);
FilterEffect* last_effect = FilterEffect* last_effect = filter_effect_builder.BuildFilterEffect(
filter_effect_builder.BuildFilterEffect(operations); operations, !context->OriginClean());
if (last_effect) { if (last_effect) {
// TODO(chrishtr): Taint the origin if needed. crbug.com/792506.
resolved_filter_ = resolved_filter_ =
PaintFilterBuilder::Build(last_effect, kInterpolationSpaceSRGB); PaintFilterBuilder::Build(last_effect, kInterpolationSpaceSRGB);
} }
...@@ -355,8 +357,8 @@ sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilter( ...@@ -355,8 +357,8 @@ sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilter(
1.0f, // Deliberately ignore zoom on the canvas element. 1.0f, // Deliberately ignore zoom on the canvas element.
&fill_flags_for_filter, &stroke_flags_for_filter); &fill_flags_for_filter, &stroke_flags_for_filter);
if (FilterEffect* last_effect = if (FilterEffect* last_effect = filter_effect_builder.BuildFilterEffect(
filter_effect_builder.BuildFilterEffect(filter_style->Filter())) { filter_style->Filter(), !context->OriginClean())) {
resolved_filter_ = resolved_filter_ =
PaintFilterBuilder::Build(last_effect, kInterpolationSpaceSRGB); PaintFilterBuilder::Build(last_effect, kInterpolationSpaceSRGB);
if (resolved_filter_) { if (resolved_filter_) {
...@@ -371,10 +373,11 @@ sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilter( ...@@ -371,10 +373,11 @@ sk_sp<PaintFilter> CanvasRenderingContext2DState::GetFilter(
} }
bool CanvasRenderingContext2DState::HasFilterForOffscreenCanvas( bool CanvasRenderingContext2DState::HasFilterForOffscreenCanvas(
IntSize canvas_size) const { IntSize canvas_size,
BaseRenderingContext2D* context) const {
// Checking for a non-null m_filterValue isn't sufficient, since this value // Checking for a non-null m_filterValue isn't sufficient, since this value
// might refer to a non-existent filter. // might refer to a non-existent filter.
return !!GetFilterForOffscreenCanvas(canvas_size); return !!GetFilterForOffscreenCanvas(canvas_size, context);
} }
bool CanvasRenderingContext2DState::HasFilter( bool CanvasRenderingContext2DState::HasFilter(
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
namespace blink { namespace blink {
class BaseRenderingContext2D;
class CanvasRenderingContext2D; class CanvasRenderingContext2D;
class CanvasStyle; class CanvasStyle;
class CSSValue; class CSSValue;
...@@ -97,8 +98,10 @@ class CanvasRenderingContext2DState final ...@@ -97,8 +98,10 @@ class CanvasRenderingContext2DState final
sk_sp<PaintFilter> GetFilter(Element*, sk_sp<PaintFilter> GetFilter(Element*,
IntSize canvas_size, IntSize canvas_size,
CanvasRenderingContext2D*) const; CanvasRenderingContext2D*) const;
sk_sp<PaintFilter> GetFilterForOffscreenCanvas(IntSize canvas_size) const; sk_sp<PaintFilter> GetFilterForOffscreenCanvas(IntSize canvas_size,
bool HasFilterForOffscreenCanvas(IntSize canvas_size) const; BaseRenderingContext2D*) const;
bool HasFilterForOffscreenCanvas(IntSize canvas_size,
BaseRenderingContext2D*) const;
bool HasFilter(Element*, bool HasFilter(Element*,
IntSize canvas_size, IntSize canvas_size,
CanvasRenderingContext2D*) const; CanvasRenderingContext2D*) const;
......
...@@ -66,7 +66,7 @@ bool OffscreenCanvasRenderingContext2D::OriginClean() const { ...@@ -66,7 +66,7 @@ bool OffscreenCanvasRenderingContext2D::OriginClean() const {
} }
void OffscreenCanvasRenderingContext2D::SetOriginTainted() { void OffscreenCanvasRenderingContext2D::SetOriginTainted() {
return Host()->SetOriginTainted(); Host()->SetOriginTainted();
} }
bool OffscreenCanvasRenderingContext2D::WouldTaintOrigin( bool OffscreenCanvasRenderingContext2D::WouldTaintOrigin(
...@@ -202,11 +202,11 @@ void OffscreenCanvasRenderingContext2D::DidDraw(const SkIRect& dirty_rect) { ...@@ -202,11 +202,11 @@ void OffscreenCanvasRenderingContext2D::DidDraw(const SkIRect& dirty_rect) {
} }
bool OffscreenCanvasRenderingContext2D::StateHasFilter() { bool OffscreenCanvasRenderingContext2D::StateHasFilter() {
return GetState().HasFilterForOffscreenCanvas(Host()->Size()); return GetState().HasFilterForOffscreenCanvas(Host()->Size(), this);
} }
sk_sp<PaintFilter> OffscreenCanvasRenderingContext2D::StateGetFilter() { sk_sp<PaintFilter> OffscreenCanvasRenderingContext2D::StateGetFilter() {
return GetState().GetFilterForOffscreenCanvas(Host()->Size()); return GetState().GetFilterForOffscreenCanvas(Host()->Size(), this);
} }
void OffscreenCanvasRenderingContext2D::ValidateStateStack() const { void OffscreenCanvasRenderingContext2D::ValidateStateStack() const {
......
...@@ -86,11 +86,13 @@ void PaintRenderingContext2D::ValidateStateStack() const { ...@@ -86,11 +86,13 @@ void PaintRenderingContext2D::ValidateStateStack() const {
} }
bool PaintRenderingContext2D::StateHasFilter() { bool PaintRenderingContext2D::StateHasFilter() {
return GetState().HasFilterForOffscreenCanvas(IntSize(Width(), Height())); return GetState().HasFilterForOffscreenCanvas(IntSize(Width(), Height()),
this);
} }
sk_sp<PaintFilter> PaintRenderingContext2D::StateGetFilter() { sk_sp<PaintFilter> PaintRenderingContext2D::StateGetFilter() {
return GetState().GetFilterForOffscreenCanvas(IntSize(Width(), Height())); return GetState().GetFilterForOffscreenCanvas(IntSize(Width(), Height()),
this);
} }
void PaintRenderingContext2D::WillOverwriteCanvas() { void PaintRenderingContext2D::WillOverwriteCanvas() {
......
...@@ -115,6 +115,12 @@ static SkDisplacementMapEffect::ChannelSelectorType ToSkiaMode( ...@@ -115,6 +115,12 @@ static SkDisplacementMapEffect::ChannelSelectorType ToSkiaMode(
sk_sp<PaintFilter> FEDisplacementMap::CreateImageFilter() { sk_sp<PaintFilter> FEDisplacementMap::CreateImageFilter() {
sk_sp<PaintFilter> color = sk_sp<PaintFilter> color =
PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()); PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace());
// FEDisplacementMap must be a pass-through filter if
// the origin is tainted. See:
// https://drafts.fxtf.org/filter-effects/#fedisplacemnentmap-restrictions.
if (InputEffect(1)->OriginTainted())
return color;
sk_sp<PaintFilter> displ = sk_sp<PaintFilter> displ =
PaintFilterBuilder::Build(InputEffect(1), OperatingInterpolationSpace()); PaintFilterBuilder::Build(InputEffect(1), OperatingInterpolationSpace());
SkDisplacementMapEffect::ChannelSelectorType type_x = SkDisplacementMapEffect::ChannelSelectorType type_x =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment