Merge "Custom looper code for Android" into sc-dev
This commit is contained in:
@@ -445,6 +445,7 @@ cc_defaults {
|
|||||||
"renderthread/TimeLord.cpp",
|
"renderthread/TimeLord.cpp",
|
||||||
"hwui/AnimatedImageDrawable.cpp",
|
"hwui/AnimatedImageDrawable.cpp",
|
||||||
"hwui/Bitmap.cpp",
|
"hwui/Bitmap.cpp",
|
||||||
|
"hwui/BlurDrawLooper.cpp",
|
||||||
"hwui/Canvas.cpp",
|
"hwui/Canvas.cpp",
|
||||||
"hwui/ImageDecoder.cpp",
|
"hwui/ImageDecoder.cpp",
|
||||||
"hwui/MinikinSkia.cpp",
|
"hwui/MinikinSkia.cpp",
|
||||||
|
|||||||
@@ -463,9 +463,7 @@ void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) {
|
void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) {
|
||||||
apply_looper(&paint, [&](const SkPaint& p) {
|
apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); });
|
||||||
mCanvas->drawPoint(x, y, p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) {
|
void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) {
|
||||||
@@ -493,9 +491,7 @@ void SkiaCanvas::drawRect(float left, float top, float right, float bottom, cons
|
|||||||
|
|
||||||
void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
|
void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
|
||||||
if (CC_UNLIKELY(paint.nothingToDraw())) return;
|
if (CC_UNLIKELY(paint.nothingToDraw())) return;
|
||||||
apply_looper(&paint, [&](const SkPaint& p) {
|
apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
|
||||||
mCanvas->drawRegion(region, p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
|
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
|
||||||
@@ -509,24 +505,18 @@ void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
|
|||||||
|
|
||||||
void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
|
void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
|
||||||
const Paint& paint) {
|
const Paint& paint) {
|
||||||
apply_looper(&paint, [&](const SkPaint& p) {
|
apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); });
|
||||||
mCanvas->drawDRRect(outer, inner, p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) {
|
void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) {
|
||||||
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
|
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
|
||||||
apply_looper(&paint, [&](const SkPaint& p) {
|
apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); });
|
||||||
mCanvas->drawCircle(x, y, radius, p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
|
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
|
||||||
if (CC_UNLIKELY(paint.nothingToDraw())) return;
|
if (CC_UNLIKELY(paint.nothingToDraw())) return;
|
||||||
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
|
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
|
||||||
apply_looper(&paint, [&](const SkPaint& p) {
|
apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
|
||||||
mCanvas->drawOval(oval, p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
|
void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
|
||||||
@@ -547,9 +537,7 @@ void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) {
|
|||||||
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
|
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
apply_looper(&paint, [&](const SkPaint& p) {
|
apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
|
||||||
mCanvas->drawPath(path, p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) {
|
void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) {
|
||||||
|
|||||||
@@ -208,29 +208,21 @@ protected:
|
|||||||
*/
|
*/
|
||||||
PaintCoW&& filterPaint(PaintCoW&& paint) const;
|
PaintCoW&& filterPaint(PaintCoW&& paint) const;
|
||||||
|
|
||||||
|
// proc(const SkPaint& modifiedPaint)
|
||||||
template <typename Proc> void apply_looper(const Paint* paint, Proc proc) {
|
template <typename Proc> void apply_looper(const Paint* paint, Proc proc) {
|
||||||
SkPaint skp;
|
SkPaint skp;
|
||||||
SkDrawLooper* looper = nullptr;
|
BlurDrawLooper* looper = nullptr;
|
||||||
if (paint) {
|
if (paint) {
|
||||||
skp = *filterPaint(paint);
|
skp = *filterPaint(paint);
|
||||||
looper = paint->getLooper();
|
looper = paint->getLooper();
|
||||||
}
|
}
|
||||||
if (looper) {
|
if (looper) {
|
||||||
SkSTArenaAlloc<256> alloc;
|
looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
|
||||||
SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
|
mCanvas->save();
|
||||||
if (ctx) {
|
mCanvas->translate(offset.fX, offset.fY);
|
||||||
SkDrawLooper::Context::Info info;
|
proc(modifiedPaint);
|
||||||
for (;;) {
|
mCanvas->restore();
|
||||||
SkPaint p = skp;
|
});
|
||||||
if (!ctx->next(&info, &p)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mCanvas->save();
|
|
||||||
mCanvas->translate(info.fTranslate.fX, info.fTranslate.fY);
|
|
||||||
proc(p);
|
|
||||||
mCanvas->restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
proc(skp);
|
proc(skp);
|
||||||
}
|
}
|
||||||
|
|||||||
45
libs/hwui/hwui/BlurDrawLooper.cpp
Normal file
45
libs/hwui/hwui/BlurDrawLooper.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BlurDrawLooper.h"
|
||||||
|
#include <SkMaskFilter.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
BlurDrawLooper::BlurDrawLooper(SkColor4f color, float blurSigma, SkPoint offset)
|
||||||
|
: mColor(color), mBlurSigma(blurSigma), mOffset(offset) {}
|
||||||
|
|
||||||
|
BlurDrawLooper::~BlurDrawLooper() = default;
|
||||||
|
|
||||||
|
SkPoint BlurDrawLooper::apply(SkPaint* paint) const {
|
||||||
|
paint->setColor(mColor);
|
||||||
|
if (mBlurSigma > 0) {
|
||||||
|
paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));
|
||||||
|
}
|
||||||
|
return mOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<BlurDrawLooper> BlurDrawLooper::Make(SkColor4f color, SkColorSpace* cs, float blurSigma,
|
||||||
|
SkPoint offset) {
|
||||||
|
if (cs) {
|
||||||
|
SkPaint tmp;
|
||||||
|
tmp.setColor(color, cs); // converts color to sRGB
|
||||||
|
color = tmp.getColor4f();
|
||||||
|
}
|
||||||
|
return sk_sp<BlurDrawLooper>(new BlurDrawLooper(color, blurSigma, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
53
libs/hwui/hwui/BlurDrawLooper.h
Normal file
53
libs/hwui/hwui/BlurDrawLooper.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
|
||||||
|
#define ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
|
||||||
|
|
||||||
|
#include <SkPaint.h>
|
||||||
|
#include <SkRefCnt.h>
|
||||||
|
|
||||||
|
class SkColorSpace;
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class BlurDrawLooper : public SkRefCnt {
|
||||||
|
public:
|
||||||
|
static sk_sp<BlurDrawLooper> Make(SkColor4f, SkColorSpace*, float blurSigma, SkPoint offset);
|
||||||
|
|
||||||
|
~BlurDrawLooper() override;
|
||||||
|
|
||||||
|
// proc(SkPoint offset, const SkPaint& modifiedPaint)
|
||||||
|
template <typename DrawProc>
|
||||||
|
void apply(const SkPaint& paint, DrawProc proc) const {
|
||||||
|
SkPaint p(paint);
|
||||||
|
proc(this->apply(&p), p); // draw the shadow
|
||||||
|
proc({0, 0}, paint); // draw the original (on top)
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SkColor4f mColor;
|
||||||
|
const float mBlurSigma;
|
||||||
|
const SkPoint mOffset;
|
||||||
|
|
||||||
|
SkPoint apply(SkPaint* paint) const;
|
||||||
|
|
||||||
|
BlurDrawLooper(SkColor4f, float, SkPoint);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
|
||||||
@@ -17,11 +17,11 @@
|
|||||||
#ifndef ANDROID_GRAPHICS_PAINT_H_
|
#ifndef ANDROID_GRAPHICS_PAINT_H_
|
||||||
#define ANDROID_GRAPHICS_PAINT_H_
|
#define ANDROID_GRAPHICS_PAINT_H_
|
||||||
|
|
||||||
|
#include "BlurDrawLooper.h"
|
||||||
#include "Typeface.h"
|
#include "Typeface.h"
|
||||||
|
|
||||||
#include <cutils/compiler.h>
|
#include <cutils/compiler.h>
|
||||||
|
|
||||||
#include <SkDrawLooper.h>
|
|
||||||
#include <SkFont.h>
|
#include <SkFont.h>
|
||||||
#include <SkPaint.h>
|
#include <SkPaint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -59,8 +59,8 @@ public:
|
|||||||
SkFont& getSkFont() { return mFont; }
|
SkFont& getSkFont() { return mFont; }
|
||||||
const SkFont& getSkFont() const { return mFont; }
|
const SkFont& getSkFont() const { return mFont; }
|
||||||
|
|
||||||
SkDrawLooper* getLooper() const { return mLooper.get(); }
|
BlurDrawLooper* getLooper() const { return mLooper.get(); }
|
||||||
void setLooper(sk_sp<SkDrawLooper> looper) { mLooper = std::move(looper); }
|
void setLooper(sk_sp<BlurDrawLooper> looper) { mLooper = std::move(looper); }
|
||||||
|
|
||||||
// These shadow the methods on SkPaint, but we need to so we can keep related
|
// These shadow the methods on SkPaint, but we need to so we can keep related
|
||||||
// attributes in-sync.
|
// attributes in-sync.
|
||||||
@@ -155,7 +155,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
SkFont mFont;
|
SkFont mFont;
|
||||||
sk_sp<SkDrawLooper> mLooper;
|
sk_sp<BlurDrawLooper> mLooper;
|
||||||
|
|
||||||
float mLetterSpacing = 0;
|
float mLetterSpacing = 0;
|
||||||
float mWordSpacing = 0;
|
float mWordSpacing = 0;
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <nativehelper/ScopedUtfChars.h>
|
#include <nativehelper/ScopedUtfChars.h>
|
||||||
#include <nativehelper/ScopedPrimitiveArray.h>
|
#include <nativehelper/ScopedPrimitiveArray.h>
|
||||||
|
|
||||||
#include "SkBlurDrawLooper.h"
|
|
||||||
#include "SkColorFilter.h"
|
#include "SkColorFilter.h"
|
||||||
#include "SkFont.h"
|
#include "SkFont.h"
|
||||||
#include "SkFontMetrics.h"
|
#include "SkFontMetrics.h"
|
||||||
@@ -39,6 +38,7 @@
|
|||||||
#include "unicode/ushape.h"
|
#include "unicode/ushape.h"
|
||||||
#include "utils/Blur.h"
|
#include "utils/Blur.h"
|
||||||
|
|
||||||
|
#include <hwui/BlurDrawLooper.h>
|
||||||
#include <hwui/MinikinSkia.h>
|
#include <hwui/MinikinSkia.h>
|
||||||
#include <hwui/MinikinUtils.h>
|
#include <hwui/MinikinUtils.h>
|
||||||
#include <hwui/Paint.h>
|
#include <hwui/Paint.h>
|
||||||
@@ -964,13 +964,13 @@ namespace PaintGlue {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
|
SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
|
||||||
paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
|
paint->setLooper(BlurDrawLooper::Make(color, cs.get(), sigma, {dx, dy}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
|
static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
|
||||||
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
|
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
|
||||||
return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
|
return paint->getLooper() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) {
|
static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) {
|
||||||
|
|||||||
@@ -194,28 +194,20 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) {
|
|||||||
return filterPaint(std::move(paint));
|
return filterPaint(std::move(paint));
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkDrawLooper* get_looper(const Paint* paint) {
|
static BlurDrawLooper* get_looper(const Paint* paint) {
|
||||||
return paint ? paint->getLooper() : nullptr;
|
return paint ? paint->getLooper() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Proc>
|
template <typename Proc>
|
||||||
void applyLooper(SkDrawLooper* looper, const SkPaint* paint, Proc proc) {
|
void applyLooper(BlurDrawLooper* looper, const SkPaint* paint, Proc proc) {
|
||||||
if (looper) {
|
if (looper) {
|
||||||
SkSTArenaAlloc<256> alloc;
|
SkPaint p;
|
||||||
SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
|
if (paint) {
|
||||||
if (ctx) {
|
p = *paint;
|
||||||
SkDrawLooper::Context::Info info;
|
|
||||||
for (;;) {
|
|
||||||
SkPaint p;
|
|
||||||
if (paint) {
|
|
||||||
p = *paint;
|
|
||||||
}
|
|
||||||
if (!ctx->next(&info, &p)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
proc(info.fTranslate.fX, info.fTranslate.fY, &p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
looper->apply(p, [&](SkPoint offset, const SkPaint& modifiedPaint) {
|
||||||
|
proc(offset.fX, offset.fY, &modifiedPaint);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
proc(0, 0, paint);
|
proc(0, 0, paint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "tests/common/TestUtils.h"
|
#include "tests/common/TestUtils.h"
|
||||||
|
|
||||||
#include <SkBlurDrawLooper.h>
|
|
||||||
#include <SkColorMatrixFilter.h>
|
#include <SkColorMatrixFilter.h>
|
||||||
#include <SkColorSpace.h>
|
#include <SkColorSpace.h>
|
||||||
#include <SkImagePriv.h>
|
#include <SkImagePriv.h>
|
||||||
@@ -85,15 +84,3 @@ TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
|
|||||||
ASSERT_EQ(sRGB1.get(), sRGB2.get());
|
ASSERT_EQ(sRGB1.get(), sRGB2.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SkiaBehavior, blurDrawLooper) {
|
|
||||||
sk_sp<SkDrawLooper> looper = SkBlurDrawLooper::Make(SK_ColorRED, 5.0f, 3.0f, 4.0f);
|
|
||||||
|
|
||||||
SkDrawLooper::BlurShadowRec blur;
|
|
||||||
bool success = looper->asABlurShadow(&blur);
|
|
||||||
ASSERT_TRUE(success);
|
|
||||||
|
|
||||||
ASSERT_EQ(SK_ColorRED, blur.fColor);
|
|
||||||
ASSERT_EQ(5.0f, blur.fSigma);
|
|
||||||
ASSERT_EQ(3.0f, blur.fOffset.fX);
|
|
||||||
ASSERT_EQ(4.0f, blur.fOffset.fY);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "tests/common/TestUtils.h"
|
#include "tests/common/TestUtils.h"
|
||||||
|
|
||||||
#include <hwui/Paint.h>
|
#include <hwui/Paint.h>
|
||||||
#include <SkBlurDrawLooper.h>
|
|
||||||
#include <SkCanvasStateUtils.h>
|
#include <SkCanvasStateUtils.h>
|
||||||
#include <SkPicture.h>
|
#include <SkPicture.h>
|
||||||
#include <SkPictureRecorder.h>
|
#include <SkPictureRecorder.h>
|
||||||
@@ -37,7 +36,7 @@ TEST(SkiaCanvas, drawShadowLayer) {
|
|||||||
// it is transparent to ensure that we still draw the rect since it has a looper
|
// it is transparent to ensure that we still draw the rect since it has a looper
|
||||||
paint.setColor(SK_ColorTRANSPARENT);
|
paint.setColor(SK_ColorTRANSPARENT);
|
||||||
// this is how view's shadow layers are implemented
|
// this is how view's shadow layers are implemented
|
||||||
paint.setLooper(SkBlurDrawLooper::Make(0xF0000000, 6.0f, 0, 10));
|
paint.setLooper(BlurDrawLooper::Make({0, 0, 0, 240.0f / 255}, nullptr, 6.0f, {0, 10}));
|
||||||
canvas.drawRect(3, 3, 7, 7, paint);
|
canvas.drawRect(3, 3, 7, 7, paint);
|
||||||
|
|
||||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
|
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include <utils/Blur.h>
|
#include <utils/Blur.h>
|
||||||
|
|
||||||
#include <SkColorFilter.h>
|
#include <SkColorFilter.h>
|
||||||
#include <SkDrawLooper.h>
|
|
||||||
#include <SkPaint.h>
|
#include <SkPaint.h>
|
||||||
#include <SkShader.h>
|
#include <SkShader.h>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user