add newImage API
authorreed <reed@google.com>
Fri, 23 Jan 2015 13:58:07 +0000 (05:58 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 23 Jan 2015 13:58:07 +0000 (05:58 -0800)
BUG=skia:3277
related bug: skbug.com/3276

Review URL: https://codereview.chromium.org/821083002

gm/drawbitmaprect.cpp
gm/image.cpp
include/core/SkFilterQuality.h [new file with mode: 0644]
include/core/SkImage.h
include/core/SkPaint.h
src/core/SkPaint.cpp
src/image/SkImage.cpp
src/image/SkImage_Base.h

index fd205f53378b9e36ce0065082eedde8b1d41c54d..39d46dbd807dd87fff7c871536af76032ae17489 100644 (file)
@@ -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"); )
index 8fe96d118521a9b92b15c66ea8a424a30dc593d5..d980cb7562e2f67d9862f6c64c1b552180f44b51 100644 (file)
@@ -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 (file)
index 0000000..db0597e
--- /dev/null
@@ -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
index 8d65870dc03883a2c8e1cfa954781ad8946d273d..b0587b2c3a884a68fef29f18a9a66774d48c0f3f 100644 (file)
@@ -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),
index 68220f65c9c25519b1884c155adc8f632478cf4b..fef4319477ed183dc4c8772cf6bebd45e39b4610 100644 (file)
 
 #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;
index 0bd641f15f0a11ab25f2189d9d68743e83044b7d..dd9611e9c6fcb3741a2ab839136d6662e047b3b6 100644 (file)
@@ -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) {
index 97e7475a34ff28dc49b6499d5ecaef86c3c8d7fa..109808842efde1a1dda5b36b5a5221d6e2991f5e 100644 (file)
@@ -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();
+}
+
+
index 8c1dfada638db398c6b645c7e0c264c54a3b470c..512c80c44beca03134cbeee92585b8c46a46e85c 100644 (file)
@@ -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;