SkDebugCanvas.cpp revision 60bd7519a9db4ddddd95e490f93165e5676f90f5
1
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkColorPriv.h"
11#include "SkDebugCanvas.h"
12#include "SkDrawCommand.h"
13#include "SkDrawFilter.h"
14#include "SkDevice.h"
15#include "SkXfermode.h"
16
17SkDebugCanvas::SkDebugCanvas(int width, int height)
18        : INHERITED(width, height)
19        , fPicture(NULL)
20        , fWidth(width)
21        , fHeight(height)
22        , fFilter(false)
23        , fMegaVizMode(false)
24        , fIndex(0)
25        , fOverdrawViz(false)
26        , fOverdrawFilter(NULL)
27        , fOverrideTexFiltering(false)
28        , fTexOverrideFilter(NULL)
29        , fOutstandingSaveCount(0) {
30    fUserMatrix.reset();
31
32    // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
33    // operations. This can lead to problems in the debugger which expects all
34    // the operations in the captured skp to appear in the debug canvas. To
35    // circumvent this we create a wide open clip here (an empty clip rect
36    // is not sufficient).
37    // Internally, the SkRect passed to clipRect is converted to an SkIRect and
38    // rounded out. The following code creates a nearly maximal rect that will
39    // not get collapsed by the coming conversions (Due to precision loss the
40    // inset has to be surprisingly large).
41    SkIRect largeIRect = SkIRect::MakeLargest();
42    largeIRect.inset(1024, 1024);
43    SkRect large = SkRect::Make(largeIRect);
44#ifdef SK_DEBUG
45    large.roundOut(&largeIRect);
46    SkASSERT(!largeIRect.isEmpty());
47#endif
48    // call the base class' version to avoid adding a draw command
49    this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
50}
51
52SkDebugCanvas::~SkDebugCanvas() {
53    fCommandVector.deleteAll();
54    SkSafeUnref(fOverdrawFilter);
55    SkSafeUnref(fTexOverrideFilter);
56}
57
58void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
59    command->setOffset(this->getOpID());
60    fCommandVector.push(command);
61}
62
63void SkDebugCanvas::draw(SkCanvas* canvas) {
64    if (!fCommandVector.isEmpty()) {
65        this->drawTo(canvas, fCommandVector.count() - 1);
66    }
67}
68
69void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
70    canvas->concat(fUserMatrix);
71}
72
73int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
74    SkBitmap bitmap;
75    bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
76
77    SkCanvas canvas(bitmap);
78    canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
79    applyUserTransform(&canvas);
80
81    int layer = 0;
82    SkColor prev = bitmap.getColor(0,0);
83    for (int i = 0; i < index; i++) {
84        if (fCommandVector[i]->isVisible()) {
85            fCommandVector[i]->execute(&canvas);
86        }
87        if (prev != bitmap.getColor(0,0)) {
88            layer = i;
89        }
90        prev = bitmap.getColor(0,0);
91    }
92    return layer;
93}
94
95class OverdrawXfermode : public SkXfermode {
96public:
97    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE {
98        // This table encodes the color progression of the overdraw visualization
99        static const SkPMColor gTable[] = {
100            SkPackARGB32(0x00, 0x00, 0x00, 0x00),
101            SkPackARGB32(0xFF, 128, 158, 255),
102            SkPackARGB32(0xFF, 170, 185, 212),
103            SkPackARGB32(0xFF, 213, 195, 170),
104            SkPackARGB32(0xFF, 255, 192, 127),
105            SkPackARGB32(0xFF, 255, 185, 85),
106            SkPackARGB32(0xFF, 255, 165, 42),
107            SkPackARGB32(0xFF, 255, 135, 0),
108            SkPackARGB32(0xFF, 255,  95, 0),
109            SkPackARGB32(0xFF, 255,  50, 0),
110            SkPackARGB32(0xFF, 255,  0, 0)
111        };
112
113        for (size_t i = 0; i < SK_ARRAY_COUNT(gTable)-1; ++i) {
114            if (gTable[i] == dst) {
115                return gTable[i+1];
116            }
117        }
118
119        return gTable[SK_ARRAY_COUNT(gTable)-1];
120    }
121
122    virtual Factory getFactory() const SK_OVERRIDE { return NULL; }
123#ifndef SK_IGNORE_TO_STRING
124    virtual void toString(SkString* str) const { str->set("OverdrawXfermode"); }
125#endif
126};
127
128// The OverdrawFilter modifies every paint to use an SkProcXfermode which
129// in turn invokes OverdrawXferModeProc
130class SkOverdrawFilter : public SkDrawFilter {
131public:
132    SkOverdrawFilter() {
133        fXferMode = SkNEW(OverdrawXfermode);
134    }
135
136    virtual ~SkOverdrawFilter() {
137        delete fXferMode;
138    }
139
140    virtual bool filter(SkPaint* p, Type) SK_OVERRIDE {
141        p->setXfermode(fXferMode);
142        return true;
143    }
144
145protected:
146    SkXfermode* fXferMode;
147
148private:
149    typedef SkDrawFilter INHERITED;
150};
151
152// SkTexOverrideFilter modifies every paint to use the specified
153// texture filtering mode
154class SkTexOverrideFilter : public SkDrawFilter {
155public:
156    SkTexOverrideFilter() : fFilterLevel(SkPaint::kNone_FilterLevel) {
157    }
158
159    void setFilterLevel(SkPaint::FilterLevel filterLevel) {
160        fFilterLevel = filterLevel;
161    }
162
163    virtual bool filter(SkPaint* p, Type) SK_OVERRIDE {
164        p->setFilterLevel(fFilterLevel);
165        return true;
166    }
167
168protected:
169    SkPaint::FilterLevel fFilterLevel;
170
171private:
172    typedef SkDrawFilter INHERITED;
173};
174
175class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
176public:
177    SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
178
179    virtual void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE {
180        SkPaint p;
181        p.setColor(SK_ColorRED);
182        p.setStyle(SkPaint::kStroke_Style);
183        p.setAntiAlias(doAA);
184        fCanvas->drawRect(r, p);
185    }
186    virtual void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE {
187        SkPaint p;
188        p.setColor(SK_ColorGREEN);
189        p.setStyle(SkPaint::kStroke_Style);
190        p.setAntiAlias(doAA);
191        fCanvas->drawRRect(rr, p);
192    }
193    virtual void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE {
194        SkPaint p;
195        p.setColor(SK_ColorBLUE);
196        p.setStyle(SkPaint::kStroke_Style);
197        p.setAntiAlias(doAA);
198        fCanvas->drawPath(path, p);
199    }
200
201protected:
202    SkCanvas* fCanvas;
203
204private:
205    typedef SkCanvas::ClipVisitor INHERITED;
206};
207
208// set up the saveLayer commands so that the active ones
209// return true in their 'active' method
210void SkDebugCanvas::markActiveCommands(int index) {
211    fActiveLayers.rewind();
212    fActiveCulls.rewind();
213
214    for (int i = 0; i < fCommandVector.count(); ++i) {
215        fCommandVector[i]->setActive(false);
216    }
217
218    for (int i = 0; i < index; ++i) {
219        SkDrawCommand::Action result = fCommandVector[i]->action();
220        if (SkDrawCommand::kPushLayer_Action == result) {
221            fActiveLayers.push(fCommandVector[i]);
222        } else if (SkDrawCommand::kPopLayer_Action == result) {
223            fActiveLayers.pop();
224        } else if (SkDrawCommand::kPushCull_Action == result) {
225            fActiveCulls.push(fCommandVector[i]);
226        } else if (SkDrawCommand::kPopCull_Action == result) {
227            fActiveCulls.pop();
228        }
229    }
230
231    for (int i = 0; i < fActiveLayers.count(); ++i) {
232        fActiveLayers[i]->setActive(true);
233    }
234
235    for (int i = 0; i < fActiveCulls.count(); ++i) {
236        fActiveCulls[i]->setActive(true);
237    }
238}
239
240void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
241    SkASSERT(!fCommandVector.isEmpty());
242    SkASSERT(index < fCommandVector.count());
243    int i = 0;
244
245    // This only works assuming the canvas and device are the same ones that
246    // were previously drawn into because they need to preserve all saves
247    // and restores.
248    // The visibility filter also requires a full re-draw - otherwise we can
249    // end up drawing the filter repeatedly.
250    if (fIndex < index && !fFilter && !fMegaVizMode) {
251        i = fIndex + 1;
252    } else {
253        for (int j = 0; j < fOutstandingSaveCount; j++) {
254            canvas->restore();
255        }
256        canvas->clear(SK_ColorTRANSPARENT);
257        canvas->resetMatrix();
258        SkRect rect = SkRect::MakeWH(SkIntToScalar(fWidth),
259                                     SkIntToScalar(fHeight));
260        canvas->clipRect(rect, SkRegion::kReplace_Op );
261        applyUserTransform(canvas);
262        fOutstandingSaveCount = 0;
263    }
264
265    // The setting of the draw filter has to go here (rather than in
266    // SkRasterWidget) due to the canvas restores this class performs.
267    // Since the draw filter is stored in the layer stack if we
268    // call setDrawFilter on anything but the root layer odd things happen.
269    if (fOverdrawViz) {
270        if (NULL == fOverdrawFilter) {
271            fOverdrawFilter = new SkOverdrawFilter;
272        }
273
274        if (fOverdrawFilter != canvas->getDrawFilter()) {
275            canvas->setDrawFilter(fOverdrawFilter);
276        }
277    } else if (fOverrideTexFiltering) {
278        if (NULL == fTexOverrideFilter) {
279            fTexOverrideFilter = new SkTexOverrideFilter;
280        }
281
282        if (fTexOverrideFilter != canvas->getDrawFilter()) {
283            canvas->setDrawFilter(fTexOverrideFilter);
284        }
285    } else {
286        canvas->setDrawFilter(NULL);
287    }
288
289    if (fMegaVizMode) {
290        this->markActiveCommands(index);
291    }
292
293    for (; i <= index; i++) {
294        if (i == index && fFilter) {
295            SkPaint p;
296            p.setColor(0xAAFFFFFF);
297            canvas->save();
298            canvas->resetMatrix();
299            SkRect mask;
300            mask.set(SkIntToScalar(0), SkIntToScalar(0),
301                    SkIntToScalar(fWidth), SkIntToScalar(fHeight));
302            canvas->clipRect(mask, SkRegion::kReplace_Op, false);
303            canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
304                    SkIntToScalar(fWidth), SkIntToScalar(fHeight), p);
305            canvas->restore();
306        }
307
308        if (fCommandVector[i]->isVisible()) {
309            if (fMegaVizMode && fCommandVector[i]->active()) {
310                // "active" commands execute their visualization behaviors:
311                //     All active saveLayers get replaced with saves so all draws go to the
312                //     visible canvas.
313                //     All active culls draw their cull box
314                fCommandVector[i]->vizExecute(canvas);
315            } else {
316                fCommandVector[i]->execute(canvas);
317            }
318
319            fCommandVector[i]->trackSaveState(&fOutstandingSaveCount);
320        }
321    }
322
323    if (fMegaVizMode) {
324        SkRect r = SkRect::MakeWH(SkIntToScalar(fWidth), SkIntToScalar(fHeight));
325        r.outset(SK_Scalar1, SK_Scalar1);
326
327        canvas->save();
328        // nuke the CTM
329        canvas->setMatrix(SkMatrix::I());
330        // turn off clipping
331        canvas->clipRect(r, SkRegion::kReplace_Op);
332
333        // visualize existing clips
334        SkDebugClipVisitor visitor(canvas);
335
336        canvas->replayClips(&visitor);
337
338        canvas->restore();
339    }
340    fMatrix = canvas->getTotalMatrix();
341    if (!canvas->getClipDeviceBounds(&fClip)) {
342        fClip.setEmpty();
343    }
344    fIndex = index;
345}
346
347void SkDebugCanvas::deleteDrawCommandAt(int index) {
348    SkASSERT(index < fCommandVector.count());
349    delete fCommandVector[index];
350    fCommandVector.remove(index);
351}
352
353SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
354    SkASSERT(index < fCommandVector.count());
355    return fCommandVector[index];
356}
357
358void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
359    SkASSERT(index < fCommandVector.count());
360    delete fCommandVector[index];
361    fCommandVector[index] = command;
362}
363
364SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) {
365    SkASSERT(index < fCommandVector.count());
366    return fCommandVector[index]->Info();
367}
368
369bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
370    SkASSERT(index < fCommandVector.count());
371    return fCommandVector[index]->isVisible();
372}
373
374const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
375    return fCommandVector;
376}
377
378SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
379    return fCommandVector;
380}
381
382// TODO(chudy): Free command string memory.
383SkTArray<SkString>* SkDebugCanvas::getDrawCommandsAsStrings() const {
384    SkTArray<SkString>* commandString = new SkTArray<SkString>(fCommandVector.count());
385    if (!fCommandVector.isEmpty()) {
386        for (int i = 0; i < fCommandVector.count(); i ++) {
387            commandString->push_back() = fCommandVector[i]->toString();
388        }
389    }
390    return commandString;
391}
392
393SkTDArray<size_t>* SkDebugCanvas::getDrawCommandOffsets() const {
394    SkTDArray<size_t>* commandOffsets = new SkTDArray<size_t>;
395    if (!fCommandVector.isEmpty()) {
396        for (int i = 0; i < fCommandVector.count(); i ++) {
397            *commandOffsets->push() = fCommandVector[i]->offset();
398        }
399    }
400    return commandOffsets;
401}
402
403void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkPaint::FilterLevel level) {
404    if (NULL == fTexOverrideFilter) {
405        fTexOverrideFilter = new SkTexOverrideFilter;
406    }
407
408    fOverrideTexFiltering = overrideTexFiltering;
409    fTexOverrideFilter->setFilterLevel(level);
410}
411
412void SkDebugCanvas::clear(SkColor color) {
413    this->addDrawCommand(new SkClearCommand(color));
414}
415
416void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
417    this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
418}
419
420void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
421    this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
422}
423
424void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
425    this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
426}
427
428void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
429    this->addDrawCommand(new SkClipRegionCommand(region, op));
430}
431
432void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
433    switch (matrix.getType()) {
434        case SkMatrix::kTranslate_Mask:
435            this->addDrawCommand(new SkTranslateCommand(matrix.getTranslateX(),
436                                                        matrix.getTranslateY()));
437            break;
438        case SkMatrix::kScale_Mask:
439            this->addDrawCommand(new SkScaleCommand(matrix.getScaleX(),
440                                                    matrix.getScaleY()));
441            break;
442        default:
443            this->addDrawCommand(new SkConcatCommand(matrix));
444            break;
445    }
446
447    this->INHERITED::didConcat(matrix);
448}
449
450void SkDebugCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
451                               SkScalar top, const SkPaint* paint = NULL) {
452    this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
453}
454
455void SkDebugCanvas::drawBitmapRectToRect(const SkBitmap& bitmap,
456                                         const SkRect* src, const SkRect& dst,
457                                         const SkPaint* paint,
458                                         SkCanvas::DrawBitmapRectFlags flags) {
459    this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
460}
461
462void SkDebugCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
463                                     const SkMatrix& matrix, const SkPaint* paint) {
464    this->addDrawCommand(new SkDrawBitmapMatrixCommand(bitmap, matrix, paint));
465}
466
467void SkDebugCanvas::drawBitmapNine(const SkBitmap& bitmap,
468        const SkIRect& center, const SkRect& dst, const SkPaint* paint) {
469    this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
470}
471
472void SkDebugCanvas::drawData(const void* data, size_t length) {
473    this->addDrawCommand(new SkDrawDataCommand(data, length));
474}
475
476void SkDebugCanvas::beginCommentGroup(const char* description) {
477    this->addDrawCommand(new SkBeginCommentGroupCommand(description));
478}
479
480void SkDebugCanvas::addComment(const char* kywd, const char* value) {
481    this->addDrawCommand(new SkCommentCommand(kywd, value));
482}
483
484void SkDebugCanvas::endCommentGroup() {
485    this->addDrawCommand(new SkEndCommentGroupCommand());
486}
487
488void SkDebugCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
489    this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
490}
491
492void SkDebugCanvas::drawPaint(const SkPaint& paint) {
493    this->addDrawCommand(new SkDrawPaintCommand(paint));
494}
495
496void SkDebugCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
497    this->addDrawCommand(new SkDrawPathCommand(path, paint));
498}
499
500void SkDebugCanvas::drawPicture(SkPicture& picture) {
501    this->addDrawCommand(new SkDrawPictureCommand(picture));
502}
503
504void SkDebugCanvas::drawPoints(PointMode mode, size_t count,
505                               const SkPoint pts[], const SkPaint& paint) {
506    this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
507}
508
509void SkDebugCanvas::drawPosText(const void* text, size_t byteLength,
510        const SkPoint pos[], const SkPaint& paint) {
511    this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint));
512}
513
514void SkDebugCanvas::drawPosTextH(const void* text, size_t byteLength,
515        const SkScalar xpos[], SkScalar constY, const SkPaint& paint) {
516    this->addDrawCommand(
517        new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint));
518}
519
520void SkDebugCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
521    // NOTE(chudy): Messing up when renamed to DrawRect... Why?
522    addDrawCommand(new SkDrawRectCommand(rect, paint));
523}
524
525void SkDebugCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
526    this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
527}
528
529void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
530                                 const SkPaint& paint) {
531    this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
532}
533
534void SkDebugCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
535                               const SkPaint* paint = NULL) {
536    this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint));
537}
538
539void SkDebugCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
540        SkScalar y, const SkPaint& paint) {
541    this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint));
542}
543
544void SkDebugCanvas::drawTextOnPath(const void* text, size_t byteLength,
545        const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) {
546    this->addDrawCommand(
547        new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint));
548}
549
550void SkDebugCanvas::drawVertices(VertexMode vmode, int vertexCount,
551        const SkPoint vertices[], const SkPoint texs[], const SkColor colors[],
552        SkXfermode*, const uint16_t indices[], int indexCount,
553        const SkPaint& paint) {
554    this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices,
555                         texs, colors, NULL, indices, indexCount, paint));
556}
557
558void SkDebugCanvas::onPushCull(const SkRect& cullRect) {
559    this->addDrawCommand(new SkPushCullCommand(cullRect));
560}
561
562void SkDebugCanvas::onPopCull() {
563    this->addDrawCommand(new SkPopCullCommand());
564}
565
566void SkDebugCanvas::willRestore() {
567    this->addDrawCommand(new SkRestoreCommand());
568    this->INHERITED::willRestore();
569}
570
571void SkDebugCanvas::willSave(SaveFlags flags) {
572    this->addDrawCommand(new SkSaveCommand(flags));
573    this->INHERITED::willSave(flags);
574}
575
576SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint,
577                                                         SaveFlags flags) {
578    this->addDrawCommand(new SkSaveLayerCommand(bounds, paint, flags));
579    this->INHERITED::willSaveLayer(bounds, paint, flags);
580    // No need for a full layer.
581    return kNoLayer_SaveLayerStrategy;
582}
583
584void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
585    this->addDrawCommand(new SkSetMatrixCommand(matrix));
586    this->INHERITED::didSetMatrix(matrix);
587}
588
589void SkDebugCanvas::toggleCommand(int index, bool toggle) {
590    SkASSERT(index < fCommandVector.count());
591    fCommandVector[index]->setVisible(toggle);
592}
593