/* * 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(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 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 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& 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> 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 fImage; bool fUseShader; typedef skiagm::GM INHERITED; }; DEF_GM( return new ImageGenExternalGM(false); ) DEF_GM( return new ImageGenExternalGM(true); )