check-point for image experiment
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 27 Jul 2012 18:02:50 +0000 (18:02 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 27 Jul 2012 18:02:50 +0000 (18:02 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@4811 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkImage.h
src/image/SkDataPixelRef.cpp [new file with mode: 0644]
src/image/SkDataPixelRef.h [new file with mode: 0644]
src/image/SkImage.cpp [new file with mode: 0644]

index 9f1bd1f..df71875 100644 (file)
@@ -8,9 +8,20 @@
 #ifndef SkImage_DEFINED
 #define SkImage_DEFINED
 
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+class SkData;
+class SkCanvas;
+class SkPaint;
+class SkShader;
+
+// need for TileMode
+#include "SkShader.h"
 
 ////// EXPERIMENTAL
 
+class SkColorSpace;
 
 /**
  *  SkImage is an abstraction for drawing a rectagle of pixels, though the
 class SkImage : public SkRefCnt {
 public:
     enum ColorType {
-        kA8_ColorType,
+        kAlpha_8_ColorType,
         kRGB_565_ColorType,
         kRGBA_8888_ColorType,
         kBGRA_8888_ColorType,
         kPMColor_ColorType,
+        
+        kLastEnum_ColorType = kPMColor_ColorType
     };
     
     enum AlphaType {
         kIgnore_AlphaType,
         kOpaque_AlphaType,
         kPremul_AlphaType,
-        kUnpremul_AlphaType
+        kUnpremul_AlphaType,
+        
+        kLastEnum_AlphaType = kUnpremul_AlphaType
     };
 
     struct Info {
@@ -51,14 +66,31 @@ public:
     static SkImage* NewRasterData(const Info&, SkColorSpace*, SkData* pixels, size_t rowBytes);
     static SkImage* NewEncodedData(SkData*);
 
-    int         width() const;
-    int         height() const;
-    uint32_t    uniqueID() const;
-    
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    uint32_t uniqueID() const { return fUniqueID; }
+
     SkShader*   newShaderClamp() const;
     SkShader*   newShader(SkShader::TileMode, SkShader::TileMode) const;
-    
-    void SkCanvas::drawImage(...);
+
+    void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
+
+protected:
+    SkImage(int width, int height) :
+        fWidth(width),
+        fHeight(height),
+        fUniqueID(NextUniqueID()) {
+
+        SkASSERT(width >= 0);
+        SkASSERT(height >= 0);
+    }
+
+private:
+    const int       fWidth;
+    const int       fHeight;
+    const uint32_t  fUniqueID;
+
+    static uint32_t NextUniqueID();
 };
 
 /**
@@ -71,20 +103,39 @@ public:
  */
 class SkSurface : public SkRefCnt {
 public:
-    static SkSurface*   NewRasterDirect(const Info&, SkColorSpace*,
+    static SkSurface*   NewRasterDirect(const SkImage::Info&, SkColorSpace*,
                                         const void* pixels, size_t rowBytes);
-    static SkSurface*   NewRaster(const Info&, SkColorSpace*);
+    static SkSurface*   NewRaster(const SkImage::Info&, SkColorSpace*);
     static SkSurface*   NewGpu(GrContext*);
     static SkSurface*   NewPDF(...);
     static SkSurface*   NewXPS(...);
     static SkSurface*   NewPicture(int width, int height);
 
     /**
-     *  Return a canvas that will draw into this surface
+     *  Return a canvas that will draw into this surface.
+     *
+     *  LIFECYCLE QUESTIONS
+     *  1. Is this owned by the surface or the caller?
+     *  2. Can the caller get a 2nd canvas, or reset the state of the first?
      */
     SkCanvas* newCanvas();
 
     /**
+     *  Return a new surface that is "compatible" with this one, in that it will
+     *  efficiently be able to be drawn into this surface. Typical calling
+     *  pattern:
+     *
+     *  SkSurface* A = SkSurface::New...();
+     *  SkCanvas* canvasA = surfaceA->newCanvas();
+     *  ...
+     *  SkSurface* surfaceB = surfaceA->newSurface(...);
+     *  SkCanvas* canvasB = surfaceB->newCanvas();
+     *  ... // draw using canvasB
+     *  canvasA->drawSurface(surfaceB); // <--- this will always be optimal!
+     */
+    SkSurface* newSurface(int width, int height);
+
+    /**
      *  Returns an image of the current state of the surface pixels up to this
      *  point. Subsequent changes to the surface (by drawing into its canvas)
      *  will not be reflected in this image.
@@ -98,7 +149,7 @@ public:
      *  we'd know that the "snapshot" need only live until we've handed it off
      *  to the canvas.
      */
-    void SkCanvas::drawSurface(...);
+    void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
 };
 
 #endif
diff --git a/src/image/SkDataPixelRef.cpp b/src/image/SkDataPixelRef.cpp
new file mode 100644 (file)
index 0000000..e02f0ec
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDataPixelRef.h"
+#include "SkData.h"
+
+SkDataPixelRef::SkDataPixelRef(SkData* data) : fData(data) {
+    fData->ref();
+    this->setPreLocked(const_cast<void*>(fData->data()), NULL);
+}
+
+SkDataPixelRef::~SkDataPixelRef() {
+    fData->unref();
+}
+
+void* SkDataPixelRef::onLockPixels(SkColorTable** ct) {
+    *ct = NULL;
+    return const_cast<void*>(fData->data());
+}
+
+void SkDataPixelRef::onUnlockPixels() {
+    // nothing to do
+}
+
+void SkDataPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+//    fData->flatten(buffer);
+}
+
+SkDataPixelRef::SkDataPixelRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, NULL) {
+
+//    fData = buffer.readData();
+            this->setPreLocked(const_cast<void*>(fData->data()), NULL);
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR(SkDataPixelRef)
diff --git a/src/image/SkDataPixelRef.h b/src/image/SkDataPixelRef.h
new file mode 100644 (file)
index 0000000..b6e16e0
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDataPixelRef_DEFINED
+#define SkDataPixelRef_DEFINED
+
+#include "SkPixelRef.h"
+
+class SkData;
+
+class SkDataPixelRef : public SkPixelRef {
+public:
+            SkDataPixelRef(SkData* data);
+    virtual ~SkDataPixelRef();
+    
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDataPixelRef)
+    
+protected:
+    virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
+    virtual void onUnlockPixels() SK_OVERRIDE;
+    
+    SkDataPixelRef(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+    
+private:
+    SkData* fData;
+    
+    typedef SkPixelRef INHERITED;
+};
+
+#endif
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
new file mode 100644 (file)
index 0000000..0573a83
--- /dev/null
@@ -0,0 +1,239 @@
+#include "SkImage.h"
+
+#include "SkBitmap.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkImage_Base : public SkImage {
+public:
+    SkImage_Base(int width, int height) : INHERITED(width, height) {}
+
+    virtual const SkBitmap* asABitmap() { return NULL; }
+
+private:    
+    typedef SkImage INHERITED;
+};
+
+static SkImage_Base* asIB(SkImage* image) {
+    return static_cast<SkImage_Base*>(image);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBitmap::Config InfoToConfig(const SkImage::Info& info, bool* isOpaque) {
+    switch (info.fColorType) {
+        case SkImage::kAlpha_8_ColorType:
+            switch (info.fAlphaType) {
+                case SkImage::kIgnore_AlphaType:
+                    // makes no sense
+                    return SkBitmap::kNo_Config;
+
+                case SkImage::kOpaque_AlphaType:
+                    *isOpaque = true;
+                    return SkBitmap::kA8_Config;
+
+                case SkImage::kPremul_AlphaType:
+                case SkImage::kUnpremul_AlphaType:
+                    *isOpaque = false;
+                    return SkBitmap::kA8_Config;
+            }
+            break;
+
+        case SkImage::kRGB_565_ColorType:
+            // we ignore fAlpahType, though some would not make sense
+            *isOpaque = true;
+            return SkBitmap::kRGB_565_Config;
+
+        case SkImage::kRGBA_8888_ColorType:
+        case SkImage::kBGRA_8888_ColorType:
+            // not supported yet
+            return SkBitmap::kNo_Config;
+
+        case SkImage::kPMColor_ColorType:
+            switch (info.fAlphaType) {
+                case SkImage::kIgnore_AlphaType:
+                case SkImage::kUnpremul_AlphaType:
+                    // not supported yet
+                    return SkBitmap::kNo_Config;
+                case SkImage::kOpaque_AlphaType:
+                    *isOpaque = true;
+                    return SkBitmap::kARGB_8888_Config;
+                case SkImage::kPremul_AlphaType:
+                    *isOpaque = false;
+                    return SkBitmap::kARGB_8888_Config;
+            }
+            break;
+    }
+    SkASSERT(!"how did we get here");
+    return SkBitmap::kNo_Config;
+}
+
+static int BytesPerPixel(SkImage::ColorType ct) {
+    static const uint8_t gColorTypeBytesPerPixel[] = {
+        1,  // kAlpha_8_ColorType
+        2,  // kRGB_565_ColorType
+        4,  // kRGBA_8888_ColorType
+        4,  // kBGRA_8888_ColorType
+        4,  // kPMColor_ColorType
+    };
+
+    SkASSERT((size_t)ct < SK_ARRAY_COUNT(gColorTypeBytesPerPixel));
+    return gColorTypeBytesPerPixel[ct];
+}
+
+static size_t ComputeMinRowBytes(const SkImage::Info& info) {
+    return info.fWidth * BytesPerPixel(info.fColorType);
+}
+
+class SkImage_Raster : public SkImage_Base {
+public:
+    static bool ValidArgs(const Info& info, SkColorSpace* cs, size_t rowBytes) {
+        const int maxDimension = SK_MaxS32 >> 2;
+        const size_t kMaxPixelByteSize = SK_MaxS32;
+
+        if (info.fWidth < 0 || info.fHeight < 0) {
+            return false;
+        }
+        if (info.fWidth > maxDimension || info.fHeight > maxDimension) {
+            return false;
+        }
+        if ((unsigned)info.fColorType > (unsigned)kLastEnum_ColorType) {
+            return false;
+        }
+        if ((unsigned)info.fAlphaType > (unsigned)kLastEnum_AlphaType) {
+            return false;
+        }
+
+        bool isOpaque;
+        if (InfoToConfig(info, &isOpaque) == SkBitmap::kNo_Config) {
+            return false;
+        }
+            
+        // TODO: check colorspace
+        
+        if (rowBytes < ComputeMinRowBytes(info)) {
+            return false;
+        }
+        
+        int64_t size = (int64_t)info.fHeight * rowBytes;
+        if (size > kMaxPixelByteSize) {
+            return false;
+        }
+        return true;
+    }
+
+    static SkImage* NewEmpty();
+
+    SkImage_Raster(const SkImage::Info&, SkColorSpace*, SkData*, size_t rb);
+    virtual ~SkImage_Raster();
+
+    virtual const SkBitmap* asABitmap() SK_OVERRIDE;
+
+private:
+    SkImage_Raster() : INHERITED(0, 0) {}
+
+    SkBitmap    fBitmap;
+
+    typedef SkImage_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkData.h"
+#include "SkDataPixelRef.h"
+
+SkImage* SkImage_Raster::NewEmpty() {
+    // Returns lazily created singleton
+    static SkImage* gEmpty;
+    if (NULL == gEmpty) {
+        gEmpty = SkNEW(SkImage_Raster);
+    }
+    gEmpty->ref();
+    return gEmpty;
+}
+
+SkImage_Raster::SkImage_Raster(const Info& info, SkColorSpace* cs,
+                               SkData* data, size_t rowBytes)
+        : INHERITED(info.fWidth, info.fHeight) {
+    bool isOpaque;
+    SkBitmap::Config config = InfoToConfig(info, &isOpaque);
+
+    fBitmap.setConfig(config, info.fWidth, info.fHeight, rowBytes);
+    fBitmap.setPixelRef(SkNEW_ARGS(SkDataPixelRef, (data)))->unref();
+    fBitmap.setIsOpaque(isOpaque);
+    fBitmap.setImmutable();   // Yea baby!
+}
+
+SkImage_Raster::~SkImage_Raster() {}
+
+const SkBitmap* SkImage_Raster::asABitmap() {
+    return &fBitmap;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage::NewRasterCopy(const SkImage::Info& info, SkColorSpace* cs,
+                                const void* pixels, size_t rowBytes) {
+    if (!SkImage_Raster::ValidArgs(info, cs, rowBytes)) {
+        return NULL;
+    }
+    if (0 == info.fWidth && 0 == info.fHeight) {
+        return SkImage_Raster::NewEmpty();
+    }
+    // check this after empty-check
+    if (NULL == pixels) {
+        return NULL;
+    }
+    
+    // Here we actually make a copy of the caller's pixel data
+    SkAutoDataUnref data(SkData::NewWithCopy(pixels, info.fHeight * rowBytes));
+    return SkNEW_ARGS(SkImage_Raster, (info, cs, data, rowBytes));
+}
+
+
+SkImage* SkImage::NewRasterData(const SkImage::Info& info, SkColorSpace* cs,
+                                SkData* pixelData, size_t rowBytes) {
+    if (!SkImage_Raster::ValidArgs(info, cs, rowBytes)) {
+        return NULL;
+    }
+    if (0 == info.fWidth && 0 == info.fHeight) {
+        return SkImage_Raster::NewEmpty();
+    }
+    // check this after empty-check
+    if (NULL == pixelData) {
+        return NULL;
+    }
+    
+    // did they give us enough data?
+    size_t size = info.fHeight * rowBytes;
+    if (pixelData->size() < size) {
+        return NULL;
+    }
+    
+    SkAutoDataUnref data(pixelData);
+    return SkNEW_ARGS(SkImage_Raster, (info, cs, data, rowBytes));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkCanvas.h"
+
+uint32_t SkImage::NextUniqueID() {
+    static int32_t gUniqueID;
+
+    // never return 0;
+    uint32_t id;
+    do {
+        id = sk_atomic_inc(&gUniqueID) + 1;
+    } while (0 == id);
+    return id;
+}
+
+void SkImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                   const SkPaint* paint) {
+    const SkBitmap* bitmap = asIB(this)->asABitmap();
+    if (bitmap) {
+        canvas->drawBitmap(*bitmap, x, y, paint);
+    }
+}
+