-
/*
* 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;
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);
}
// 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;
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);
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);
}
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"); )
*/
#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"
};
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; )
+
--- /dev/null
+/*
+ * 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
#ifndef SkImage_DEFINED
#define SkImage_DEFINED
+#include "SkFilterQuality.h"
#include "SkImageInfo.h"
#include "SkImageEncoder.h"
#include "SkRefCnt.h"
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),
#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;
*/
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
};
/**
* 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
unsigned fStyle : 2;
unsigned fTextEncoding : 2; // 3 values
unsigned fHinting : 2;
- unsigned fFilterLevel : 2;
+ unsigned fFilterQuality : 2;
//unsigned fFreeBits : 2;
} fBitfields;
uint32_t fBitfieldsUInt;
*this = init;
}
-void SkPaint::setFilterLevel(FilterLevel level) {
- fBitfields.fFilterLevel = level;
+void SkPaint::setFilterQuality(SkFilterQuality quality) {
+ fBitfields.fFilterQuality = quality;
}
void SkPaint::setHinting(Hinting hintingLevel) {
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) {
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();
+}
+
+
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;