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 #define MAX_ADDITIONAL_AREA 0.65
     44 #define MAX_ADDITIONAL_PICTURES 32
     45 
     46 #define BUCKET_SIZE 1024
     47 #define MAX_BUCKET_COUNT_X 16
     48 #define MAX_BUCKET_COUNT_Y 64
     49 
     50 #include <wtf/CurrentTime.h>
     51 
     52 #include <cutils/log.h>
     53 #include <wtf/text/CString.h>
     54 
     55 #undef XLOGC
     56 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
     57 
     58 #ifdef DEBUG
     59 
     60 #undef XLOG
     61 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
     62 
     63 #else
     64 
     65 #undef XLOG
     66 #define XLOG(...)
     67 
     68 #endif // DEBUG
     69 
     70 #if PICTURE_SET_DEBUG
     71 class MeasureStream : public SkWStream {
     72 public:
     73     MeasureStream() : mTotal(0) {}
     74     virtual bool write(const void* , size_t size) {
     75         mTotal += size;
     76         return true;
     77     }
     78     size_t mTotal;
     79 };
     80 #endif
     81 
     82 namespace android {
     83 
     84 PictureSet::PictureSet() :
     85 #ifdef FAST_PICTURESET
     86     mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE),
     87     mBucketCountX(0), mBucketCountY(0),
     88 #endif
     89     mHeight(0), mWidth(0)
     90 {
     91     setDimensions(0, 0);
     92     mBaseArea = mAdditionalArea = 0;
     93 }
     94 
     95 PictureSet::PictureSet(SkPicture* picture) :
     96 #ifdef FAST_PICTURESET
     97     mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE),
     98     mBucketCountX(0), mBucketCountY(0),
     99 #endif
    100     mHeight(0), mWidth(0)
    101 {
    102     mBaseArea = mAdditionalArea = 0;
    103     if (!picture) {
    104         setDimensions(0, 0);
    105         return;
    106     }
    107     setDimensions(picture->width(), picture->height());
    108     mBaseArea = mWidth * mHeight;
    109 #ifdef FAST_PICTURESET
    110     SkIRect area;
    111     area.set(0, 0, mWidth, mHeight);
    112     splitAdd(area);
    113     WTF::Vector<Bucket*>* buckets = bucketsToUpdate();
    114     for (unsigned int i = 0; i < buckets->size(); i++) {
    115         Bucket* bucket = (*buckets)[i];
    116         for (unsigned int j = 0; j < bucket->size(); j++) {
    117             BucketPicture& bucketPicture = (*bucket)[j];
    118             const SkIRect& inval = bucketPicture.mRealArea;
    119             SkPicture *splitPicture = new SkPicture();
    120             SkCanvas *canvas = splitPicture->beginRecording(
    121                     inval.width(), inval.height(),
    122                     SkPicture::kUsePathBoundsForClip_RecordingFlag);
    123             canvas->translate(-inval.fLeft, -inval.fTop);
    124             picture->draw(canvas);
    125             splitPicture->endRecording();
    126             SkSafeUnref(bucketPicture.mPicture);
    127             bucketPicture.mPicture = splitPicture;
    128         }
    129     }
    130     buckets->clear();
    131 #else
    132     Pictures pictureAndBounds;
    133     pictureAndBounds.mPicture = picture;
    134     SkSafeRef(pictureAndBounds.mPicture);
    135     pictureAndBounds.mEmpty = false;
    136     pictureAndBounds.mArea.setRect(0, 0, mWidth, mHeight);
    137     pictureAndBounds.mSplit = false;
    138     pictureAndBounds.mBase = true;
    139     pictureAndBounds.mElapsed = 0;
    140     pictureAndBounds.mWroteElapsed = false;
    141     mPictures.append(pictureAndBounds);
    142 #endif // FAST_PICTURESET
    143 }
    144 
    145 PictureSet::~PictureSet()
    146 {
    147     clear();
    148 }
    149 
    150 #ifdef FAST_PICTURESET
    151 #else
    152 void PictureSet::add(const Pictures* temp)
    153 {
    154     Pictures pictureAndBounds = *temp;
    155     SkSafeRef(pictureAndBounds.mPicture);
    156     pictureAndBounds.mWroteElapsed = false;
    157     mPictures.append(pictureAndBounds);
    158 }
    159 #endif // FAST_PICTURESET
    160 
    161 void PictureSet::add(const SkRegion& area, SkPicture* picture,
    162                      uint32_t elapsed, bool split)
    163 {
    164     if (area.isRect()) {
    165 #ifdef FAST_PICTURESET
    166         splitAdd(area.getBounds());
    167 #else
    168         add(area, picture, elapsed, split, false);
    169 #endif // FAST_PICTURESET
    170     } else {
    171         SkRegion::Iterator cliperator(area);
    172         while (!cliperator.done()) {
    173             SkIRect ir = cliperator.rect();
    174 #ifdef FAST_PICTURESET
    175             splitAdd(ir);
    176 #else
    177             SkRegion newArea;
    178             newArea.setRect(ir);
    179             add(newArea, picture, elapsed, split, false);
    180 #endif // FAST_PICTURESET
    181             cliperator.next();
    182         }
    183     }
    184 }
    185 
    186 #ifdef FAST_PICTURESET
    187 
    188 Bucket* PictureSet::getBucket(int x, int y)
    189 {
    190     // only create buckets for valid, positive coordinates, ignore and return
    191     // NULL otherwise
    192     if (x < 0 || y < 0)
    193         return 0;
    194 
    195     BucketPosition position(x+1, y+1);
    196     if (!mBuckets.contains(position)) {
    197         XLOG("PictureSet::getBucket(%d, %d) adding new bucket", x, y);
    198         Bucket* bucket = new Bucket();
    199         mBuckets.add(position, bucket);
    200     }
    201     return mBuckets.get(position);
    202 }
    203 
    204 void PictureSet::displayBucket(Bucket* bucket)
    205 {
    206     BucketPicture* first = bucket->begin();
    207     BucketPicture* last = bucket->end();
    208     for (BucketPicture* current = first; current != last; current++) {
    209         XLOGC("- in %x, bucketPicture %d,%d,%d,%d - %dx%d, picture: %x, base: %x",
    210               bucket,
    211               current->mArea.fLeft,
    212               current->mArea.fTop,
    213               current->mArea.fRight,
    214               current->mArea.fBottom,
    215               current->mArea.width(),
    216               current->mArea.height(),
    217               current->mPicture,
    218               current->mBase);
    219     }
    220 }
    221 
    222 void PictureSet::displayBuckets()
    223 {
    224     XLOGC("\n\n****** DISPLAY BUCKETS ON PictureSet %x ******", this);
    225     for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
    226         XLOGC("\n*** Bucket %x for %d, %d", iter->second, iter->first.first, iter->first.second);
    227         displayBucket(iter->second);
    228     }
    229     XLOGC("\n****** END OF DISPLAY BUCKETS ******\n\n");
    230 }
    231 
    232 // When we receive an inval in a Bucket, we try to see if we intersect with
    233 // existing invals/pictures in the Bucket.
    234 void PictureSet::addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect)
    235 {
    236     bool resetBase = false;
    237 
    238     SkIRect totalArea = rect;
    239     BucketPicture* first = bucket->begin();
    240     BucketPicture* last = bucket->end();
    241 
    242     // If the inval covers a large area of the base inval, let's repaint the
    243     // entire bucket.
    244     if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * mBucketSizeX * mBucketSizeY)
    245         resetBase = true;
    246 
    247     // let's gather all the BucketPicture intersecting with the new invalidated
    248     // area, collect their area and remove their picture
    249     for (BucketPicture* current = first; current != last; current++) {
    250         bool remove = resetBase;
    251         bool intersect = false;
    252 
    253         if (!remove)
    254             intersect = SkIRect::Intersects(current->mArea, rect);
    255         // If the current picture is not a base, and we intersect, remove it
    256         if (!remove && !current->mBase && intersect)
    257             remove = true;
    258         // If the current picture is a base, check if the new inval completely
    259         // contains the base, and if so remove it.
    260         if (!remove && current->mBase && rect.contains(current->mArea))
    261             remove = true;
    262         // If the current picture is a base and it intersects,
    263         // also check that it fully covers the bucket -- otherwise,
    264         // let's aggregate it with the new inval.
    265         if (!remove && current->mBase && intersect
    266             && (current->mArea.width() < mBucketSizeX || current->mArea.height() < mBucketSizeY)) {
    267             remove = true;
    268         }
    269 
    270         if (remove) {
    271             totalArea.join(current->mArea);
    272             current->mBase = false;
    273             current->mArea.setEmpty();
    274             SkSafeUnref(current->mPicture);
    275             current->mPicture = 0;
    276         }
    277     }
    278 
    279     // Now, let's add the new BucketPicture to the list, with the correct
    280     // area that needs to be repainted
    281     SkRegion region;
    282     SkIRect area = totalArea;
    283     area.offset(dx, dy);
    284     BucketPicture picture = { 0, totalArea, area, false };
    285 
    286     bucket->append(picture);
    287 
    288     first = bucket->begin();
    289     last = bucket->end();
    290 
    291     bool clearUp = false;
    292     if (last - first > MAX_ADDITIONAL_PICTURES) {
    293         // too many pictures in the bucket, let's collapse
    294         clearUp = true;
    295     }
    296 
    297     float bucketBaseArea = 0;
    298     float bucketAdditionalArea = 0;
    299     for (BucketPicture* current = first; current != last; current++) {
    300         float area = current->mArea.width() * current->mArea.height();
    301         if (current->mBase)
    302             bucketBaseArea += area;
    303         else
    304             bucketAdditionalArea += area;
    305     }
    306 
    307     if (bucketBaseArea > 0 && bucketBaseArea * MAX_ADDITIONAL_AREA <= bucketAdditionalArea) {
    308         // additional area too large, not worth maintaining
    309         clearUp = true;
    310     }
    311 
    312     // To clear things up, we just need to mark the pictures' area as empty
    313     // We only keep the base surface.
    314     if (clearUp) {
    315         for (BucketPicture* current = first; current != last; current++) {
    316             if (!current->mBase)
    317                 current->mArea.setEmpty();
    318             SkSafeUnref(current->mPicture);
    319             current->mPicture = 0;
    320         }
    321     }
    322 
    323     // let's do a pass to collapse out empty areas
    324     BucketPicture* writer = first;
    325     for (BucketPicture* current = first; current != last; current++) {
    326         if (current && current->mArea.isEmpty())
    327             continue;
    328         *writer++ = *current;
    329     }
    330 
    331     bucket->shrink(writer - first);
    332 
    333     // let's recompute the bases
    334     first = bucket->begin();
    335     last = bucket->end();
    336     SkRegion drawn;
    337     drawn.setEmpty();
    338     for (BucketPicture* current = first; current != last; current++) {
    339         if (drawn.contains(current->mArea) == false) {
    340             current->mBase = true;
    341         }
    342         drawn.op(current->mArea, SkRegion::kUnion_Op);
    343     }
    344 }
    345 
    346 void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect)
    347 {
    348     XLOG("\n--- gatherBucketsForArea for rect %d, %d, %d, %d (%d x %d)",
    349           rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
    350           rect.width(), rect.height());
    351 
    352     if (!mBucketSizeX || !mBucketSizeY) {
    353         XLOGC("PictureSet::gatherBucketsForArea() called with bad bucket size: x=%d y=%d",
    354               mBucketSizeX, mBucketSizeY);
    355         return;
    356     }
    357 
    358     int x = rect.fLeft;
    359     int y = rect.fTop;
    360     int firstTileX = rect.fLeft / mBucketSizeX;
    361     int firstTileY = rect.fTop / mBucketSizeY;
    362     int lastTileX = rect.fRight / mBucketSizeX;
    363     int lastTileY = rect.fBottom / mBucketSizeY;
    364 
    365     for (int i = firstTileX; i <= lastTileX; i++) {
    366         for (int j = firstTileY; j <= lastTileY; j++) {
    367             Bucket* bucket = getBucket(i, j);
    368             XLOG("gather bucket %x for %d, %d", bucket, i+1, j+1);
    369             if (bucket)
    370                 list.append(bucket);
    371         }
    372     }
    373 }
    374 
    375 // When we receive a new inval rect, we first find the Buckets that intersect
    376 // with it; then we split the original inval into a serie of invals (one for
    377 // each Bucket we intersect with). We then send that inval to the Bucket.
    378 void PictureSet::splitAdd(const SkIRect& rect)
    379 {
    380     XLOG("\n--- splitAdd for rect %d, %d, %d, %d (%d x %d)",
    381           rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
    382           rect.width(), rect.height());
    383 
    384     if (!mBucketSizeX || !mBucketSizeY) {
    385         XLOGC("PictureSet::splitAdd() called with bad bucket size: x=%d y=%d",
    386               mBucketSizeX, mBucketSizeY);
    387         return;
    388     }
    389 
    390     // TODO: reuse gatherBucketsForArea() (change Bucket to be a class)
    391     int x = rect.fLeft;
    392     int y = rect.fTop;
    393     int firstTileX = rect.fLeft / mBucketSizeX;
    394     int firstTileY = rect.fTop / mBucketSizeY;
    395     int lastTileX = rect.fRight / mBucketSizeX;
    396     int lastTileY = rect.fBottom / mBucketSizeY;
    397 
    398     XLOG("--- firstTile(%d, %d) lastTile(%d, %d)",
    399           firstTileX, firstTileY,
    400           lastTileX, lastTileY);
    401 
    402     for (int i = firstTileX; i <= lastTileX; i++) {
    403         for (int j = firstTileY; j <= lastTileY; j++) {
    404             Bucket* bucket = getBucket(i, j);
    405             if (!bucket)
    406                 continue;
    407 
    408             SkIRect newRect;
    409             int deltaX = i * mBucketSizeX;
    410             int deltaY = j * mBucketSizeY;
    411             int left = (i == firstTileX) ? rect.fLeft - deltaX : 0;
    412             int top = (j == firstTileY) ? rect.fTop - deltaY : 0;
    413             int right = (i == lastTileX) ? rect.fRight % mBucketSizeX : mBucketSizeX;
    414             int bottom = (j == lastTileY) ? rect.fBottom % mBucketSizeY : mBucketSizeY;
    415 
    416             newRect.set(left, top, right, bottom);
    417             addToBucket(bucket, deltaX, deltaY, newRect);
    418             mUpdatedBuckets.append(bucket);
    419         }
    420     }
    421 
    422     XLOG("--- splitAdd DONE\n");
    423 }
    424 
    425 #endif // FAST_PICTURESET
    426 
    427 // This function is used to maintain the list of Pictures.
    428 // Pictures contain an SkPicture covering a specific area; some
    429 // Pictures are "base" Pictures -- i.e. there is no Pictures
    430 // underneath them.
    431 // The idea here is to keep a balance between the number of Pictures
    432 // we have (more Pictures slow us down) and the area of Pictures that
    433 // need to be repainted (obviously, smaller areas are better).
    434 // To do so, we try to not update/repaint the base pictures -- by
    435 // construction, they usually cover a large area (the entire page).
    436 // We only reset a base picture if the new invalidated area entirely
    437 // contains it.
    438 // Most of the time we thus work on smaller pictures on top of the
    439 // base ones; We compute the total area of all pictures intersecting
    440 // with the passed invalidated area (as they would need to be invalidated),
    441 // and use that as the basis for the correct area we want to invalidate
    442 // (we then can simply delete the pictures we intersect with).
    443 // In addition, we do a couple of things to limit the total number of pictures
    444 // we keep in the list:
    445 // - if the total area of additional textures reach 65% of the base pictures,
    446 //   we delete the additional pictures and mark the base pictures as
    447 //   needing a full repaint
    448 // - we limit the number of pictures to 32 -- above that, we do the same
    449 //   things (deleting additional pictures + full repaint of base pictures)
    450 #ifdef FAST_PICTURESET
    451 #else
    452 void PictureSet::add(const SkRegion& area, SkPicture* picture,
    453     uint32_t elapsed, bool split, bool empty)
    454 {
    455     bool checkForNewBases = false;
    456 
    457     Pictures* first = mPictures.begin();
    458     Pictures* last = mPictures.end();
    459 #ifdef DEBUG
    460     XLOG("--- before adding the new inval ---");
    461     for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
    462         SkIRect currentArea = working->mArea.getBounds();
    463         XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
    464              working - first,
    465              currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
    466              currentArea.width(), currentArea.height(),
    467              working->mArea.isRect() ? 'Y' : 'N',
    468              working->mBase ? 'Y' : 'N');
    469     }
    470     XLOG("----------------------------------");
    471 #endif
    472 
    473     // let's gather all the Pictures intersecting with the new invalidated
    474     // area, collect their area and remove their picture
    475     SkIRect totalArea = area.getBounds();
    476     for (Pictures* working = first; working != last; working++) {
    477         SkIRect inval = area.getBounds();
    478         bool remove = false;
    479         if (!working->mBase && working->mArea.intersects(inval))
    480             remove = true;
    481         if (working->mBase) {
    482             SkIRect baseArea = working->mArea.getBounds();
    483             if (area.contains(baseArea)) {
    484                 remove = true;
    485                 checkForNewBases = true;
    486             }
    487         }
    488 
    489         if (remove) {
    490             SkIRect currentArea = working->mArea.getBounds();
    491             if (working->mBase)
    492                 mBaseArea -= currentArea.width() * currentArea.height();
    493             else
    494                 mAdditionalArea -= currentArea.width() * currentArea.height();
    495 
    496             totalArea.join(currentArea);
    497             XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) intersects with the new inval area (%d, %d, %d, %d - %d x %d) (isRect? %c, we remove it",
    498                  working - first,
    499                  currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
    500                  currentArea.width(), currentArea.height(),
    501                  working->mArea.isRect() ? 'Y' : 'N',
    502                  inval.fLeft, inval.fTop, inval.fRight, inval.fBottom,
    503                  inval.width(), inval.height(),
    504                  area.isRect() ? 'Y' : 'N');
    505             working->mArea.setEmpty();
    506             SkSafeUnref(working->mPicture);
    507             working->mPicture = 0;
    508         }
    509     }
    510 
    511     // Now we can add the new Picture to the list, with the correct area
    512     // that need to be repainted
    513     SkRegion collect;
    514     collect.setRect(totalArea);
    515     Pictures pictureAndBounds = {collect, 0, collect.getBounds(),
    516         elapsed, split, false, false, empty};
    517 
    518 #ifdef FAST_PICTURESET
    519     if (mPictures.size() == 0)
    520         checkForNewBases = true;
    521 #endif
    522 
    523     mPictures.append(pictureAndBounds);
    524     mAdditionalArea += totalArea.width() * totalArea.height();
    525     last = mPictures.end();
    526     first = mPictures.begin();
    527 
    528     // Then, let's see if we have to clear up the pictures in order to keep
    529     // the total number of pictures under our limit
    530     bool clearUp = false;
    531     if (last - first > MAX_ADDITIONAL_PICTURES) {
    532         XLOG("--- too many pictures, only keeping the bases : %d", last - first);
    533         clearUp = true;
    534     }
    535 
    536     if (!clearUp) {
    537         if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) {
    538             XLOG("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f",
    539                  MAX_ADDITIONAL_AREA * 100, mBaseArea * 0.65, mBaseArea, mAdditionalArea);
    540             clearUp = true;
    541         }
    542     }
    543 
    544     if (clearUp) {
    545         for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
    546             if (!working->mBase)
    547                 working->mArea.setEmpty();
    548             SkSafeUnref(working->mPicture);
    549             working->mPicture = 0;
    550         }
    551     }
    552 
    553 #ifdef DEBUG
    554     XLOG("--- after adding the new inval, but before collapsing ---");
    555     for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
    556         SkIRect currentArea = working->mArea.getBounds();
    557         XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
    558              working - first,
    559              currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
    560              currentArea.width(), currentArea.height(),
    561              working->mArea.isRect() ? 'Y' : 'N',
    562              working->mBase ? 'Y' : 'N');
    563     }
    564     XLOG("----------------------------------");
    565     XLOG("let's collapse...");
    566 #endif
    567 
    568     // Finally, let's do a pass to collapse out empty regions
    569     Pictures* writer = first;
    570     for (Pictures* working = first; working != last; working++) {
    571         if (working && working->mArea.isEmpty())
    572             continue;
    573         *writer++ = *working;
    574     }
    575     XLOG("shiking of %d elements", writer - first);
    576     mPictures.shrink(writer - first);
    577 
    578 #ifdef DEBUG
    579     XLOG("--- after adding the new inval ---");
    580     for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
    581         SkIRect currentArea = working->mArea.getBounds();
    582         XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c picture %x",
    583              working - first,
    584              currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
    585              currentArea.width(), currentArea.height(),
    586              working->mArea.isRect() ? 'Y' : 'N',
    587              working->mBase ? 'Y' : 'N', working->mPicture);
    588     }
    589     XLOG("----------------------------------");
    590 #endif
    591 
    592     // Base pictures might have been removed/added -- let's recompute them
    593     SkRegion drawn;
    594     if (checkForNewBases) {
    595         drawn.setEmpty();
    596         Pictures* last = mPictures.end();
    597         XLOG("checkForNewBases...");
    598         for (Pictures* working = mPictures.begin(); working != last; working++) {
    599             SkRegion& area = working->mArea;
    600             const SkIRect& a = area.getBounds();
    601             if (drawn.contains(working->mArea) == false) {
    602                 working->mBase = true;
    603                 float area = a.width() * a.height();
    604                 mBaseArea += area;
    605                 mAdditionalArea -= area;
    606             }
    607             drawn.op(working->mArea, SkRegion::kUnion_Op);
    608         }
    609     }
    610 }
    611 #endif // FAST_PICTURESET
    612 
    613 void PictureSet::setDimensions(int width, int height, SkRegion* inval)
    614 {
    615     // Note that setDimensions() may be called by our ctor and should behave accordingly
    616     if (mWidth == width && mHeight == height)
    617         return;
    618     DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
    619         mWidth, mHeight, width, height);
    620     bool clearCache = false;
    621     if (inval) {
    622         if (mWidth == width && height > mHeight) { // only grew vertically
    623             SkIRect rect;
    624             rect.set(0, mHeight, width, height);
    625             inval->op(rect, SkRegion::kUnion_Op);
    626         } else {
    627             clearCache = true;
    628             inval->setRect(0, 0, width, height);
    629         }
    630     }
    631 #ifdef FAST_PICTURESET
    632     // First figure out how large each bucket would be if we used all of the buckets
    633     int tmpSizeX = (width + MAX_BUCKET_COUNT_X - 1) / MAX_BUCKET_COUNT_X;
    634     int tmpSizeY = (height + MAX_BUCKET_COUNT_Y - 1) / MAX_BUCKET_COUNT_Y;
    635 
    636     // Then round the bucket size up to the nearest chunk
    637     int bucketSizeX = ((tmpSizeX - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE;
    638     int bucketSizeY = ((tmpSizeY - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE;
    639 
    640     int bucketCountX = (width + bucketSizeX - 1) / bucketSizeX;
    641     int bucketCountY = (height + bucketSizeY - 1) / bucketSizeY;
    642 
    643     // Clear the cache if the horizontal bucket count changed or the vertical
    644     // count shrank
    645     if (bucketCountX != mBucketCountX || bucketCountY < mBucketCountY)
    646         clearCache = true;
    647 
    648     // Or if the bucket size changed
    649     if (bucketSizeX != mBucketSizeX || bucketSizeY != mBucketSizeY)
    650         clearCache = true;
    651 
    652     XLOG("old width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d",
    653          mWidth, mHeight, mBucketSizeX, mBucketSizeY, mBucketCountX, mBucketCountY, clearCache);
    654     XLOG("new width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d",
    655          width, height, bucketSizeX, bucketSizeY, bucketCountX, bucketCountY, clearCache);
    656 #endif
    657     if (clearCache)
    658         clear();
    659     mWidth = width;
    660     mHeight = height;
    661 #ifdef FAST_PICTURESET
    662     mBucketSizeX = bucketSizeX;
    663     mBucketSizeY = bucketSizeY;
    664     mBucketCountX = bucketCountX;
    665     mBucketCountY = bucketCountY;
    666 #endif
    667 }
    668 
    669 void PictureSet::clear()
    670 {
    671     DBG_SET_LOG("");
    672 #ifdef FAST_PICTURESET
    673     for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
    674          Bucket* bucket = iter->second;
    675          BucketPicture* first = bucket->begin();
    676          BucketPicture* last = bucket->end();
    677          for (BucketPicture* current = first; current != last; current++) {
    678              SkSafeUnref(current->mPicture);
    679              current->mPicture = 0;
    680          }
    681          bucket->clear();
    682     }
    683     mBuckets.clear();
    684     mBucketSizeX = mBucketSizeY = BUCKET_SIZE;
    685 #else
    686     Pictures* last = mPictures.end();
    687     for (Pictures* working = mPictures.begin(); working != last; working++) {
    688         working->mArea.setEmpty();
    689         SkSafeUnref(working->mPicture);
    690     }
    691     mPictures.clear();
    692 #endif // FAST_PICTURESET
    693     mWidth = mHeight = 0;
    694 }
    695 
    696 bool PictureSet::draw(SkCanvas* canvas)
    697 {
    698 #ifdef FAST_PICTURESET
    699     XLOG("PictureSet %x draw on canvas %x", this, canvas);
    700     SkRect bounds;
    701     if (canvas->getClipBounds(&bounds) == false)
    702         return false;
    703     SkIRect irect;
    704     bounds.roundOut(&irect);
    705 
    706     WTF::Vector<Bucket*> list;
    707     gatherBucketsForArea(list, irect);
    708 
    709     XLOG("PictureSet draw on canvas %x, we have %d buckets", canvas, list.size());
    710     for (unsigned int i = 0; i < list.size(); i++) {
    711         Bucket* bucket = list[i];
    712         XLOG("We paint using bucket %x with %d pictures", bucket, bucket->size());
    713         for (unsigned int j = 0; j < bucket->size(); j++)  {
    714             BucketPicture& picture = bucket->at(j);
    715             if (!picture.mPicture)
    716                 continue;
    717             int saved = canvas->save();
    718             SkRect pathBounds;
    719             pathBounds.set(picture.mRealArea);
    720             XLOG("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d",
    721                   j, bucket->size(),
    722                   picture.mRealArea.fLeft,
    723                   picture.mRealArea.fTop,
    724                   picture.mRealArea.fRight,
    725                   picture.mRealArea.fBottom,
    726                   picture.mRealArea.width(),
    727                   picture.mRealArea.height());
    728             canvas->clipRect(pathBounds);
    729             canvas->translate(pathBounds.fLeft, pathBounds.fTop);
    730             canvas->save();
    731             canvas->drawPicture(*picture.mPicture);
    732             canvas->restoreToCount(saved);
    733         }
    734     }
    735     return false;
    736 
    737 #else
    738 
    739     validate(__FUNCTION__);
    740     Pictures* first = mPictures.begin();
    741     Pictures* last = mPictures.end();
    742     Pictures* working;
    743     SkRect bounds;
    744     if (canvas->getClipBounds(&bounds) == false)
    745         return false;
    746     SkIRect irect;
    747     bounds.roundOut(&irect);
    748     for (working = last; working != first; ) {
    749         --working;
    750         if (working->mArea.contains(irect)) {
    751 #if PICTURE_SET_DEBUG
    752             const SkIRect& b = working->mArea.getBounds();
    753             DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
    754                 " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
    755                 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
    756 #endif
    757             first = working;
    758             break;
    759         }
    760     }
    761     DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
    762         last - mPictures.begin());
    763     uint32_t maxElapsed = 0;
    764     for (working = first; working != last; working++) {
    765         const SkRegion& area = working->mArea;
    766         if (area.quickReject(irect)) {
    767 #if PICTURE_SET_DEBUG
    768             const SkIRect& b = area.getBounds();
    769             DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
    770                 " irect={%d,%d,%d,%d}", working - first, working,
    771                 b.fLeft, b.fTop, b.fRight, b.fBottom,
    772                 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
    773 #endif
    774             working->mElapsed = 0;
    775             continue;
    776         }
    777         int saved = canvas->save();
    778         SkRect pathBounds;
    779         if (area.isComplex()) {
    780             SkPath pathClip;
    781             area.getBoundaryPath(&pathClip);
    782             canvas->clipPath(pathClip);
    783             pathBounds = pathClip.getBounds();
    784         } else {
    785             pathBounds.set(area.getBounds());
    786             canvas->clipRect(pathBounds);
    787         }
    788         canvas->translate(pathBounds.fLeft, pathBounds.fTop);
    789         canvas->save();
    790         uint32_t startTime = getThreadMsec();
    791         canvas->drawPicture(*working->mPicture);
    792         size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
    793         working->mWroteElapsed = true;
    794         if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
    795                 pathBounds.height() >= MIN_SPLITTABLE))
    796             maxElapsed = elapsed;
    797         canvas->restoreToCount(saved);
    798 #define DRAW_TEST_IMAGE 01
    799 #if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
    800         SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
    801         canvas->drawColor(color);
    802         SkPaint paint;
    803         color ^= 0x00ffffff;
    804         paint.setColor(color);
    805         char location[256];
    806         for (int x = area.getBounds().fLeft & ~0x3f;
    807                 x < area.getBounds().fRight; x += 0x40) {
    808             for (int y = area.getBounds().fTop & ~0x3f;
    809                     y < area.getBounds().fBottom; y += 0x40) {
    810                 int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
    811                 canvas->drawText(location, len, x, y, paint);
    812             }
    813         }
    814 #endif
    815         DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
    816             working - first, working,
    817             area.getBounds().fLeft, area.getBounds().fTop,
    818             area.getBounds().fRight, area.getBounds().fBottom,
    819             working->mElapsed, working->mBase ? "true" : "false");
    820     }
    821  //   dump(__FUNCTION__);
    822     return maxElapsed >= MAX_DRAW_TIME;
    823 #endif // FAST_PICTURESET
    824 }
    825 
    826 void PictureSet::dump(const char* label) const
    827 {
    828 #if PICTURE_SET_DUMP
    829     DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
    830         mWidth, mHeight);
    831     const Pictures* last = mPictures.end();
    832     for (const Pictures* working = mPictures.begin(); working != last; working++) {
    833         const SkIRect& bounds = working->mArea.getBounds();
    834         const SkIRect& unsplit = working->mUnsplit;
    835         MeasureStream measure;
    836         if (working->mPicture != NULL)
    837             working->mPicture->serialize(&measure);
    838         LOGD(" [%d]"
    839             " mArea.bounds={%d,%d,r=%d,b=%d}"
    840             " mPicture=%p"
    841             " mUnsplit={%d,%d,r=%d,b=%d}"
    842             " mElapsed=%d"
    843             " mSplit=%s"
    844             " mWroteElapsed=%s"
    845             " mBase=%s"
    846             " pict-size=%d",
    847             working - mPictures.begin(),
    848             bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
    849             working->mPicture,
    850             unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
    851             working->mElapsed, working->mSplit ? "true" : "false",
    852             working->mWroteElapsed ? "true" : "false",
    853             working->mBase ? "true" : "false",
    854             measure.mTotal);
    855     }
    856 #endif
    857 }
    858 
    859 class IsEmptyBounder : public SkBounder {
    860     virtual bool onIRect(const SkIRect& rect) {
    861         return false;
    862     }
    863 };
    864 
    865 class IsEmptyCanvas : public SkCanvas {
    866 public:
    867     IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
    868             mPicture(picture), mEmpty(true) {
    869         setBounder(bounder);
    870     }
    871 
    872     void notEmpty() {
    873         mEmpty = false;
    874         mPicture->abortPlayback();
    875     }
    876 
    877     virtual bool clipPath(const SkPath&, SkRegion::Op) {
    878         // this can be expensive to actually do, and doesn't affect the
    879         // question of emptiness, so we make it a no-op
    880         return true;
    881     }
    882 
    883     virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
    884             const SkMatrix& , const SkPaint& ) {
    885         if (bitmap.width() <= 1 || bitmap.height() <= 1)
    886             return;
    887         DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
    888         notEmpty();
    889     }
    890 
    891     virtual void drawPaint(const SkPaint& paint) {
    892     }
    893 
    894     virtual void drawPath(const SkPath& , const SkPaint& paint) {
    895         DBG_SET_LOG("abort");
    896         notEmpty();
    897     }
    898 
    899     virtual void drawPoints(PointMode , size_t , const SkPoint [],
    900                             const SkPaint& paint) {
    901     }
    902 
    903     virtual void drawRect(const SkRect& , const SkPaint& paint) {
    904         // wait for visual content
    905         if (paint.getColor() != SK_ColorWHITE)
    906             notEmpty();
    907     }
    908 
    909     virtual void drawSprite(const SkBitmap& , int , int ,
    910                             const SkPaint* paint = NULL) {
    911         DBG_SET_LOG("abort");
    912         notEmpty();
    913     }
    914 
    915     virtual void drawText(const void* , size_t byteLength, SkScalar ,
    916                           SkScalar , const SkPaint& paint) {
    917         DBG_SET_LOGD("abort %d", byteLength);
    918         notEmpty();
    919     }
    920 
    921     virtual void drawPosText(const void* , size_t byteLength,
    922                              const SkPoint [], const SkPaint& paint) {
    923         DBG_SET_LOGD("abort %d", byteLength);
    924         notEmpty();
    925     }
    926 
    927     virtual void drawPosTextH(const void* , size_t byteLength,
    928                               const SkScalar [], SkScalar ,
    929                               const SkPaint& paint) {
    930         DBG_SET_LOGD("abort %d", byteLength);
    931         notEmpty();
    932     }
    933 
    934     virtual void drawTextOnPath(const void* , size_t byteLength,
    935                                 const SkPath& , const SkMatrix* ,
    936                                 const SkPaint& paint) {
    937         DBG_SET_LOGD("abort %d", byteLength);
    938         notEmpty();
    939     }
    940 
    941     virtual void drawPicture(SkPicture& picture) {
    942         SkCanvas::drawPicture(picture);
    943     }
    944 
    945     SkPicture* mPicture;
    946     bool mEmpty;
    947 };
    948 
    949 bool PictureSet::emptyPicture(SkPicture* picture) const
    950 {
    951     IsEmptyBounder isEmptyBounder;
    952     IsEmptyCanvas checker(&isEmptyBounder, picture);
    953     SkBitmap bitmap;
    954     bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
    955     checker.setBitmapDevice(bitmap);
    956     checker.drawPicture(*picture);
    957     return checker.mEmpty;
    958 }
    959 
    960 bool PictureSet::isEmpty() const
    961 {
    962 #ifdef FAST_PICTURESET
    963     // For now, just assume the pictureset is *not* empty
    964     // if the hashmap contains something
    965     for (BucketMap::const_iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
    966         if (iter->second->size() > 0)
    967             return false;
    968     }
    969     return true;
    970 #else
    971     const Pictures* last = mPictures.end();
    972     for (const Pictures* working = mPictures.begin(); working != last; working++) {
    973         if (!working->mEmpty)
    974             return false;
    975     }
    976     return true;
    977 #endif // FAST_PICTURESET
    978 }
    979 
    980 void PictureSet::set(const PictureSet& src)
    981 {
    982     DBG_SET_LOGD("start %p src=%p", this, &src);
    983     clear();
    984     setDimensions(src.mWidth, src.mHeight);
    985 #ifdef FAST_PICTURESET
    986     XLOG("\n--- set picture ---");
    987     for (BucketMap::const_iterator iter = src.mBuckets.begin();
    988          iter != src.mBuckets.end(); ++iter) {
    989          Bucket* sourceBucket = iter->second;
    990          Bucket* targetBucket = getBucket(iter->first.first-1, iter->first.second-1);
    991          BucketPicture* first = sourceBucket->begin();
    992          BucketPicture* last = sourceBucket->end();
    993          XLOG("set from bucket %x (%d, %d), %d pictures", sourceBucket,
    994               iter->first.first, iter->first.second, sourceBucket->size());
    995          for (BucketPicture* current = first; current != last; current++) {
    996              XLOG("set picture %x from bucket %x in bucket %x (%d, %d)",
    997                   current->mPicture, sourceBucket, targetBucket,
    998                   iter->first.first, iter->first.second);
    999              SkSafeRef(current->mPicture);
   1000              BucketPicture picture = { current->mPicture, current->mArea,
   1001                                        current->mRealArea, current->mBase };
   1002              targetBucket->append(picture);
   1003          }
   1004     }
   1005     XLOG("--- DONE set picture ---\n");
   1006 #else
   1007     const Pictures* last = src.mPictures.end();
   1008     for (const Pictures* working = src.mPictures.begin(); working != last; working++)
   1009         add(working);
   1010  //   dump(__FUNCTION__);
   1011     validate(__FUNCTION__);
   1012     DBG_SET_LOG("end");
   1013 #endif // FAST_PICTURESET
   1014 }
   1015 
   1016 #ifdef FAST_PICTURESET
   1017 #else
   1018 
   1019 bool PictureSet::reuseSubdivided(const SkRegion& inval)
   1020 {
   1021     validate(__FUNCTION__);
   1022 
   1023     if (inval.isComplex())
   1024         return false;
   1025     Pictures* working, * last = mPictures.end();
   1026     const SkIRect& invalBounds = inval.getBounds();
   1027     bool steal = false;
   1028     for (working = mPictures.begin(); working != last; working++) {
   1029         if (working->mSplit && invalBounds == working->mUnsplit) {
   1030             steal = true;
   1031             continue;
   1032         }
   1033         if (steal == false)
   1034             continue;
   1035         SkRegion temp = SkRegion(inval);
   1036         temp.op(working->mArea, SkRegion::kIntersect_Op);
   1037         if (temp.isEmpty() || temp == working->mArea)
   1038             continue;
   1039         return false;
   1040     }
   1041     if (steal == false)
   1042         return false;
   1043     for (working = mPictures.begin(); working != last; working++) {
   1044         if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
   1045                 inval.contains(working->mArea) == false)
   1046             continue;
   1047         SkSafeUnref(working->mPicture);
   1048         working->mPicture = NULL;
   1049     }
   1050     return true;
   1051 }
   1052 
   1053 void PictureSet::setDrawTimes(const PictureSet& src)
   1054 {
   1055     validate(__FUNCTION__);
   1056     if (mWidth != src.mWidth || mHeight != src.mHeight)
   1057         return;
   1058     Pictures* last = mPictures.end();
   1059     Pictures* working = mPictures.begin();
   1060     if (working == last)
   1061         return;
   1062     const Pictures* srcLast = src.mPictures.end();
   1063     const Pictures* srcWorking = src.mPictures.begin();
   1064     for (; srcWorking != srcLast; srcWorking++) {
   1065         if (srcWorking->mWroteElapsed == false)
   1066             continue;
   1067         while ((srcWorking->mArea != working->mArea ||
   1068                 srcWorking->mPicture != working->mPicture)) {
   1069             if (++working == last)
   1070                 return;
   1071         }
   1072         DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
   1073             this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
   1074             working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
   1075             working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
   1076             working->mElapsed, srcWorking->mElapsed);
   1077         working->mElapsed = srcWorking->mElapsed;
   1078     }
   1079 }
   1080 
   1081 void PictureSet::setPicture(size_t i, SkPicture* p)
   1082 {
   1083     SkSafeUnref(mPictures[i].mPicture);
   1084     mPictures[i].mPicture = p;
   1085     mPictures[i].mEmpty = emptyPicture(p);
   1086 }
   1087 
   1088 void PictureSet::split(PictureSet* out) const
   1089 {
   1090     dump(__FUNCTION__);
   1091     DBG_SET_LOGD("%p", this);
   1092     SkIRect totalBounds;
   1093     out->mWidth = mWidth;
   1094     out->mHeight = mHeight;
   1095     totalBounds.set(0, 0, mWidth, mHeight);
   1096     SkRegion* total = new SkRegion(totalBounds);
   1097     const Pictures* last = mPictures.end();
   1098     const Pictures* working;
   1099     uint32_t balance = 0;
   1100     int multiUnsplitFastPictures = 0; // > 1 has more than 1
   1101     for (working = mPictures.begin(); working != last; working++) {
   1102         if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
   1103             continue;
   1104         if (++multiUnsplitFastPictures > 1)
   1105             break;
   1106     }
   1107     for (working = mPictures.begin(); working != last; working++) {
   1108         uint32_t elapsed = working->mElapsed;
   1109         if (elapsed < MAX_DRAW_TIME) {
   1110             bool split = working->mSplit;
   1111             DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
   1112                 "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
   1113                 total->getBounds().fLeft, total->getBounds().fTop,
   1114                 total->getBounds().fRight, total->getBounds().fBottom,
   1115                 split ? "true" : "false");
   1116             if (multiUnsplitFastPictures <= 1 || split) {
   1117                 total->op(working->mArea, SkRegion::kDifference_Op);
   1118                 out->add(working->mArea, working->mPicture, elapsed, split,
   1119                     working->mEmpty);
   1120             } else if (balance < elapsed)
   1121                 balance = elapsed;
   1122             continue;
   1123         }
   1124         total->op(working->mArea, SkRegion::kDifference_Op);
   1125         const SkIRect& bounds = working->mArea.getBounds();
   1126         int width = bounds.width();
   1127         int height = bounds.height();
   1128         int across = 1;
   1129         int down = 1;
   1130         while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
   1131             if (height >= width) {
   1132                 height >>= 1;
   1133                 down <<= 1;
   1134             } else {
   1135                 width >>= 1;
   1136                 across <<= 1 ;
   1137             }
   1138             if ((elapsed >>= 1) < MAX_DRAW_TIME)
   1139                 break;
   1140         }
   1141         width = bounds.width();
   1142         height = bounds.height();
   1143         int top = bounds.fTop;
   1144         for (int indexY = 0; indexY < down; ) {
   1145             int bottom = bounds.fTop + height * ++indexY / down;
   1146             int left = bounds.fLeft;
   1147             for (int indexX = 0; indexX < across; ) {
   1148                 int right = bounds.fLeft + width * ++indexX / across;
   1149                 SkIRect cBounds;
   1150                 cBounds.set(left, top, right, bottom);
   1151                 out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
   1152                     working->mPicture, elapsed, true,
   1153                     (across | down) != 1 ? false : working->mEmpty);
   1154                 left = right;
   1155             }
   1156             top = bottom;
   1157         }
   1158     }
   1159     DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
   1160         this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
   1161         multiUnsplitFastPictures);
   1162     if (!total->isEmpty() && multiUnsplitFastPictures > 1)
   1163         out->add(*total, NULL, balance, false, false);
   1164     delete total;
   1165     validate(__FUNCTION__);
   1166     out->dump("split-out");
   1167 }
   1168 
   1169 #endif // FAST_PICTURESET
   1170 
   1171 bool PictureSet::validate(const char* funct) const
   1172 {
   1173 #ifdef FAST_PICTURESET
   1174     return true;
   1175 #else
   1176     bool valid = true;
   1177 #if PICTURE_SET_VALIDATE
   1178     SkRegion all;
   1179     const Pictures* first = mPictures.begin();
   1180     for (const Pictures* working = mPictures.end(); working != first; ) {
   1181         --working;
   1182         const SkPicture* pict = working->mPicture;
   1183         const SkRegion& area = working->mArea;
   1184         const SkIRect& bounds = area.getBounds();
   1185         bool localValid = false;
   1186         if (working->mUnsplit.isEmpty())
   1187             LOGD("%s working->mUnsplit.isEmpty()", funct);
   1188         else if (working->mUnsplit.contains(bounds) == false)
   1189             LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
   1190         else if (working->mElapsed >= 1000)
   1191             LOGD("%s working->mElapsed >= 1000", funct);
   1192         else if ((working->mSplit & 0xfe) != 0)
   1193             LOGD("%s (working->mSplit & 0xfe) != 0", funct);
   1194         else if ((working->mWroteElapsed & 0xfe) != 0)
   1195             LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
   1196         else if (pict != NULL) {
   1197             int pictWidth = pict->width();
   1198             int pictHeight = pict->height();
   1199             if (pictWidth < bounds.width())
   1200                 LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
   1201             else if (pictHeight < bounds.height())
   1202                 LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
   1203             else if (working->mArea.isEmpty())
   1204                 LOGD("%s working->mArea.isEmpty()", funct);
   1205             else
   1206                 localValid = true;
   1207         } else
   1208             localValid = true;
   1209         working->mArea.validate();
   1210         if (localValid == false) {
   1211             if (all.contains(area) == true)
   1212                 LOGD("%s all.contains(area) == true", funct);
   1213             else
   1214                 localValid = true;
   1215         }
   1216         valid &= localValid;
   1217         all.op(area, SkRegion::kUnion_Op);
   1218     }
   1219     const SkIRect& allBounds = all.getBounds();
   1220     if (valid) {
   1221         valid = false;
   1222         if (allBounds.width() != mWidth)
   1223             LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
   1224         else if (allBounds.height() != mHeight)
   1225             LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
   1226         else
   1227             valid = true;
   1228     }
   1229     while (valid == false)
   1230         ;
   1231 #endif
   1232     return valid;
   1233 #endif // FAST_PICTURESET
   1234 }
   1235 
   1236 } /* namespace android */
   1237