1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/*
2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Copyright 2012 Google Inc.
3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *
4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Use of this source code is governed by a BSD-style license that can be
5ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * found in the LICENSE file.
6ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */
7ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
8ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "Benchmark.h"
9ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "SkCanvas.h"
10ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "SkPaint.h"
11ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "SkRandom.h"
12ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "SkShader.h"
13ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "SkString.h"
14ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
15ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This bench simulates the calls Skia sees from various HTML5 canvas
16ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// game bench marks
17ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovclass GameBench : public Benchmark {
18ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovpublic:
19ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    enum Type {
20ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        kScale_Type,
21ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        kTranslate_Type,
22ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        kRotate_Type
23ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    };
24ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
25ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    enum Clear {
26ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        kFull_Clear,
27ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        kPartial_Clear
28ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    };
29ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    GameBench(Type type, Clear clear,
31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov              bool aligned = false, bool useAtlas = false,
32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov              bool useDrawVertices = false)
33ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        : fType(type)
34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fClear(clear)
35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fAligned(aligned)
36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fUseAtlas(useAtlas)
37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fUseDrawVertices(useDrawVertices)
38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fName("game")
39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fNumSaved(0)
40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        , fInitialized(false) {
41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        switch (fType) {
43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        case kScale_Type:
44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_scale");
45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            break;
46ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        case kTranslate_Type:
47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_trans");
48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            break;
49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        case kRotate_Type:
50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_rot");
51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            break;
52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        };
53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (aligned) {
55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_aligned");
56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (kPartial_Clear == clear) {
59ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_partial");
60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        } else {
61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_full");
62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
64ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (useAtlas) {
65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_atlas");
66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (useDrawVertices) {
69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fName.append("_drawVerts");
70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
72ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // It's HTML 5 canvas, so always AA
73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        fName.append("_aa");
74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovprotected:
77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    virtual const char* onGetName() SK_OVERRIDE {
78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return fName.c_str();
79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    virtual void onPreDraw() SK_OVERRIDE {
82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (!fInitialized) {
83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            this->makeCheckerboard();
84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            this->makeAtlas();
85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fInitialized = true;
86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
89ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkRandom scaleRand;
91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkRandom transRand;
92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkRandom rotRand;
93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
94ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        int width, height;
95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (fUseAtlas) {
96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            width = kAtlasCellWidth;
97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            height = kAtlasCellHeight;
98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        } else {
99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            width = kCheckerboardWidth;
100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            height = kCheckerboardHeight;
101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkPaint clearPaint;
104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        clearPaint.setColor(0xFF000000);
105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        clearPaint.setAntiAlias(true);
106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkISize size = canvas->getDeviceSize();
108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkScalar maxTransX, maxTransY;
110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (kScale_Type == fType) {
112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            maxTransX = size.fWidth  - (1.5f * width);
113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            maxTransY = size.fHeight - (1.5f * height);
114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        } else if (kTranslate_Type == fType) {
115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            maxTransX = SkIntToScalar(size.fWidth  - width);
116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            maxTransY = SkIntToScalar(size.fHeight - height);
117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        } else {
118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            SkASSERT(kRotate_Type == fType);
119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            // Yes, some rotations will be off the top and left sides
120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            maxTransX = size.fWidth  - SK_ScalarSqrt2 * height;
121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkMatrix mat;
125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkPoint verts[4] = { // for drawVertices path
128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            { 0, 0 },
129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            { 0, SkIntToScalar(height) },
130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            { SkIntToScalar(width), SkIntToScalar(height) },
131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            { SkIntToScalar(width), 0 }
132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        };
133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkPaint p;
136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        p.setColor(0xFF000000);
137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        p.setFilterLevel(SkPaint::kLow_FilterLevel);
138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        SkPaint p2;         // for drawVertices path
140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        p2.setColor(0xFF000000);
141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        p2.setFilterLevel(SkPaint::kLow_FilterLevel);
142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        p2.setShader(SkShader::CreateBitmapShader(fAtlas,
143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  SkShader::kClamp_TileMode,
144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                  SkShader::kClamp_TileMode))->unref();
145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        for (int i = 0; i < loops; ++i, ++fNumSaved) {
147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (0 == i % kNumBeforeClear) {
148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                if (kPartial_Clear == fClear) {
149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                    for (int j = 0; j < fNumSaved; ++j) {
150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        canvas->setMatrix(SkMatrix::I());
151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        mat.setTranslate(fSaved[j][0], fSaved[j][1]);
152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        if (kScale_Type == fType) {
154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                            mat.preScale(fSaved[j][2], fSaved[j][2]);
155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        } else if (kRotate_Type == fType) {
156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                            mat.preRotate(fSaved[j][2]);
157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        }
158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        canvas->concat(mat);
160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                        canvas->drawRect(clearRect, clearPaint);
161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                    }
162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                } else {
163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                    canvas->clear(0xFF000000);
164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                }
165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                fNumSaved = 0;
167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            }
168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            SkASSERT(fNumSaved < kNumBeforeClear);
170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            canvas->setMatrix(SkMatrix::I());
172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (fAligned) {
176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                // make the translations integer aligned
177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            }
180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            if (kScale_Type == fType) {
184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            } else if (kRotate_Type == fType) {
187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
188                mat.preRotate(fSaved[fNumSaved][2]);
189            }
190
191            canvas->concat(mat);
192            if (fUseAtlas) {
193                const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
194                SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
195
196                if (fUseDrawVertices) {
197                    SkPoint uvs[4] = {
198                        { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fBottom) },
199                        { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fTop) },
200                        { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
201                        { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
202                    };
203                    canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
204                                         4, verts, uvs, NULL, NULL,
205                                         indices, 6, p2);
206                } else {
207                    canvas->drawBitmapRect(fAtlas, &src, dst, &p,
208                                           SkCanvas::kBleed_DrawBitmapRectFlag);
209                }
210            } else {
211                canvas->drawBitmapRect(fCheckerboard, NULL, dst, &p);
212            }
213        }
214    }
215
216private:
217    static const int kCheckerboardWidth = 64;
218    static const int kCheckerboardHeight = 128;
219
220    static const int kAtlasCellWidth = 48;
221    static const int kAtlasCellHeight = 36;
222    static const int kNumAtlasedX = 5;
223    static const int kNumAtlasedY = 5;
224    static const int kAtlasSpacer = 2;
225    static const int kTotAtlasWidth  = kNumAtlasedX * kAtlasCellWidth +
226                                       (kNumAtlasedX+1) * kAtlasSpacer;
227    static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
228                                       (kNumAtlasedY+1) * kAtlasSpacer;
229    static const int kNumBeforeClear = 100;
230
231    Type     fType;
232    Clear    fClear;
233    bool     fAligned;
234    bool     fUseAtlas;
235    bool     fUseDrawVertices;
236    SkString fName;
237    int      fNumSaved; // num draws stored in 'fSaved'
238    bool     fInitialized;
239
240    // 0 & 1 are always x & y translate. 2 is either scale or rotate.
241    SkScalar fSaved[kNumBeforeClear][3];
242
243    SkBitmap fCheckerboard;
244    SkBitmap fAtlas;
245    SkIRect  fAtlasRects[kNumAtlasedX][kNumAtlasedY];
246
247    // Note: the resulting checker board has transparency
248    void makeCheckerboard() {
249        static int kCheckSize = 16;
250
251        fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
252        SkAutoLockPixels lock(fCheckerboard);
253        for (int y = 0; y < kCheckerboardHeight; ++y) {
254            int even = (y / kCheckSize) % 2;
255
256            SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
257
258            for (int x = 0; x < kCheckerboardWidth; ++x) {
259                if (even == (x / kCheckSize) % 2) {
260                    *scanline++ = 0xFFFF0000;
261                } else {
262                    *scanline++ = 0x00000000;
263                }
264            }
265        }
266    }
267
268    // Note: the resulting atlas has transparency
269    void makeAtlas() {
270        SkRandom rand;
271
272        SkColor colors[kNumAtlasedX][kNumAtlasedY];
273
274        for (int y = 0; y < kNumAtlasedY; ++y) {
275            for (int x = 0; x < kNumAtlasedX; ++x) {
276                colors[x][y] = rand.nextU() | 0xff000000;
277                fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
278                                                      kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
279                                                      kAtlasCellWidth,
280                                                      kAtlasCellHeight);
281            }
282        }
283
284        fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
285        SkAutoLockPixels lock(fAtlas);
286
287        for (int y = 0; y < kTotAtlasHeight; ++y) {
288            int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
289            bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
290
291            SkPMColor* scanline = fAtlas.getAddr32(0, y);
292
293            for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
294                int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
295                bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
296
297                if (inColorX && inColorY) {
298                    SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
299                    *scanline = colors[colorX][colorY];
300                } else {
301                    *scanline = 0x00000000;
302                }
303            }
304        }
305    }
306
307    typedef Benchmark INHERITED;
308};
309
310// Partial clear
311DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type,
312                                            GameBench::kPartial_Clear)); )
313DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
314                                            GameBench::kPartial_Clear)); )
315DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
316                                            GameBench::kPartial_Clear, true)); )
317DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type,
318                                            GameBench::kPartial_Clear)); )
319
320// Full clear
321DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type,
322                                            GameBench::kFull_Clear)); )
323DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
324                                            GameBench::kFull_Clear)); )
325DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
326                                            GameBench::kFull_Clear, true)); )
327DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type,
328                                            GameBench::kFull_Clear)); )
329
330// Atlased
331DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
332                                            GameBench::kFull_Clear, false, true)); )
333DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
334                                            GameBench::kFull_Clear, false, true, true)); )
335