Revert "Revert "Add experimental API to draw a set of SkImages in one SkCanvas call.""
This reverts commit 8d5b41b553
.
Bug: skia:8444
Change-Id: I29b52c6fe9475c6113ec954b7918cf591111846c
Reviewed-on: https://skia-review.googlesource.com/c/161627
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
11422c63d3
commit
d7065e72da
@ -4957,6 +4957,67 @@ void draw(SkCanvas* canvas) {
|
||||
|
||||
#Subtopic Lattice ##
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
#Enum QuadAAFlags
|
||||
#Line # don't use this ##
|
||||
#Private
|
||||
##
|
||||
#NoExample
|
||||
##
|
||||
##
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
#Struct ImageSetEntry
|
||||
#Line # don't use this ##
|
||||
#Private
|
||||
##
|
||||
|
||||
#Member SkImage* fImage
|
||||
#Line # image to draw ##
|
||||
##
|
||||
|
||||
#Member SkRect fSrcRect
|
||||
#Line # image src rectangle ##
|
||||
##
|
||||
|
||||
#Member SkRect fDstRect
|
||||
#Line # local space rectangle ##
|
||||
##
|
||||
|
||||
#Member unsigned fAAFlags
|
||||
#Line # antialiasing flags ##
|
||||
##
|
||||
|
||||
#NoExample
|
||||
##
|
||||
|
||||
##
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
#Method void experimental_DrawImageSetV0(const ImageSetEntry imageSet[], int cnt, float alpha,
|
||||
SkFilterQuality quality, SkBlendMode mode);
|
||||
#Private
|
||||
##
|
||||
#In Draw_Image
|
||||
#In Draw
|
||||
#Line # draws a set a of images (don't call this) ##
|
||||
|
||||
Draws a set of images. Do not use this method.
|
||||
|
||||
#Param imageSet images ##
|
||||
#Param cnt number of images ##
|
||||
#Param alpha alpha ##
|
||||
#Param quality filter quality ##
|
||||
#Param mode blend mode ##
|
||||
|
||||
#NoExample
|
||||
##
|
||||
|
||||
##
|
||||
|
||||
#Subtopic Draw_Image ##
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -574,6 +574,22 @@ void SkPipeCanvas::onDrawImageLattice(const SkImage* image, const Lattice& latti
|
||||
}
|
||||
}
|
||||
|
||||
void SkPipeCanvas::onDrawImageSet(const ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
SkPipeWriter writer(this);
|
||||
writer.write32(pack_verb(SkPipeVerb::kDrawImageSet));
|
||||
writer.writeInt(count);
|
||||
writer.writeScalar(SkFloatToScalar(alpha));
|
||||
writer.writeInt((int)filterQuality);
|
||||
writer.writeInt((int)mode);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
writer.writeImage(set[i].fImage.get());
|
||||
writer.writeRect(set[i].fSrcRect);
|
||||
writer.writeRect(set[i].fDstRect);
|
||||
writer.writeUInt(set[i].fAAFlags);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
|
@ -133,6 +133,8 @@ protected:
|
||||
const SkPaint*) override;
|
||||
void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageSet(const ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
|
||||
SkBlendMode, const SkPaint&) override;
|
||||
|
||||
|
@ -45,6 +45,7 @@ enum class SkPipeVerb : uint8_t {
|
||||
kDrawImageRect, // extra == constraint | has_src_rect | has_paint
|
||||
kDrawImageNine, // extra == has_paint:1
|
||||
kDrawImageLattice, // extra == has_paint:1
|
||||
kDrawImageSet, // extra == 0
|
||||
|
||||
kDrawVertices,
|
||||
|
||||
|
@ -538,6 +538,23 @@ static void drawImageLattice_handler(SkPipeReader& reader, uint32_t packedVerb,
|
||||
canvas->drawImageLattice(image.get(), lattice, *dst, paint);
|
||||
}
|
||||
|
||||
static void drawImageSet_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {
|
||||
SkASSERT(SkPipeVerb::kDrawImageSet == unpack_verb(packedVerb));
|
||||
|
||||
int cnt = reader.readInt();
|
||||
float alpha = SkScalarToFloat(reader.readScalar());
|
||||
SkFilterQuality filterQuality = (SkFilterQuality)reader.readInt();
|
||||
SkBlendMode mode = (SkBlendMode)reader.readInt();
|
||||
SkAutoTArray<SkCanvas::ImageSetEntry> set(cnt);
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
set[i].fImage = reader.readImage();
|
||||
reader.readRect(&set[i].fSrcRect);
|
||||
reader.readRect(&set[i].fDstRect);
|
||||
set[i].fAAFlags = reader.readUInt();
|
||||
}
|
||||
canvas->experimental_DrawImageSetV0(set.get(), cnt, alpha, filterQuality, mode);
|
||||
}
|
||||
|
||||
static void drawVertices_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {
|
||||
SkASSERT(SkPipeVerb::kDrawVertices == unpack_verb(packedVerb));
|
||||
SkBlendMode bmode = (SkBlendMode)unpack_verb_extra(packedVerb);
|
||||
@ -735,6 +752,7 @@ const HandlerRec gPipeHandlers[] = {
|
||||
HANDLER(drawImageRect),
|
||||
HANDLER(drawImageNine),
|
||||
HANDLER(drawImageLattice),
|
||||
HANDLER(drawImageSet),
|
||||
|
||||
HANDLER(drawVertices),
|
||||
|
||||
|
168
gm/aaflags.cpp
168
gm/aaflags.cpp
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
|
||||
#include "GrClip.h"
|
||||
#include "GrContext.h"
|
||||
#include "GrRenderTargetContext.h"
|
||||
#include "SkGradientShader.h"
|
||||
#include "SkImage_Base.h"
|
||||
#include "SkSurface.h"
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
class AAFlagsGM : public GM {
|
||||
private:
|
||||
SkString onShortName() final { return SkString("aaflags"); }
|
||||
SkISize onISize() override { return SkISize::Make(1000, 1450); }
|
||||
void onOnceBeforeDraw() override {
|
||||
static constexpr SkScalar kW = SkIntToScalar(kTileW * kM);
|
||||
static constexpr SkScalar kH = SkIntToScalar(kTileH * kN);
|
||||
|
||||
auto surf = SkSurface::MakeRaster(
|
||||
SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
|
||||
surf->getCanvas()->clear(SK_ColorLTGRAY);
|
||||
|
||||
static constexpr SkScalar kStripeW = 10;
|
||||
static constexpr SkScalar kStripeSpacing = 30;
|
||||
SkPaint paint;
|
||||
|
||||
static constexpr SkPoint pts1[] = {{0.f, 0.f}, {kW, kH}};
|
||||
static constexpr SkColor kColors1[] = {SK_ColorCYAN, SK_ColorBLACK};
|
||||
auto grad =
|
||||
SkGradientShader::MakeLinear(pts1, kColors1, nullptr, 2, SkShader::kClamp_TileMode);
|
||||
paint.setShader(std::move(grad));
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(kStripeW);
|
||||
SkPoint stripePts[] = {{-kW - kStripeW, -kStripeW}, {kStripeW, kH + kStripeW}};
|
||||
while (stripePts[0].fX <= kW) {
|
||||
surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
|
||||
stripePts[0].fX += kStripeSpacing;
|
||||
stripePts[1].fX += kStripeSpacing;
|
||||
}
|
||||
|
||||
static constexpr SkPoint pts2[] = {{0.f, kH}, {kW, 0.f}};
|
||||
static constexpr SkColor kColors2[] = {SK_ColorMAGENTA, SK_ColorBLACK};
|
||||
grad = SkGradientShader::MakeLinear(pts2, kColors2, nullptr, 2, SkShader::kClamp_TileMode);
|
||||
paint.setShader(std::move(grad));
|
||||
paint.setBlendMode(SkBlendMode::kMultiply);
|
||||
stripePts[0] = {-kW - kStripeW, kH + kStripeW};
|
||||
stripePts[1] = {kStripeW, -kStripeW};
|
||||
while (stripePts[0].fX <= kW) {
|
||||
surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
|
||||
stripePts[0].fX += kStripeSpacing;
|
||||
stripePts[1].fX += kStripeSpacing;
|
||||
}
|
||||
|
||||
auto img = surf->makeImageSnapshot();
|
||||
for (int y = 0; y < kN; ++y) {
|
||||
for (int x = 0; x < kM; ++x) {
|
||||
fImage[x][y] =
|
||||
img->makeSubset(SkIRect::MakeXYWH(x * kTileW, y * kTileH, kTileW, kTileH));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
GrContext* ctx = canvas->getGrContext();
|
||||
if (!ctx) {
|
||||
DrawGpuOnlyMessage(canvas);
|
||||
return;
|
||||
}
|
||||
GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
|
||||
SkASSERT(rtc);
|
||||
|
||||
SkScalar d = SkVector{kM * kTileW, kN * kTileH}.length();
|
||||
SkMatrix matrices[4];
|
||||
// rotation
|
||||
matrices[0].setRotate(30);
|
||||
matrices[0].postTranslate(d / 3, 0);
|
||||
// perespective
|
||||
SkPoint src[4];
|
||||
SkRect::MakeWH(kM * kTileW, kN * kTileH).toQuad(src);
|
||||
SkPoint dst[4] = {{0, 0},
|
||||
{kM * kTileW + 10.f, -5.f},
|
||||
{kM * kTileW - 28.f, kN * kTileH + 40.f},
|
||||
{45.f, kN * kTileH - 25.f}};
|
||||
matrices[1].setPolyToPoly(src, dst, 4);
|
||||
matrices[1].postTranslate(d, 50.f);
|
||||
// skew
|
||||
matrices[2].setRotate(-60.f);
|
||||
matrices[2].postSkew(0.5f, -1.35f);
|
||||
matrices[2].postScale(0.6f, 1.05f);
|
||||
matrices[2].postTranslate(d, 2.5f * d);
|
||||
// perspective + mirror in x.
|
||||
dst[1] = {0, 0};
|
||||
dst[0] = {3.f / 4.f * kM * kTileW, 0};
|
||||
dst[3] = {2.f / 3.f * kM * kTileW, 1 / 4.f * kN * kTileH};
|
||||
dst[2] = {1.f / 3.f * kM * kTileW, 1 / 4.f * kN * kTileH - 25.f};
|
||||
matrices[3].setPolyToPoly(src, dst, 4);
|
||||
matrices[3].postTranslate(100.f, d);
|
||||
for (size_t m = 0; m < SK_ARRAY_COUNT(matrices); ++m) {
|
||||
// Draw grid of green "lines" at interior tile boundaries.
|
||||
static constexpr SkScalar kLineOutset = 10.f;
|
||||
static constexpr SkScalar kLineHalfW = 4.f;
|
||||
auto color = GrColor4f(0.f, 1.f, 0.f, 1.f);
|
||||
for (int x = 1; x < kM; ++x) {
|
||||
GrPaint paint;
|
||||
paint.setColor4f(color);
|
||||
SkRect lineRect =
|
||||
SkRect::MakeLTRB(x * kTileW - kLineHalfW, -kLineOutset,
|
||||
x * kTileW + kLineHalfW, kN * kTileH + kLineOutset);
|
||||
rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kYes, matrices[m], lineRect);
|
||||
}
|
||||
for (int y = 1; y < kN; ++y) {
|
||||
GrPaint paint;
|
||||
paint.setColor4f(color);
|
||||
SkRect lineRect =
|
||||
SkRect::MakeLTRB(-kLineOutset, y * kTileH - kLineHalfW,
|
||||
kTileW * kM + kLineOutset, y * kTileH + kLineHalfW);
|
||||
rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kYes, matrices[m], lineRect);
|
||||
}
|
||||
static constexpr GrColor kColor = GrColor_WHITE;
|
||||
for (int x = 0; x < kM; ++x) {
|
||||
for (int y = 0; y < kN; ++y) {
|
||||
auto proxy = as_IB(fImage[x][y])
|
||||
->asTextureProxyRef(ctx, GrSamplerState::ClampBilerp(),
|
||||
nullptr, nullptr, nullptr);
|
||||
if (!proxy) {
|
||||
continue;
|
||||
}
|
||||
auto srcR = proxy->getBoundsRect();
|
||||
auto dstR = SkRect::MakeXYWH(x * kTileW, y * kTileH, kTileW, kTileH);
|
||||
GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
|
||||
if (x == 0) {
|
||||
aaFlags |= GrQuadAAFlags::kLeft;
|
||||
}
|
||||
if (x == kM - 1) {
|
||||
aaFlags |= GrQuadAAFlags::kRight;
|
||||
}
|
||||
if (y == 0) {
|
||||
aaFlags |= GrQuadAAFlags::kTop;
|
||||
}
|
||||
if (y == kN - 1) {
|
||||
aaFlags |= GrQuadAAFlags::kBottom;
|
||||
}
|
||||
rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kBilerp, kColor,
|
||||
srcR, dstR, aaFlags, SkCanvas::kFast_SrcRectConstraint,
|
||||
matrices[m], nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static constexpr int kM = 4;
|
||||
static constexpr int kN = 4;
|
||||
static constexpr SkScalar kTileW = 100;
|
||||
static constexpr SkScalar kTileH = 100;
|
||||
sk_sp<SkImage> fImage[kM][kN];
|
||||
};
|
||||
|
||||
DEF_GM(return new AAFlagsGM();)
|
||||
|
||||
} // namespace skiagm
|
167
gm/drawimageset.cpp
Normal file
167
gm/drawimageset.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2018 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
|
||||
#include "SkGradientShader.h"
|
||||
#include "SkSurface.h"
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
class DrawImageSetGM : public GM {
|
||||
private:
|
||||
SkString onShortName() final { return SkString("draw_image_set"); }
|
||||
SkISize onISize() override { return SkISize::Make(1000, 725); }
|
||||
void onOnceBeforeDraw() override {
|
||||
static constexpr SkScalar kW = SkIntToScalar(kTileW * kM);
|
||||
static constexpr SkScalar kH = SkIntToScalar(kTileH * kN);
|
||||
|
||||
auto surf = SkSurface::MakeRaster(
|
||||
SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
|
||||
surf->getCanvas()->clear(SK_ColorLTGRAY);
|
||||
|
||||
static constexpr SkScalar kStripeW = 10;
|
||||
static constexpr SkScalar kStripeSpacing = 30;
|
||||
SkPaint paint;
|
||||
|
||||
static constexpr SkPoint pts1[] = {{0.f, 0.f}, {kW, kH}};
|
||||
static constexpr SkColor kColors1[] = {SK_ColorCYAN, SK_ColorBLACK};
|
||||
auto grad =
|
||||
SkGradientShader::MakeLinear(pts1, kColors1, nullptr, 2, SkShader::kClamp_TileMode);
|
||||
paint.setShader(std::move(grad));
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(kStripeW);
|
||||
SkPoint stripePts[] = {{-kW - kStripeW, -kStripeW}, {kStripeW, kH + kStripeW}};
|
||||
while (stripePts[0].fX <= kW) {
|
||||
surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
|
||||
stripePts[0].fX += kStripeSpacing;
|
||||
stripePts[1].fX += kStripeSpacing;
|
||||
}
|
||||
|
||||
static constexpr SkPoint pts2[] = {{0.f, kH}, {kW, 0.f}};
|
||||
static constexpr SkColor kColors2[] = {SK_ColorMAGENTA, SK_ColorBLACK};
|
||||
grad = SkGradientShader::MakeLinear(pts2, kColors2, nullptr, 2, SkShader::kClamp_TileMode);
|
||||
paint.setShader(std::move(grad));
|
||||
paint.setBlendMode(SkBlendMode::kMultiply);
|
||||
stripePts[0] = {-kW - kStripeW, kH + kStripeW};
|
||||
stripePts[1] = {kStripeW, -kStripeW};
|
||||
while (stripePts[0].fX <= kW) {
|
||||
surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
|
||||
stripePts[0].fX += kStripeSpacing;
|
||||
stripePts[1].fX += kStripeSpacing;
|
||||
}
|
||||
|
||||
auto img = surf->makeImageSnapshot();
|
||||
for (int y = 0; y < kN; ++y) {
|
||||
for (int x = 0; x < kM; ++x) {
|
||||
fImage[x][y] =
|
||||
img->makeSubset(SkIRect::MakeXYWH(x * kTileW, y * kTileH, kTileW, kTileH));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
SkScalar d = SkVector{kM * kTileW, kN * kTileH}.length();
|
||||
SkMatrix matrices[4];
|
||||
// rotation
|
||||
matrices[0].setRotate(30);
|
||||
matrices[0].postTranslate(d / 3, 0);
|
||||
// perespective
|
||||
SkPoint src[4];
|
||||
SkRect::MakeWH(kM * kTileW, kN * kTileH).toQuad(src);
|
||||
SkPoint dst[4] = {{0, 0},
|
||||
{kM * kTileW + 10.f, -5.f},
|
||||
{kM * kTileW - 28.f, kN * kTileH + 40.f},
|
||||
{45.f, kN * kTileH - 25.f}};
|
||||
SkAssertResult(matrices[1].setPolyToPoly(src, dst, 4));
|
||||
matrices[1].postTranslate(d, 50.f);
|
||||
// skew
|
||||
matrices[2].setRotate(-60.f);
|
||||
matrices[2].postSkew(0.5f, -1.15f);
|
||||
matrices[2].postScale(0.6f, 1.05f);
|
||||
matrices[2].postTranslate(d, 2.6f * d);
|
||||
// perspective + mirror in x.
|
||||
dst[1] = {-.25 * kM * kTileW, 0};
|
||||
dst[0] = {5.f / 4.f * kM * kTileW, 0};
|
||||
dst[3] = {2.f / 3.f * kM * kTileW, 1 / 2.f * kN * kTileH};
|
||||
dst[2] = {1.f / 3.f * kM * kTileW, 1 / 2.f * kN * kTileH - 0.1f * kTileH};
|
||||
SkAssertResult(matrices[3].setPolyToPoly(src, dst, 4));
|
||||
matrices[3].postTranslate(100.f, d);
|
||||
SkCanvas::ImageSetEntry set[kM * kN];
|
||||
for (int x = 0; x < kM; ++x) {
|
||||
for (int y = 0; y < kN; ++y) {
|
||||
set[y * kM + x].fAAFlags = SkCanvas::kNone_QuadAAFlags;
|
||||
if (x == 0) {
|
||||
set[y * kM + x].fAAFlags |= SkCanvas::kLeft_QuadAAFlag;
|
||||
}
|
||||
if (x == kM - 1) {
|
||||
set[y * kM + x].fAAFlags |= SkCanvas::kRight_QuadAAFlag;
|
||||
}
|
||||
if (y == 0) {
|
||||
set[y * kM + x].fAAFlags |= SkCanvas::kTop_QuadAAFlag;
|
||||
}
|
||||
if (y == kN - 1) {
|
||||
set[y * kM + x].fAAFlags |= SkCanvas::kBottom_QuadAAFlag;
|
||||
}
|
||||
set[y * kM + x].fSrcRect = SkRect::MakeWH(kTileW, kTileH);
|
||||
set[y * kM + x].fDstRect = SkRect::MakeXYWH(x * kTileW, y * kTileH, kTileW, kTileH);
|
||||
set[y * kM + x].fImage = fImage[x][y];
|
||||
}
|
||||
}
|
||||
for (auto fm : {kNone_SkFilterQuality, kLow_SkFilterQuality}) {
|
||||
for (size_t m = 0; m < SK_ARRAY_COUNT(matrices); ++m) {
|
||||
// Draw grid of red lines at interior tile boundaries.
|
||||
static constexpr SkScalar kLineOutset = 10.f;
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(SK_ColorRED);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(0.f);
|
||||
for (int x = 1; x < kM; ++x) {
|
||||
SkPoint pts[] = {{x * kTileW, 0}, {x * kTileW, kN * kTileH}};
|
||||
matrices[m].mapPoints(pts, 2);
|
||||
SkVector v = pts[1] - pts[0];
|
||||
v.setLength(v.length() + kLineOutset);
|
||||
canvas->drawLine(pts[1] - v, pts[0] + v, paint);
|
||||
}
|
||||
for (int y = 1; y < kN; ++y) {
|
||||
SkPoint pts[] = {{0, y * kTileH}, {kTileW * kM, y * kTileH}};
|
||||
matrices[m].mapPoints(pts, 2);
|
||||
SkVector v = pts[1] - pts[0];
|
||||
v.setLength(v.length() + kLineOutset);
|
||||
canvas->drawLine(pts[1] - v, pts[0] + v, paint);
|
||||
}
|
||||
canvas->save();
|
||||
canvas->concat(matrices[m]);
|
||||
canvas->experimental_DrawImageSetV0(set, kM * kN, 1.f, fm, SkBlendMode::kSrcOver);
|
||||
canvas->restore();
|
||||
}
|
||||
// A more exotic case with an unusual blend mode, all aa flags set, and alpha, and
|
||||
// subsets the image
|
||||
SkCanvas::ImageSetEntry entry;
|
||||
entry.fSrcRect = SkRect::MakeWH(kTileW, kTileH).makeInset(kTileW / 4.f, kTileH / 4.f);
|
||||
entry.fDstRect = SkRect::MakeWH(2 * kTileW, 2 * kTileH).makeOffset(d / 4, 2 * d);
|
||||
entry.fImage = fImage[0][0];
|
||||
entry.fAAFlags = SkCanvas::kAll_QuadAAFlags;
|
||||
canvas->save();
|
||||
canvas->rotate(3.f);
|
||||
canvas->experimental_DrawImageSetV0(&entry, 1, 0.7f, fm, SkBlendMode::kLuminosity);
|
||||
canvas->restore();
|
||||
canvas->translate(2 * d, 0);
|
||||
}
|
||||
}
|
||||
static constexpr int kM = 4;
|
||||
static constexpr int kN = 4;
|
||||
static constexpr SkScalar kTileW = 50;
|
||||
static constexpr SkScalar kTileH = 50;
|
||||
sk_sp<SkImage> fImage[kM][kN];
|
||||
};
|
||||
|
||||
DEF_GM(return new DrawImageSetGM();)
|
||||
|
||||
} // namespace skiagm
|
@ -10,7 +10,6 @@ gm_sources = [
|
||||
"$_gm/3dgm.cpp",
|
||||
"$_gm/aaa.cpp",
|
||||
"$_gm/aaclip.cpp",
|
||||
"$_gm/aaflags.cpp",
|
||||
"$_gm/aarectmodes.cpp",
|
||||
"$_gm/aaxfermodes.cpp",
|
||||
"$_gm/addarc.cpp",
|
||||
@ -120,6 +119,7 @@ gm_sources = [
|
||||
"$_gm/drawatlascolor.cpp",
|
||||
"$_gm/drawbitmaprect.cpp",
|
||||
"$_gm/drawlooper.cpp",
|
||||
"$_gm/drawimageset.cpp",
|
||||
"$_gm/drawminibitmaprect.cpp",
|
||||
"$_gm/drawregion.cpp",
|
||||
"$_gm/drawregionmodes.cpp",
|
||||
|
@ -1820,6 +1820,37 @@ public:
|
||||
void drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
|
||||
const SkPaint* paint = nullptr);
|
||||
|
||||
/**
|
||||
* Controls anti-aliasing of each edge of images in an image-set.
|
||||
*/
|
||||
enum QuadAAFlags : unsigned {
|
||||
kLeft_QuadAAFlag = 0b0001,
|
||||
kTop_QuadAAFlag = 0b0010,
|
||||
kRight_QuadAAFlag = 0b0100,
|
||||
kBottom_QuadAAFlag = 0b1000,
|
||||
|
||||
kNone_QuadAAFlags = 0b0000,
|
||||
kAll_QuadAAFlags = 0b1111,
|
||||
};
|
||||
|
||||
/** This is used by the experimental API below. */
|
||||
struct ImageSetEntry {
|
||||
sk_sp<const SkImage> fImage;
|
||||
SkRect fSrcRect;
|
||||
SkRect fDstRect;
|
||||
unsigned fAAFlags; // QuadAAFlags
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an experimental API for the SkiaRenderer Chromium project. The signature will
|
||||
* surely evolve if this is not removed. It currently offers no performance advantage over
|
||||
* drawing images independently, though may in the future. The antialiasing flags are intended
|
||||
* to allow control over each edge's AA status, to allow perfect seaming for tile sets. The
|
||||
* current implementation only antialiases if all edges are flagged, however.
|
||||
*/
|
||||
void experimental_DrawImageSetV0(const ImageSetEntry imageSet[], int cnt, float alpha,
|
||||
SkFilterQuality quality, SkBlendMode mode);
|
||||
|
||||
/** Draws text, with origin at (x, y), using clip, SkMatrix, and SkPaint paint.
|
||||
|
||||
text meaning depends on SkPaint::TextEncoding; by default, text is encoded as
|
||||
@ -2446,6 +2477,9 @@ protected:
|
||||
virtual void onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
|
||||
const SkPaint* paint);
|
||||
|
||||
virtual void onDrawImageSet(const ImageSetEntry imageSet[], int count, float alpha,
|
||||
SkFilterQuality, SkBlendMode);
|
||||
|
||||
virtual void onDrawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy,
|
||||
const SkPaint* paint);
|
||||
virtual void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
|
||||
|
@ -58,6 +58,16 @@ protected:
|
||||
void onDrawImageLattice(const SkImage* image, const SkCanvas::Lattice& lattice,
|
||||
const SkRect& dst, const SkPaint* paint) override = 0;
|
||||
|
||||
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||
// This is under active development for Chrome and not used in Android. Hold off on adding
|
||||
// implementations in Android's SkCanvas subclasses until this stabilizes.
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override {};
|
||||
#else
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override = 0;
|
||||
#endif
|
||||
|
||||
void onDrawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy,
|
||||
const SkPaint* paint) override = 0;
|
||||
void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
SrcRectConstraint) override;
|
||||
void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override;
|
||||
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
|
||||
void onDrawImageSet(const ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
|
||||
void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
|
||||
SrcRectConstraint) override;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <chrono>
|
||||
#include "GrSharedEnums.h"
|
||||
#include "GrTypes.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkImageInfoPriv.h"
|
||||
#include "SkRefCnt.h"
|
||||
@ -287,30 +288,24 @@ enum class GrAllowMixedSamples : bool { kNo = false, kYes = true };
|
||||
|
||||
GrAAType GrChooseAAType(GrAA, GrFSAAType, GrAllowMixedSamples, const GrCaps&);
|
||||
|
||||
/**
|
||||
* Controls anti-aliasing of a quad on a per-edge basis. Currently only used by GrTextureOp.
|
||||
* This will be moved to public API and renamed when this functionality is exposed.
|
||||
*/
|
||||
enum class GrQuadAAFlags : unsigned {
|
||||
kLeft = 0b0001,
|
||||
kTop = 0b0010,
|
||||
kRight = 0b0100,
|
||||
kBottom = 0b1000,
|
||||
enum class GrQuadAAFlags {
|
||||
kLeft = SkCanvas::kLeft_QuadAAFlag,
|
||||
kTop = SkCanvas::kTop_QuadAAFlag,
|
||||
kRight = SkCanvas::kRight_QuadAAFlag,
|
||||
kBottom = SkCanvas::kBottom_QuadAAFlag,
|
||||
|
||||
kNone = 0b0000,
|
||||
kAll = 0b1111,
|
||||
|
||||
kTopLeft = kTop | kLeft,
|
||||
kTopRight = kTop | kRight,
|
||||
kBottomRight = kBottom | kRight,
|
||||
kBottomLeft = kBottom | kLeft,
|
||||
kNone = SkCanvas::kNone_QuadAAFlags,
|
||||
kAll = SkCanvas::kAll_QuadAAFlags
|
||||
};
|
||||
|
||||
GR_MAKE_BITFIELD_CLASS_OPS(GrQuadAAFlags)
|
||||
|
||||
static inline GrQuadAAFlags SkToGrQuadAAFlags(unsigned flags) {
|
||||
return static_cast<GrQuadAAFlags>(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of shader-language-specific boxed variables we can create. (Currently only GrGLShaderVars,
|
||||
* but should be applicable to other shader languages.)
|
||||
* Types of shader-language-specific boxed variables we can create.
|
||||
*/
|
||||
enum GrSLType {
|
||||
kVoid_GrSLType,
|
||||
|
@ -63,10 +63,11 @@ protected:
|
||||
const SkPaint*, SrcRectConstraint) override;
|
||||
void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
|
||||
void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
|
||||
const SkPaint*) override;
|
||||
void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
|
||||
|
@ -71,6 +71,8 @@ protected:
|
||||
const SkPaint*) override {}
|
||||
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&,
|
||||
const SkPaint*) override {}
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int, float, SkFilterQuality,
|
||||
SkBlendMode) override {}
|
||||
void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&,
|
||||
const SkPaint*) override {}
|
||||
void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
|
||||
|
@ -88,6 +88,8 @@ protected:
|
||||
const SkPaint*) override;
|
||||
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
|
||||
SkBlendMode, const SkPaint&) override;
|
||||
void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
|
||||
|
@ -1796,6 +1796,15 @@ void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, co
|
||||
}
|
||||
}
|
||||
|
||||
void SkCanvas::experimental_DrawImageSetV0(const ImageSetEntry imageSet[], int cnt, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
RETURN_ON_NULL(imageSet);
|
||||
RETURN_ON_FALSE(cnt);
|
||||
|
||||
this->onDrawImageSet(imageSet, cnt, alpha, filterQuality, mode);
|
||||
}
|
||||
|
||||
void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
if (bitmap.drawsNothing()) {
|
||||
@ -2397,6 +2406,16 @@ void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
|
||||
LOOPER_END
|
||||
}
|
||||
|
||||
void SkCanvas::onDrawImageSet(const ImageSetEntry imageSet[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
SkPaint paint;
|
||||
LOOPER_BEGIN(paint, nullptr);
|
||||
while (iter.next()) {
|
||||
iter.fDevice->drawImageSet(imageSet, count, alpha, filterQuality, mode);
|
||||
}
|
||||
LOOPER_END
|
||||
}
|
||||
|
||||
void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
|
||||
const SkRect& dst, const SkPaint* paint) {
|
||||
SkPaint realPaint;
|
||||
|
@ -171,6 +171,18 @@ public:
|
||||
dst, MaybePaint(paint, fXformer.get()));
|
||||
}
|
||||
}
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) override {
|
||||
SkAutoTArray<ImageSetEntry> xformedSet(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
xformedSet[i].fImage = this->prepareImage(set[i].fImage.get());
|
||||
xformedSet[i].fSrcRect = set[i].fSrcRect;
|
||||
xformedSet[i].fDstRect = set[i].fDstRect;
|
||||
xformedSet[i].fAAFlags = set[i].fAAFlags;
|
||||
}
|
||||
fTarget->experimental_DrawImageSetV0(xformedSet.get(), count, alpha, filterQuality, mode);
|
||||
}
|
||||
|
||||
void onDrawAtlas(const SkImage* atlas, const SkRSXform* xforms, const SkRect* tex,
|
||||
const SkColor* colors, int count, SkBlendMode mode,
|
||||
const SkRect* cull, const SkPaint* paint) override {
|
||||
|
@ -202,6 +202,22 @@ void SkBaseDevice::drawImageLattice(const SkImage* image,
|
||||
}
|
||||
}
|
||||
|
||||
void SkBaseDevice::drawImageSet(const SkCanvas::ImageSetEntry images[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(SkTPin(filterQuality, kNone_SkFilterQuality, kLow_SkFilterQuality));
|
||||
paint.setAlpha(SkToUInt(SkTClamp(SkScalarRoundToInt(alpha * 255), 0, 255)));
|
||||
paint.setBlendMode(mode);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
// TODO: Handle per-edge AA. Right now this mirrors the SkiaRenderer component of Chrome
|
||||
// which turns off antialiasing unless all four edges should be antialiased. This avoids
|
||||
// seaming in tiled composited layers.
|
||||
paint.setAntiAlias(images[i].fAAFlags == SkCanvas::kAll_QuadAAFlags);
|
||||
this->drawImageRect(images[i].fImage.get(), &images[i].fSrcRect, images[i].fDstRect, paint,
|
||||
SkCanvas::kFast_SrcRectConstraint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkBaseDevice::drawBitmapLattice(const SkBitmap& bitmap,
|
||||
const SkCanvas::Lattice& lattice, const SkRect& dst,
|
||||
const SkPaint& paint) {
|
||||
|
@ -212,6 +212,8 @@ protected:
|
||||
virtual void drawImageLattice(const SkImage*, const SkCanvas::Lattice&,
|
||||
const SkRect& dst, const SkPaint&);
|
||||
|
||||
virtual void drawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha,
|
||||
SkFilterQuality, SkBlendMode);
|
||||
|
||||
virtual void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
|
||||
SkBlendMode, const SkPaint&) = 0;
|
||||
|
@ -5,16 +5,17 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkLiteDL.h"
|
||||
#include <algorithm>
|
||||
#include "SkCanvas.h"
|
||||
#include "SkData.h"
|
||||
#include "SkDrawShadowInfo.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkImageFilter.h"
|
||||
#include "SkLiteDL.h"
|
||||
#include "SkMath.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkRegion.h"
|
||||
#include "SkRSXform.h"
|
||||
#include "SkRegion.h"
|
||||
#include "SkTextBlob.h"
|
||||
#include "SkVertices.h"
|
||||
|
||||
@ -46,15 +47,15 @@ static const D* pod(const T* op, size_t offset = 0) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
#define TYPES(M) \
|
||||
M(Flush) M(Save) M(Restore) M(SaveLayer) \
|
||||
M(Concat) M(SetMatrix) M(Translate) \
|
||||
M(ClipPath) M(ClipRect) M(ClipRRect) M(ClipRegion) \
|
||||
M(DrawPaint) M(DrawPath) M(DrawRect) M(DrawRegion) M(DrawOval) M(DrawArc) \
|
||||
M(DrawRRect) M(DrawDRRect) M(DrawAnnotation) M(DrawDrawable) M(DrawPicture) \
|
||||
M(DrawImage) M(DrawImageNine) M(DrawImageRect) M(DrawImageLattice) \
|
||||
M(DrawText) M(DrawPosText) M(DrawPosTextH) \
|
||||
M(DrawTextRSXform) M(DrawTextBlob) \
|
||||
#define TYPES(M) \
|
||||
M(Flush) M(Save) M(Restore) M(SaveLayer) \
|
||||
M(Concat) M(SetMatrix) M(Translate) \
|
||||
M(ClipPath) M(ClipRect) M(ClipRRect) M(ClipRegion) \
|
||||
M(DrawPaint) M(DrawPath) M(DrawRect) M(DrawRegion) M(DrawOval) M(DrawArc) \
|
||||
M(DrawRRect) M(DrawDRRect) M(DrawAnnotation) M(DrawDrawable) M(DrawPicture) \
|
||||
M(DrawImage) M(DrawImageNine) M(DrawImageRect) M(DrawImageLattice) M(DrawImageSet) \
|
||||
M(DrawText) M(DrawPosText) M(DrawPosTextH) \
|
||||
M(DrawTextRSXform) M(DrawTextBlob) \
|
||||
M(DrawPatch) M(DrawPoints) M(DrawVertices) M(DrawAtlas) M(DrawShadowRec)
|
||||
|
||||
#define M(T) T,
|
||||
@ -325,7 +326,22 @@ namespace {
|
||||
&paint);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawImageSet final : Op {
|
||||
static const auto kType = Type::DrawImageSet;
|
||||
DrawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality quality, SkBlendMode xfermode)
|
||||
: count(count), alpha(alpha), quality(quality), xfermode(xfermode), set(count) {
|
||||
std::copy_n(set, count, this->set.get());
|
||||
}
|
||||
int count;
|
||||
float alpha;
|
||||
SkFilterQuality quality;
|
||||
SkBlendMode xfermode;
|
||||
SkAutoTArray<SkCanvas::ImageSetEntry> set;
|
||||
void draw(SkCanvas* c, const SkMatrix&) const {
|
||||
c->experimental_DrawImageSetV0(set.get(), count, alpha, quality, xfermode);
|
||||
}
|
||||
};
|
||||
struct DrawText final : Op {
|
||||
static const auto kType = Type::DrawText;
|
||||
DrawText(size_t bytes, SkScalar x, SkScalar y, const SkPaint& paint)
|
||||
@ -604,6 +620,11 @@ void SkLiteDL::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Latt
|
||||
lattice.fRectTypes, fs);
|
||||
}
|
||||
|
||||
void SkLiteDL::drawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
this->push<DrawImageSet>(0, set, count, alpha, filterQuality, mode);
|
||||
}
|
||||
|
||||
void SkLiteDL::drawText(const void* text, size_t bytes,
|
||||
SkScalar x, SkScalar y, const SkPaint& paint) {
|
||||
void* pod = this->push<DrawText>(bytes, bytes, x, y, paint);
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
SkCanvas::SrcRectConstraint);
|
||||
void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&,
|
||||
const SkRect&, const SkPaint*);
|
||||
void drawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode);
|
||||
|
||||
void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4],
|
||||
SkBlendMode, const SkPaint&);
|
||||
|
@ -159,6 +159,10 @@ void SkLiteRecorder::onDrawImageLattice(const SkImage* img,
|
||||
fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint);
|
||||
}
|
||||
|
||||
void SkLiteRecorder::onDrawImageSet(const ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
fDL->drawImageSet(set, count, alpha, filterQuality, mode);
|
||||
}
|
||||
|
||||
void SkLiteRecorder::onDrawPatch(const SkPoint cubics[12],
|
||||
const SkColor colors[4], const SkPoint texCoords[4],
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override;
|
||||
void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
|
||||
SrcRectConstraint) override;
|
||||
void onDrawImageSet(const ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
|
||||
void onDrawPatch(const SkPoint[12], const SkColor[4],
|
||||
const SkPoint[4], SkBlendMode, const SkPaint&) override;
|
||||
|
@ -272,6 +272,13 @@ void SkOverdrawCanvas::onDrawImageLattice(const SkImage* image, const Lattice& l
|
||||
}
|
||||
}
|
||||
|
||||
void SkOverdrawCanvas::onDrawImageSet(const ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality, SkBlendMode) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
fList[0]->onDrawRect(set[i].fDstRect, fPaint);
|
||||
}
|
||||
}
|
||||
|
||||
void SkOverdrawCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
|
||||
const SkPaint*) {
|
||||
fList[0]->onDrawRect(SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()), fPaint);
|
||||
|
@ -97,7 +97,8 @@ enum DrawType {
|
||||
|
||||
FLUSH,
|
||||
|
||||
LAST_DRAWTYPE_ENUM = FLUSH
|
||||
DRAW_IMAGE_SET,
|
||||
LAST_DRAWTYPE_ENUM = DRAW_IMAGE_SET
|
||||
};
|
||||
|
||||
enum DrawVertexFlags {
|
||||
|
@ -355,6 +355,22 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
|
||||
|
||||
canvas->legacy_drawImageRect(image, src, dst, paint, constraint);
|
||||
} break;
|
||||
case DRAW_IMAGE_SET: {
|
||||
int cnt = reader->readInt();
|
||||
float alpha = SkScalarToFloat(reader->readScalar());
|
||||
SkFilterQuality filterQuality = (SkFilterQuality)reader->readUInt();
|
||||
SkBlendMode mode = (SkBlendMode)reader->readUInt();
|
||||
SkAutoTArray<SkCanvas::ImageSetEntry> set(cnt);
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
set[i].fImage = sk_ref_sp(fPictureData->getImage(reader));
|
||||
reader->readRect(&set[i].fSrcRect);
|
||||
reader->readRect(&set[i].fDstRect);
|
||||
set[i].fAAFlags = reader->readUInt();
|
||||
}
|
||||
BREAK_ON_READ_ERROR(reader);
|
||||
|
||||
canvas->experimental_DrawImageSetV0(set.get(), cnt, alpha, filterQuality, mode);
|
||||
} break;
|
||||
case DRAW_OVAL: {
|
||||
const SkPaint* paint = fPictureData->getPaint(reader);
|
||||
SkRect rect;
|
||||
|
@ -543,6 +543,25 @@ void SkPictureRecord::onDrawImageLattice(const SkImage* image, const Lattice& la
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
|
||||
void SkPictureRecord::onDrawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
// op + count + alpha + fq + mode + (image index, src rect, dst rect, aa flags) * cnt
|
||||
size_t size =
|
||||
4 * kUInt32Size + sizeof(SkScalar) + (2 * kUInt32Size + 2 * sizeof(SkRect)) * count;
|
||||
size_t initialOffset = this->addDraw(DRAW_IMAGE_SET, &size);
|
||||
this->addInt(count);
|
||||
this->addScalar(SkFloatToScalar(alpha));
|
||||
this->addInt((int)filterQuality);
|
||||
this->addInt((int)mode);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
this->addImage(set[i].fImage.get());
|
||||
this->addRect(set[i].fSrcRect);
|
||||
this->addRect(set[i].fDstRect);
|
||||
this->addInt((int)set[i].fAAFlags);
|
||||
}
|
||||
this->validate(initialOffset, size);
|
||||
}
|
||||
|
||||
void SkPictureRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) {
|
||||
// op + paint index + length + 'length' worth of chars + x + y
|
||||
|
@ -193,6 +193,8 @@ protected:
|
||||
const SkPaint*) override;
|
||||
void onDrawImageLattice(const SkImage*, const SkCanvas::Lattice& lattice, const SkRect& dst,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
|
||||
void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
|
||||
SkBlendMode, const SkPaint&) override;
|
||||
|
@ -109,6 +109,7 @@ template <> void Draw::draw(const DrawImageLattice& r) {
|
||||
|
||||
DRAW(DrawImageRect, legacy_drawImageRect(r.image.get(), r.src, r.dst, r.paint, r.constraint));
|
||||
DRAW(DrawImageNine, drawImageNine(r.image.get(), r.center, r.dst, r.paint));
|
||||
DRAW(DrawImageSet, experimental_DrawImageSetV0(r.set.get(), r.count, r.alpha, r.quality, r.mode));
|
||||
DRAW(DrawOval, drawOval(r.oval, r.paint));
|
||||
DRAW(DrawPaint, drawPaint(r.paint));
|
||||
DRAW(DrawPath, drawPath(r.path, r.paint));
|
||||
@ -383,6 +384,13 @@ private:
|
||||
Bounds bounds(const DrawImageNine& op) const {
|
||||
return this->adjustAndMap(op.dst, op.paint);
|
||||
}
|
||||
Bounds bounds(const DrawImageSet& op) const {
|
||||
SkRect rect = SkRect::MakeEmpty();
|
||||
for (int i = 0; i < op.count; ++i) {
|
||||
rect.join(this->adjustAndMap(op.set[i].fDstRect, nullptr));
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
Bounds bounds(const DrawPath& op) const {
|
||||
return op.path.isInverseFillType() ? fCullRect
|
||||
: this->adjustAndMap(op.path.getBounds(), &op.paint);
|
||||
|
@ -253,6 +253,15 @@ void SkRecorder::onDrawImageLattice(const SkImage* image, const Lattice& lattice
|
||||
this->copy(lattice.fColors, flagCount), *lattice.fBounds, dst);
|
||||
}
|
||||
|
||||
void SkRecorder::onDrawImageSet(const ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
SkAutoTArray<ImageSetEntry> setCopy(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
setCopy[i] = set[i];
|
||||
}
|
||||
this->append<SkRecords::DrawImageSet>(std::move(setCopy), count, alpha, filterQuality, mode);
|
||||
}
|
||||
|
||||
void SkRecorder::onDrawText(const void* text, size_t byteLength,
|
||||
SkScalar x, SkScalar y, const SkPaint& paint) {
|
||||
this->append<SkRecords::DrawText>(
|
||||
|
@ -116,6 +116,8 @@ public:
|
||||
const SkPaint*) override;
|
||||
void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
|
||||
SkBlendMode, const SkPaint&) override;
|
||||
void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
|
||||
|
@ -64,6 +64,7 @@ namespace SkRecords {
|
||||
M(DrawImageLattice) \
|
||||
M(DrawImageRect) \
|
||||
M(DrawImageNine) \
|
||||
M(DrawImageSet) \
|
||||
M(DrawDRRect) \
|
||||
M(DrawOval) \
|
||||
M(DrawPaint) \
|
||||
@ -265,6 +266,12 @@ RECORD(DrawImageNine, kDraw_Tag|kHasImage_Tag|kHasPaint_Tag,
|
||||
sk_sp<const SkImage> image;
|
||||
SkIRect center;
|
||||
SkRect dst);
|
||||
RECORD(DrawImageSet, kDraw_Tag|kHasImage_Tag,
|
||||
SkAutoTArray<SkCanvas::ImageSetEntry> set;
|
||||
int count;
|
||||
float alpha;
|
||||
SkFilterQuality quality;
|
||||
SkBlendMode mode);
|
||||
RECORD(DrawOval, kDraw_Tag|kHasPaint_Tag,
|
||||
SkPaint paint;
|
||||
SkRect oval);
|
||||
|
@ -798,6 +798,21 @@ void GrRenderTargetContext::drawTexture(const GrClip& clip, sk_sp<GrTextureProxy
|
||||
this->addDrawOp(clip, std::move(op));
|
||||
}
|
||||
|
||||
void GrRenderTargetContext::drawTextureSet(const GrClip& clip, const TextureSetEntry set[], int cnt,
|
||||
GrSamplerState::Filter filter, GrColor color,
|
||||
const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> texXform,
|
||||
sk_sp<GrColorSpaceXform> colorXform) {
|
||||
ASSERT_SINGLE_OWNER
|
||||
RETURN_IF_ABANDONED
|
||||
SkDEBUGCODE(this->validate();)
|
||||
GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureSet", fContext);
|
||||
GrAAType aaType = this->chooseAAType(GrAA::kYes, GrAllowMixedSamples::kNo);
|
||||
auto op = GrTextureOp::Make(fContext, set, cnt, filter, color, aaType, viewMatrix,
|
||||
std::move(texXform), std::move(colorXform));
|
||||
this->addDrawOp(clip, std::move(op));
|
||||
}
|
||||
|
||||
void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip,
|
||||
GrPaint&& paint,
|
||||
GrAA aa,
|
||||
|
@ -139,6 +139,21 @@ public:
|
||||
SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> texXform, sk_sp<GrColorSpaceXform> colorXform);
|
||||
|
||||
/** Used with drawTextureSet */
|
||||
struct TextureSetEntry {
|
||||
sk_sp<GrTextureProxy> fProxy;
|
||||
SkRect fSrcRect;
|
||||
SkRect fDstRect;
|
||||
GrQuadAAFlags fAAFlags;
|
||||
};
|
||||
/**
|
||||
* Draws a set of textures with a shared filter, color, view matrix, color xform, and
|
||||
* texture color xform. The textures must all have the same GrTextureType and GrConfig.
|
||||
*/
|
||||
void drawTextureSet(const GrClip&, const TextureSetEntry[], int cnt, GrSamplerState::Filter,
|
||||
GrColor, const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform,
|
||||
sk_sp<GrColorSpaceXform> colorXform);
|
||||
|
||||
/**
|
||||
* Draw a roundrect using a paint.
|
||||
*
|
||||
|
@ -1446,6 +1446,62 @@ void SkGpuDevice::drawBitmapLattice(const SkBitmap& bitmap,
|
||||
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
|
||||
}
|
||||
|
||||
void SkGpuDevice::drawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
SkASSERT(count > 0);
|
||||
if (mode != SkBlendMode::kSrcOver ||
|
||||
!fContext->contextPriv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
|
||||
INHERITED::drawImageSet(set, count, alpha, filterQuality, mode);
|
||||
return;
|
||||
}
|
||||
GrSamplerState sampler;
|
||||
sampler.setFilterMode(kNone_SkFilterQuality == filterQuality ? GrSamplerState::Filter::kNearest
|
||||
: GrSamplerState::Filter::kBilerp);
|
||||
SkAutoTArray<GrRenderTargetContext::TextureSetEntry> textures(count);
|
||||
GrColor color = GrColorPackA4(SkToUInt(SkTClamp(SkScalarRoundToInt(alpha * 255), 0, 255)));
|
||||
// We accumulate compatible proxies until we find an an incompatible one or reach the end and
|
||||
// issue the accumulated 'n' draws starting at 'base'.
|
||||
int base = 0, n = 0;
|
||||
auto draw = [&] {
|
||||
if (n > 0) {
|
||||
auto textureXform = GrColorSpaceXform::Make(
|
||||
set[base].fImage->colorSpace(), set[base].fImage->alphaType(),
|
||||
fRenderTargetContext->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
|
||||
fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
|
||||
sampler.filter(), color, this->ctm(),
|
||||
std::move(textureXform), nullptr);
|
||||
}
|
||||
};
|
||||
for (int i = 0; i < count; ++i) {
|
||||
textures[i].fProxy =
|
||||
as_IB(set[i].fImage.get())
|
||||
->asTextureProxyRef(fContext.get(), GrSamplerState::ClampBilerp(), nullptr,
|
||||
nullptr, nullptr);
|
||||
textures[i].fSrcRect = set[i].fSrcRect;
|
||||
textures[i].fDstRect = set[i].fDstRect;
|
||||
textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
|
||||
// If we failed to make a proxy or our proxy can't be grouped with the previous proxies then
|
||||
// draw the accumulated set and reset.
|
||||
if (!textures[i].fProxy) {
|
||||
draw();
|
||||
base = i + 1;
|
||||
n = 0;
|
||||
} else if (n > 0 &&
|
||||
(textures[i].fProxy->textureType() != textures[base].fProxy->textureType() ||
|
||||
textures[i].fProxy->config() != textures[base].fProxy->config() ||
|
||||
set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
|
||||
!SkColorSpace::Equals(set[i].fImage->colorSpace(),
|
||||
set[base].fImage->colorSpace()))) {
|
||||
draw();
|
||||
base = i;
|
||||
n = 1;
|
||||
} else {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
||||
static bool init_vertices_paint(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo,
|
||||
const SkPaint& skPaint, const SkMatrix& matrix, SkBlendMode bmode,
|
||||
bool hasTexs, bool hasColors, GrPaint* grPaint) {
|
||||
|
@ -105,6 +105,8 @@ public:
|
||||
const SkRect& dst, const SkPaint&) override;
|
||||
void drawBitmapLattice(const SkBitmap&, const SkCanvas::Lattice&,
|
||||
const SkRect& dst, const SkPaint&) override;
|
||||
void drawImageSet(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
|
||||
void drawSpecial(SkSpecialImage*, int left, int top, const SkPaint& paint,
|
||||
SkImage*, const SkMatrix&) override;
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "GrTextureOp.h"
|
||||
|
||||
#include <new>
|
||||
#include "GrAppliedClip.h"
|
||||
#include "GrCaps.h"
|
||||
#include "GrContext.h"
|
||||
@ -27,13 +27,13 @@
|
||||
#include "SkMatrixPriv.h"
|
||||
#include "SkPoint.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkRectPriv.h"
|
||||
#include "SkTo.h"
|
||||
#include "glsl/GrGLSLColorSpaceXformHelper.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryProcessor.h"
|
||||
#include "glsl/GrGLSLVarying.h"
|
||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include <new>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -271,8 +271,6 @@ private:
|
||||
// output.
|
||||
static void compute_quad_edges_and_outset_vertices(GrQuadAAFlags aaFlags, Sk4f* x, Sk4f* y, Sk4f* a,
|
||||
Sk4f* b, Sk4f* c, bool outsetCorners) {
|
||||
SkASSERT(GrQuadAAFlags::kNone != aaFlags);
|
||||
|
||||
static constexpr auto fma = SkNx_fma<4, float>;
|
||||
// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
|
||||
// order.
|
||||
@ -594,34 +592,56 @@ public:
|
||||
std::move(proxy), filter, color, srcRect, dstRect, aaType, aaFlags, constraint,
|
||||
viewMatrix, std::move(textureColorSpaceXform), std::move(paintColorSpaceXform));
|
||||
}
|
||||
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
||||
const GrRenderTargetContext::TextureSetEntry set[],
|
||||
int cnt, GrSamplerState::Filter filter, GrColor color,
|
||||
GrAAType aaType, const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> textureColorSpaceXform,
|
||||
sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
|
||||
size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
|
||||
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
|
||||
void* mem = pool->allocate(size);
|
||||
return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(
|
||||
set, cnt, filter, color, aaType, viewMatrix, std::move(textureColorSpaceXform),
|
||||
std::move(paintColorSpaceXform)));
|
||||
}
|
||||
|
||||
~TextureOp() override {
|
||||
if (fFinalized) {
|
||||
fProxy->completedRead();
|
||||
} else {
|
||||
fProxy->unref();
|
||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||
if (fFinalized) {
|
||||
fProxies[p].fProxy->completedRead();
|
||||
} else {
|
||||
fProxies[p].fProxy->unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* name() const override { return "TextureOp"; }
|
||||
|
||||
void visitProxies(const VisitProxyFunc& func) const override { func(fProxy); }
|
||||
void visitProxies(const VisitProxyFunc& func) const override {
|
||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||
func(fProxies[p].fProxy);
|
||||
}
|
||||
}
|
||||
|
||||
SkString dumpInfo() const override {
|
||||
SkString str;
|
||||
str.appendf("# draws: %d\n", fDraws.count());
|
||||
str.appendf("Proxy ID: %d, Filter: %d\n", fProxy->uniqueID().asUInt(),
|
||||
static_cast<int>(fFilter));
|
||||
for (int i = 0; i < fDraws.count(); ++i) {
|
||||
const Draw& draw = fDraws[i];
|
||||
str.appendf(
|
||||
"%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
|
||||
"Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
|
||||
i, draw.color(), draw.srcRect().fLeft, draw.srcRect().fTop,
|
||||
draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX,
|
||||
draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY,
|
||||
draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX,
|
||||
draw.quad().point(3).fY);
|
||||
str.appendf("# draws: %d\n", fQuads.count());
|
||||
int q = 0;
|
||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||
str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
|
||||
static_cast<int>(fFilter));
|
||||
for (int i = 0; i < fProxies[p].fQuadCnt; ++i, ++q) {
|
||||
const Quad& quad = fQuads[q];
|
||||
str.appendf(
|
||||
"%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
|
||||
"Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
|
||||
i, quad.color(), quad.srcRect().fLeft, quad.srcRect().fTop,
|
||||
quad.srcRect().fRight, quad.srcRect().fBottom, quad.quad().point(0).fX,
|
||||
quad.quad().point(0).fY, quad.quad().point(1).fX, quad.quad().point(1).fY,
|
||||
quad.quad().point(2).fX, quad.quad().point(2).fY, quad.quad().point(3).fX,
|
||||
quad.quad().point(3).fY);
|
||||
}
|
||||
}
|
||||
str += INHERITED::dumpInfo();
|
||||
return str;
|
||||
@ -630,8 +650,10 @@ public:
|
||||
RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
|
||||
SkASSERT(!fFinalized);
|
||||
fFinalized = true;
|
||||
fProxy->addPendingRead();
|
||||
fProxy->unref();
|
||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||
fProxies[p].fProxy->addPendingRead();
|
||||
fProxies[p].fProxy->unref();
|
||||
}
|
||||
return RequiresDstTexture::kNo;
|
||||
}
|
||||
|
||||
@ -653,7 +675,6 @@ private:
|
||||
: INHERITED(ClassID())
|
||||
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
||||
, fPaintColorSpaceXform(std::move(paintColorSpaceXform))
|
||||
, fProxy(proxy.release())
|
||||
, fFilter(filter)
|
||||
, fAAType(static_cast<unsigned>(aaType))
|
||||
, fFinalized(0) {
|
||||
@ -673,11 +694,11 @@ private:
|
||||
SK_ABORT("Should not use mixed sample AA");
|
||||
break;
|
||||
}
|
||||
fPerspective = viewMatrix.hasPerspective();
|
||||
fPerspective = static_cast<unsigned>(viewMatrix.hasPerspective());
|
||||
auto quad = GrPerspQuad(dstRect, viewMatrix);
|
||||
auto bounds = quad.bounds();
|
||||
// We expect our caller to have already caught this optimization.
|
||||
SkASSERT(!srcRect.contains(fProxy->getWorstCaseBoundsRect()) ||
|
||||
SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
|
||||
constraint == SkCanvas::kFast_SrcRectConstraint);
|
||||
if (viewMatrix.rectStaysRect()) {
|
||||
// Disable filtering when there is no scaling or fractional translation.
|
||||
@ -707,48 +728,107 @@ private:
|
||||
this->aaType() != GrAAType::kCoverage) {
|
||||
constraint = SkCanvas::kFast_SrcRectConstraint;
|
||||
}
|
||||
const auto& draw = fDraws.emplace_back(srcRect, quad, aaFlags, constraint, color);
|
||||
this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
|
||||
fDomain = static_cast<bool>(draw.domain());
|
||||
const auto& draw = fQuads.emplace_back(srcRect, quad, aaFlags, constraint, color);
|
||||
fProxyCnt = 1;
|
||||
fProxies[0] = {proxy.release(), 1};
|
||||
this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
|
||||
fDomain = static_cast<unsigned>(draw.domain());
|
||||
}
|
||||
TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
|
||||
GrSamplerState::Filter filter, GrColor color, GrAAType aaType,
|
||||
const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
|
||||
sk_sp<GrColorSpaceXform> paintColorSpaceXform)
|
||||
: INHERITED(ClassID())
|
||||
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
||||
, fPaintColorSpaceXform(std::move(paintColorSpaceXform))
|
||||
, fFilter(filter)
|
||||
, fAAType(static_cast<unsigned>(aaType))
|
||||
, fFinalized(0) {
|
||||
fQuads.reserve(cnt);
|
||||
fProxyCnt = SkToUInt(cnt);
|
||||
SkRect bounds = SkRectPriv::MakeLargestInverted();
|
||||
bool aa = false;
|
||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||
fProxies[p].fProxy = SkRef(set[p].fProxy.get());
|
||||
fProxies[p].fQuadCnt = 1;
|
||||
SkASSERT(fProxies[p].fProxy->textureType() == fProxies[0].fProxy->textureType());
|
||||
SkASSERT(fProxies[p].fProxy->config() == fProxies[0].fProxy->config());
|
||||
auto quad = GrPerspQuad(set[p].fDstRect, viewMatrix);
|
||||
bounds.joinPossiblyEmptyRect(quad.bounds());
|
||||
GrQuadAAFlags aaFlags = set[p].fAAFlags;
|
||||
switch (aaType) {
|
||||
case GrAAType::kNone:
|
||||
aaFlags = GrQuadAAFlags::kNone;
|
||||
break;
|
||||
case GrAAType::kCoverage:
|
||||
break;
|
||||
case GrAAType::kMSAA:
|
||||
aaFlags = GrQuadAAFlags::kAll;
|
||||
break;
|
||||
case GrAAType::kMixedSamples:
|
||||
SK_ABORT("Should not use mixed sample AA");
|
||||
break;
|
||||
}
|
||||
aa = aa || (set[p].fAAFlags != GrQuadAAFlags::kNone);
|
||||
fQuads.emplace_back(set[p].fSrcRect, quad, aaFlags, SkCanvas::kFast_SrcRectConstraint,
|
||||
color);
|
||||
}
|
||||
if (!aa) {
|
||||
fAAType = static_cast<unsigned>(GrAAType::kNone);
|
||||
}
|
||||
this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
|
||||
fPerspective = static_cast<unsigned>(viewMatrix.hasPerspective());
|
||||
fDomain = static_cast<unsigned>(false);
|
||||
}
|
||||
|
||||
template <typename Pos, Domain D, GrAA AA>
|
||||
void tess(void* v, const GrGeometryProcessor* gp) const {
|
||||
void tess(void* v, const GrGeometryProcessor* gp, const GrTextureProxy* proxy, int start,
|
||||
int cnt) const {
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
using Vertex = TextureGeometryProcessor::Vertex<Pos, D, AA>;
|
||||
SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex));
|
||||
auto vertices = static_cast<Vertex*>(v);
|
||||
auto origin = fProxy->origin();
|
||||
const auto* texture = fProxy->peekTexture();
|
||||
auto origin = proxy->origin();
|
||||
const auto* texture = proxy->peekTexture();
|
||||
float iw = 1.f / texture->width();
|
||||
float ih = 1.f / texture->height();
|
||||
|
||||
for (const auto& draw : fDraws) {
|
||||
tessellate_quad<Vertex>(draw.quad(), draw.aaFlags(), draw.srcRect(), draw.color(),
|
||||
origin, fFilter, vertices, iw, ih, draw.domain());
|
||||
for (int i = start; i < start + cnt; ++i) {
|
||||
const auto q = fQuads[i];
|
||||
tessellate_quad<Vertex>(q.quad(), q.aaFlags(), q.srcRect(), q.color(), origin, fFilter,
|
||||
vertices, iw, ih, q.domain());
|
||||
vertices += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void onPrepareDraws(Target* target) override {
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
bool hasPerspective = false;
|
||||
Domain domain = Domain::kNo;
|
||||
int numOps = 0;
|
||||
int numProxies = 0;
|
||||
auto textureType = fProxies[0].fProxy->textureType();
|
||||
auto config = fProxies[0].fProxy->config();
|
||||
for (const auto& op : ChainRange<TextureOp>(this)) {
|
||||
++numOps;
|
||||
hasPerspective |= op.fPerspective;
|
||||
if (op.fDomain) {
|
||||
domain = Domain::kYes;
|
||||
}
|
||||
if (!op.fProxy->instantiate(target->resourceProvider())) {
|
||||
return;
|
||||
numProxies += op.fProxyCnt;
|
||||
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
|
||||
auto* proxy = op.fProxies[p].fProxy;
|
||||
if (!proxy->instantiate(target->resourceProvider())) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(proxy->config() == config);
|
||||
SkASSERT(proxy->textureType() == textureType);
|
||||
}
|
||||
}
|
||||
|
||||
bool coverageAA = GrAAType::kCoverage == this->aaType();
|
||||
sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
|
||||
fProxy->textureType(), fProxy->config(), fFilter,
|
||||
std::move(fTextureColorSpaceXform), std::move(fPaintColorSpaceXform), coverageAA,
|
||||
hasPerspective, domain, *target->caps().shaderCaps());
|
||||
textureType, config, fFilter, std::move(fTextureColorSpaceXform),
|
||||
std::move(fPaintColorSpaceXform), coverageAA, hasPerspective, domain,
|
||||
*target->caps().shaderCaps());
|
||||
GrPipeline::InitArgs args;
|
||||
args.fProxy = target->proxy();
|
||||
args.fCaps = &target->caps();
|
||||
@ -763,12 +843,12 @@ private:
|
||||
// Otherwise, we use fixed dynamic state to specify the single op's proxy.
|
||||
GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
|
||||
GrPipeline::FixedDynamicState* fixedDynamicState;
|
||||
if (numOps > 1) {
|
||||
dynamicStateArrays = target->allocDynamicStateArrays(numOps, 1, false);
|
||||
if (numProxies > 1) {
|
||||
dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false);
|
||||
fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 0);
|
||||
} else {
|
||||
fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1);
|
||||
fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy;
|
||||
fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
|
||||
}
|
||||
const auto* pipeline =
|
||||
target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip));
|
||||
@ -799,44 +879,53 @@ private:
|
||||
|
||||
SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride());
|
||||
|
||||
GrMesh* meshes = target->allocMeshes(numOps);
|
||||
int i = 0;
|
||||
GrMesh* meshes = target->allocMeshes(numProxies);
|
||||
int m = 0;
|
||||
for (const auto& op : ChainRange<TextureOp>(this)) {
|
||||
int vstart;
|
||||
const GrBuffer* vbuffer;
|
||||
void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
|
||||
4 * op.fDraws.count(), &vbuffer, &vstart);
|
||||
if (!vdata) {
|
||||
SkDebugf("Could not allocate vertices\n");
|
||||
return;
|
||||
}
|
||||
|
||||
(op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get());
|
||||
|
||||
if (op.fDraws.count() > 1) {
|
||||
meshes[i].setPrimitiveType(GrPrimitiveType::kTriangles);
|
||||
sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
|
||||
if (!ibuffer) {
|
||||
SkDebugf("Could not allocate quad indices\n");
|
||||
int q = 0;
|
||||
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
|
||||
int quadCnt = op.fProxies[p].fQuadCnt;
|
||||
auto* proxy = op.fProxies[p].fProxy;
|
||||
int vstart;
|
||||
const GrBuffer* vbuffer;
|
||||
void* vdata = target->makeVertexSpace(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize,
|
||||
4 * quadCnt, &vbuffer, &vstart);
|
||||
if (!vdata) {
|
||||
SkDebugf("Could not allocate vertices\n");
|
||||
return;
|
||||
}
|
||||
meshes[i].setIndexedPatterned(ibuffer.get(), 6, 4, op.fDraws.count(),
|
||||
GrResourceProvider::QuadCountOfQuadBuffer());
|
||||
} else {
|
||||
meshes[i].setPrimitiveType(GrPrimitiveType::kTriangleStrip);
|
||||
meshes[i].setNonIndexedNonInstanced(4);
|
||||
|
||||
(op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get(), proxy, q,
|
||||
quadCnt);
|
||||
|
||||
if (quadCnt > 1) {
|
||||
meshes[m].setPrimitiveType(GrPrimitiveType::kTriangles);
|
||||
sk_sp<const GrBuffer> ibuffer =
|
||||
target->resourceProvider()->refQuadIndexBuffer();
|
||||
if (!ibuffer) {
|
||||
SkDebugf("Could not allocate quad indices\n");
|
||||
return;
|
||||
}
|
||||
meshes[m].setIndexedPatterned(ibuffer.get(), 6, 4, quadCnt,
|
||||
GrResourceProvider::QuadCountOfQuadBuffer());
|
||||
} else {
|
||||
meshes[m].setPrimitiveType(GrPrimitiveType::kTriangleStrip);
|
||||
meshes[m].setNonIndexedNonInstanced(4);
|
||||
}
|
||||
meshes[m].setVertexData(vbuffer, vstart);
|
||||
if (dynamicStateArrays) {
|
||||
dynamicStateArrays->fPrimitiveProcessorTextures[m] = proxy;
|
||||
}
|
||||
++m;
|
||||
q += quadCnt;
|
||||
}
|
||||
meshes[i].setVertexData(vbuffer, vstart);
|
||||
if (dynamicStateArrays) {
|
||||
dynamicStateArrays->fPrimitiveProcessorTextures[i] = op.fProxy;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
target->draw(std::move(gp), pipeline, fixedDynamicState, dynamicStateArrays, meshes,
|
||||
numOps);
|
||||
numProxies);
|
||||
}
|
||||
|
||||
CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
const auto* that = t->cast<TextureOp>();
|
||||
if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
|
||||
that->fTextureColorSpaceXform.get())) {
|
||||
@ -854,17 +943,21 @@ private:
|
||||
if (fFilter != that->fFilter) {
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
if (fProxy->uniqueID() != that->fProxy->uniqueID() || that->isChained()) {
|
||||
auto thisProxy = fProxies[0].fProxy;
|
||||
auto thatProxy = that->fProxies[0].fProxy;
|
||||
if (fProxyCnt > 1 || that->fProxyCnt > 1 ||
|
||||
thisProxy->uniqueID() != thatProxy->uniqueID() || that->isChained()) {
|
||||
// We can't merge across different proxies (and we're disallowed from merging when
|
||||
// 'that' is chained. Check if we can be chained with 'that'.
|
||||
if (fProxy->config() == that->fProxy->config() &&
|
||||
fProxy->textureType() == that->fProxy->textureType() &&
|
||||
if (thisProxy->config() == thatProxy->config() &&
|
||||
thisProxy->textureType() == thatProxy->textureType() &&
|
||||
caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
|
||||
return CombineResult::kMayChain;
|
||||
}
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin());
|
||||
fProxies[0].fQuadCnt += that->fQuads.count();
|
||||
fQuads.push_back_n(that->fQuads.count(), that->fQuads.begin());
|
||||
this->joinBounds(*that);
|
||||
fPerspective |= that->fPerspective;
|
||||
fDomain |= that->fDomain;
|
||||
@ -873,9 +966,9 @@ private:
|
||||
|
||||
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
|
||||
|
||||
class Draw {
|
||||
class Quad {
|
||||
public:
|
||||
Draw(const SkRect& srcRect, const GrPerspQuad& quad, GrQuadAAFlags aaFlags,
|
||||
Quad(const SkRect& srcRect, const GrPerspQuad& quad, GrQuadAAFlags aaFlags,
|
||||
SkCanvas::SrcRectConstraint constraint, GrColor color)
|
||||
: fSrcRect(srcRect)
|
||||
, fQuad(quad)
|
||||
@ -897,16 +990,21 @@ private:
|
||||
unsigned fHasDomain : 1;
|
||||
unsigned fAAFlags : 4;
|
||||
};
|
||||
SkSTArray<1, Draw, true> fDraws;
|
||||
struct Proxy {
|
||||
GrTextureProxy* fProxy;
|
||||
int fQuadCnt;
|
||||
};
|
||||
SkSTArray<1, Quad, true> fQuads;
|
||||
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
|
||||
sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
|
||||
GrTextureProxy* fProxy;
|
||||
GrSamplerState::Filter fFilter;
|
||||
unsigned fAAType : 2;
|
||||
unsigned fPerspective : 1;
|
||||
unsigned fDomain : 1;
|
||||
// Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
|
||||
unsigned fFinalized : 1;
|
||||
unsigned fProxyCnt : 32 - 5;
|
||||
Proxy fProxies[1];
|
||||
|
||||
typedef GrMeshDrawOp INHERITED;
|
||||
};
|
||||
@ -932,6 +1030,19 @@ std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
||||
std::move(paintColorSpaceXform));
|
||||
}
|
||||
|
||||
std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
||||
const GrRenderTargetContext::TextureSetEntry set[],
|
||||
int cnt,
|
||||
GrSamplerState::Filter filter,
|
||||
GrColor color,
|
||||
GrAAType aaType,
|
||||
const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> textureColorSpaceXform,
|
||||
sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
|
||||
return TextureOp::Make(context, set, cnt, filter, color, aaType, viewMatrix,
|
||||
std::move(textureColorSpaceXform), std::move(paintColorSpaceXform));
|
||||
}
|
||||
|
||||
} // namespace GrTextureOp
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "GrColor.h"
|
||||
#include "GrRenderTargetContext.h"
|
||||
#include "GrSamplerState.h"
|
||||
#include "GrTypesPriv.h"
|
||||
#include "SkCanvas.h"
|
||||
@ -38,4 +39,14 @@ std::unique_ptr<GrDrawOp> Make(GrContext*,
|
||||
const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> textureXform,
|
||||
sk_sp<GrColorSpaceXform> paintXform);
|
||||
|
||||
std::unique_ptr<GrDrawOp> Make(GrContext*,
|
||||
const GrRenderTargetContext::TextureSetEntry[],
|
||||
int cnt,
|
||||
GrSamplerState::Filter,
|
||||
GrColor,
|
||||
GrAAType,
|
||||
const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> textureXform,
|
||||
sk_sp<GrColorSpaceXform> paintXform);
|
||||
}
|
||||
|
@ -257,6 +257,14 @@ void SkNWayCanvas::onDrawImageLattice(const SkImage* image, const Lattice& latti
|
||||
}
|
||||
}
|
||||
|
||||
void SkNWayCanvas::onDrawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
Iter iter(fList);
|
||||
while (iter.next()) {
|
||||
iter->experimental_DrawImageSetV0(set, count, alpha, filterQuality, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void SkNWayCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) {
|
||||
Iter iter(fList);
|
||||
|
@ -173,6 +173,18 @@ void SkPaintFilterCanvas::onDrawImageLattice(const SkImage* image, const Lattice
|
||||
}
|
||||
}
|
||||
|
||||
void SkPaintFilterCanvas::onDrawImageSet(const SkCanvas::ImageSetEntry set[], int count,
|
||||
float alpha, SkFilterQuality filterQuality,
|
||||
SkBlendMode mode) {
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(mode);
|
||||
AutoPaintFilter apf(this, kBitmap_Type, &paint);
|
||||
mode = paint.getBlendMode();
|
||||
if (apf.shouldDraw()) {
|
||||
this->SkNWayCanvas::onDrawImageSet(set, count, alpha, filterQuality, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices,
|
||||
const SkVertices::Bone bones[], int boneCount,
|
||||
SkBlendMode bmode, const SkPaint& paint) {
|
||||
|
@ -357,6 +357,11 @@ void SkDebugCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center,
|
||||
this->addDrawCommand(new SkDrawImageNineCommand(image, center, dst, paint));
|
||||
}
|
||||
|
||||
void SkDebugCanvas::onDrawImageSet(const SkCanvas::ImageSetEntry set[], int count, float alpha,
|
||||
SkFilterQuality filterQuality, SkBlendMode mode) {
|
||||
this->addDrawCommand(new SkDrawImageSetCommand(set, count, alpha, filterQuality, mode));
|
||||
}
|
||||
|
||||
void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
|
||||
this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
|
||||
}
|
||||
|
@ -158,6 +158,8 @@ protected:
|
||||
const SkRect& dst, const SkPaint* paint) override;
|
||||
void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
|
||||
const SkPaint*, SrcRectConstraint) override;
|
||||
void onDrawImageSet(const ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode) override;
|
||||
void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
|
||||
const SkPaint*) override;
|
||||
void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
|
||||
|
@ -7,12 +7,15 @@
|
||||
|
||||
#include "SkDrawCommand.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "SkAutoMalloc.h"
|
||||
#include "SkClipOpPriv.h"
|
||||
#include "SkColorFilter.h"
|
||||
#include "SkDashPathEffect.h"
|
||||
#include "SkDrawable.h"
|
||||
#include "SkImageFilter.h"
|
||||
#include "SkJsonWriteBuffer.h"
|
||||
#include "SkLatticeIter.h"
|
||||
#include "SkMaskFilterBase.h"
|
||||
#include "SkPaintDefaults.h"
|
||||
#include "SkPathEffect.h"
|
||||
@ -20,13 +23,11 @@
|
||||
#include "SkPngEncoder.h"
|
||||
#include "SkReadBuffer.h"
|
||||
#include "SkRectPriv.h"
|
||||
#include "SkTextBlobPriv.h"
|
||||
#include "SkShadowFlags.h"
|
||||
#include "SkTHash.h"
|
||||
#include "SkTextBlobPriv.h"
|
||||
#include "SkTypeface.h"
|
||||
#include "SkWriteBuffer.h"
|
||||
#include "SkClipOpPriv.h"
|
||||
#include <SkLatticeIter.h>
|
||||
#include <SkShadowFlags.h>
|
||||
|
||||
#define SKDEBUGCANVAS_ATTRIBUTE_COMMAND "command"
|
||||
#define SKDEBUGCANVAS_ATTRIBUTE_VISIBLE "visible"
|
||||
@ -227,6 +228,7 @@ const char* SkDrawCommand::GetCommandString(OpType type) {
|
||||
case kDrawImageLattice_OpType: return "DrawImageLattice";
|
||||
case kDrawImageNine_OpType: return "DrawImageNine";
|
||||
case kDrawImageRect_OpType: return "DrawImageRect";
|
||||
case kDrawImageSet_OpType: return "DrawImageSet";
|
||||
case kDrawOval_OpType: return "DrawOval";
|
||||
case kDrawPaint_OpType: return "DrawPaint";
|
||||
case kDrawPatch_OpType: return "DrawPatch";
|
||||
@ -1552,6 +1554,22 @@ Json::Value SkDrawImageRectCommand::toJSON(UrlDataManager& urlDataManager) const
|
||||
return result;
|
||||
}
|
||||
|
||||
SkDrawImageSetCommand::SkDrawImageSetCommand(const SkCanvas::ImageSetEntry set[], int count,
|
||||
float alpha, SkFilterQuality filterQuality,
|
||||
SkBlendMode mode)
|
||||
: INHERITED(kDrawImageSet_OpType)
|
||||
, fSet(count)
|
||||
, fCount(count)
|
||||
, fAlpha(alpha)
|
||||
, fFilterQuality(filterQuality)
|
||||
, fMode(mode) {
|
||||
std::copy_n(set, count, fSet.get());
|
||||
}
|
||||
|
||||
void SkDrawImageSetCommand::execute(SkCanvas* canvas) const {
|
||||
canvas->experimental_DrawImageSetV0(fSet.get(), fCount, fAlpha, fFilterQuality, fMode);
|
||||
}
|
||||
|
||||
SkDrawImageNineCommand::SkDrawImageNineCommand(const SkImage* image, const SkIRect& center,
|
||||
const SkRect& dst, const SkPaint* paint)
|
||||
: INHERITED(kDrawImageNine_OpType)
|
||||
|
@ -43,6 +43,7 @@ public:
|
||||
kDrawImageLattice_OpType,
|
||||
kDrawImageNine_OpType,
|
||||
kDrawImageRect_OpType,
|
||||
kDrawImageSet_OpType,
|
||||
kDrawOval_OpType,
|
||||
kDrawArc_OpType,
|
||||
kDrawPaint_OpType,
|
||||
@ -363,6 +364,22 @@ private:
|
||||
typedef SkDrawCommand INHERITED;
|
||||
};
|
||||
|
||||
class SkDrawImageSetCommand : public SkDrawCommand {
|
||||
public:
|
||||
SkDrawImageSetCommand(const SkCanvas::ImageSetEntry[], int count, float alpha, SkFilterQuality,
|
||||
SkBlendMode);
|
||||
void execute(SkCanvas* canvas) const override;
|
||||
|
||||
private:
|
||||
SkAutoTArray<SkCanvas::ImageSetEntry> fSet;
|
||||
int fCount;
|
||||
float fAlpha;
|
||||
SkFilterQuality fFilterQuality;
|
||||
SkBlendMode fMode;
|
||||
|
||||
typedef SkDrawCommand INHERITED;
|
||||
};
|
||||
|
||||
class SkDrawOvalCommand : public SkDrawCommand {
|
||||
public:
|
||||
SkDrawOvalCommand(const SkRect& oval, const SkPaint& paint);
|
||||
|
Loading…
Reference in New Issue
Block a user