Home | History | Annotate | Download | only in jni
      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
     45 class MeasureStream : public SkWStream {
     46 public:
     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 
     56 namespace android {
     57 
     58 PictureSet::PictureSet()
     59 {
     60     mWidth = mHeight = 0;
     61 }
     62 
     63 PictureSet::~PictureSet()
     64 {
     65     clear();
     66 }
     67 
     68 void PictureSet::add(const Pictures* temp)
     69 {
     70     Pictures pictureAndBounds = *temp;
     71     pictureAndBounds.mPicture->safeRef();
     72     pictureAndBounds.mWroteElapsed = false;
     73     mPictures.append(pictureAndBounds);
     74 }
     75 
     76 void 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 /*
     95 Pictures are discarded when they are fully drawn over.
     96 When a picture is partially drawn over, it is discarded if it is not a base, and
     97 its rectangular bounds is reduced if it is a base.
     98 */
     99 bool 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 
    190 void 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 
    208 void 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 
    220 bool 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 
    308 void 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 
    341 class IsEmptyBounder : public SkBounder {
    342     virtual bool onIRect(const SkIRect& rect) {
    343         return false;
    344     }
    345 };
    346 
    347 class IsEmptyCanvas : public SkCanvas {
    348 public:
    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 
    429 bool 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 
    440 bool 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 
    450 bool 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 
    483 void 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 
    497 void 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 
    525 void 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 
    532 void 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 
    613 bool 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