blob: b6e86c4126c5c303924e91d9b1833ef288a91d45 [file] [log] [blame]
/*
* Copyright 2015 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 "SkImage.h"
#include "SkImageGenerator.h"
#include "SkMutex.h"
#include "SkSurface.h"
#include "SkTArray.h"
namespace {
void release_proc(void*, void* releaseCtx) {
reinterpret_cast<SkImage*>(releaseCtx)->unref();
}
class ExternalGenerator : public SkImageGenerator {
public:
ExternalGenerator(const SkISize size)
: INHERITED(SkImageInfo::MakeN32Premul(size.width(), size.height())) {
int level = 0;
for (int size = kMaxSize; size; size /= 2) {
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(size, size);
DrawRings(surface->getCanvas(), 0xff008000, level++);
fMips.emplace_back(surface->makeImageSnapshot());
}
}
virtual ~ExternalGenerator() {}
protected:
bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
SkPMColor[], int*) override {
sk_sp<SkSurface> s = SkSurface::MakeRasterDirect(info, pixels, rowBytes);
s->getCanvas()->clear(SK_ColorTRANSPARENT);
DrawRings(s->getCanvas(), SK_ColorRED);
return true;
}
bool onAccessScaledImage(const SkRect& src, const SkMatrix& matrix, SkFilterQuality,
ScaledImageRec* rec) override {
// Not strictly needed for this immutable class.
SkAutoExclusive lock(fMutex);
SkSize scaleSize;
if (!matrix.decomposeScale(&scaleSize, nullptr)) {
return false;
}
scaleSize.set(scaleSize.width() * this->getInfo().width() / kMaxSize,
scaleSize.height() * this->getInfo().height() / kMaxSize);
const SkScalar scale = SkTMin(scaleSize.width(), scaleSize.height());
const int lvl = SkScalarFloorToInt(-SkScalarLog2(scale));
const sk_sp<SkImage>& img = fMips[SkTPin(lvl, 0, fMips.count())];
SkAssertResult(img->peekPixels(&rec->fPixmap));
const SkRect origBounds = SkRect::Make(this->getInfo().bounds());
const SkRect newBounds = SkRect::Make(img->bounds());
SkMatrix srcMap = SkMatrix::MakeScale(newBounds.width() / origBounds.width(),
newBounds.height() / origBounds.height());
srcMap.preTranslate(src.x(), src.y());
srcMap.mapRect(&rec->fSrcRect, SkRect::MakeWH(src.width(), src.height()));
rec->fQuality = kLow_SkFilterQuality;
rec->fReleaseProc = release_proc;
rec->fReleaseCtx = SkRef(img.get());
return true;
}
private:
static void DrawRings(SkCanvas* c, SkColor color, int lvl = 0) {
static constexpr SkScalar kStep = 0.2f;
SkRect rect = SkRect::MakeWH(1, 1);
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(0.02f);
p.setAntiAlias(true);
p.setColor(color);
c->concat(SkMatrix::MakeRectToRect(SkRect::MakeWH(1, 1),
SkRect::MakeIWH(c->imageInfo().width(),
c->imageInfo().height()),
SkMatrix::kFill_ScaleToFit));
while (!rect.isEmpty()) {
c->drawRect(rect, p);
rect.inset(kStep, kStep);
}
static constexpr SkScalar kTxtSize = 0.2f;
SkASSERT(lvl >= 0 && lvl <= 9);
const char label = '0' + lvl;
p.setTextSize(kTxtSize);
p.setLinearText(true);
p.setStyle(SkPaint::kFill_Style);
SkRect labelBounds;
p.measureText(&label, 1, &labelBounds);
c->drawText(&label, 1, 0.5f - labelBounds.width() / 2, 0.5f + labelBounds.height() / 2, p);
}
SkMutex fMutex;
static constexpr int kMaxSize = 512;
SkTArray<sk_sp<SkImage>> fMips;
typedef SkImageGenerator INHERITED;
};
} // anonymous ns
class ImageGenExternalGM : public skiagm::GM {
public:
explicit ImageGenExternalGM(bool useShader) : fUseShader(useShader) {}
protected:
SkString onShortName() override {
return SkStringPrintf("ImageGeneratorExternal%s", fUseShader ? "_shader" : "_rect");
}
SkISize onISize() override {
return SkISize::Make(800, 800);
}
void onOnceBeforeDraw() override {
fImage = SkImage::MakeFromGenerator(new ExternalGenerator(SkISize::Make(kGeneratorSize,
kGeneratorSize)));
}
void onDraw(SkCanvas* canvas) override {
static const SkRect gSubsets[] = {
SkRect::MakeLTRB(0 , 0 , 1 , 1 ),
SkRect::MakeLTRB(0 , 0 , 0.5f , 0.5f ),
SkRect::MakeLTRB(0.5f , 0 , 1 , 0.5f ),
SkRect::MakeLTRB(0.5f , 0.5f , 1 , 1 ),
SkRect::MakeLTRB(0 , 0.5f , 0.5f , 1 ),
SkRect::MakeLTRB(0.25f, 0.25f, 0.75f, 0.75f),
};
SkPaint p;
p.setFilterQuality(kLow_SkFilterQuality);
for (int i = 1; i <= 4; ++i) {
const SkRect dst = SkRect::MakeIWH(kGeneratorSize / i, kGeneratorSize / i);
canvas->save();
for (size_t j = 0; j < SK_ARRAY_COUNT(gSubsets); ++j) {
SkRect subset = gSubsets[j];
subset.set(kGeneratorSize * subset.left(),
kGeneratorSize * subset.top(),
kGeneratorSize * subset.right(),
kGeneratorSize * subset.bottom());
this->drawSubset(canvas, subset, dst, p);
canvas->translate(kGeneratorSize * 1.1f, 0);
}
canvas->restore();
canvas->translate(0, dst.height() * 1.2f);
}
}
private:
void drawSubset(SkCanvas* canvas, const SkRect& src, const SkRect& dst,
const SkPaint& paint) const {
if (fUseShader) {
SkPaint p(paint);
SkMatrix localMatrix = SkMatrix::MakeRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
p.setShader(fImage->makeShader(SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode,
&localMatrix));
canvas->drawRect(dst, p);
} else {
canvas->drawImageRect(fImage, src, dst, &paint);
}
}
static constexpr int kGeneratorSize = 200;
sk_sp<SkImage> fImage;
bool fUseShader;
typedef skiagm::GM INHERITED;
};
DEF_GM( return new ImageGenExternalGM(false); )
DEF_GM( return new ImageGenExternalGM(true); )