static void test_peephole() {
    SkRandom rand;

    for (int j = 0; j < 100; j++) {
        SkRandom rand2(rand); // remember the seed

        SkPicture picture;
        SkCanvas* canvas = picture.beginRecording(100, 100);

        for (int i = 0; i < 1000; ++i) {
            rand_op(canvas, rand);
        }
        picture.endRecording();

        rand = rand2;
    }

    {
        SkPicture picture;
        SkCanvas* canvas = picture.beginRecording(100, 100);
        SkRect rect = SkRect::MakeWH(50, 50);

        for (int i = 0; i < 100; ++i) {
            canvas->save();
        }
        while (canvas->getSaveCount() > 1) {
            canvas->clipRect(rect);
            canvas->restore();
        }
        picture.endRecording();
    }
}
Beispiel #2
0
    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

        SkMatrix matrix;

        SkGroupShape* gs = new SkGroupShape;
        SkAutoUnref aur(gs);
        gs->appendShape(&fGroup);
        matrix.setScale(-SK_Scalar1, SK_Scalar1);
        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
        gs->appendShape(&fGroup, matrix);
        matrix.setTranslate(SkIntToScalar(240), 0);
        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
        gs->appendShape(&fGroup, matrix);

#if 1
        SkPicture* pict = new SkPicture;
        SkCanvas* cv = pict->beginRecording(1000, 1000);
        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
        gs->draw(cv);
        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
        cv->scale(-SK_Scalar1, SK_Scalar1);
        gs->draw(cv);
        pict->endRecording();
        canvas->drawPicture(*pict);
        pict->unref();
#endif
}
// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
static void test_serializing_empty_picture() {
    SkPicture picture;
    picture.beginRecording(0, 0);
    picture.endRecording();
    SkDynamicMemoryWStream stream;
    picture.serialize(&stream);
}
static bool nativeSerializeViewState(JNIEnv* env, jobject, jint jbaseLayer,
                                     jobject jstream, jbyteArray jstorage)
{
    BaseLayerAndroid* baseLayer = (BaseLayerAndroid*) jbaseLayer;
    if (!baseLayer)
        return false;

    SkWStream *stream = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
#if USE(ACCELERATED_COMPOSITING)
// SAMSUNG CHANGE >> White flickering issue.
// WAS:stream->write32(baseLayer->getBackgroundColor().rgb());
    stream->write32(baseLayer->getBackgroundColor());
// SAMSUNG CHANGE <<
#else
    stream->write32(0);
#endif
    SkPicture picture;
    PictureSet* content = baseLayer->content();
    baseLayer->drawCanvas(picture.beginRecording(content->width(), content->height(),
            SkPicture::kUsePathBoundsForClip_RecordingFlag));
    picture.endRecording();
    if (!stream)
        return false;
    picture.serialize(stream);
    int childCount = baseLayer->countChildren();
    XLOG("BaseLayer has %d child(ren)", childCount);
    stream->write32(childCount);
    for (int i = 0; i < childCount; i++) {
        LayerAndroid* layer = static_cast<LayerAndroid*>(baseLayer->getChild(i));
        serializeLayer(layer, stream);
    }
    delete stream;
    return true;
}
    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);
        
        SkMatrix saveM = *fMatrixRefs[3];
        SkScalar c = SkIntToScalar(50);
        fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
        
        SkMatrix matrix;
     
        SkGroupShape* gs = new SkGroupShape;
        SkAutoUnref aur(gs);
        gs->appendShape(&fGroup);
        matrix.setScale(-SK_Scalar1, SK_Scalar1);
        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
        gs->appendShape(&fGroup, matrix);
        matrix.setTranslate(SkIntToScalar(240), 0);
        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
        gs->appendShape(&fGroup, matrix);
        
#if 0        
        canvas->drawShape(gs);
#else
        SkPicture pict;
        SkCanvas* cv = pict.beginRecording(1000, 1000);
        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
        cv->drawShape(gs);
        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
        cv->scale(-SK_Scalar1, SK_Scalar1);
        cv->drawShape(gs);
        pict.endRecording();
        canvas->drawPicture(pict);
#endif

        *fMatrixRefs[3] = saveM;
}
// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
// run in debug mode.
static void test_deleting_empty_playback() {
    SkPicture picture;
    // Creates an SkPictureRecord
    picture.beginRecording(0, 0);
    // Turns that into an SkPicturePlayback
    picture.endRecording();
    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
    picture.beginRecording(0, 0);
}
void PicturePileLayerContent::serialize(SkWStream* stream)
{
    if (!stream)
       return;
    SkPicture picture;
    draw(picture.beginRecording(width(), height(),
                                SkPicture::kUsePathBoundsForClip_RecordingFlag));
    picture.endRecording();
    picture.serialize(stream);
}
// Return a picture with the bitmaps drawn at the specified positions.
static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
                                 int count, DrawBitmapProc proc) {
    SkPicture* pic = new SkPicture;
    SkCanvas* canvas = pic->beginRecording(1000, 1000);
    for (int i = 0; i < count; ++i) {
        proc(canvas, bm[i], pos[i]);
    }
    pic->endRecording();
    return pic;
}
Beispiel #9
0
/** Converts fPicture to a picture that uses a BBoxHierarchy.
 *  PictureRenderer subclasses that are used to test picture playback
 *  should call this method during init.
 */
void PictureRenderer::buildBBoxHierarchy() {
    SkASSERT(NULL != fPicture);
    if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
        SkPicture* newPicture = this->createPicture();
        SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
                                                        this->recordFlags());
        fPicture->draw(recorder);
        newPicture->endRecording();
        fPicture->unref();
        fPicture = newPicture;
    }
}
// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
// should never do this.
static void test_bad_bitmap() {
    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
    // fail.
    SkBitmap bm;
    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
    SkPicture picture;
    SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
    recordingCanvas->drawBitmap(bm, 0, 0);
    picture.endRecording();

    SkCanvas canvas;
    canvas.drawPicture(picture);
}
    static SkPicture* LoadPdf(const char path[]) {
        SkAutoTDelete<SkPdfRenderer> renderer(SkPdfRenderer::CreateFromFile(path));
        if (NULL == renderer.get()) {
            return NULL;
        }

        SkPicture* pic = SkNEW(SkPicture);
        SkCanvas* canvas = pic->beginRecording((int) renderer->MediaBox(0).width(),
                                               (int) renderer->MediaBox(0).height());
        renderer->renderPage(0, canvas, renderer->MediaBox(0));
        pic->endRecording();
        return pic;
    }
Beispiel #12
0
static void bench_record(SkPicture* src, const char* name) {
    const SkMSec start = SkTime::GetMSecs();
    const int width  = src ? src->width()  : FLAGS_nullSize;
    const int height = src ? src->height() : FLAGS_nullSize;

    for (int i = 0; i < FLAGS_loops; i++) {
        SkPicture dst;
        SkCanvas* canvas = dst.beginRecording(width, height, FLAGS_flags);
        if (src) src->draw(canvas);
        if (FLAGS_endRecording) dst.endRecording();
    }

    const SkMSec elapsed = SkTime::GetMSecs() - start;
    const double msPerLoop = elapsed / (double)FLAGS_loops;
    printf("%.2g\t%s\n", msPerLoop, name);
}
    virtual void onDrawContent(SkCanvas* canvas) {
        drawSomething(canvas);

        SkPicture* pict = new SkPicture;
        SkAutoUnref aur(pict);

        drawSomething(pict->beginRecording(100, 100));
        pict->endRecording();

        canvas->save();
        canvas->translate(SkIntToScalar(300), SkIntToScalar(50));
        canvas->scale(-SK_Scalar1, -SK_Scalar1);
        canvas->translate(-SkIntToScalar(100), -SkIntToScalar(50));
        canvas->drawPicture(*pict);
        canvas->restore();

        canvas->save();
        canvas->translate(SkIntToScalar(200), SkIntToScalar(150));
        canvas->scale(SK_Scalar1, -SK_Scalar1);
        canvas->translate(0, -SkIntToScalar(50));
        canvas->drawPicture(*pict);
        canvas->restore();

        canvas->save();
        canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
        canvas->scale(-SK_Scalar1, SK_Scalar1);
        canvas->translate(-SkIntToScalar(100), 0);
        canvas->drawPicture(*pict);
        canvas->restore();

#ifdef SK_DEVELOPER
        if (false) {
            SkDebugfDumper dumper;
            SkDumpCanvas dumpCanvas(&dumper);
            dumpCanvas.drawPicture(*pict);
        }
#endif

        // test that we can re-record a subpicture, and see the results

        SkMWCRandom rand(SampleCode::GetAnimTime());
        canvas->translate(SkIntToScalar(10), SkIntToScalar(250));
        drawCircle(fSubPicture->beginRecording(50, 50), 25,
                   rand.nextU() | 0xFF000000);
        canvas->drawPicture(*fPicture);
        delayInval(500);
    }
Beispiel #14
0
    virtual void onDrawContent(SkCanvas* canvas) {
        SkScalar angle = SampleCode::GetAnimScalar(SkIntToScalar(180),
                                                   SkIntToScalar(360));

        SkMatrix saveM = *fMatrixRefs[3];
        SkScalar c = SkIntToScalar(50);
        fMatrixRefs[3]->preRotate(angle, c, c);
        
        const SkScalar dx = 350;
        const SkScalar dy = 500;
        const int N = 1;
        for (int v = -N; v <= N; v++) {
            for (int h = -N; h <= N; h++) {
                SkAutoCanvasRestore acr(canvas, true);
                canvas->translate(h * dx, v * dy);
        
        SkMatrix matrix;
     
        SkGroupShape* gs = new SkGroupShape;
        SkAutoUnref aur(gs);
        gs->appendShape(&fGroup);
        matrix.setScale(-SK_Scalar1, SK_Scalar1);
        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
        gs->appendShape(&fGroup, matrix);
        matrix.setTranslate(SkIntToScalar(240), 0);
        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
        gs->appendShape(&fGroup, matrix);
        
#if 1
        SkPicture* pict = new SkPicture;
        SkCanvas* cv = pict->beginRecording(1000, 1000);
        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
        gs->draw(cv);
        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
        cv->scale(-SK_Scalar1, SK_Scalar1);
        gs->draw(cv);
        pict->endRecording();
        
        drawpicture(canvas, *pict);
        pict->unref();
#endif

        }}

        *fMatrixRefs[3] = saveM;
        this->inval(NULL);
}
    virtual void onDraw(SkCanvas*) {
        int n = (int)(N * this->innerLoopScale());
        n = SkMax32(1, n);

        for (int i = 0; i < n; i++) {

            SkPicture picture;

            SkCanvas* pCanvas = picture.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT);
            recordCanvas(pCanvas);

            // we don't need to draw the picture as the endRecording step will
            // do the work of transferring the recorded content into a playback
            // object.
            picture.endRecording();
        }
    }
Beispiel #16
0
void PicturePile::updatePicture(PicturePainter* painter, PictureContainer& pc)
{
    /* The ref counting here is a bit unusual. What happens is begin/end recording
     * will ref/unref the recording canvas. However, 'canvas' might be pointing
     * at an SkNWayCanvas instead of the recording canvas, which needs to be
     * unref'd. Thus what we do is ref the recording canvas so that we can
     * always unref whatever canvas we have at the end.
     */
    TRACE_METHOD();
    SkPicture* picture = new SkPicture();
    SkCanvas* canvas = picture->beginRecording(pc.area.width(), pc.area.height(),
            SkPicture::kUsePathBoundsForClip_RecordingFlag);
    SkSafeRef(canvas);
    canvas->translate(-pc.area.x(), -pc.area.y());
    IntRect drawArea = pc.area;
    if (pc.prerendered.get()) {
        SkCanvas* prerender = painter->createPrerenderCanvas(pc.prerendered.get());
        if (!prerender) {
            ALOGV("Failed to create prerendered for " INT_RECT_FORMAT,
                    INT_RECT_ARGS(pc.prerendered->area));
            pc.prerendered.clear();
        } else {
            drawArea.unite(pc.prerendered->area);
            SkNWayCanvas* nwayCanvas = new SkNWayCanvas(drawArea.width(), drawArea.height());
            nwayCanvas->translate(-drawArea.x(), -drawArea.y());
            nwayCanvas->addCanvas(canvas);
            nwayCanvas->addCanvas(prerender);
            SkSafeUnref(canvas);
            SkSafeUnref(prerender);
            canvas = nwayCanvas;
        }
    }
    WebCore::PlatformGraphicsContextSkia pgc(canvas);
    WebCore::GraphicsContext gc(&pgc);
    ALOGV("painting picture: " INT_RECT_FORMAT, INT_RECT_ARGS(drawArea));
    painter->paintContents(&gc, drawArea);
    SkSafeUnref(canvas);
    picture->endRecording();

    SkSafeUnref(pc.picture);
    pc.picture = picture;
    pc.dirty = false;
}
static void test_clone_empty(skiatest::Reporter* reporter) {
    // This is a regression test for crbug.com/172062
    // Before the fix, we used to crash accessing a null pointer when we
    // had a picture with no paints. This test passes by not crashing.
    {
        SkPicture picture;
        picture.beginRecording(1, 1);
        picture.endRecording();
        SkPicture* destPicture = picture.clone();
        REPORTER_ASSERT(reporter, NULL != destPicture);
        destPicture->unref();
    }
    {
        // Test without call to endRecording
        SkPicture picture;
        picture.beginRecording(1, 1);
        SkPicture* destPicture = picture.clone();
        REPORTER_ASSERT(reporter, NULL != destPicture);
        destPicture->unref();
    }
}
    virtual void onDraw(SkCanvas* canvas) {
        int offset = 35000;
        int extents = 1000;

        // We record a picture of huge vertical extents in which we clear the canvas to red, create
        // a 'extents' by 'extents' round rect clip at a vertical offset of 'offset', then draw
        // green into that.
        SkPicture pict;
        SkCanvas* rec = pict.beginRecording(100, offset + extents);
        rec->drawColor(0xffff0000);
        rec->save();
        SkRect r = {
            SkIntToScalar(-extents),
            SkIntToScalar(offset - extents),
            SkIntToScalar(extents),
            SkIntToScalar(offset + extents)
        };
        SkPath p;
        p.addRoundRect(r, 5, 5);
        rec->clipPath(p, SkRegion::kIntersect_Op, true);
        rec->drawColor(0xff00ff00);
        rec->restore();
        pict.endRecording();

        // Next we play that picture into another picture of the same size.
        SkPicture pict2;
        pict.draw(pict2.beginRecording(100, offset + extents));
        pict2.endRecording();

        // Finally we play the part of that second picture that should be green into the canvas.
        canvas->save();
        canvas->translate(SkIntToScalar(extents / 2),
                          SkIntToScalar(-(offset - extents / 2)));
        pict2.draw(canvas);
        canvas->restore();

        // If the image is red, we erroneously decided the clipPath was empty and didn't record
        // the green drawColor, if it's green we're all good.
    }
Beispiel #19
0
/**
 * Called only by render_picture().
 */
static bool render_picture_internal(const SkString& inputPath, const SkString* outputDir,
                                    sk_tools::PictureRenderer& renderer,
                                    SkBitmap** out) {
    SkString inputFilename;
    sk_tools::get_basename(&inputFilename, inputPath);
    SkString outputDirString;
    if (NULL != outputDir && outputDir->size() > 0 && !FLAGS_writeEncodedImages) {
        outputDirString.set(*outputDir);
    }

    SkFILEStream inputStream;
    inputStream.setPath(inputPath.c_str());
    if (!inputStream.isValid()) {
        SkDebugf("Could not open file %s\n", inputPath.c_str());
        return false;
    }

    SkPicture::InstallPixelRefProc proc;
    if (FLAGS_deferImageDecoding) {
        proc = &sk_tools::LazyDecodeBitmap;
    } else if (FLAGS_writeEncodedImages) {
        SkASSERT(!FLAGS_writePath.isEmpty());
        reset_image_file_base_name(inputFilename);
        proc = &write_image_to_file;
    } else {
        proc = &SkImageDecoder::DecodeMemory;
    }

    SkDebugf("deserializing... %s\n", inputPath.c_str());

    SkPicture* picture = SkPicture::CreateFromStream(&inputStream, proc);

    if (NULL == picture) {
        SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
        return false;
    }

    while (FLAGS_bench_record) {
        const int kRecordFlags = 0;
        SkPicture other;
        picture->draw(other.beginRecording(picture->width(), picture->height(), kRecordFlags));
        other.endRecording();
    }

    for (int i = 0; i < FLAGS_clone; ++i) {
        SkPicture* clone = picture->clone();
        SkDELETE(picture);
        picture = clone;
    }

    SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(),
             inputPath.c_str());

    renderer.init(picture, &outputDirString, &inputFilename, FLAGS_writeChecksumBasedFilenames);

    if (FLAGS_preprocess) {
        if (NULL != renderer.getCanvas()) {
            renderer.getCanvas()->EXPERIMENTAL_optimize(picture);
        }
    }

    renderer.setup();

    bool success = renderer.render(out);
    if (!success) {
        SkDebugf("Failed to render %s\n", inputFilename.c_str());
    }

    renderer.end();

    SkDELETE(picture);
    return success;
}
Beispiel #20
0
// construct the pattern removed by the SkPictureRecord::remove_save_layer1
// optimization, i.e.:
//   SAVE_LAYER
//       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
//   RESTORE
//
// saveLayerHasPaint - control if the saveLayer has a paint (the optimization
//                     takes a different path if this is false)
// dbmr2rHasPaint    - control if the dbmr2r has a paint (the optimization
//                     takes a different path if this is false)
// colorsMatch       - control if the saveLayer and dbmr2r paint colors
//                     match (the optimization will fail if they do not)
static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern,
                                          SkTDArray<DrawType>* postOptPattern,
                                          const SkBitmap& checkerBoard,
                                          bool saveLayerHasPaint,
                                          bool dbmr2rHasPaint,
                                          bool colorsMatch)  {
    // Create the pattern that should trigger the optimization
    preOptPattern->setCount(5);
    (*preOptPattern)[0] = SAVE;
    (*preOptPattern)[1] = SAVE_LAYER;
    (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
    (*preOptPattern)[3] = RESTORE;
    (*preOptPattern)[4] = RESTORE;

    if (colorsMatch) {
        // Create the pattern that should appear after the optimization
        postOptPattern->setCount(5);
        (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
        (*postOptPattern)[1] = SAVE;
        (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT;
        (*postOptPattern)[3] = RESTORE;
        (*postOptPattern)[4] = RESTORE;
    } else {
        // Create the pattern that appears if the optimization doesn't fire
        postOptPattern->setCount(7);
        (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw
        (*postOptPattern)[1] = SAVE;
        (*postOptPattern)[2] = SAVE_LAYER;
        (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT;
        (*postOptPattern)[4] = RESTORE;
        (*postOptPattern)[5] = RESTORE;
        (*postOptPattern)[6] = RESTORE;
    }

    SkPicture* result = new SkPicture;

    // have to disable the optimizations while generating the picture
    SkCanvas* canvas = result->beginRecording(100, 100,
                         SkPicture::kDisableRecordOptimizations_RecordingFlag);

    SkPaint saveLayerPaint;
    saveLayerPaint.setColor(0xCC000000);

    // saveLayer's 'bounds' parameter must be NULL for this optimization
    if (saveLayerHasPaint) {
        canvas->saveLayer(NULL, &saveLayerPaint);
    } else {
        canvas->saveLayer(NULL, NULL);
    }

    SkRect rect = { 10, 10, 90, 90 };

    // The dbmr2r's paint must be opaque
    SkPaint dbmr2rPaint;
    if (colorsMatch) {
        dbmr2rPaint.setColor(0xFF000000);
    } else {
        dbmr2rPaint.setColor(0xFFFF0000);
    }

    if (dbmr2rHasPaint) {
        canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint);
    } else {
        canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL);
    }
    canvas->restore();

    result->endRecording();

    return result;
}
 static void endRecording(JNIEnv* env, jobject, jlong pictHandle) {
     SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle);
     pict->endRecording();
 }
static int filter_picture(const SkString& inFile, const SkString& outFile) {
    SkAutoTDelete<SkPicture> inPicture;

    SkFILEStream inStream(inFile.c_str());
    if (inStream.isValid()) {
        inPicture.reset(SkPicture::CreateFromStream(&inStream));
    }

    if (NULL == inPicture.get()) {
        SkDebugf("Could not read file %s\n", inFile.c_str());
        return -1;
    }

    int localCount[SK_ARRAY_COUNT(gOptTable)];

    memset(localCount, 0, sizeof(localCount));

    SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height());
    debugCanvas.setBounds(inPicture->width(), inPicture->height());
    inPicture->draw(&debugCanvas);

    // delete the initial save and restore since replaying the commands will
    // re-add them
    if (debugCanvas.getSize() > 1) {
        debugCanvas.deleteDrawCommandAt(0);
        debugCanvas.deleteDrawCommandAt(debugCanvas.getSize()-1);
    }

    bool changed = true;
    int numBefore = debugCanvas.getSize();

    while (changed) {
        changed = false;
        for (int i = 0; i < debugCanvas.getSize(); ++i) {
            for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
                if ((*gOptTable[opt].fCheck)(&debugCanvas, i)) {
                    (*gOptTable[opt].fApply)(&debugCanvas, i);

                    ++gOptTable[opt].fNumTimesApplied;
                    ++localCount[opt];

                    if (debugCanvas.getSize() == i) {
                        // the optimization removed all the remaining operations
                        break;
                    }

                    opt = 0;          // try all the opts all over again
                    changed = true;
                }
            }
        }
    }

    int numAfter = debugCanvas.getSize();

    if (!outFile.isEmpty()) {
        SkPicture outPicture;

        SkCanvas* canvas = outPicture.beginRecording(inPicture->width(), inPicture->height());
        debugCanvas.draw(canvas);
        outPicture.endRecording();

        SkFILEWStream outStream(outFile.c_str());

        outPicture.serialize(&outStream);
    }

    bool someOptFired = false;
    for (size_t opt = 0; opt < SK_ARRAY_COUNT(gOptTable); ++opt) {
        if (0 != localCount[opt]) {
            SkDebugf("%d: %d ", opt, localCount[opt]);
            someOptFired = true;
        }
    }

    if (!someOptFired) {
        SkDebugf("No opts fired\n");
    } else {
        SkDebugf("\t before: %d after: %d delta: %d\n",
                 numBefore, numAfter, numBefore-numAfter);
    }

    return 0;
}
static void test_collapse(skiatest::Reporter* reporter) {
    PFEmitStruct gStructure[] = { emit_struct0, emit_struct1, emit_struct2, emit_struct3 };
    PFEmitBody gBody[] = { emit_body0, emit_body1, emit_body2, emit_body3 };
    PFEmitMC gMCs[] = { emit_clip_and_mat, emit_mat_and_clip,
                        emit_double_mat_and_clip, emit_mat_clip_clip };

    for (size_t i = 0; i < SK_ARRAY_COUNT(gStructure); ++i) {
        for (size_t j = 0; j < SK_ARRAY_COUNT(gBody); ++j) {
            for (size_t k = 0; k < SK_ARRAY_COUNT(gMCs); ++k) {
                for (int l = 0; l < kMatTypeCount; ++l) {
                    for (int m = 0; m < kClipTypeCount; ++m) {
                        for (int n = 0; n < kNonSaveLayerDrawOpTypeCount; ++n) {
#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
                            static int testID = -1;
                            ++testID;
                            if (testID < -1) {
                                continue;
                            }
                            SkDebugf("test: %d\n", testID);
#endif

                            SkTDArray<DrawType> expected, actual;

                            SkPicture picture;

                            // Note: beginRecording/endRecording add a save/restore pair
                            SkCanvas* canvas = picture.beginRecording(100, 100);
                            (*gStructure[i])(canvas,
                                             gMCs[k],
                                             (MatType) l,
                                             (ClipType) m,
                                             gBody[j],
                                             (DrawOpType) n,
                                             &expected);
                            picture.endRecording();

                            gets_ops(picture, &actual);

                            REPORTER_ASSERT(reporter, expected.count() == actual.count());

                            if (expected.count() != actual.count()) {
#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
                                print(expected, actual);
#endif
                                continue;
                            }

                            for (int i = 0; i < expected.count(); ++i) {
                                REPORTER_ASSERT(reporter, expected[i] == actual[i]);
#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
                                if (expected[i] != actual[i]) {
                                    print(expected, actual);
                                }
#endif
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}