diff options
-rw-r--r-- | gm/drawbitmaprect.cpp | 109 | ||||
-rw-r--r-- | gm/image.cpp | 97 | ||||
-rw-r--r-- | include/core/SkFilterQuality.h | 24 | ||||
-rw-r--r-- | include/core/SkImage.h | 22 | ||||
-rw-r--r-- | include/core/SkPaint.h | 36 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 4 | ||||
-rw-r--r-- | src/image/SkImage.cpp | 53 | ||||
-rw-r--r-- | src/image/SkImage_Base.h | 4 |
8 files changed, 310 insertions, 39 deletions
diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp index fd205f5337..39d46dbd80 100644 --- a/gm/drawbitmaprect.cpp +++ b/gm/drawbitmaprect.cpp @@ -1,18 +1,17 @@ - /* * Copyright 2011 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 "SkBlurMask.h" #include "SkBlurMaskFilter.h" #include "SkColorPriv.h" #include "SkGradientShader.h" #include "SkShader.h" - -namespace skiagm { +#include "SkImage.h" static SkBitmap make_chessbm(int w, int h) { SkBitmap bm; @@ -28,7 +27,13 @@ static SkBitmap make_chessbm(int w, int h) { return bm; } -static void makebm(SkBitmap* bm, int w, int h) { +static SkImage* image_from_bitmap(const SkBitmap& bm) { + SkBitmap b(bm); + b.lockPixels(); + return SkImage::NewRasterCopy(b.info(), b.getPixels(), b.rowBytes()); +} + +static SkImage* makebm(SkBitmap* bm, int w, int h) { bm->allocN32Pixels(w, h); bm->eraseColor(SK_ColorTRANSPARENT); @@ -70,31 +75,83 @@ static void makebm(SkBitmap* bm, int w, int h) { } // Let backends know we won't change this, so they don't have to deep copy it defensively. bm->setImmutable(); + + return image_from_bitmap(*bm); } +static void canvasproc(SkCanvas* canvas, SkImage*, const SkBitmap& bm, const SkIRect* srcR, + const SkRect& dstR) { + canvas->drawBitmapRect(bm, srcR, dstR); +} + +static void imageproc(SkCanvas* canvas, SkImage* image, const SkBitmap&, const SkIRect* srcIR, + const SkRect& dstR) { + SkRect storage, *srcR = NULL; + if (srcIR) { + storage.set(*srcIR); + srcR = &storage; + } + canvas->drawImageRect(image, srcR, dstR); +} + +static void imagescaleproc(SkCanvas* canvas, SkImage* image, const SkBitmap&, const SkIRect* srcIR, + const SkRect& dstR) { + const int newW = SkScalarRoundToInt(dstR.width()); + const int newH = SkScalarRoundToInt(dstR.height()); + SkAutoTUnref<SkImage> newImage(image->newImage(newW, newH, srcIR)); + +#ifdef SK_DEBUG + const SkIRect baseR = SkIRect::MakeWH(image->width(), image->height()); + const bool containsSubset = !srcIR || baseR.contains(*srcIR); +#endif + + if (newImage) { + SkASSERT(containsSubset); + canvas->drawImage(newImage, dstR.x(), dstR.y()); + } else { + // newImage() does not support subsets that are not contained by the original + // but drawImageRect does, so we just draw an X in its place to indicate that we are + // deliberately not drawing here. + SkASSERT(!containsSubset); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(4); + canvas->drawLine(4, 4, newW - 4.0f, newH - 4.0f, paint); + canvas->drawLine(4, newH - 4.0f, newW - 4.0f, 4, paint); + } +} + +typedef void DrawRectRectProc(SkCanvas*, SkImage*, const SkBitmap&, const SkIRect*, const SkRect&); + static const int gSize = 1024; +static const int gBmpSize = 2048; -class DrawBitmapRectGM : public GM { +class DrawBitmapRectGM : public skiagm::GM { public: - DrawBitmapRectGM() { + DrawBitmapRectGM(DrawRectRectProc proc, const char suffix[]) : fProc(proc) { + fName.set("drawbitmaprect"); + if (suffix) { + fName.append(suffix); + } } - SkBitmap fLargeBitmap; + DrawRectRectProc* fProc; + SkBitmap fLargeBitmap; + SkAutoTUnref<SkImage> fImage; + SkString fName; protected: - SkString onShortName() { - return SkString("drawbitmaprect"); - } + SkString onShortName() SK_OVERRIDE { return fName; } - SkISize onISize() { return SkISize::Make(gSize, gSize); } + SkISize onISize() SK_OVERRIDE { return SkISize::Make(gSize, gSize); } - virtual void onDraw(SkCanvas* canvas) { - static const int kBmpSize = 2048; - if (fLargeBitmap.isNull()) { - makebm(&fLargeBitmap, kBmpSize, kBmpSize); - } + void onOnceBeforeDraw() SK_OVERRIDE { + fImage.reset(makebm(&fLargeBitmap, gBmpSize, gBmpSize)); + } + + void onDraw(SkCanvas* canvas) SK_OVERRIDE { SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)}; - static const int kMaxSrcRectSize = 1 << (SkNextLog2(kBmpSize) + 2); + static const int kMaxSrcRectSize = 1 << (SkNextLog2(gBmpSize) + 2); static const int kPadX = 30; static const int kPadY = 40; @@ -113,7 +170,7 @@ protected: blackPaint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&blackPaint); SkString title; - title.printf("Bitmap size: %d x %d", kBmpSize, kBmpSize); + title.printf("Bitmap size: %d x %d", gBmpSize, gBmpSize); canvas->drawText(title.c_str(), title.size(), 0, titleHeight, blackPaint); @@ -123,10 +180,8 @@ protected: for (int w = 1; w <= kMaxSrcRectSize; w *= 4) { for (int h = 1; h <= kMaxSrcRectSize; h *= 4) { - SkIRect srcRect = SkIRect::MakeXYWH((kBmpSize - w) / 2, - (kBmpSize - h) / 2, - w, h); - canvas->drawBitmapRect(fLargeBitmap, &srcRect, dstRect); + SkIRect srcRect = SkIRect::MakeXYWH((gBmpSize - w) / 2, (gBmpSize - h) / 2, w, h); + fProc(canvas, fImage, fLargeBitmap, &srcRect, dstRect); SkString label; label.appendf("%d x %d", w, h); @@ -176,11 +231,9 @@ protected: } private: - typedef GM INHERITED; + typedef skiagm::GM INHERITED; }; -////////////////////////////////////////////////////////////////////////////// - -static GM* MyFactory(void*) { return new DrawBitmapRectGM; } -static GMRegistry reg(MyFactory); -} +DEF_GM( return new DrawBitmapRectGM(canvasproc, NULL); ) +DEF_GM( return new DrawBitmapRectGM(imageproc, "-imagerect"); ) +DEF_GM( return new DrawBitmapRectGM(imagescaleproc, "-imagescale"); ) diff --git a/gm/image.cpp b/gm/image.cpp index 8fe96d1185..d980cb7562 100644 --- a/gm/image.cpp +++ b/gm/image.cpp @@ -6,10 +6,11 @@ */ #include "gm.h" -#include "SkSurface.h" +#include "SkData.h" #include "SkCanvas.h" +#include "SkRandom.h" #include "SkStream.h" -#include "SkData.h" +#include "SkSurface.h" #if SK_SUPPORT_GPU #include "GrContext.h" @@ -196,3 +197,95 @@ private: }; DEF_GM( return new ImageGM; ) +class ImageResizeGM : public skiagm::GM { + enum { + W = 100, + H = 100, + }; +public: + ImageResizeGM() {} + +protected: + SkString onShortName() SK_OVERRIDE { return SkString("image-resize"); } + + SkISize onISize() SK_OVERRIDE { return SkISize::Make(510, 480); } + + void drawIntoImage(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(3); + SkRandom rand; + for (int i = 0; i < 60; ++i) { + paint.setColor(rand.nextU()); + SkScalar x = rand.nextUScalar1() * W; + SkScalar y = rand.nextUScalar1() * H; + SkScalar r = rand.nextUScalar1() * W / 2; + canvas->drawCircle(x, y, r, paint); + } + } + + SkImage* makeImage(SkCanvas* canvas) { + const SkImageInfo info = SkImageInfo::MakeN32Premul(W, H); + SkAutoTUnref<SkSurface> surface(canvas->newSurface(info)); + if (!surface) { + surface.reset(SkSurface::NewRaster(info)); + } + this->drawIntoImage(surface->getCanvas()); + return surface->newImageSnapshot(); + } + + void drawResized(SkCanvas* canvas, SkImage* image, int newW, int newH, const SkIRect* subset, + SkFilterQuality fq) { + // canvas method + SkPaint paint; + paint.setFilterQuality(fq); + SkRect dstR = SkRect::MakeWH(SkIntToScalar(newW), SkIntToScalar(newH)); + SkRect srcR; + if (subset) { + srcR.set(*subset); + } + canvas->drawImageRect(image, subset ? &srcR : NULL, dstR, &paint); + canvas->translate(newW + 20.0f, 0); + + // image method + SkAutoTUnref<SkImage> image2(image->newImage(newW, newH, subset, fq)); + canvas->drawImage(image2, 0, 0, NULL); + canvas->translate(image2->width() + 20.0f, 0); + } + + void drawImage(SkCanvas* canvas, SkImage* image, SkFilterQuality fq) { + + canvas->drawImage(image, 0, 0, NULL); + canvas->translate(image->width() + 20.0f, 0); + this->drawResized(canvas, image, image->width()*4/10, image->height()*4/10, NULL, fq); + + SkIRect subset = SkIRect::MakeLTRB(W/4, H/4, W/2, H/2); + this->drawResized(canvas, image, W, H, &subset, fq); + } + + void onDraw(SkCanvas* canvas) SK_OVERRIDE { + canvas->translate(10, 10); + + SkAutoTUnref<SkImage> image(this->makeImage(canvas)); + + const SkFilterQuality fq[] = { + kNone_SkFilterQuality, + kLow_SkFilterQuality, + kMedium_SkFilterQuality, + kHigh_SkFilterQuality, + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(fq); ++i) { + { + SkAutoCanvasRestore acr(canvas, true); + this->drawImage(canvas, image, fq[i]); + } + canvas->translate(0, image->height() + 20.0f); + } + } + +private: + typedef skiagm::GM INHERITED; +}; +DEF_GM( return new ImageResizeGM; ) + diff --git a/include/core/SkFilterQuality.h b/include/core/SkFilterQuality.h new file mode 100644 index 0000000000..db0597e697 --- /dev/null +++ b/include/core/SkFilterQuality.h @@ -0,0 +1,24 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkFilterQuality_DEFINED +#define SkFilterQuality_DEFINED + +#include "SkTypes.h" + +/** + * Controls how much filtering to be done when scaling/transforming complex colors + * e.g. images + */ +enum SkFilterQuality { + kNone_SkFilterQuality, //!< fastest but lowest quality, typically nearest-neighbor + kLow_SkFilterQuality, //!< typically bilerp + kMedium_SkFilterQuality, //!< typically bilerp + mipmaps for down-scaling + kHigh_SkFilterQuality //!< slowest but highest quality, typically bicubic or better +}; + +#endif diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 8d65870dc0..b0587b2c3a 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -8,6 +8,7 @@ #ifndef SkImage_DEFINED #define SkImage_DEFINED +#include "SkFilterQuality.h" #include "SkImageInfo.h" #include "SkImageEncoder.h" #include "SkRefCnt.h" @@ -132,6 +133,27 @@ public: const char* toString(SkString*) const; + /** + * Return an image that is a rescale of this image (using newWidth, newHeight). + * + * If subset is NULL, then the entire original image is used as the src for the scaling. + * If subset is not NULL, then it specifies subset of src-pixels used for scaling. If + * subset extends beyond the bounds of the original image, then NULL is returned. + * + * Notes: + * - newWidth and newHeight must be > 0 or NULL will be returned. + * + * - it is legal for the returned image to be the same instance as the src image + * (if the new dimensions == the src dimensions and subset is NULL or == src dimensions). + * + * - it is legal for the "scaled" image to have changed its SkAlphaType from unpremul + * to premul (as required by the impl). The image should draw (nearly) identically, + * since during drawing we will "apply the alpha" to the pixels. Future optimizations + * may take away this caveat, preserving unpremul. + */ + SkImage* newImage(int newWidth, int newHeight, const SkIRect* subset = NULL, + SkFilterQuality = kNone_SkFilterQuality) const; + protected: SkImage(int width, int height) : fWidth(width), diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 68220f65c9..fef4319477 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -10,9 +10,13 @@ #include "SkColor.h" #include "SkDrawLooper.h" +#include "SkFilterQuality.h" #include "SkMatrix.h" #include "SkXfermode.h" +// TODO: clean up Skia internals so we can remove this and only keep it for clients +#define SK_SUPPORT_LEGACY_FILTERLEVEL_ENUM + class SkAnnotation; class SkAutoGlyphCache; class SkColorFilter; @@ -294,11 +298,12 @@ public: */ void setDistanceFieldTextTEMP(bool distanceFieldText); +#ifdef SK_SUPPORT_LEGACY_FILTERLEVEL_ENUM enum FilterLevel { - kNone_FilterLevel, - kLow_FilterLevel, - kMedium_FilterLevel, - kHigh_FilterLevel + kNone_FilterLevel = kNone_SkFilterQuality, + kLow_FilterLevel = kLow_SkFilterQuality, + kMedium_FilterLevel = kMedium_SkFilterQuality, + kHigh_FilterLevel = kHigh_SkFilterQuality }; /** @@ -306,14 +311,31 @@ public: * drawing scaled images. */ FilterLevel getFilterLevel() const { - return (FilterLevel)fBitfields.fFilterLevel; + return (FilterLevel)this->getFilterQuality(); } /** * Set the filter level. This affects the quality (and performance) of * drawing scaled images. */ - void setFilterLevel(FilterLevel); + void setFilterLevel(FilterLevel level) { + this->setFilterQuality((SkFilterQuality)level); + } +#endif + + /** + * Return the filter level. This affects the quality (and performance) of + * drawing scaled images. + */ + SkFilterQuality getFilterQuality() const { + return (SkFilterQuality)fBitfields.fFilterQuality; + } + + /** + * Set the filter quality. This affects the quality (and performance) of + * drawing scaled images. + */ + void setFilterQuality(SkFilterQuality quality); /** * If the predicate is true, set the filterLevel to Low, else set it to @@ -1040,7 +1062,7 @@ private: unsigned fStyle : 2; unsigned fTextEncoding : 2; // 3 values unsigned fHinting : 2; - unsigned fFilterLevel : 2; + unsigned fFilterQuality : 2; //unsigned fFreeBits : 2; } fBitfields; uint32_t fBitfieldsUInt; diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 0bd641f15f..dd9611e9c6 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -171,8 +171,8 @@ void SkPaint::reset() { *this = init; } -void SkPaint::setFilterLevel(FilterLevel level) { - fBitfields.fFilterLevel = level; +void SkPaint::setFilterQuality(SkFilterQuality quality) { + fBitfields.fFilterQuality = quality; } void SkPaint::setHinting(Hinting hintingLevel) { diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 97e7475a34..109808842e 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -94,6 +94,30 @@ const char* SkImage::toString(SkString* str) const { return str->c_str(); } +SkImage* SkImage::newImage(int newWidth, int newHeight, const SkIRect* subset, + SkFilterQuality quality) const { + if (newWidth <= 0 || newHeight <= 0) { + return NULL; + } + + const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height()); + + if (subset) { + if (!bounds.contains(*subset)) { + return NULL; + } + if (bounds == *subset) { + subset = NULL; // and fall through to check below + } + } + + if (NULL == subset && this->width() == newWidth && this->height() == newHeight) { + return SkRef(const_cast<SkImage*>(this)); + } + + return as_IB(this)->onNewImage(newWidth, newHeight, subset, quality); +} + /////////////////////////////////////////////////////////////////////////////// static bool raster_canvas_supports(const SkImageInfo& info) { @@ -126,3 +150,32 @@ bool SkImage_Base::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, siz return true; } + +SkImage* SkImage_Base::onNewImage(int newWidth, int newHeight, const SkIRect* subset, + SkFilterQuality quality) const { + const bool opaque = this->isOpaque(); + const SkImageInfo info = SkImageInfo::Make(newWidth, newHeight, kN32_SkColorType, + opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); + SkAutoTUnref<SkSurface> surface(this->newSurface(info, NULL)); + if (!surface.get()) { + return NULL; + } + + SkRect src; + if (subset) { + src.set(*subset); + } else { + src = SkRect::MakeIWH(this->width(), this->height()); + } + + surface->getCanvas()->scale(newWidth / src.width(), newHeight / src.height()); + surface->getCanvas()->translate(-src.x(), -src.y()); + + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + paint.setFilterQuality(quality); + surface->getCanvas()->drawImage(this, 0, 0, &paint); + return surface->newImageSnapshot(); +} + + diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h index 8c1dfada63..512c80c44b 100644 --- a/src/image/SkImage_Base.h +++ b/src/image/SkImage_Base.h @@ -59,6 +59,10 @@ public: SkShader::TileMode, const SkMatrix* localMatrix) const { return NULL; }; + // newWidth > 0, newHeight > 0, subset either NULL or a proper subset of this bounds + virtual SkImage* onNewImage(int newWidth, int newHeight, const SkIRect* subset, + SkFilterQuality) const; + private: const SkSurfaceProps fProps; |