Add a class to allocate small objects w/o extra calls to new.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 7 Mar 2014 03:24:41 +0000 (03:24 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 7 Mar 2014 03:24:41 +0000 (03:24 +0000)
Add SkSmallAllocator, a template for allocating small (as defined by the
instantiation) objects without extra calls to new. Add a helper macro to
make using it simple.

Remove SkTemplatesPriv.h, whose behavior is replaced by SkSmallAllocator.
The old SK_PLACEMENT_NEW had the following drawbacks:
- Easily confused with SkNEW_PLACEMENT.
- Requires passing around lots of void*s along with the storageSize.
- Requires using a separate class for deleting it.
- We had multiple ways Auto objects for deleting in different places.
- It always did a straight heap allocation on Windows, meaning Windows
  did not get any advantages from the confusing code.
The new SkSmallAllocator simplifies things:
- It is clear about what it does.
- It takes care of the deletion in one place that is automatically
  handled.

Further, the new class can be used to create more than one object. This
is in preparation for BUG=skia:1976, for which we would like to create
a new object without extra heap allocations. The plan is to create both
the blitter and the new object on the stack using the SkSmallAllocator.

Add a new test for SkSmallAllocator.

SkShader.h:
Move the private version of CreateBitmapShader to SkBitmapProcShader
(which already has the implementation) and remove the friend class
(which was only used to call this private function). This allows
SkSmallAllocator to reside in the private src/ directory.

SkBitmapProcShader:
Move CreateBitmapShader and the macro for the storage size here. With
the macro in a (private) header, the (private) headers with function
declarations (which now depend on the storage size used) can see the
macro.
Use SkSmallAllocator in CreateBitmapShader.
Change the macro to kBlitterStorageByteCount, since SkSmallAllocator
takes a byte count as its template parameter.

SkBlitter:
Use the SkSmallAllocator.
Remove Sk3DShader::fKillProc and SkAutoCallProc. Both of their
behaviors have been moved into SkSmallAllocator (SkAutoCallProc was
unnecessary anyway, because the only time we ever used it we also
called detach(), so its auto behavior never happened).
Create the Sk3DShader on the stack, if there's room.
Remove the helper version of Choose, which was unused.

SmallAllocatorTest:
Test for the new class.

The rest:
Use SkSmallAllocator.

BUG=skia:1976
R=reed@google.com, mtklein@google.com

Author: scroggo@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13696 2bbb7eff-a529-9590-31e7-b0007b416f81

18 files changed:
gyp/core.gypi
gyp/tests.gypi
include/core/SkShader.h
src/core/SkBitmapProcShader.cpp
src/core/SkBitmapProcShader.h
src/core/SkBlitter.cpp
src/core/SkBlitter.h
src/core/SkBlitter_RGB16.cpp
src/core/SkBlitter_Sprite.cpp
src/core/SkCoreBlitters.h
src/core/SkDraw.cpp
src/core/SkShader.cpp
src/core/SkSmallAllocator.h [new file with mode: 0644]
src/core/SkSpriteBlitter.h
src/core/SkSpriteBlitter_ARGB32.cpp
src/core/SkSpriteBlitter_RGB16.cpp
src/core/SkTemplatesPriv.h [deleted file]
tests/SmallAllocatorTest.cpp [new file with mode: 0644]

index db4960214667b96fca84d0a0c3214f9029063f9b..e4f73e3d081f651cad35fdf442444509626c12d2 100644 (file)
         '<(skia_src_path)/core/SkStrokeRec.cpp',
         '<(skia_src_path)/core/SkStrokerPriv.cpp',
         '<(skia_src_path)/core/SkStrokerPriv.h',
-        '<(skia_src_path)/core/SkTemplatesPriv.h',
         '<(skia_src_path)/core/SkTextFormatParams.h',
         '<(skia_src_path)/core/SkTileGrid.cpp',
         '<(skia_src_path)/core/SkTileGrid.h',
index c2b2c548e33a9a0659e26c6159ce35a3260d02f2..69904642795876d2ca11e9c077154bd2729b9fb9 100644 (file)
@@ -1,3 +1,4 @@
+# Common gypi for unit tests.
 {
   'include_dirs': [
     '../src/core',
     '../tests/ShaderImageFilterTest.cpp',
     '../tests/ShaderOpacityTest.cpp',
     '../tests/SkBase64Test.cpp',
+    '../tests/SmallAllocatorTest.cpp',
     '../tests/SortTest.cpp',
     '../tests/SrcOverTest.cpp',
     '../tests/StreamTest.cpp',
index da23a20a4b5efe300473ca9da370029947fb350f..d0fac49f7e22cc30cbcb847c1538aada687fa18e 100644 (file)
@@ -371,10 +371,6 @@ private:
     uint8_t             fTotalInverseClass;
     SkDEBUGCODE(SkBool8 fInSetContext;)
 
-    static SkShader* CreateBitmapShader(const SkBitmap& src,
-                                        TileMode, TileMode,
-                                        void* storage, size_t storageSize);
-    friend class SkAutoBitmapShaderInstall;
     typedef SkFlattenable INHERITED;
 };
 
index 74b9e86669eead31a270822791558e2b2c2c0adc..55a772ec10ace94578a4be78cedbd508de1deb97 100644 (file)
@@ -294,8 +294,6 @@ static bool canUseColorShader(const SkBitmap& bm, SkColor* color) {
     return false;
 }
 
-#include "SkTemplatesPriv.h"
-
 static bool bitmapIsTooBig(const SkBitmap& bm) {
     // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
     // communicates between its matrix-proc and its sampler-proc. Until we can
@@ -306,20 +304,29 @@ static bool bitmapIsTooBig(const SkBitmap& bm) {
     return bm.width() > maxSize || bm.height() > maxSize;
 }
 
-SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
-                                       TileMode tmx, TileMode tmy,
-                                       void* storage, size_t storageSize) {
+SkShader* CreateBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
+        SkShader::TileMode tmy, SkTBlitterAllocator* allocator) {
     SkShader* shader;
     SkColor color;
     if (src.isNull() || bitmapIsTooBig(src)) {
-        SK_PLACEMENT_NEW(shader, SkEmptyShader, storage, storageSize);
+        if (NULL == allocator) {
+            shader = SkNEW(SkEmptyShader);
+        } else {
+            shader = allocator->createT<SkEmptyShader>();
+        }
     }
     else if (canUseColorShader(src, &color)) {
-        SK_PLACEMENT_NEW_ARGS(shader, SkColorShader, storage, storageSize,
-                              (color));
+        if (NULL == allocator) {
+            shader = SkNEW_ARGS(SkColorShader, (color));
+        } else {
+            shader = allocator->createT<SkColorShader>(color);
+        }
     } else {
-        SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
-                              storageSize, (src, tmx, tmy));
+        if (NULL == allocator) {
+            shader = SkNEW_ARGS(SkBitmapProcShader, (src, tmx, tmy));
+        } else {
+            shader = allocator->createT<SkBitmapProcShader>(src, tmx, tmy);
+        }
     }
     return shader;
 }
index b7e60f1ca1075e2413e791ced221e1b9de3b3110..7a4ddb45ab6587334a99e6ea1cf03ef8c99b49f8 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "SkShader.h"
 #include "SkBitmapProcState.h"
+#include "SkSmallAllocator.h"
 
 class SkBitmapProcShader : public SkShader {
 public:
@@ -48,4 +49,14 @@ private:
     typedef SkShader INHERITED;
 };
 
+// Commonly used allocator. It currently is only used to allocate up to 2 objects. The total
+// bytes requested is calculated using one of our large shaders plus the size of an Sk3DBlitter
+// in SkDraw.cpp
+typedef SkSmallAllocator<2, sizeof(SkBitmapProcShader) + sizeof(void*) * 2> SkTBlitterAllocator;
+
+// If alloc is non-NULL, it will be used to allocate the returned SkShader, and MUST outlive
+// the SkShader.
+SkShader* CreateBitmapShader(const SkBitmap& src, SkShader::TileMode, SkShader::TileMode,
+                             SkTBlitterAllocator* alloc);
+
 #endif
index 98325c024501aad838387952cfffdf6fcac98d58..f925eaa7b2d7f193ef076a7b696ef0db42c339ca 100644 (file)
 #include "SkAntiRun.h"
 #include "SkColor.h"
 #include "SkColorFilter.h"
+#include "SkCoreBlitters.h"
 #include "SkFilterShader.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkMask.h"
 #include "SkMaskFilter.h"
-#include "SkTemplatesPriv.h"
+#include "SkString.h"
 #include "SkTLazy.h"
 #include "SkUtils.h"
 #include "SkXfermode.h"
-#include "SkString.h"
 
 SkBlitter::~SkBlitter() {}
 
@@ -705,15 +705,10 @@ private:
 
 class Sk3DBlitter : public SkBlitter {
 public:
-    Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader, void (*killProc)(void*))
-            : fProxy(proxy), f3DShader(shader), fKillProc(killProc) {
-        shader->ref();
-    }
-
-    virtual ~Sk3DBlitter() {
-        f3DShader->unref();
-        fKillProc(fProxy);
-    }
+    Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader)
+        : fProxy(proxy)
+        , f3DShader(SkRef(shader))
+    {}
 
     virtual void blitH(int x, int y, int width) {
         fProxy->blitH(x, y, width);
@@ -747,50 +742,15 @@ public:
     }
 
 private:
-    SkBlitter*  fProxy;
-    Sk3DShader* f3DShader;
-    void        (*fKillProc)(void*);
+    // fProxy is unowned. It will be deleted by SkSmallAllocator.
+    SkBlitter*               fProxy;
+    SkAutoTUnref<Sk3DShader> f3DShader;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkCoreBlitters.h"
 
-class SkAutoCallProc {
-public:
-    typedef void (*Proc)(void*);
-
-    SkAutoCallProc(void* obj, Proc proc)
-    : fObj(obj), fProc(proc) {}
-
-    ~SkAutoCallProc() {
-        if (fObj && fProc) {
-            fProc(fObj);
-        }
-    }
-
-    void* get() const { return fObj; }
-
-    void* detach() {
-        void* obj = fObj;
-        fObj = NULL;
-        return obj;
-    }
-
-private:
-    void*   fObj;
-    Proc    fProc;
-};
-#define SkAutoCallProc(...) SK_REQUIRE_LOCAL_VAR(SkAutoCallProc)
-
-static void destroy_blitter(void* blitter) {
-    ((SkBlitter*)blitter)->~SkBlitter();
-}
-
-static void delete_blitter(void* blitter) {
-    SkDELETE((SkBlitter*)blitter);
-}
-
 static bool just_solid_color(const SkPaint& paint) {
     if (paint.getAlpha() == 0xFF && paint.getColorFilter() == NULL) {
         SkShader* shader = paint.getShader();
@@ -852,9 +812,9 @@ static XferInterp interpret_xfermode(const SkPaint& paint, SkXfermode* xfer,
 SkBlitter* SkBlitter::Choose(const SkBitmap& device,
                              const SkMatrix& matrix,
                              const SkPaint& origPaint,
-                             void* storage, size_t storageSize,
+                             SkTBlitterAllocator* allocator,
                              bool drawCoverage) {
-    SkASSERT(storageSize == 0 || storage != NULL);
+    SkASSERT(allocator != NULL);
 
     SkBlitter*  blitter = NULL;
 
@@ -862,7 +822,7 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device,
     // (e.g. they have a bounder that always aborts the draw)
     if (kUnknown_SkColorType == device.colorType() ||
             (drawCoverage && (kAlpha_8_SkColorType != device.colorType()))) {
-        SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+        blitter = allocator->createT<SkNullBlitter>();
         return blitter;
     }
 
@@ -887,9 +847,10 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device,
                 mode = NULL;
                 paint.writable()->setXfermode(NULL);
                 break;
-            case kSkipDrawing_XferInterp:
-                SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+            case kSkipDrawing_XferInterp:{
+                blitter = allocator->createT<SkNullBlitter>();
                 return blitter;
+            }
             default:
                 break;
         }
@@ -940,7 +901,7 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device,
      *  not fail) in its destructor.
      */
     if (shader && !shader->setContext(device, *paint, matrix)) {
-        SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+        blitter = allocator->createT<SkNullBlitter>();
         return blitter;
     }
 
@@ -950,49 +911,40 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device,
             if (drawCoverage) {
                 SkASSERT(NULL == shader);
                 SkASSERT(NULL == paint->getXfermode());
-                SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Coverage_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkA8_Coverage_Blitter>(device, *paint);
             } else if (shader) {
-                SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkA8_Shader_Blitter>(device, *paint);
             } else {
-                SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkA8_Blitter>(device, *paint);
             }
             break;
 
         case kRGB_565_SkColorType:
-            blitter = SkBlitter_ChooseD565(device, *paint, storage, storageSize);
+            blitter = SkBlitter_ChooseD565(device, *paint, allocator);
             break;
 
         case kPMColor_SkColorType:
             if (shader) {
-                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkARGB32_Shader_Blitter>(device, *paint);
             } else if (paint->getColor() == SK_ColorBLACK) {
-                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkARGB32_Black_Blitter>(device, *paint);
             } else if (paint->getAlpha() == 0xFF) {
-                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkARGB32_Opaque_Blitter>(device, *paint);
             } else {
-                SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter,
-                                      storage, storageSize, (device, *paint));
+                blitter = allocator->createT<SkARGB32_Blitter>(device, *paint);
             }
             break;
 
         default:
             SkDEBUGFAIL("unsupported device config");
-            SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+            blitter = allocator->createT<SkNullBlitter>();
             break;
     }
 
     if (shader3D) {
-        void (*proc)(void*) = ((void*)storage == (void*)blitter) ? destroy_blitter : delete_blitter;
-        SkAutoCallProc  tmp(blitter, proc);
-
-        blitter = SkNEW_ARGS(Sk3DBlitter, (blitter, shader3D, proc));
-        (void)tmp.detach();
+        SkBlitter* innerBlitter = blitter;
+        // innerBlitter was allocated by allocator, which will delete it.
+        blitter = allocator->createT<Sk3DBlitter>(innerBlitter, shader3D);
     }
     return blitter;
 }
index b659fe473581635d8475068a9c680747cb2059ff..d19a34badcd4d9ecbb78cd6f22f074f2ff0927d5 100644 (file)
 #define SkBlitter_DEFINED
 
 #include "SkBitmap.h"
+#include "SkBitmapProcShader.h"
+#include "SkMask.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
 #include "SkRefCnt.h"
 #include "SkRegion.h"
-#include "SkMask.h"
+#include "SkShader.h"
+#include "SkSmallAllocator.h"
 
 /** SkBlitter and its subclasses are responsible for actually writing pixels
     into memory. Besides efficiency, they handle clipping and antialiasing.
@@ -67,23 +70,17 @@ public:
     /** @name Factories
         Return the correct blitter to use given the specified context.
      */
-    static SkBlitter* Choose(const SkBitmap& device,
-                             const SkMatrix& matrix,
-                             const SkPaint& paint) {
-        return Choose(device, matrix, paint, NULL, 0);
-    }
-
     static SkBlitter* Choose(const SkBitmap& device,
                              const SkMatrix& matrix,
                              const SkPaint& paint,
-                             void* storage, size_t storageSize,
+                             SkTBlitterAllocator*,
                              bool drawCoverage = false);
 
     static SkBlitter* ChooseSprite(const SkBitmap& device,
                                    const SkPaint&,
                                    const SkBitmap& src,
                                    int left, int top,
-                                   void* storage, size_t storageSize);
+                                   SkTBlitterAllocator*);
     ///@}
 
 private:
index 256cbc6936f6daad6a1c48c798936a3b184dbfc0..b01800b51f52aec576a5fc61fa5dbb292d1f3ebb 100644 (file)
@@ -12,7 +12,6 @@
 #include "SkColorPriv.h"
 #include "SkDither.h"
 #include "SkShader.h"
-#include "SkTemplatesPriv.h"
 #include "SkUtils.h"
 #include "SkXfermode.h"
 
@@ -1013,7 +1012,9 @@ void SkRGB16_Shader_Xfermode_Blitter::blitAntiH(int x, int y,
 ///////////////////////////////////////////////////////////////////////////////
 
 SkBlitter* SkBlitter_ChooseD565(const SkBitmap& device, const SkPaint& paint,
-                                void* storage, size_t storageSize) {
+        SkTBlitterAllocator* allocator) {
+    SkASSERT(allocator != NULL);
+
     SkBlitter* blitter;
     SkShader* shader = paint.getShader();
     SkXfermode* mode = paint.getXfermode();
@@ -1023,31 +1024,25 @@ SkBlitter* SkBlitter_ChooseD565(const SkBitmap& device, const SkPaint& paint,
 
     if (shader) {
         if (mode) {
-            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_Blitter,
-                                  storage, storageSize, (device, paint));
+            blitter = allocator->createT<SkRGB16_Shader_Xfermode_Blitter>(device, paint);
         } else if (shader->canCallShadeSpan16()) {
-            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader16_Blitter,
-                                  storage, storageSize, (device, paint));
+            blitter = allocator->createT<SkRGB16_Shader16_Blitter>(device, paint);
         } else {
-            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Blitter,
-                                  storage, storageSize, (device, paint));
+            blitter = allocator->createT<SkRGB16_Shader_Blitter>(device, paint);
         }
     } else {
         // no shader, no xfermode, (and we always ignore colorfilter)
         SkColor color = paint.getColor();
         if (0 == SkColorGetA(color)) {
-            SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+            blitter = allocator->createT<SkNullBlitter>();
 #ifdef USE_BLACK_BLITTER
         } else if (SK_ColorBLACK == color) {
-            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Black_Blitter, storage,
-                                  storageSize, (device, paint));
+            blitter = allocator->createT<SkRGB16_Black_Blitter>(device, paint);
 #endif
         } else if (0xFF == SkColorGetA(color)) {
-            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Opaque_Blitter, storage,
-                                  storageSize, (device, paint));
+            blitter = allocator->createT<SkRGB16_Opaque_Blitter>(device, paint);
         } else {
-            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Blitter, storage,
-                                  storageSize, (device, paint));
+            blitter = allocator->createT<SkRGB16_Blitter>(device, paint);
         }
     }
 
index 86251ff59f501fe064c3c59f9208990970f82bb1..e15e2fcde4bd4f17eef5e3ec102ab4ae498348b1 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,7 @@
  * found in the LICENSE file.
  */
 
-
+#include "SkSmallAllocator.h"
 #include "SkSpriteBlitter.h"
 
 SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source)
@@ -49,11 +48,8 @@ void SkSpriteBlitter::blitMask(const SkMask&, const SkIRect& clip) {
 
 // returning null means the caller will call SkBlitter::Choose() and
 // have wrapped the source bitmap inside a shader
-SkBlitter* SkBlitter::ChooseSprite( const SkBitmap& device,
-                                    const SkPaint& paint,
-                                    const SkBitmap& source,
-                                    int left, int top,
-                                    void* storage, size_t storageSize) {
+SkBlitter* SkBlitter::ChooseSprite(const SkBitmap& device, const SkPaint& paint,
+        const SkBitmap& source, int left, int top, SkTBlitterAllocator* allocator) {
     /*  We currently ignore antialiasing and filtertype, meaning we will take our
         special blitters regardless of these settings. Ignoring filtertype seems fine
         since by definition there is no scale in the matrix. Ignoring antialiasing is
@@ -63,17 +59,16 @@ SkBlitter* SkBlitter::ChooseSprite( const SkBitmap& device,
         paint and return null if it is set, forcing the client to take the slow shader case
         (which does respect soft edges).
     */
+    SkASSERT(allocator != NULL);
 
     SkSpriteBlitter* blitter;
 
     switch (device.colorType()) {
         case kRGB_565_SkColorType:
-            blitter = SkSpriteBlitter::ChooseD16(source, paint, storage,
-                                                 storageSize);
+            blitter = SkSpriteBlitter::ChooseD16(source, paint, allocator);
             break;
         case kPMColor_SkColorType:
-            blitter = SkSpriteBlitter::ChooseD32(source, paint, storage,
-                                                 storageSize);
+            blitter = SkSpriteBlitter::ChooseD32(source, paint, allocator);
             break;
         default:
             blitter = NULL;
index 1605a5273dcd7c2496fb65af8d741d7728cd8620..285184050ba88737b3b94391812a5af246e5da31 100644 (file)
@@ -8,8 +8,11 @@
 #ifndef SkCoreBlitters_DEFINED
 #define SkCoreBlitters_DEFINED
 
+#include "SkBitmapProcShader.h"
 #include "SkBlitter.h"
 #include "SkBlitRow.h"
+#include "SkShader.h"
+#include "SkSmallAllocator.h"
 
 class SkRasterBlitter : public SkBlitter {
 public:
@@ -175,8 +178,7 @@ private:
     SkBlitter::Choose(...)
  */
 
-extern SkBlitter* SkBlitter_ChooseD565(const SkBitmap& device,
-                                       const SkPaint& paint,
-                                       void* storage, size_t storageSize);
+SkBlitter* SkBlitter_ChooseD565(const SkBitmap& device, const SkPaint& paint,
+                                SkTBlitterAllocator* allocator);
 
 #endif
index 175abb68440b34de7270c543c354fd9c273415b6..05b06453dbf200df8cebccfc3aee4c69825d5143 100644 (file)
@@ -21,9 +21,9 @@
 #include "SkRRect.h"
 #include "SkScan.h"
 #include "SkShader.h"
+#include "SkSmallAllocator.h"
 #include "SkString.h"
 #include "SkStroke.h"
-#include "SkTemplatesPriv.h"
 #include "SkTLazy.h"
 #include "SkUtils.h"
 
@@ -32,9 +32,9 @@
 #include "SkDrawProcs.h"
 #include "SkMatrixUtils.h"
 
+
 //#define TRACE_BITMAP_DRAWS
 
-#define kBlitterStorageLongCount    (sizeof(SkBitmapProcShader) >> 2)
 
 /** Helper for allocating small blitters on the stack.
  */
@@ -45,16 +45,8 @@ public:
     }
     SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
                         const SkPaint& paint, bool drawCoverage = false) {
-        fBlitter = SkBlitter::Choose(device, matrix, paint,
-                                     fStorage, sizeof(fStorage), drawCoverage);
-    }
-
-    ~SkAutoBlitterChoose() {
-        if ((void*)fBlitter == (void*)fStorage) {
-            fBlitter->~SkBlitter();
-        } else {
-            SkDELETE(fBlitter);
-        }
+        fBlitter = SkBlitter::Choose(device, matrix, paint, &fAllocator,
+                                     drawCoverage);
     }
 
     SkBlitter*  operator->() { return fBlitter; }
@@ -63,13 +55,13 @@ public:
     void choose(const SkBitmap& device, const SkMatrix& matrix,
                 const SkPaint& paint) {
         SkASSERT(!fBlitter);
-        fBlitter = SkBlitter::Choose(device, matrix, paint,
-                                     fStorage, sizeof(fStorage));
+        fBlitter = SkBlitter::Choose(device, matrix, paint, &fAllocator);
     }
 
 private:
-    SkBlitter*  fBlitter;
-    uint32_t    fStorage[kBlitterStorageLongCount];
+    // Owned by fAllocator, which will handle the delete.
+    SkBlitter*          fBlitter;
+    SkTBlitterAllocator fAllocator;
 };
 #define SkAutoBlitterChoose(...) SK_REQUIRE_LOCAL_VAR(SkAutoBlitterChoose)
 
@@ -82,34 +74,29 @@ class SkAutoBitmapShaderInstall : SkNoncopyable {
 public:
     SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint)
             : fPaint(paint) /* makes a copy of the paint */ {
-        fPaint.setShader(SkShader::CreateBitmapShader(src,
-                           SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
-                           fStorage, sizeof(fStorage)));
+        fPaint.setShader(CreateBitmapShader(src, SkShader::kClamp_TileMode,
+                                            SkShader::kClamp_TileMode,
+                                            &fAllocator));
         // we deliberately left the shader with an owner-count of 2
         SkASSERT(2 == fPaint.getShader()->getRefCnt());
     }
 
     ~SkAutoBitmapShaderInstall() {
-        SkShader* shader = fPaint.getShader();
-        // since we manually destroy shader, we insist that owners == 2
-        SkASSERT(2 == shader->getRefCnt());
+        // since fAllocator will destroy shader, we insist that owners == 2
+        SkASSERT(2 == fPaint.getShader()->getRefCnt());
 
         fPaint.setShader(NULL); // unref the shader by 1
 
-        // now destroy to take care of the 2nd owner-count
-        if ((void*)shader == (void*)fStorage) {
-            shader->~SkShader();
-        } else {
-            SkDELETE(shader);
-        }
     }
 
     // return the new paint that has the shader applied
     const SkPaint& paintWithShader() const { return fPaint; }
 
 private:
-    SkPaint     fPaint; // copy of caller's paint (which we then modify)
-    uint32_t    fStorage[kBlitterStorageLongCount];
+    // copy of caller's paint (which we then modify)
+    SkPaint             fPaint;
+    // Stores the shader.
+    SkTBlitterAllocator fAllocator;
 };
 #define SkAutoBitmapShaderInstall(...) SK_REQUIRE_LOCAL_VAR(SkAutoBitmapShaderInstall)
 
@@ -1323,12 +1310,11 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
         int ix = SkScalarRoundToInt(matrix.getTranslateX());
         int iy = SkScalarRoundToInt(matrix.getTranslateY());
         if (clipHandlesSprite(*fRC, ix, iy, bitmap)) {
-            uint32_t    storage[kBlitterStorageLongCount];
-            SkBlitter*  blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
-                                                ix, iy, storage, sizeof(storage));
+            SkTBlitterAllocator allocator;
+            // blitter will be owned by the allocator.
+            SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+                                                         ix, iy, &allocator);
             if (blitter) {
-                SkAutoTPlacementDelete<SkBlitter>   ad(blitter, storage);
-
                 SkIRect    ir;
                 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
 
@@ -1378,13 +1364,12 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
     paint.setStyle(SkPaint::kFill_Style);
 
     if (NULL == paint.getColorFilter() && clipHandlesSprite(*fRC, x, y, bitmap)) {
-        uint32_t    storage[kBlitterStorageLongCount];
-        SkBlitter*  blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
-                                                x, y, storage, sizeof(storage));
+        SkTBlitterAllocator allocator;
+        // blitter will be owned by the allocator.
+        SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+                                                     x, y, &allocator);
 
         if (blitter) {
-            SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
-
             if (fBounder && !fBounder->doIRect(bounds)) {
                 return;
             }
index 47889f0d094d45ed0143ea92149f75e36a58ee60..b06e0c26ff6391e3125d21d847746b05d76c7aa0 100644 (file)
@@ -5,13 +5,13 @@
  * found in the LICENSE file.
  */
 
-
+#include "SkBitmapProcShader.h"
+#include "SkReadBuffer.h"
+#include "SkMallocPixelRef.h"
+#include "SkPaint.h"
 #include "SkScalar.h"
 #include "SkShader.h"
-#include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
-#include "SkPaint.h"
-#include "SkMallocPixelRef.h"
 
 SkShader::SkShader() {
     fLocalMatrix.reset();
@@ -176,7 +176,7 @@ GrEffectRef* SkShader::asNewEffect(GrContext*, const SkPaint&) const {
 
 SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
                                        TileMode tmx, TileMode tmy) {
-    return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
+    return ::CreateBitmapShader(src, tmx, tmy, NULL);
 }
 
 #ifdef SK_DEVELOPER
diff --git a/src/core/SkSmallAllocator.h b/src/core/SkSmallAllocator.h
new file mode 100644 (file)
index 0000000..d5ebf3b
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2014 Google, Inc
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSmallAllocator_DEFINED
+#define SkSmallAllocator_DEFINED
+
+#include "SkTDArray.h"
+#include "SkTypes.h"
+
+// Used by SkSmallAllocator to call the destructor for objects it has
+// allocated.
+template<typename T> void destroyT(void* ptr) {
+   static_cast<T*>(ptr)->~T();
+}
+
+/*
+ *  Template class for allocating small objects without additional heap memory
+ *  allocations. kMaxObjects is a hard limit on the number of objects that can
+ *  be allocated using this class. After that, attempts to create more objects
+ *  with this class will assert and return NULL.
+ *  kTotalBytes is the total number of bytes provided for storage for all
+ *  objects created by this allocator. If an object to be created is larger
+ *  than the storage (minus storage already used), it will be allocated on the
+ *  heap. This class's destructor will handle calling the destructor for each
+ *  object it allocated and freeing its memory.
+ */
+template<uint32_t kMaxObjects, size_t kTotalBytes>
+class SkSmallAllocator : public SkNoncopyable {
+public:
+    SkSmallAllocator()
+    : fStorageUsed(0)
+    , fNumObjects(0)
+    {}
+
+    ~SkSmallAllocator() {
+        // Destruct in reverse order, in case an earlier object points to a
+        // later object.
+        while (fNumObjects > 0) {
+            fNumObjects--;
+            Rec* rec = &fRecs[fNumObjects];
+            rec->fKillProc(rec->fObj);
+            // Safe to do if fObj is in fStorage, since fHeapStorage will
+            // point to NULL.
+            sk_free(rec->fHeapStorage);
+        }
+    }
+
+    /*
+     *  Create a new object of type T. Its lifetime will be handled by this
+     *  SkSmallAllocator.
+     *  Each version behaves the same but takes a different number of
+     *  arguments.
+     *  Note: If kMaxObjects have been created by this SkSmallAllocator, NULL
+     *  will be returned.
+     */
+    template<typename T>
+    T* createT() {
+        void* buf = this->reserveT<T>();
+        if (NULL == buf) {
+            return NULL;
+        }
+        SkNEW_PLACEMENT(buf, T);
+        return static_cast<T*>(buf);
+    }
+
+    template<typename T, typename A1> T* createT(const A1& a1) {
+        void* buf = this->reserveT<T>();
+        if (NULL == buf) {
+            return NULL;
+        }
+        SkNEW_PLACEMENT_ARGS(buf, T, (a1));
+        return static_cast<T*>(buf);
+    }
+
+    template<typename T, typename A1, typename A2>
+    T* createT(const A1& a1, const A2& a2) {
+        void* buf = this->reserveT<T>();
+        if (NULL == buf) {
+            return NULL;
+        }
+        SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2));
+        return static_cast<T*>(buf);
+    }
+
+    template<typename T, typename A1, typename A2, typename A3>
+    T* createT(const A1& a1, const A2& a2, const A3& a3) {
+        void* buf = this->reserveT<T>();
+        if (NULL == buf) {
+            return NULL;
+        }
+        SkNEW_PLACEMENT_ARGS(buf, T, (a1, a2, a3));
+        return static_cast<T*>(buf);
+    }
+
+private:
+    /*
+     *  Helper function to provide space for one T. The space will be in
+     *  fStorage if there is room, or on the heap otherwise. Either way, this
+     *  class will call ~T() in its destructor and free the heap allocation if
+     *  necessary.
+     */
+    template<typename T> void* reserveT() {
+        SkASSERT(fNumObjects < kMaxObjects);
+        if (kMaxObjects == fNumObjects) {
+            return NULL;
+        }
+        const size_t storageRemaining = SkAlign4(kTotalBytes) - fStorageUsed;
+        const size_t storageRequired = SkAlign4(sizeof(T));
+        Rec* rec = &fRecs[fNumObjects];
+        if (storageRequired > storageRemaining) {
+            // Allocate on the heap. Ideally we want to avoid this situation,
+            // but we're not sure we can catch all callers, so handle it but
+            // assert false in debug mode.
+            SkASSERT(false);
+            rec->fHeapStorage = sk_malloc_throw(storageRequired);
+            rec->fObj = static_cast<void*>(rec->fHeapStorage);
+        } else {
+            // There is space in fStorage.
+            rec->fHeapStorage = NULL;
+            SkASSERT(SkIsAlign4(fStorageUsed));
+            rec->fObj = static_cast<void*>(fStorage + (fStorageUsed / 4));
+            fStorageUsed += storageRequired;
+        }
+        rec->fKillProc = destroyT<T>;
+        fNumObjects++;
+        return rec->fObj;
+    }
+
+private:
+    struct Rec {
+        void* fObj;
+        void* fHeapStorage;
+        void  (*fKillProc)(void*);
+    };
+
+    // Number of bytes used so far.
+    size_t              fStorageUsed;
+    // Pad the storage size to be 4-byte aligned.
+    uint32_t            fStorage[SkAlign4(kTotalBytes) >> 2];
+    uint32_t            fNumObjects;
+    Rec                 fRecs[kMaxObjects];
+};
+
+#endif // SkSmallAllocator_DEFINED
index ae79afe1d8e68e6803fba7f96c62a3b41c18b77f..f69a55a2a0bfdc79f483ce6a821beec0c1794d1b 100644 (file)
 #ifndef SkSpriteBlitter_DEFINED
 #define SkSpriteBlitter_DEFINED
 
-#include "SkBlitter.h"
 #include "SkBitmap.h"
+#include "SkBitmapProcShader.h"
+#include "SkBlitter.h"
+#include "SkShader.h"
+#include "SkSmallAllocator.h"
 
 class SkPaint;
 
@@ -32,9 +35,9 @@ public:
 #endif
 
     static SkSpriteBlitter* ChooseD16(const SkBitmap& source, const SkPaint&,
-                                      void* storage, size_t storageSize);
+                                      SkTBlitterAllocator*);
     static SkSpriteBlitter* ChooseD32(const SkBitmap& source, const SkPaint&,
-                                      void* storage, size_t storageSize);
+                                      SkTBlitterAllocator*);
 
 protected:
     const SkBitmap* fDevice;
index 3e8612dda3e9e6cdace44dbfb8ff73877efe5e83..b05e5033e44d1334fd710847df3d57357d11bf5e 100644 (file)
@@ -263,11 +263,10 @@ public:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#include "SkTemplatesPriv.h"
+SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source, const SkPaint& paint,
+        SkTBlitterAllocator* allocator) {
+    SkASSERT(allocator != NULL);
 
-SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source,
-                                            const SkPaint& paint,
-                                            void* storage, size_t storageSize) {
     if (paint.getMaskFilter() != NULL) {
         return NULL;
     }
@@ -283,27 +282,22 @@ SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source,
                 return NULL;    // we only have opaque sprites
             }
             if (xfermode || filter) {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_XferFilter,
-                                      storage, storageSize, (source, paint));
+                blitter = allocator->createT<Sprite_D32_S4444_XferFilter>(source, paint);
             } else if (source.isOpaque()) {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_Opaque,
-                                      storage, storageSize, (source));
+                blitter = allocator->createT<Sprite_D32_S4444_Opaque>(source);
             } else {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444,
-                                      storage, storageSize, (source));
+                blitter = allocator->createT<Sprite_D32_S4444>(source);
             }
             break;
         case kPMColor_SkColorType:
             if (xfermode || filter) {
                 if (255 == alpha) {
                     // this can handle xfermode or filter, but not alpha
-                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter,
-                                      storage, storageSize, (source, paint));
+                    blitter = allocator->createT<Sprite_D32_S32A_XferFilter>(source, paint);
                 }
             } else {
                 // this can handle alpha, but not xfermode or filter
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32,
-                              storage, storageSize, (source, alpha));
+                blitter = allocator->createT<Sprite_D32_S32>(source, alpha);
             }
             break;
         default:
index 7428c8a74a38b8cf525efbf7c73151eb4cb8a9f6..74c1961dbdf8516d28ac707c47c91664213360b7 100644 (file)
@@ -306,11 +306,11 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#include "SkTemplatesPriv.h"
+SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source, const SkPaint& paint,
+        SkTBlitterAllocator* allocator) {
+
+    SkASSERT(allocator != NULL);
 
-SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source,
-                                            const SkPaint& paint,
-                                            void* storage, size_t storageSize) {
     if (paint.getMaskFilter() != NULL) { // may add cases for this
         return NULL;
     }
@@ -325,26 +325,22 @@ SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source,
     unsigned alpha = paint.getAlpha();
 
     switch (source.colorType()) {
-        case kPMColor_SkColorType:
-            SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_BlitRowProc,
-                                  storage, storageSize, (source));
+        case kPMColor_SkColorType: {
+            blitter = allocator->createT<Sprite_D16_S32_BlitRowProc>(source);
             break;
+        }
         case kARGB_4444_SkColorType:
             if (255 == alpha) {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Opaque,
-                                      storage, storageSize, (source));
+                blitter = allocator->createT<Sprite_D16_S4444_Opaque>(source);
             } else {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Blend,
-                                    storage, storageSize, (source, alpha >> 4));
+                blitter = allocator->createT<Sprite_D16_S4444_Blend>(source, alpha >> 4);
             }
             break;
         case kRGB_565_SkColorType:
             if (255 == alpha) {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Opaque,
-                                      storage, storageSize, (source));
+                blitter = allocator->createT<Sprite_D16_S16_Opaque>(source);
             } else {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Blend,
-                                      storage, storageSize, (source, alpha));
+                blitter = allocator->createT<Sprite_D16_S16_Blend>(source, alpha);
             }
             break;
         case kIndex_8_SkColorType:
@@ -354,19 +350,15 @@ SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source,
             }
             if (source.isOpaque()) {
                 if (255 == alpha) {
-                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Opaque,
-                                          storage, storageSize, (source));
+                    blitter = allocator->createT<Sprite_D16_SIndex8_Opaque>(source);
                 } else {
-                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Blend,
-                                         storage, storageSize, (source, alpha));
+                    blitter = allocator->createT<Sprite_D16_SIndex8_Blend>(source, alpha);
                 }
             } else {
                 if (255 == alpha) {
-                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Opaque,
-                                          storage, storageSize, (source));
+                    blitter = allocator->createT<Sprite_D16_SIndex8A_Opaque>(source);
                 } else {
-                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Blend,
-                                         storage, storageSize, (source, alpha));
+                    blitter = allocator->createT<Sprite_D16_SIndex8A_Blend>(source, alpha);
                 }
             }
             break;
diff --git a/src/core/SkTemplatesPriv.h b/src/core/SkTemplatesPriv.h
deleted file mode 100644 (file)
index 79ae609..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkTemplatesPriv_DEFINED
-#define SkTemplatesPriv_DEFINED
-
-#include "SkTemplates.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef SK_BUILD_FOR_WIN32
-    #define SK_PLACEMENT_NEW(result, classname, storage, storageSize)   \
-        result = SkNEW(classname)
-
-    #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storageSize, args)    \
-        result = SkNEW_ARGS(classname, args)
-#else
-    #include <new>
-    #define SK_PLACEMENT_NEW(result, classname, storage, storagesize)       \
-    do {                                                                    \
-        if (storagesize)                                                    \
-        {                                                                   \
-            SkASSERT(storageSize >= sizeof(classname));                     \
-            result = new(storage) classname;                                \
-        }                                                                   \
-        else                                                                \
-            result = SkNEW(classname);                                      \
-    } while (0)
-
-    #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storagesize, args)        \
-    do {                                                                                \
-        if (storagesize)                                                                \
-        {                                                                               \
-            SkASSERT(storageSize >= sizeof(classname));                                 \
-            result = new(storage) classname args;                                       \
-        }                                                                               \
-        else                                                                            \
-            result = SkNEW_ARGS(classname, args);                                       \
-    } while (0)
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <class T> class SkAutoTPlacementDelete {
-public:
-    SkAutoTPlacementDelete(T* obj, void* storage) : fObj(obj), fStorage(storage)
-    {
-    }
-    ~SkAutoTPlacementDelete()
-    {
-        if (fObj)
-        {
-            if (fObj == fStorage)
-                fObj->~T();
-            else
-                delete fObj;
-        }
-    }
-    T* detach()
-    {
-        T*  obj = fObj;
-        fObj = NULL;
-        return obj;
-    }
-private:
-    T*      fObj;
-    void*   fStorage;
-};
-
-#endif
diff --git a/tests/SmallAllocatorTest.cpp b/tests/SmallAllocatorTest.cpp
new file mode 100644 (file)
index 0000000..30c8ffa
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 Google, Inc
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSmallAllocator.h"
+#include "SkTypes.h"
+#include "Test.h"
+
+class CountingClass {
+public:
+    CountingClass() {
+        kCount++;
+    }
+
+    ~CountingClass() {
+        kCount--;
+    }
+
+    static int GetCount() { return kCount; }
+
+private:
+    static int kCount;
+};
+
+int CountingClass::kCount;
+
+template<uint32_t kMaxObjects, size_t kBytes> void test_allocator(skiatest::Reporter* reporter) {
+    {
+        SkSmallAllocator<kMaxObjects, kBytes> alloc;
+        for (uint32_t i = 0; i < kMaxObjects; ++i) {
+            CountingClass* c = alloc.template createT<CountingClass>();
+            REPORTER_ASSERT(reporter, c != NULL);
+            REPORTER_ASSERT(reporter, CountingClass::GetCount() == static_cast<int>(i+1));
+        }
+    }
+    REPORTER_ASSERT(reporter, CountingClass::GetCount() == 0);
+}
+
+// Tests that ensure that the destructor is called, whether the objects
+// were created in fStorage or on the heap.
+DEF_TEST(SmallAllocator_destructor, reporter) {
+    // Four times as many bytes as objects will never require any heap
+    // allocations (since SkAlign4(sizeof(CountingClass)) == 4 and the allocator
+    // will stop once it reaches kMaxObjects).
+    test_allocator<5, 20>(reporter);
+    test_allocator<10, 40>(reporter);
+    test_allocator<20, 80>(reporter);
+
+#ifndef SK_DEBUG
+    // Allowing less bytes than objects means some will be allocated on the
+    // heap. Don't run these in debug where we assert.
+    test_allocator<50, 20>(reporter);
+    test_allocator<100, 20>(reporter);
+#endif
+}
+
+class Dummy {
+};
+
+class DummyContainer {
+public:
+    explicit DummyContainer(Dummy* d)
+        :fDummy(d)
+    {}
+
+    Dummy* getDummy() const { return fDummy; }
+
+private:
+    Dummy* fDummy;
+};
+
+// Test that using a createT with a constructor taking a pointer as a
+// parameter works as expected.
+DEF_TEST(SmallAllocator_pointer, reporter) {
+    SkSmallAllocator<1, 8> alloc;
+    Dummy d;
+    DummyContainer* container = alloc.createT<DummyContainer>(&d);
+    REPORTER_ASSERT(reporter, container != NULL);
+    REPORTER_ASSERT(reporter, container->getDummy() == &d);
+}