text: /*
   * 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 "../src/image/SkImagePriv.h"
  #include "../src/image/SkSurface_Base.h"
  #include "SkBitmap.h"
  #include "SkBitmapDevice.h"
  #include "SkBitmapProcShader.h"
  #include "SkDeferredCanvas.h"
  #include "SkGradientShader.h"
  #include "SkShader.h"
  #include "SkSurface.h"
  #include "Test.h"
  #include "sk_tool_utils.h"
  
  #if SK_SUPPORT_GPU
  #include "GrContextFactory.h"
  #else
  class GrContextFactory;
  #endif
  
  static const int gWidth = 2;
  static const int gHeight = 2;
  
  static void create(SkBitmap* bm, SkColor color) {
      bm->allocN32Pixels(gWidth, gHeight);
      bm->eraseColor(color);
  }
  
  static SkSurface* createSurface(SkColor color) {
      SkSurface* surface = SkSurface::NewRasterPMColor(gWidth, gHeight);
      surface->getCanvas()->clear(color);
      return surface;
  }
  
  static SkPMColor read_pixel(SkSurface* surface, int x, int y) {
      SkPMColor pixel = 0;
      SkBitmap bitmap;
      bitmap.installPixels(SkImageInfo::MakeN32Premul(1, 1), &pixel, 4);
      SkCanvas canvas(bitmap);
  
      SkPaint paint;
      paint.setXfermodeMode(SkXfermode::kSrc_Mode);
      surface->draw(&canvas, -SkIntToScalar(x), -SkIntToScalar(y), &paint);
      return pixel;
  }
  
  class MockSurface : public SkSurface_Base {
  public:
      MockSurface(int width, int height) : SkSurface_Base(width, height, NULL) {
          clearCounts();
          fBitmap.allocN32Pixels(width, height);
      }
  
      virtual SkCanvas* onNewCanvas() SK_OVERRIDE {
          return SkNEW_ARGS(SkCanvas, (fBitmap));
      }
  
      virtual SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE {
          return NULL;
      }
  
      virtual SkImage* onNewImageSnapshot() SK_OVERRIDE {
          return SkNewImageFromBitmap(fBitmap, true);
      }
  
      virtual void onCopyOnWrite(ContentChangeMode mode) SK_OVERRIDE {
          if (mode == SkSurface::kDiscard_ContentChangeMode) {
              fDiscardCount++;
          } else {
              fRetainCount++;
          }
      }
  
      void clearCounts() {
          fDiscardCount = 0;
          fRetainCount = 0;
      }
  
      int fDiscardCount, fRetainCount;
      SkBitmap fBitmap;
  };
  
  static void TestDeferredCanvasWritePixelsToSurface(skiatest::Reporter* reporter) {
      SkAutoTUnref<MockSurface> surface(SkNEW_ARGS(MockSurface, (10, 10)));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      SkBitmap srcBitmap;
      srcBitmap.allocPixels(SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
      srcBitmap.eraseColor(SK_ColorGREEN);
      // Tests below depend on this bitmap being recognized as opaque
  
      // Preliminary sanity check: no copy on write if no active snapshot
      surface->clearCounts();
      canvas->clear(SK_ColorWHITE);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 1: Discard notification happens upon flushing
      // with an Image attached.
      surface->clearCounts();
      SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->clear(SK_ColorWHITE);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 2: Opaque writePixels
      surface->clearCounts();
      SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 3: writePixels that partially covers the canvas
      surface->clearCounts();
      SkAutoTUnref<SkImage> image3(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 4: unpremultiplied opaque writePixels that entirely
      // covers the canvas
      surface->clearCounts();
      SkAutoTUnref<SkImage> image4(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->writePixels(srcBitmap, 0, 0);
      REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 5: unpremultiplied opaque writePixels that partially
      // covers the canvas
      surface->clearCounts();
      SkAutoTUnref<SkImage> image5(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->writePixels(srcBitmap, 5, 0);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 6: unpremultiplied opaque writePixels that entirely
      // covers the canvas, preceded by clear
      surface->clearCounts();
      SkAutoTUnref<SkImage> image6(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->clear(SK_ColorWHITE);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->writePixels(srcBitmap, 0, 0);
      REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 7: unpremultiplied opaque writePixels that partially
      // covers the canvas, preceeded by a clear
      surface->clearCounts();
      SkAutoTUnref<SkImage> image7(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->clear(SK_ColorWHITE);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->writePixels(srcBitmap, 5, 0);
      REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); // because of the clear
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      // Case 8: unpremultiplied opaque writePixels that partially
      // covers the canvas, preceeded by a drawREct that partially
      // covers the canvas
      surface->clearCounts();
      SkAutoTUnref<SkImage> image8(canvas->newImageSnapshot());
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      SkPaint paint;
      canvas->drawRect(SkRect::MakeLTRB(0, 0, 5, 5), paint);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->writePixels(srcBitmap, 5, 0);
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
  
      surface->clearCounts();
      canvas->flush();
      REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
      REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
  }
  
  static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      canvas->clear(0x00000000);
  
      // verify that clear was deferred
      REPORTER_ASSERT(reporter, 0xFFFFFFFF == read_pixel(surface, 0, 0));
  
      canvas->flush();
  
      // verify that clear was executed
      REPORTER_ASSERT(reporter, 0 == read_pixel(surface, 0, 0));
  }
  
  static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
      SkRect fullRect;
      fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
          SkIntToScalar(gHeight));
      SkRect partialRect;
      partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
          SkIntToScalar(1), SkIntToScalar(1));
  
      SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      // verify that frame is intially fresh
      REPORTER_ASSERT(reporter, canvas->isFreshFrame());
      // no clearing op since last call to isFreshFrame -> not fresh
      REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
  
      // Verify that clear triggers a fresh frame
      canvas->clear(0x00000000);
      REPORTER_ASSERT(reporter, canvas->isFreshFrame());
  
      // Verify that clear with saved state triggers a fresh frame
      canvas->save();
      canvas->clear(0x00000000);
      canvas->restore();
      REPORTER_ASSERT(reporter, canvas->isFreshFrame());
  
      // Verify that clear within a layer does NOT trigger a fresh frame
      canvas->saveLayer(NULL, NULL);
      canvas->clear(0x00000000);
      canvas->restore();
      REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
  
      // Verify that a clear with clipping triggers a fresh frame
      // (clear is not affected by clipping)
      canvas->save();
      canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false);
      canvas->clear(0x00000000);
      canvas->restore();
      REPORTER_ASSERT(reporter, canvas->isFreshFrame());
  
      // Verify that full frame rects with different forms of opaque paint
      // trigger frames to be marked as fresh
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(255);
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, canvas->isFreshFrame());
      }
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(255);
          paint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          SkBitmap bmp;
          create(&bmp, 0xFFFFFFFF);
          bmp.setAlphaType(kOpaque_SkAlphaType);
          SkShader* shader = SkShader::CreateBitmapShader(bmp,
              SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
          paint.setShader(shader)->unref();
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, canvas->isFreshFrame());
      }
  
      // Verify that full frame rects with different forms of non-opaque paint
      // do not trigger frames to be marked as fresh
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(254);
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          // Defining a cone that partially overlaps the canvas
          const SkPoint pt1 = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(0));
          const SkScalar r1 = SkIntToScalar(1);
          const SkPoint pt2 = SkPoint::Make(SkIntToScalar(10), SkIntToScalar(0));
          const SkScalar r2 = SkIntToScalar(5);
          const SkColor colors[2] = {SK_ColorWHITE, SK_ColorWHITE};
          const SkScalar pos[2] = {0, SK_Scalar1};
          SkShader* shader = SkGradientShader::CreateTwoPointConical(
              pt1, r1, pt2, r2, colors, pos, 2, SkShader::kClamp_TileMode);
          paint.setShader(shader)->unref();
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          SkBitmap bmp;
          create(&bmp, 0xFFFFFFFF);
          bmp.setAlphaType(kPremul_SkAlphaType);
          SkShader* shader = SkShader::CreateBitmapShader(bmp,
              SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
          paint.setShader(shader)->unref();
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
  
      // Verify that incomplete coverage does not trigger a fresh frame
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(255);
          canvas->drawRect(partialRect, paint);
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
  
      // Verify that incomplete coverage due to clipping does not trigger a fresh
      // frame
      {
          canvas->save();
          canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false);
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(255);
          canvas->drawRect(fullRect, paint);
          canvas->restore();
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
      {
          canvas->save();
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(255);
          SkPath path;
          path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2));
          canvas->clipPath(path, SkRegion::kIntersect_Op, false);
          canvas->drawRect(fullRect, paint);
          canvas->restore();
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
  
      // Verify that stroked rect does not trigger a fresh frame
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kStroke_Style);
          paint.setAlpha(255);
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
      }
  
      // Verify kSrcMode triggers a fresh frame even with transparent color
      {
          SkPaint paint;
          paint.setStyle(SkPaint::kFill_Style);
          paint.setAlpha(100);
          paint.setXfermodeMode(SkXfermode::kSrc_Mode);
          canvas->drawRect(fullRect, paint);
          REPORTER_ASSERT(reporter, canvas->isFreshFrame());
      }
  }
  
  class MockDevice : public SkBitmapDevice {
  public:
      MockDevice(const SkBitmap& bm) : SkBitmapDevice(bm) {
          fDrawBitmapCallCount = 0;
      }
      virtual void drawBitmap(const SkDraw&, const SkBitmap&,
                              const SkMatrix&, const SkPaint&) SK_OVERRIDE {
          fDrawBitmapCallCount++;
      }
  
      int fDrawBitmapCallCount;
  };
  
  class NotificationCounter : public SkDeferredCanvas::NotificationClient {
  public:
      NotificationCounter() {
          fPrepareForDrawCount = fStorageAllocatedChangedCount =
              fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0;
      }
  
      virtual void prepareForDraw() SK_OVERRIDE {
          fPrepareForDrawCount++;
      }
      virtual void storageAllocatedForRecordingChanged(size_t) SK_OVERRIDE {
          fStorageAllocatedChangedCount++;
      }
      virtual void flushedDrawCommands() SK_OVERRIDE {
          fFlushedDrawCommandsCount++;
      }
      virtual void skippedPendingDrawCommands() SK_OVERRIDE {
          fSkippedPendingDrawCommandsCount++;
      }
  
      int fPrepareForDrawCount;
      int fStorageAllocatedChangedCount;
      int fFlushedDrawCommandsCount;
      int fSkippedPendingDrawCommandsCount;
  
  private:
      typedef SkDeferredCanvas::NotificationClient INHERITED;
  };
  
  // Verifies that the deferred canvas triggers a flush when its memory
  // limit is exceeded
  static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      NotificationCounter notificationCounter;
      canvas->setNotificationClient(&notificationCounter);
  
      canvas->setMaxRecordingStorage(160000);
  
      SkBitmap sourceImage;
      // 100 by 100 image, takes 40,000 bytes in memory
      sourceImage.allocN32Pixels(100, 100);
      sourceImage.eraseColor(SK_ColorGREEN);
  
      for (int i = 0; i < 5; i++) {
          sourceImage.notifyPixelsChanged(); // to force re-serialization
          canvas->drawBitmap(sourceImage, 0, 0, NULL);
      }
  
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
  }
  
  static void TestDeferredCanvasSilentFlush(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(createSurface(0));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      NotificationCounter notificationCounter;
      canvas->setNotificationClient(&notificationCounter);
  
      canvas->silentFlush(); // will skip the initial clear that was recorded in createSurface
  
      REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
  }
  
  static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      NotificationCounter notificationCounter;
      canvas->setNotificationClient(&notificationCounter);
  
      const int imageCount = 2;
      SkBitmap sourceImages[imageCount];
      for (int i = 0; i < imageCount; i++) {
          sourceImages[i].allocN32Pixels(100, 100);
          sourceImages[i].eraseColor(SK_ColorGREEN);
      }
  
      size_t bitmapSize = sourceImages[0].getSize();
  
      canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount);
      // stored bitmap + drawBitmap command
      REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > bitmapSize);
  
      // verify that nothing can be freed at this point
      REPORTER_ASSERT(reporter, 0 == canvas->freeMemoryIfPossible(~0U));
  
      // verify that flush leaves image in cache
      REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
      REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount);
      canvas->flush();
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount);
      REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() >= bitmapSize);
  
      // verify that after a flush, cached image can be freed
      REPORTER_ASSERT(reporter, canvas->freeMemoryIfPossible(~0U) >= bitmapSize);
  
      // Verify that caching works for avoiding multiple copies of the same bitmap
      canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
      REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
      canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
      REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
      REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < 2 * bitmapSize);
  
      // Verify partial eviction based on bytesToFree
      canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
      canvas->flush();
      REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
      REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2 * bitmapSize);
      size_t bytesFreed = canvas->freeMemoryIfPossible(1);
      REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
      REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
      REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
  
      // Verifiy that partial purge works, image zero is in cache but not reffed by
      // a pending draw, while image 1 is locked-in.
      canvas->freeMemoryIfPossible(~0U);
      REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
      canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
      canvas->flush();
      canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
      bytesFreed = canvas->freeMemoryIfPossible(~0U);
      // only one bitmap should have been freed.
      REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
      REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
      // Clear for next test
      canvas->flush();
      canvas->freeMemoryIfPossible(~0U);
      REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < bitmapSize);
  
      // Verify the image cache is sensitive to genID bumps
      canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
      sourceImages[1].notifyPixelsChanged();
      canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
      REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2*bitmapSize);
  
      // Verify that nothing in this test caused commands to be skipped
      REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount);
  }
  
  static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      NotificationCounter notificationCounter;
      canvas->setNotificationClient(&notificationCounter);
      canvas->clear(0x0);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
      REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
      canvas->flush();
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
      REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
  
  }
  
  static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) {
      // This is a regression test for crbug.com/155875
      // This test covers a code path that inserts bitmaps into the bitmap heap through the
      // flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through
      // the flattening and unflattening of the shader.
      SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
      // test will fail if nbIterations is not in sync with
      // BITMAPS_TO_KEEP in SkGPipeWrite.cpp
      const int nbIterations = 5;
      size_t bytesAllocated = 0;
      for(int pass = 0; pass < 2; ++pass) {
          for(int i = 0; i < nbIterations; ++i) {
              SkPaint paint;
              SkBitmap paintPattern;
              paintPattern.allocN32Pixels(10, 10);
              paintPattern.eraseColor(SK_ColorGREEN);
              paint.setShader(SkNEW_ARGS(SkBitmapProcShader,
                  (paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref();
              canvas->drawPaint(paint);
              canvas->flush();
  
              // In the first pass, memory allocation should be monotonically increasing as
              // the bitmap heap slots fill up.  In the second pass memory allocation should be
              // stable as bitmap heap slots get recycled.
              size_t newBytesAllocated = canvas->storageAllocatedForRecording();
              if (pass == 0) {
                  REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated);
                  bytesAllocated = newBytesAllocated;
              } else {
                  REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated);
              }
          }
      }
      // All cached resources should be evictable since last canvas call was flush()
      canvas->freeMemoryIfPossible(~0U);
      REPORTER_ASSERT(reporter, 0 == canvas->storageAllocatedForRecording());
  }
  
  static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
  
      SkBitmap sourceImage;
      // 100 by 100 image, takes 40,000 bytes in memory
      sourceImage.allocN32Pixels(100, 100);
      sourceImage.eraseColor(SK_ColorGREEN);
  
      // 1 under : should not store the image
      {
          SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
          canvas->setBitmapSizeThreshold(39999);
          canvas->drawBitmap(sourceImage, 0, 0, NULL);
          size_t newBytesAllocated = canvas->storageAllocatedForRecording();
          REPORTER_ASSERT(reporter, newBytesAllocated == 0);
      }
  
      // exact value : should store the image
      {
          SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
          canvas->setBitmapSizeThreshold(40000);
          canvas->drawBitmap(sourceImage, 0, 0, NULL);
          size_t newBytesAllocated = canvas->storageAllocatedForRecording();
          REPORTER_ASSERT(reporter, newBytesAllocated > 0);
      }
  
      // 1 over : should still store the image
      {
          SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
          canvas->setBitmapSizeThreshold(40001);
          canvas->drawBitmap(sourceImage, 0, 0, NULL);
          size_t newBytesAllocated = canvas->storageAllocatedForRecording();
          REPORTER_ASSERT(reporter, newBytesAllocated > 0);
      }
  }
  
  
  typedef const void* PixelPtr;
  // Returns an opaque pointer which, either points to a GrTexture or RAM pixel
  // buffer. Used to test pointer equality do determine whether a surface points
  // to the same pixel data storage as before.
  static PixelPtr get_surface_ptr(SkSurface* surface, bool useGpu) {
  #if SK_SUPPORT_GPU
      if (useGpu) {
          return surface->getCanvas()->internal_private_accessTopLayerRenderTarget()->asTexture();
      } else
  #endif
      {
          return surface->peekPixels(NULL, NULL);
      }
  }
  
  static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
      SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10);
      bool useGpu = SkToBool(factory);
      int cnt;
  #if SK_SUPPORT_GPU
      if (useGpu) {
          cnt = GrContextFactory::kGLContextTypeCnt;
      } else {
          cnt = 1;
      }
  #else
      SkASSERT(!useGpu);
      cnt = 1;
  #endif
      for (int i = 0; i < cnt; ++i) {
          SkSurface* surface;
  #if SK_SUPPORT_GPU
          if (useGpu) {
              GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
              if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
                  continue;
              }
              GrContext* context = factory->get(glCtxType);
              if (NULL == context) {
                  return;
              }
  
              surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL);
          } else
  #endif
          {
             surface = SkSurface::NewRaster(imageSpec);
          }
          SkASSERT(surface);
          SkAutoTUnref<SkSurface> aur(surface);
          SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface));
  
          SkImage* image1 = canvas->newImageSnapshot();
          SkAutoTUnref<SkImage> aur_i1(image1);
          PixelPtr pixels1 = get_surface_ptr(surface, useGpu);
          // The following clear would normally trigger a copy on write, but
          // it won't because rendering is deferred.
          canvas->clear(SK_ColorBLACK);
          // Obtaining a snapshot directly from the surface (as opposed to the
          // SkDeferredCanvas) will not trigger a flush of deferred draw operations
          // and will therefore return the same image as the previous snapshot.
          SkImage* image2 = surface->newImageSnapshot();
          SkAutoTUnref<SkImage> aur_i2(image2);
          // Images identical because of deferral
          REPORTER_ASSERT(reporter, image1->uniqueID() == image2->uniqueID());
          // Now we obtain a snpshot via the deferred canvas, which triggers a flush.
          // Because there is a pending clear, this will generate a different image.
          SkImage* image3 = canvas->newImageSnapshot();
          SkAutoTUnref<SkImage> aur_i3(image3);
          REPORTER_ASSERT(reporter, image1->uniqueID() != image3->uniqueID());
          // Verify that backing store is now a different buffer because of copy on
          // write
          PixelPtr pixels2 = get_surface_ptr(surface, useGpu);
          REPORTER_ASSERT(reporter, pixels1 != pixels2);
          // Verify copy-on write with a draw operation that gets deferred by
          // the in order draw buffer.
          SkPaint paint;
          canvas->drawPaint(paint);
          SkImage* image4 = canvas->newImageSnapshot();  // implicit flush
          SkAutoTUnref<SkImage> aur_i4(image4);
          REPORTER_ASSERT(reporter, image4->uniqueID() != image3->uniqueID());
          PixelPtr pixels3 = get_surface_ptr(surface, useGpu);
          REPORTER_ASSERT(reporter, pixels2 != pixels3);
          // Verify that a direct canvas flush with a pending draw does not trigger
          // a copy on write when the surface is not sharing its buffer with an
          // SkImage.
          canvas->clear(SK_ColorWHITE);
          canvas->flush();
          PixelPtr pixels4 = get_surface_ptr(surface, useGpu);
          canvas->drawPaint(paint);
          canvas->flush();
          PixelPtr pixels5 = get_surface_ptr(surface, useGpu);
          REPORTER_ASSERT(reporter, pixels4 == pixels5);
      }
  }
  
  static void TestDeferredCanvasSetSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
      SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10);
      SkSurface* surface;
      SkSurface* alternateSurface;
      bool useGpu = SkToBool(factory);
      int cnt;
  #if SK_SUPPORT_GPU
      if (useGpu) {
          cnt = GrContextFactory::kGLContextTypeCnt;
      } else {
          cnt = 1;
      }
  #else
      SkASSERT(!useGpu);
      cnt = 1;
  #endif
  
      for (int i = 0; i < cnt; ++i) {
  #if SK_SUPPORT_GPU
          if (useGpu) {
              GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
              if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
                  continue;
              }
              GrContext* context = factory->get(glCtxType);
              if (NULL == context) {
                  continue;
              }
              surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL);
              alternateSurface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL);
          } else
  #endif
          {
              surface = SkSurface::NewRaster(imageSpec);
              alternateSurface = SkSurface::NewRaster(imageSpec);
          }
          SkASSERT(surface);
          SkASSERT(alternateSurface);
          SkAutoTUnref<SkSurface> aur1(surface);
          SkAutoTUnref<SkSurface> aur2(alternateSurface);
          PixelPtr pixels1 = get_surface_ptr(surface, useGpu);
          PixelPtr pixels2 = get_surface_ptr(alternateSurface, useGpu);
          SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface));
          SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
          canvas->setSurface(alternateSurface);
          SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
          REPORTER_ASSERT(reporter, image1->uniqueID() != image2->uniqueID());
          // Verify that none of the above operations triggered a surface copy on write.
          REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1);
          REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) == pixels2);
          // Verify that a flushed draw command will trigger a copy on write on alternateSurface.
          canvas->clear(SK_ColorWHITE);
          canvas->flush();
          REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1);
          REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) != pixels2);
      }
  }
  
  static void TestDeferredCanvasCreateCompatibleDevice(skiatest::Reporter* reporter) {
      SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
  
      NotificationCounter notificationCounter;
      canvas->setNotificationClient(&notificationCounter);
  
      SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
      SkAutoTUnref<SkSurface> secondarySurface(canvas->newSurface(info));
  
      SkRect rect = SkRect::MakeWH(5, 5);
      SkPaint paint;
      // After spawning a compatible canvas:
      // 1) Verify that secondary canvas is usable and does not report to the notification client.
      surface->getCanvas()->drawRect(rect, paint);
      REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 0);
      // 2) Verify that original canvas is usable and still reports to the notification client.
      canvas->drawRect(rect, paint);
      REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 1);
  }
  
  static void TestDeferredCanvasGetCanvasSize(skiatest::Reporter* reporter) {
      SkRect rect;
      rect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), SkIntToScalar(gHeight));
      SkRect clip;
      clip.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(1), SkIntToScalar(1));
  
      SkPaint paint;
      SkISize size = SkISize::Make(gWidth, gHeight);
  
      SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
      SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
      SkSurface* newSurface = SkSurface::NewRasterPMColor(4, 4);
      SkAutoTUnref<SkSurface> aur(newSurface);
  
      for (int i = 0; i < 2; ++i) {
          if (i == 1) {
              canvas->setSurface(newSurface);
              size = SkISize::Make(4, 4);
          }
  
          // verify that canvas size is correctly initialized or set
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
  
          // Verify that clear, clip and draw the canvas will not change its size
          canvas->clear(0x00000000);
          canvas->clipRect(clip, SkRegion::kIntersect_Op, false);
          canvas->drawRect(rect, paint);
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
  
          // Verify that flush the canvas will not change its size
          canvas->flush();
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
  
          // Verify that clear canvas with saved state will not change its size
          canvas->save();
          canvas->clear(0xFFFFFFFF);
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
  
          // Verify that restore canvas state will not change its size
          canvas->restore();
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
  
          // Verify that clear within a layer will not change canvas size
          canvas->saveLayer(&clip, &paint);
          canvas->clear(0x00000000);
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
  
          // Verify that restore from a layer will not change canvas size
          canvas->restore();
          REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
      }
  }
  
  DEF_TEST(DeferredCanvas_CPU, reporter) {
      TestDeferredCanvasFlush(reporter);
      TestDeferredCanvasSilentFlush(reporter);
      TestDeferredCanvasFreshFrame(reporter);
      TestDeferredCanvasMemoryLimit(reporter);
      TestDeferredCanvasBitmapCaching(reporter);
      TestDeferredCanvasSkip(reporter);
      TestDeferredCanvasBitmapShaderNoLeak(reporter);
      TestDeferredCanvasBitmapSizeThreshold(reporter);
      TestDeferredCanvasCreateCompatibleDevice(reporter);
      TestDeferredCanvasWritePixelsToSurface(reporter);
      TestDeferredCanvasGetCanvasSize(reporter);
      TestDeferredCanvasSurface(reporter, NULL);
      TestDeferredCanvasSetSurface(reporter, NULL);
  }
  
  DEF_GPUTEST(DeferredCanvas_GPU, reporter, factory) {
      if (factory != NULL) {
          TestDeferredCanvasSurface(reporter, factory);
          TestDeferredCanvasSetSurface(reporter, factory);
      }
  }
  