1/*
2 * Copyright 2008, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_NDEBUG 0
27#define LOG_TAG "pictureset"
28
29//#include <config.h>
30#include "CachedPrefix.h"
31#include "android_graphics.h"
32#include "PictureSet.h"
33#include "SkBounder.h"
34#include "SkCanvas.h"
35#include "SkPicture.h"
36#include "SkRect.h"
37#include "SkRegion.h"
38#include "SkStream.h"
39#include "TimeCounter.h"
40
41#define MAX_DRAW_TIME 100
42#define MIN_SPLITTABLE 400
43
44#if PICTURE_SET_DEBUG
45class MeasureStream : public SkWStream {
46public:
47    MeasureStream() : mTotal(0) {}
48    virtual bool write(const void* , size_t size) {
49        mTotal += size;
50        return true;
51    }
52    size_t mTotal;
53};
54#endif
55
56namespace android {
57
58PictureSet::PictureSet()
59{
60    mWidth = mHeight = 0;
61}
62
63PictureSet::~PictureSet()
64{
65    clear();
66}
67
68void PictureSet::add(const Pictures* temp)
69{
70    Pictures pictureAndBounds = *temp;
71    pictureAndBounds.mPicture->safeRef();
72    pictureAndBounds.mWroteElapsed = false;
73    mPictures.append(pictureAndBounds);
74}
75
76void PictureSet::add(const SkRegion& area, SkPicture* picture,
77    uint32_t elapsed, bool split, bool empty)
78{
79    DBG_SET_LOGD("%p area={%d,%d,r=%d,b=%d} pict=%p elapsed=%d split=%d", this,
80        area.getBounds().fLeft, area.getBounds().fTop,
81        area.getBounds().fRight, area.getBounds().fBottom, picture,
82        elapsed, split);
83    picture->safeRef();
84    /* if nothing is drawn beneath part of the new picture, mark it as a base */
85    SkRegion diff = SkRegion(area);
86    Pictures* last = mPictures.end();
87    for (Pictures* working = mPictures.begin(); working != last; working++)
88        diff.op(working->mArea, SkRegion::kDifference_Op);
89    Pictures pictureAndBounds = {area, picture, area.getBounds(),
90        elapsed, split, false, diff.isEmpty() == false, empty};
91    mPictures.append(pictureAndBounds);
92}
93
94/*
95Pictures are discarded when they are fully drawn over.
96When a picture is partially drawn over, it is discarded if it is not a base, and
97its rectangular bounds is reduced if it is a base.
98*/
99bool PictureSet::build()
100{
101    bool rebuild = false;
102    DBG_SET_LOGD("%p", this);
103    // walk pictures back to front, removing or trimming obscured ones
104    SkRegion drawn;
105    SkRegion inval;
106    Pictures* first = mPictures.begin();
107    Pictures* last = mPictures.end();
108    Pictures* working;
109    bool checkForNewBases = false;
110    for (working = last; working != first; ) {
111        --working;
112        SkRegion& area = working->mArea;
113        SkRegion visibleArea(area);
114        visibleArea.op(drawn, SkRegion::kDifference_Op);
115#if PICTURE_SET_DEBUG
116        const SkIRect& a = area.getBounds();
117        const SkIRect& d = drawn.getBounds();
118        const SkIRect& i = inval.getBounds();
119        const SkIRect& v = visibleArea.getBounds();
120        DBG_SET_LOGD("%p [%d] area={%d,%d,r=%d,b=%d} drawn={%d,%d,r=%d,b=%d}"
121            " inval={%d,%d,r=%d,b=%d} vis={%d,%d,r=%d,b=%d}",
122            this, working - first,
123            a.fLeft, a.fTop, a.fRight, a.fBottom,
124            d.fLeft, d.fTop, d.fRight, d.fBottom,
125            i.fLeft, i.fTop, i.fRight, i.fBottom,
126            v.fLeft, v.fTop, v.fRight, v.fBottom);
127#endif
128        bool tossPicture = false;
129        if (working->mBase == false) {
130            if (area != visibleArea) {
131                if (visibleArea.isEmpty() == false) {
132                    DBG_SET_LOGD("[%d] partially overdrawn", working - first);
133                    inval.op(visibleArea, SkRegion::kUnion_Op);
134                } else
135                    DBG_SET_LOGD("[%d] fully hidden", working - first);
136                area.setEmpty();
137                tossPicture = true;
138            }
139        } else {
140            const SkIRect& visibleBounds = visibleArea.getBounds();
141            const SkIRect& areaBounds = area.getBounds();
142            if (visibleBounds != areaBounds) {
143                DBG_SET_LOGD("[%d] base to be reduced", working - first);
144                area.setRect(visibleBounds);
145                checkForNewBases = tossPicture = true;
146            }
147            if (area.intersects(inval)) {
148                DBG_SET_LOGD("[%d] base to be redrawn", working - first);
149                tossPicture = true;
150            }
151        }
152        if (tossPicture) {
153            working->mPicture->safeUnref();
154            working->mPicture = NULL; // mark to redraw
155        }
156        if (working->mPicture == NULL) // may have been set to null elsewhere
157            rebuild = true;
158        drawn.op(area, SkRegion::kUnion_Op);
159    }
160    // collapse out empty regions
161    Pictures* writer = first;
162    for (working = first; working != last; working++) {
163        if (working->mArea.isEmpty())
164            continue;
165        *writer++ = *working;
166    }
167#if PICTURE_SET_DEBUG
168    if ((unsigned) (writer - first) != mPictures.size())
169        DBG_SET_LOGD("shrink=%d (was %d)", writer - first, mPictures.size());
170#endif
171    mPictures.shrink(writer - first);
172    /* When a base is discarded because it was entirely drawn over, all
173       remaining pictures are checked to see if one has become a base. */
174    if (checkForNewBases) {
175        drawn.setEmpty();
176        Pictures* last = mPictures.end();
177        for (working = mPictures.begin(); working != last; working++) {
178            SkRegion& area = working->mArea;
179            if (drawn.contains(working->mArea) == false) {
180                working->mBase = true;
181                DBG_SET_LOGD("[%d] new base", working - mPictures.begin());
182            }
183            drawn.op(working->mArea, SkRegion::kUnion_Op);
184        }
185    }
186    validate(__FUNCTION__);
187    return rebuild;
188}
189
190void PictureSet::checkDimensions(int width, int height, SkRegion* inval)
191{
192    if (mWidth == width && mHeight == height)
193        return;
194    DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
195        mWidth, mHeight, width, height);
196    if (mWidth == width && height > mHeight) { // only grew vertically
197        SkIRect rect;
198        rect.set(0, mHeight, width, height - mHeight);
199        inval->op(rect, SkRegion::kUnion_Op);
200    } else {
201        clear(); // if both width/height changed, clear the old cache
202        inval->setRect(0, 0, width, height);
203    }
204    mWidth = width;
205    mHeight = height;
206}
207
208void PictureSet::clear()
209{
210    DBG_SET_LOG("");
211    Pictures* last = mPictures.end();
212    for (Pictures* working = mPictures.begin(); working != last; working++) {
213        working->mArea.setEmpty();
214        working->mPicture->safeUnref();
215    }
216    mPictures.clear();
217    mWidth = mHeight = 0;
218}
219
220bool PictureSet::draw(SkCanvas* canvas)
221{
222    validate(__FUNCTION__);
223    Pictures* first = mPictures.begin();
224    Pictures* last = mPictures.end();
225    Pictures* working;
226    SkRect bounds;
227    if (canvas->getClipBounds(&bounds) == false)
228        return false;
229    SkIRect irect;
230    bounds.roundOut(&irect);
231    for (working = last; working != first; ) {
232        --working;
233        if (working->mArea.contains(irect)) {
234#if PICTURE_SET_DEBUG
235            const SkIRect& b = working->mArea.getBounds();
236            DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
237                " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
238                irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
239#endif
240            first = working;
241            break;
242        }
243    }
244    DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
245        last - mPictures.begin());
246    uint32_t maxElapsed = 0;
247    for (working = first; working != last; working++) {
248        const SkRegion& area = working->mArea;
249        if (area.quickReject(irect)) {
250#if PICTURE_SET_DEBUG
251            const SkIRect& b = area.getBounds();
252            DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
253                " irect={%d,%d,%d,%d}", working - first, working,
254                b.fLeft, b.fTop, b.fRight, b.fBottom,
255                irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
256#endif
257            working->mElapsed = 0;
258            continue;
259        }
260        int saved = canvas->save();
261        SkRect pathBounds;
262        if (area.isComplex()) {
263            SkPath pathClip;
264            area.getBoundaryPath(&pathClip);
265            canvas->clipPath(pathClip);
266            pathBounds = pathClip.getBounds();
267        } else {
268            pathBounds.set(area.getBounds());
269            canvas->clipRect(pathBounds);
270        }
271        canvas->translate(pathBounds.fLeft, pathBounds.fTop);
272        canvas->save();
273        uint32_t startTime = getThreadMsec();
274        canvas->drawPicture(*working->mPicture);
275        size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
276        working->mWroteElapsed = true;
277        if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
278                pathBounds.height() >= MIN_SPLITTABLE))
279            maxElapsed = elapsed;
280        canvas->restoreToCount(saved);
281#define DRAW_TEST_IMAGE 01
282#if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
283        SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
284        canvas->drawColor(color);
285        SkPaint paint;
286        color ^= 0x00ffffff;
287        paint.setColor(color);
288        char location[256];
289        for (int x = area.getBounds().fLeft & ~0x3f;
290                x < area.getBounds().fRight; x += 0x40) {
291            for (int y = area.getBounds().fTop & ~0x3f;
292                    y < area.getBounds().fBottom; y += 0x40) {
293                int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
294                canvas->drawText(location, len, x, y, paint);
295            }
296        }
297#endif
298        DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
299            working - first, working,
300            area.getBounds().fLeft, area.getBounds().fTop,
301            area.getBounds().fRight, area.getBounds().fBottom,
302            working->mElapsed, working->mBase ? "true" : "false");
303    }
304 //   dump(__FUNCTION__);
305    return maxElapsed >= MAX_DRAW_TIME;
306}
307
308void PictureSet::dump(const char* label) const
309{
310#if PICTURE_SET_DUMP
311    DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
312        mWidth, mHeight);
313    const Pictures* last = mPictures.end();
314    for (const Pictures* working = mPictures.begin(); working != last; working++) {
315        const SkIRect& bounds = working->mArea.getBounds();
316        const SkIRect& unsplit = working->mUnsplit;
317        MeasureStream measure;
318        if (working->mPicture != NULL)
319            working->mPicture->serialize(&measure);
320        LOGD(" [%d]"
321            " mArea.bounds={%d,%d,r=%d,b=%d}"
322            " mPicture=%p"
323            " mUnsplit={%d,%d,r=%d,b=%d}"
324            " mElapsed=%d"
325            " mSplit=%s"
326            " mWroteElapsed=%s"
327            " mBase=%s"
328            " pict-size=%d",
329            working - mPictures.begin(),
330            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
331            working->mPicture,
332            unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
333            working->mElapsed, working->mSplit ? "true" : "false",
334            working->mWroteElapsed ? "true" : "false",
335            working->mBase ? "true" : "false",
336            measure.mTotal);
337    }
338#endif
339}
340
341class IsEmptyBounder : public SkBounder {
342    virtual bool onIRect(const SkIRect& rect) {
343        return false;
344    }
345};
346
347class IsEmptyCanvas : public SkCanvas {
348public:
349    IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
350            mPicture(picture), mEmpty(true) {
351        setBounder(bounder);
352    }
353
354    void notEmpty() {
355        mEmpty = false;
356        mPicture->abortPlayback();
357    }
358
359    virtual bool clipPath(const SkPath&, SkRegion::Op) {
360        // this can be expensive to actually do, and doesn't affect the
361        // question of emptiness, so we make it a no-op
362        return true;
363    }
364
365    virtual void commonDrawBitmap(const SkBitmap& bitmap,
366            const SkMatrix& , const SkPaint& ) {
367        if (bitmap.width() <= 1 || bitmap.height() <= 1)
368            return;
369        DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
370        notEmpty();
371    }
372
373    virtual void drawPaint(const SkPaint& paint) {
374    }
375
376    virtual void drawPath(const SkPath& , const SkPaint& paint) {
377        DBG_SET_LOG("abort");
378        notEmpty();
379    }
380
381    virtual void drawPoints(PointMode , size_t , const SkPoint [],
382                            const SkPaint& paint) {
383    }
384
385    virtual void drawRect(const SkRect& , const SkPaint& paint) {
386        // wait for visual content
387    }
388
389    virtual void drawSprite(const SkBitmap& , int , int ,
390                            const SkPaint* paint = NULL) {
391        DBG_SET_LOG("abort");
392        notEmpty();
393    }
394
395    virtual void drawText(const void* , size_t byteLength, SkScalar ,
396                          SkScalar , const SkPaint& paint) {
397        DBG_SET_LOGD("abort %d", byteLength);
398        notEmpty();
399    }
400
401    virtual void drawPosText(const void* , size_t byteLength,
402                             const SkPoint [], const SkPaint& paint) {
403        DBG_SET_LOGD("abort %d", byteLength);
404        notEmpty();
405    }
406
407    virtual void drawPosTextH(const void* , size_t byteLength,
408                              const SkScalar [], SkScalar ,
409                              const SkPaint& paint) {
410        DBG_SET_LOGD("abort %d", byteLength);
411        notEmpty();
412    }
413
414    virtual void drawTextOnPath(const void* , size_t byteLength,
415                                const SkPath& , const SkMatrix* ,
416                                const SkPaint& paint) {
417        DBG_SET_LOGD("abort %d", byteLength);
418        notEmpty();
419    }
420
421    virtual void drawPicture(SkPicture& picture) {
422        SkCanvas::drawPicture(picture);
423    }
424
425    SkPicture* mPicture;
426    bool mEmpty;
427};
428
429bool PictureSet::emptyPicture(SkPicture* picture) const
430{
431    IsEmptyBounder isEmptyBounder;
432    IsEmptyCanvas checker(&isEmptyBounder, picture);
433    SkBitmap bitmap;
434    bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
435    checker.setBitmapDevice(bitmap);
436    checker.drawPicture(*picture);
437    return checker.mEmpty;
438}
439
440bool PictureSet::isEmpty() const
441{
442    const Pictures* last = mPictures.end();
443    for (const Pictures* working = mPictures.begin(); working != last; working++) {
444        if (!working->mEmpty)
445            return false;
446    }
447    return true;
448}
449
450bool PictureSet::reuseSubdivided(const SkRegion& inval)
451{
452    validate(__FUNCTION__);
453    if (inval.isComplex())
454        return false;
455    Pictures* working, * last = mPictures.end();
456    const SkIRect& invalBounds = inval.getBounds();
457    bool steal = false;
458    for (working = mPictures.begin(); working != last; working++) {
459        if (working->mSplit && invalBounds == working->mUnsplit) {
460            steal = true;
461            continue;
462        }
463        if (steal == false)
464            continue;
465        SkRegion temp = SkRegion(inval);
466        temp.op(working->mArea, SkRegion::kIntersect_Op);
467        if (temp.isEmpty() || temp == working->mArea)
468            continue;
469        return false;
470    }
471    if (steal == false)
472        return false;
473    for (working = mPictures.begin(); working != last; working++) {
474        if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
475                inval.contains(working->mArea) == false)
476            continue;
477        working->mPicture->safeUnref();
478        working->mPicture = NULL;
479    }
480    return true;
481}
482
483void PictureSet::set(const PictureSet& src)
484{
485    DBG_SET_LOGD("start %p src=%p", this, &src);
486    clear();
487    mWidth = src.mWidth;
488    mHeight = src.mHeight;
489    const Pictures* last = src.mPictures.end();
490    for (const Pictures* working = src.mPictures.begin(); working != last; working++)
491        add(working);
492 //   dump(__FUNCTION__);
493    validate(__FUNCTION__);
494    DBG_SET_LOG("end");
495}
496
497void PictureSet::setDrawTimes(const PictureSet& src)
498{
499    validate(__FUNCTION__);
500    if (mWidth != src.mWidth || mHeight != src.mHeight)
501        return;
502    Pictures* last = mPictures.end();
503    Pictures* working = mPictures.begin();
504    if (working == last)
505        return;
506    const Pictures* srcLast = src.mPictures.end();
507    const Pictures* srcWorking = src.mPictures.begin();
508    for (; srcWorking != srcLast; srcWorking++) {
509        if (srcWorking->mWroteElapsed == false)
510            continue;
511        while ((srcWorking->mArea != working->mArea ||
512                srcWorking->mPicture != working->mPicture)) {
513            if (++working == last)
514                return;
515        }
516        DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
517            this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
518            working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
519            working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
520            working->mElapsed, srcWorking->mElapsed);
521        working->mElapsed = srcWorking->mElapsed;
522    }
523}
524
525void PictureSet::setPicture(size_t i, SkPicture* p)
526{
527    mPictures[i].mPicture->safeUnref();
528    mPictures[i].mPicture = p;
529    mPictures[i].mEmpty = emptyPicture(p);
530}
531
532void PictureSet::split(PictureSet* out) const
533{
534    dump(__FUNCTION__);
535    DBG_SET_LOGD("%p", this);
536    SkIRect totalBounds;
537    out->mWidth = mWidth;
538    out->mHeight = mHeight;
539    totalBounds.set(0, 0, mWidth, mHeight);
540    SkRegion* total = new SkRegion(totalBounds);
541    const Pictures* last = mPictures.end();
542    const Pictures* working;
543    uint32_t balance = 0;
544    int multiUnsplitFastPictures = 0; // > 1 has more than 1
545    for (working = mPictures.begin(); working != last; working++) {
546        if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
547            continue;
548        if (++multiUnsplitFastPictures > 1)
549            break;
550    }
551    for (working = mPictures.begin(); working != last; working++) {
552        uint32_t elapsed = working->mElapsed;
553        if (elapsed < MAX_DRAW_TIME) {
554            bool split = working->mSplit;
555            DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
556                "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
557                total->getBounds().fLeft, total->getBounds().fTop,
558                total->getBounds().fRight, total->getBounds().fBottom,
559                split ? "true" : "false");
560            if (multiUnsplitFastPictures <= 1 || split) {
561                total->op(working->mArea, SkRegion::kDifference_Op);
562                out->add(working->mArea, working->mPicture, elapsed, split,
563                    working->mEmpty);
564            } else if (balance < elapsed)
565                balance = elapsed;
566            continue;
567        }
568        total->op(working->mArea, SkRegion::kDifference_Op);
569        const SkIRect& bounds = working->mArea.getBounds();
570        int width = bounds.width();
571        int height = bounds.height();
572        int across = 1;
573        int down = 1;
574        while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
575            if (height >= width) {
576                height >>= 1;
577                down <<= 1;
578            } else {
579                width >>= 1;
580                across <<= 1 ;
581            }
582            if ((elapsed >>= 1) < MAX_DRAW_TIME)
583                break;
584        }
585        width = bounds.width();
586        height = bounds.height();
587        int top = bounds.fTop;
588        for (int indexY = 0; indexY < down; ) {
589            int bottom = bounds.fTop + height * ++indexY / down;
590            int left = bounds.fLeft;
591            for (int indexX = 0; indexX < across; ) {
592                int right = bounds.fLeft + width * ++indexX / across;
593                SkIRect cBounds;
594                cBounds.set(left, top, right, bottom);
595                out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
596                    working->mPicture, elapsed, true,
597                    (across | down) != 1 ? false : working->mEmpty);
598                left = right;
599            }
600            top = bottom;
601        }
602    }
603    DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
604        this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
605        multiUnsplitFastPictures);
606    if (!total->isEmpty() && multiUnsplitFastPictures > 1)
607        out->add(*total, NULL, balance, false, false);
608    delete total;
609    validate(__FUNCTION__);
610    out->dump("split-out");
611}
612
613bool PictureSet::validate(const char* funct) const
614{
615    bool valid = true;
616#if PICTURE_SET_VALIDATE
617    SkRegion all;
618    const Pictures* first = mPictures.begin();
619    for (const Pictures* working = mPictures.end(); working != first; ) {
620        --working;
621        const SkPicture* pict = working->mPicture;
622        const SkRegion& area = working->mArea;
623        const SkIRect& bounds = area.getBounds();
624        bool localValid = false;
625        if (working->mUnsplit.isEmpty())
626            LOGD("%s working->mUnsplit.isEmpty()", funct);
627        else if (working->mUnsplit.contains(bounds) == false)
628            LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
629        else if (working->mElapsed >= 1000)
630            LOGD("%s working->mElapsed >= 1000", funct);
631        else if ((working->mSplit & 0xfe) != 0)
632            LOGD("%s (working->mSplit & 0xfe) != 0", funct);
633        else if ((working->mWroteElapsed & 0xfe) != 0)
634            LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
635        else if (pict != NULL) {
636            int pictWidth = pict->width();
637            int pictHeight = pict->height();
638            if (pictWidth < bounds.width())
639                LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
640            else if (pictHeight < bounds.height())
641                LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
642            else if (working->mArea.isEmpty())
643                LOGD("%s working->mArea.isEmpty()", funct);
644            else
645                localValid = true;
646        } else
647            localValid = true;
648        working->mArea.validate();
649        if (localValid == false) {
650            if (all.contains(area) == true)
651                LOGD("%s all.contains(area) == true", funct);
652            else
653                localValid = true;
654        }
655        valid &= localValid;
656        all.op(area, SkRegion::kUnion_Op);
657    }
658    const SkIRect& allBounds = all.getBounds();
659    if (valid) {
660        valid = false;
661        if (allBounds.width() != mWidth)
662            LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
663        else if (allBounds.height() != mHeight)
664            LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
665        else
666            valid = true;
667    }
668    while (valid == false)
669        ;
670#endif
671    return valid;
672}
673
674} /* namespace android */
675