add encodeData() to SkImageEncoder, and add encoding to SkImage

BUG=
R=scroggo@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@9193 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index e6ac682..9b7dfd1 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -21,8 +21,6 @@
 // need for TileMode
 #include "SkShader.h"
 
-////// EXPERIMENTAL
-
 /**
  *  SkImage is an abstraction for drawing a rectagle of pixels, though the
  *  particular type of image could be actually storing its data on the GPU, or
@@ -84,6 +82,24 @@
 
     void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
 
+    enum EncodeType {
+        kBMP_EncodeType,
+        kGIF_EncodeType,
+        kICO_EncodeType,
+        kJPEG_EncodeType,
+        kPNG_EncodeType,
+        kWBMP_EncodeType,
+        kWEBP_EncodeType,
+    };
+    /**
+     *  Encode the image's pixels and return the result as a new SkData, which
+     *  the caller must manage (i.e. call unref() when they are done).
+     *
+     *  If the image type cannot be encoded, or the requested encoder type is
+     *  not supported, this will return NULL.
+     */
+    SkData* encode(EncodeType t = kPNG_EncodeType, int quality = 80) const;
+
 protected:
     SkImage(int width, int height) :
         fWidth(width),
diff --git a/include/images/SkImageEncoder.h b/include/images/SkImageEncoder.h
index 2965c88..b990aff 100644
--- a/include/images/SkImageEncoder.h
+++ b/include/images/SkImageEncoder.h
@@ -11,6 +11,7 @@
 #include "SkTypes.h"
 
 class SkBitmap;
+class SkData;
 class SkWStream;
 
 class SkImageEncoder {
@@ -35,25 +36,28 @@
     };
 
     /**
+     *  Encode bitmap 'bm', returning the results in an SkData, at quality level
+     *  'quality' (which can be in range 0-100). If the bitmap cannot be
+     *  encoded, return null. On success, the caller is responsible for
+     *  calling unref() on the data when they are finished.
+     */
+    SkData* encodeData(const SkBitmap&, int quality);
+
+    /**
      * Encode bitmap 'bm' in the desired format, writing results to
      * file 'file', at quality level 'quality' (which can be in range
-     * 0-100).
-     *
-     * Calls the particular implementation's onEncode() method to
-     * actually do the encoding.
+     * 0-100). Returns false on failure.
      */
     bool encodeFile(const char file[], const SkBitmap& bm, int quality);
 
     /**
      * Encode bitmap 'bm' in the desired format, writing results to
      * stream 'stream', at quality level 'quality' (which can be in
-     * range 0-100).
-     *
-     * Calls the particular implementation's onEncode() method to
-     * actually do the encoding.
+     * range 0-100). Returns false on failure.
      */
     bool encodeStream(SkWStream* stream, const SkBitmap& bm, int quality);
 
+    static SkData* EncodeData(const SkBitmap&, Type, int quality);
     static bool EncodeFile(const char file[], const SkBitmap&, Type,
                            int quality);
     static bool EncodeStream(SkWStream*, const SkBitmap&, Type,
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 1169459..13af631 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -9,6 +9,7 @@
 #include "SkImagePriv.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "../images/SkImageEncoder.h"
 
 SK_DEFINE_INST_COUNT(SkImage)
 
@@ -16,6 +17,10 @@
     return static_cast<SkImage_Base*>(image);
 }
 
+static const SkImage_Base* asIB(const SkImage* image) {
+    return static_cast<const SkImage_Base*>(image);
+}
+
 uint32_t SkImage::NextUniqueID() {
     static int32_t gUniqueID;
 
@@ -35,3 +40,29 @@
 GrTexture* SkImage::getTexture() {
     return asIB(this)->onGetTexture();
 }
+
+static const struct {
+    SkImageEncoder::Type    fIE;
+    SkImage::EncodeType     fET;
+} gTable[] = {
+    { SkImageEncoder::kBMP_Type,    SkImage::kBMP_EncodeType  },
+    { SkImageEncoder::kGIF_Type,    SkImage::kGIF_EncodeType  },
+    { SkImageEncoder::kICO_Type,    SkImage::kICO_EncodeType  },
+    { SkImageEncoder::kJPEG_Type,   SkImage::kJPEG_EncodeType },
+    { SkImageEncoder::kPNG_Type,    SkImage::kPNG_EncodeType  },
+    { SkImageEncoder::kWBMP_Type,   SkImage::kWBMP_EncodeType },
+    { SkImageEncoder::kWEBP_Type,   SkImage::kWEBP_EncodeType },
+};
+
+SkData* SkImage::encode(EncodeType et, int quality) const {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTable); ++i) {
+        if (gTable[i].fET == et) {
+            SkBitmap bm;
+            if (asIB(this)->getROPixels(&bm)) {
+                return SkImageEncoder::EncodeData(bm, gTable[i].fIE, quality);
+            }
+            break;
+        }
+    }
+    return NULL;
+}
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index d8e64a9..0b3ddb4 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -16,6 +16,10 @@
 
     virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) = 0;
     virtual GrTexture* onGetTexture() { return NULL; }
+    
+    // return a read-only copy of the pixels. We promise to not modify them,
+    // but only inspect them (or encode them).
+    virtual bool getROPixels(SkBitmap*) const { return false; }
 
 private:
     typedef SkImage INHERITED;
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 451ad07..b6e4211 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -22,6 +22,10 @@
 
     virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) SK_OVERRIDE;
     virtual GrTexture* onGetTexture() SK_OVERRIDE;
+    virtual bool getROPixels(SkBitmap*) const SK_OVERRIDE {
+        // TODO
+        return false;
+    }
 
     GrTexture* getTexture() { return fTexture; }
 
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 4a18f4a..5e1e8a0 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -55,6 +55,7 @@
     virtual ~SkImage_Raster();
 
     virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) SK_OVERRIDE;
+    virtual bool getROPixels(SkBitmap*) const SK_OVERRIDE;
 
     // exposed for SkSurface_Raster via SkNewImageFromPixelRef
     SkImage_Raster(const SkImage::Info&, SkPixelRef*, size_t rowBytes);
@@ -108,6 +109,11 @@
     canvas->drawBitmap(fBitmap, x, y, paint);
 }
 
+bool SkImage_Raster::getROPixels(SkBitmap* dst) const {
+    *dst = fBitmap;
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkImage* SkImage::NewRasterCopy(const SkImage::Info& info, const void* pixels, size_t rowBytes) {
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
index 4b52fcd..5f5ffb8 100644
--- a/src/images/SkImageEncoder.cpp
+++ b/src/images/SkImageEncoder.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2009 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkImageEncoder.h"
 #include "SkBitmap.h"
 #include "SkStream.h"
@@ -27,6 +25,15 @@
     return this->onEncode(&stream, bm, quality);
 }
 
+SkData* SkImageEncoder::encodeData(const SkBitmap& bm, int quality) {
+    SkDynamicMemoryWStream stream;
+    quality = SkMin32(100, SkMax32(0, quality));
+    if (this->onEncode(&stream, bm, quality)) {
+        return stream.copyToData();
+    }
+    return NULL;
+}
+
 bool SkImageEncoder::EncodeFile(const char file[], const SkBitmap& bm, Type t,
                                 int quality) {
     SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
@@ -34,7 +41,13 @@
 }
 
 bool SkImageEncoder::EncodeStream(SkWStream* stream, const SkBitmap& bm, Type t,
-                                int quality) {
+                                  int quality) {
     SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
     return enc.get() && enc.get()->encodeStream(stream, bm, quality);
 }
+
+SkData* SkImageEncoder::EncodeData(const SkBitmap& bm, Type t, int quality) {
+    SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+    return enc.get() ? enc.get()->encodeData(bm, quality) : NULL;
+}
+
diff --git a/tools/skhello.cpp b/tools/skhello.cpp
index a4451ac..3b8ddee 100644
--- a/tools/skhello.cpp
+++ b/tools/skhello.cpp
@@ -7,8 +7,11 @@
 
 #include "SkCanvas.h"
 #include "SkCommandLineFlags.h"
+#include "SkData.h"
 #include "SkGraphics.h"
-#include "SkImageEncoder.h"
+#include "SkSurface.h"
+#include "SkImage.h"
+#include "SkStream.h"
 #include "SkString.h"
 
 DEFINE_string2(outFile, o, "skhello.png", "The filename to write the image.");
@@ -33,29 +36,29 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     paint.setTextSize(SkIntToScalar(30));
+    paint.setTextAlign(SkPaint::kCenter_Align);
+
     SkScalar width = paint.measureText(text.c_str(), text.size());
     SkScalar spacing = paint.getFontSpacing();
 
     int w = SkScalarRound(width) + 30;
     int h = SkScalarRound(spacing) + 30;
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    bitmap.allocPixels();
 
-    SkCanvas canvas(bitmap);
-    canvas.drawColor(SK_ColorWHITE);
+    SkImage::Info info = {
+        w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
+    };
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
+    SkCanvas* canvas = surface->getCanvas();
 
-    paint.setTextAlign(SkPaint::kCenter_Align);
-    canvas.drawText(text.c_str(), text.size(),
-                    SkIntToScalar(w)/2, SkIntToScalar(h)*2/3,
-                    paint);
+    canvas->drawColor(SK_ColorWHITE);
+    canvas->drawText(text.c_str(), text.size(),
+                     SkIntToScalar(w)/2, SkIntToScalar(h)*2/3,
+                     paint);
 
-    bool success = SkImageEncoder::EncodeFile(path.c_str(), bitmap,
-                               SkImageEncoder::kPNG_Type, 100);
-    if (!success) {
-        SkDebugf("--- failed to write %s\n", path.c_str());
-    }
-    return !success;
+    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
+    SkAutoDataUnref data(image->encode());
+    SkFILEWStream stream(path.c_str());
+    return stream.write(data->data(), data->size());
 }
 
 #if !defined SK_BUILD_FOR_IOS