Add createNewCompatibleDevice. Allow devices to have a NULL factory and saveLayer will fall back on createNewCompatibleDevice.

Review URL: http://codereview.appspot.com/4633044/



git-svn-id: http://skia.googlecode.com/svn/trunk@1625 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index d5032a7..9b96790 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -53,15 +53,19 @@
 */
 class SK_API SkCanvas : public SkRefCnt {
 public:
-    /** Construct a canvas with the given device factory.
+    /**
+        DEPRECATED: Will be replaced by SkDevice::createCompatibleDevice
+
+        Construct a canvas with the given device factory.
         @param factory  Specify the factory for generating additional devices.
                         The factory may be null, in which case
                         SkRasterDeviceFactory will be used.
     */
     explicit SkCanvas(SkDeviceFactory* factory = NULL);
 
-    /** Construct a canvas with the specified device to draw into.  The device
+    /** Construct a canvas with the specified device to draw into.  The device

         factory will be retrieved from the passed device.
+
         @param device   Specifies a device for the canvas to draw into.
     */
     explicit SkCanvas(SkDevice* device);
@@ -96,13 +100,6 @@
      */
     SkDevice* getTopDevice() const;
 
-    /** May be overridden by subclasses. This returns a compatible device
-        for this canvas, with the specified config/width/height. If the device
-        is raster, the pixels will be allocated automatically.
-     */
-    virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
-                                   bool isOpaque);
-
     /**
      *  Create a new raster device and make it current. This also returns
      *  the new device.
@@ -110,18 +107,29 @@
     SkDevice* setBitmapDevice(const SkBitmap& bitmap);
 
     /**
+     * DEPRECATED: Will be replaced by SkDevice::createCompatibleDevice
+     *
      *  Return the current device factory, or NULL. The reference count of
      *  the returned factory is not changed.
      */
     SkDeviceFactory* getDeviceFactory() const { return fDeviceFactory; }
 
     /**
+     *  DEPRECATED: Will be replaced by SkDevice::createCompatibleDevice
+     *
      *  Replace any existing factory with the specified factory, unrefing the
      *  previous (if any), and refing the new one (if any). For convenience,
      *  the factory parameter is also returned.
      */
     SkDeviceFactory* setDeviceFactory(SkDeviceFactory*);
 
+    /**
+     * Shortcut for getDevice()->createCompatibleDevice(...)
+     */
+    SkDevice* createCompatibleDevice(SkBitmap::Config config, 
+                                    int width, int height,
+                                    bool isOpaque);
+
     ///////////////////////////////////////////////////////////////////////////
 
     /**
@@ -834,6 +842,9 @@
 
     friend class SkDrawIter;    // needs setupDrawForLayerDevice()
 
+    SkDevice* createLayerDevice(SkBitmap::Config, int width, int height, 
+                                bool isOpaque);
+
     SkDevice* init(SkDevice*);
     void internalDrawBitmap(const SkBitmap&, const SkIRect*, const SkMatrix& m,
                                   const SkPaint* paint);
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index ff9c4d1..59e95e7 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -32,6 +32,8 @@
 
 /** \class SkDeviceFactory
 
+    DEPRECATED: Will be replaced by SkDevice::createCompatibleDevice
+
     Devices that extend SkDevice should also provide a SkDeviceFactory class
     to pass into SkCanvas.  Doing so will eliminate the need to extend
     SkCanvas as well.
@@ -79,12 +81,27 @@
     virtual ~SkDevice();
 
     /**
+     *  DEPRECATED: Will be replaced by SkDevice::createCompatibleDevice
+     *
      *  Return the factory that will create this subclass of SkDevice.
      *  The returned factory is cached by the device, and so its reference count
      *  is not changed by this call.
      */
     SkDeviceFactory* getDeviceFactory();
 
+    /**
+     * Creates a device that is of the same type as this device (e.g. SW-raster,
+     * GPU, or PDF).
+     *
+     * @param width     width of the device to create
+     * @param height    height of the device to create
+     * @param isOpaque  
+     * @param usage     clients should always use the default, kGeneral_Usage.
+     */
+    SkDevice* createCompatibleDevice(SkBitmap::Config config, 
+                                     int width, int height,
+                                     bool isOpaque);
+
     enum Capabilities {
         kGL_Capability     = 0x1,  //!< mask indicating GL support
         kVector_Capability = 0x2,  //!< mask indicating a vector representation
@@ -264,9 +281,13 @@
 
 protected:
     /**
-     *  subclasses must override this to return a new (or ref'd) instance of
+     *  DEPRECATED: Will be replaced by SkDevice::createCompatibleDevice
+     *
+     *  subclasses can override this to return a new (or ref'd) instance of
      *  a device factory that will create this subclass of device. This value
      *  is cached, so it should get called at most once for a given instance.
+     *
+     *  If not overriden then createCompatibleDevice will be used by canvas.
      */
     virtual SkDeviceFactory* onNewDeviceFactory();
 
@@ -276,6 +297,18 @@
     */
     virtual void onAccessBitmap(SkBitmap*);
 
+    enum Usage {
+       kGeneral_Usage,
+       kSaveLayer_Usage, // <! internal use only
+    };
+    /**
+     * subclasses should override this to implement createCompatibleDevice.
+     */
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config, 
+                                               int width, int height, 
+                                               bool isOpaque,
+                                               Usage usage);
+
     SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
     // just for subclasses, to assign a custom pixelref
     SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset) {
@@ -287,6 +320,10 @@
     friend class SkCanvas;
     // just called by SkCanvas when built as a layer
     void setOrigin(int x, int y) { fOrigin.set(x, y); }
+    // just called by SkCanvas for saveLayer
+    SkDevice* createCompatibleDeviceForSaveLayer(SkBitmap::Config config, 
+                                                 int width, int height,
+                                                 bool isOpaque);
 
     SkBitmap    fBitmap;
     SkIPoint    fOrigin;
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 4d0efeb..3a675be 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -37,11 +37,12 @@
      *  New device that will create an offscreen renderTarget based on the
      *  config, width, height.
      *
-     *  isForSaveLayer is a special flag that should only be set by SkCanvas
+     *  usage is a special flag that should only be set by SkCanvas
      *  internally.
      */
-    SkGpuDevice(GrContext*, SkBitmap::Config, int width, int height,
-                bool isForSaveLayer = false);
+    SkGpuDevice(GrContext*, SkBitmap::Config,
+                int width, int height, 
+                SkDevice::Usage usage = SkDevice::kGeneral_Usage);
 
     /**
      *  New device that will render to the specified renderTarget.
@@ -132,11 +133,15 @@
     virtual SkDeviceFactory* onNewDeviceFactory();
 
     class TexCache;
+    enum TexType {
+        kBitmap_TexType,
+        kDeviceRenderTarget_TexType,
+        kSaveLayerDeviceRenderTarget_TexType
+    };
     TexCache* lockCachedTexture(const SkBitmap& bitmap,
                                 const GrSamplerState& sampler,
                                 GrTexture** texture,
-                                bool forDeviceRenderTarget = false,
-                                bool isSaveLayer = false);
+                                TexType type = kBitmap_TexType);
     void unlockCachedTexture(TexCache*);
 
     class SkAutoCachedTexture {
@@ -192,6 +197,12 @@
                                GrPaint* grPaint,
                                bool constantColor);
 
+    // override from SkDevice
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config, 
+                                               int width, int height, 
+                                               bool isOpaque,
+                                               Usage usage);
+
     SkDrawProcs* initDrawForText(GrTextContext*);
     bool bindDeviceAsTexture(GrPaint* paint);
 
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index 6e4d8db..2205f6c 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -173,6 +173,12 @@
     SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
                 const SkRegion& existingClipRegion);
 
+    // override from SkDevice
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config, 
+                                               int width, int height, 
+                                               bool isOpaque,
+                                               Usage usage);
+
     void init();
     void cleanUp();
     void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
index 37f584e..1810723 100755
--- a/samplecode/SampleTextureDomain.cpp
+++ b/samplecode/SampleTextureDomain.cpp
@@ -54,7 +54,7 @@
         // Note:  GPU-backed bitmaps follow a different rendering path
         // when copying from one GPU device to another.
         SkRefPtr<SkDevice> primaryDevice(canvas->getDevice());
-        SkRefPtr<SkDevice> secondDevice(canvas->createDevice(
+        SkRefPtr<SkDevice> secondDevice(canvas->createCompatibleDevice(
                 SkBitmap::kARGB_8888_Config, 5, 5, true));
         secondDevice->unref();
         SkCanvas secondCanvas(secondDevice.get());
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 5b2e964..0b6f086 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -434,10 +434,9 @@
         : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
     inc_canvas();
 
-    fDeviceFactory = device->getDeviceFactory();
-    SkASSERT(fDeviceFactory);
-    fDeviceFactory->ref();
-              
+    fDeviceFactory = device->getDeviceFactory();

+    SkSafeRef(fDeviceFactory);
+
     this->init(device);
 }
 
@@ -446,9 +445,8 @@
     inc_canvas();
 
     SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap));
-    fDeviceFactory = device->getDeviceFactory();
-    SkASSERT(fDeviceFactory);
-    fDeviceFactory->ref();
+    fDeviceFactory = device->getDeviceFactory();

+    SkSafeRef(fDeviceFactory);
 
     this->init(device)->unref();
 }
@@ -736,8 +734,9 @@
     bool isOpaque;
     SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
 
-    SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
-                                          isOpaque);
+    SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
+                                               isOpaque);
+
     device->setOrigin(ir.fLeft, ir.fTop);
     DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
     device->unref();
@@ -1176,11 +1175,32 @@
     }
 }
 
-SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
-                                 bool isOpaque) {
-    return fDeviceFactory->newDevice(this, config, width, height, isOpaque, true);
+SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
+                                      int width, int height,
+                                      bool isOpaque) {
+    if (fDeviceFactory) {
+        return fDeviceFactory->newDevice(this, config, width, height,
+                                         isOpaque, true);
+    } else {
+        return this->getDevice()->createCompatibleDeviceForSaveLayer(
+                                config, width, height,
+                                isOpaque);
+    }
 }
 
+SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config, 
+                                           int width, int height,
+                                           bool isOpaque) {
+    SkDevice* device = this->getDevice();
+    if (device) {
+        return device->createCompatibleDevice(config, width, height,
+                                              isOpaque);
+    } else {
+        return NULL;
+    }
+}
+
+
 //////////////////////////////////////////////////////////////////////////////
 //  These are the virtual drawing methods
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 0c01335..ae283b0 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -57,7 +57,7 @@
 }
 
 SkDeviceFactory* SkDevice::onNewDeviceFactory() {
-    return SkNEW(SkRasterDeviceFactory);
+    return NULL;
 }
 
 SkDeviceFactory* SkDevice::getDeviceFactory() {
@@ -67,6 +67,27 @@
     return fCachedDeviceFactory;
 }
 
+SkDevice* SkDevice::createCompatibleDevice(SkBitmap::Config config, 
+                                           int width, int height,
+                                           bool isOpaque) {
+    return this->onCreateCompatibleDevice(config, width, height,
+                                          isOpaque, kGeneral_Usage);
+}
+
+SkDevice* SkDevice::createCompatibleDeviceForSaveLayer(SkBitmap::Config config,
+                                                       int width, int height,
+                                                       bool isOpaque) {
+    return this->onCreateCompatibleDevice(config, width, height,
+                                          isOpaque, kSaveLayer_Usage);
+}
+
+SkDevice* SkDevice::onCreateCompatibleDevice(SkBitmap::Config config, 
+                                             int width, int height, 
+                                             bool isOpaque,
+                                             Usage usage) {
+    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque));
+}
+
 SkMetaData& SkDevice::getMetaData() {
     // metadata users are rare, so we lazily allocate it. If that changes we
     // can decide to just make it a field in the device (rather than a ptr)
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 189b692..cda10d2 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -76,7 +76,7 @@
         fTex = NULL;
     } else {
         // look it up in our cache
-        fTex = device->lockCachedTexture(bitmap, sampler, &texture, false);
+        fTex = device->lockCachedTexture(bitmap, sampler, &texture);
     }
     return texture;
 }
@@ -166,7 +166,7 @@
 }
 
 SkGpuDevice::SkGpuDevice(GrContext* context, SkBitmap::Config config, int width,
-                         int height, bool isForSaveLayer)
+                         int height, Usage usage)
 : SkDevice(config, width, height, false /*isOpaque*/) {
     fNeedPrepareRenderTarget = false;
     fDrawProcs = NULL;
@@ -186,9 +186,11 @@
     bm.setConfig(config, width, height);
 
 #if CACHE_LAYER_TEXTURES
-
+    TexType type = (kSaveLayer_Usage == usage) ? 
+                            kSaveLayerDeviceRenderTarget_TexType :
+                            kDeviceRenderTarget_TexType;
     fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(),
-                                     &fTexture, true, isForSaveLayer);
+                                     &fTexture, type);
     if (fCache) {
         SkASSERT(NULL != fTexture);
         SkASSERT(NULL != fTexture->asRenderTarget());
@@ -1394,13 +1396,12 @@
 SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
                                                       const GrSamplerState& sampler,
                                                       GrTexture** texture,
-                                                      bool forDeviceRenderTarget,
-                                                      bool isSaveLayer) {
+                                                      TexType type) {
     GrTexture* newTexture = NULL;
     GrTextureEntry* entry = NULL;
     GrContext* ctx = this->context();
 
-    if (forDeviceRenderTarget) {
+    if (kBitmap_TexType != type) {
         const GrTextureDesc desc = {
             kRenderTarget_GrTextureFlagBit,
             kNone_GrAALevel,
@@ -1408,12 +1409,13 @@
             bitmap.height(),
             SkGr::Bitmap2PixelConfig(bitmap)
         };
-        if (isSaveLayer) {
+        if (kSaveLayerDeviceRenderTarget_TexType == type) {
             // we know layers will only be drawn through drawDevice.
             // drawDevice has been made to work with content embedded in a
             // larger texture so its okay to use the approximate version.
             entry = ctx->findApproximateKeylessTexture(desc);
         } else {
+            SkASSERT(kDeviceRenderTarget_TexType == type);
             entry = ctx->lockKeylessTexture(desc);
         }
     } else {
@@ -1446,6 +1448,15 @@
     this->context()->unlockTexture((GrTextureEntry*)cache);
 }
 
+SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config, 
+                                                int width, int height, 
+                                                bool isOpaque,
+                                                Usage usage) {
+    return SkNEW_ARGS(SkGpuDevice,(this->context(), config, 
+                                   width, height, usage));
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGpuDeviceFactory::SkGpuDeviceFactory(GrContext* context,
@@ -1499,3 +1510,4 @@
         return SkNEW_ARGS(SkGpuDevice, (fContext, fRootRenderTarget));
     }
 }
+
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index d17f864..67bc64a 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -423,6 +423,17 @@
     }
 }
 
+SkDevice* SkPDFDevice::onCreateCompatibleDevice(SkBitmap::Config config, 
+                                                int width, int height, 
+                                                bool isOpaque,
+                                                Usage usage) {
+    SkMatrix initialTransform;
+    initialTransform.reset();
+    SkISize size = SkISize::Make(width, height);
+    return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
+}
+
+
 struct ContentEntry {
     GraphicStateEntry fState;
     SkDynamicMemoryWStream fContent;
diff --git a/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp
index a7b7f97..33f77e4 100644
--- a/src/utils/SkProxyCanvas.cpp
+++ b/src/utils/SkProxyCanvas.cpp
@@ -151,8 +151,3 @@
     return fProxy->setDrawFilter(filter);
 }
 
-SkDevice* SkProxyCanvas::createDevice(SkBitmap::Config config, int width, int height,
-                                      bool isOpaque) {
-    return fProxy->createDevice(config, width, height, isOpaque);
-}
-