1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrRecordReplaceDraw.h"
9#include "SkImage.h"
10#include "SkRecordDraw.h"
11
12GrReplacements::ReplacementInfo* GrReplacements::push() {
13    SkDEBUGCODE(this->validate());
14    return fReplacements.push();
15}
16
17void GrReplacements::freeAll() {
18    for (int i = 0; i < fReplacements.count(); ++i) {
19        fReplacements[i].fImage->unref();
20        SkDELETE(fReplacements[i].fPaint);
21    }
22    fReplacements.reset();
23}
24
25#ifdef SK_DEBUG
26void GrReplacements::validate() const {
27    // Check that the ranges are monotonically increasing and non-overlapping
28    if (fReplacements.count() > 0) {
29        SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop);
30
31        for (int i = 1; i < fReplacements.count(); ++i) {
32            SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop);
33            SkASSERT(fReplacements[i - 1].fStop < fReplacements[i].fStart);
34        }
35    }
36}
37#endif
38
39const GrReplacements::ReplacementInfo*
40GrReplacements::lookupByStart(size_t start, int* searchStart) const {
41    SkDEBUGCODE(this->validate());
42    for (int i = *searchStart; i < fReplacements.count(); ++i) {
43        if (start == fReplacements[i].fStart) {
44            *searchStart = i + 1;
45            return &fReplacements[i];
46        } else if (start < fReplacements[i].fStart) {
47            return NULL;  // the ranges are monotonically increasing and non-overlapping
48        }
49    }
50
51    return NULL;
52}
53
54static inline void draw_replacement_bitmap(const GrReplacements::ReplacementInfo* ri,
55                                           SkCanvas* canvas,
56                                           const SkMatrix& initialMatrix) {
57    SkRect src = SkRect::Make(ri->fSrcRect);
58    SkRect dst = SkRect::MakeXYWH(SkIntToScalar(ri->fPos.fX),
59                                  SkIntToScalar(ri->fPos.fY),
60                                  SkIntToScalar(ri->fSrcRect.width()),
61                                  SkIntToScalar(ri->fSrcRect.height()));
62
63    canvas->save();
64    canvas->setMatrix(initialMatrix);
65    canvas->drawImageRect(ri->fImage, &src, dst, ri->fPaint);
66    canvas->restore();
67}
68
69void GrRecordReplaceDraw(const SkRecord& record,
70                         SkCanvas* canvas,
71                         const SkBBoxHierarchy* bbh,
72                         const GrReplacements* replacements,
73                         SkDrawPictureCallback* callback) {
74    SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
75
76    SkRecords::Draw draw(canvas);
77    const GrReplacements::ReplacementInfo* ri = NULL;
78    int searchStart = 0;
79
80    const SkMatrix initialMatrix = canvas->getTotalMatrix();
81
82    if (bbh) {
83        // Draw only ops that affect pixels in the canvas's current clip.
84        // The SkRecord and BBH were recorded in identity space.  This canvas
85        // is not necessarily in that same space.  getClipBounds() returns us
86        // this canvas' clip bounds transformed back into identity space, which
87        // lets us query the BBH.
88        SkRect query = { 0, 0, 0, 0 };
89        (void)canvas->getClipBounds(&query);
90
91        SkTDArray<void*> ops;
92        bbh->search(query, &ops);
93
94        for (int i = 0; i < ops.count(); i++) {
95            if (callback && callback->abortDrawing()) {
96                return;
97            }
98            ri = replacements->lookupByStart((uintptr_t)ops[i], &searchStart);
99            if (ri) {
100                draw_replacement_bitmap(ri, canvas, initialMatrix);
101
102                while ((uintptr_t)ops[i] < ri->fStop) {
103                    ++i;
104                }
105                SkASSERT((uintptr_t)ops[i] == ri->fStop);
106                continue;
107            }
108
109            record.visit<void>((uintptr_t)ops[i], draw);
110        }
111    } else {
112        for (unsigned int i = 0; i < record.count(); ++i) {
113            if (callback && callback->abortDrawing()) {
114                return;
115            }
116            ri = replacements->lookupByStart(i, &searchStart);
117            if (ri) {
118                draw_replacement_bitmap(ri, canvas, initialMatrix);
119                i = ri->fStop;
120                continue;
121            }
122
123            record.visit<void>(i, draw);
124        }
125    }
126}
127