• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
MeasureStream()73     MeasureStream() : mTotal(0) {}
write(const void *,size_t size)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 
PictureSet()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 
PictureSet(SkPicture * picture)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 
~PictureSet()145 PictureSet::~PictureSet()
146 {
147     clear();
148 }
149 
150 #ifdef FAST_PICTURESET
151 #else
add(const Pictures * temp)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 
add(const SkRegion & area,SkPicture * picture,uint32_t elapsed,bool split)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 
getBucket(int x,int y)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 
displayBucket(Bucket * bucket)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 
displayBuckets()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.
addToBucket(Bucket * bucket,int dx,int dy,SkIRect & rect)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 
gatherBucketsForArea(WTF::Vector<Bucket * > & list,const SkIRect & rect)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.
splitAdd(const SkIRect & rect)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
add(const SkRegion & area,SkPicture * picture,uint32_t elapsed,bool split,bool empty)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 
setDimensions(int width,int height,SkRegion * inval)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 
clear()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 
draw(SkCanvas * canvas)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 
dump(const char * label) const826 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 {
onIRect(const SkIRect & rect)860     virtual bool onIRect(const SkIRect& rect) {
861         return false;
862     }
863 };
864 
865 class IsEmptyCanvas : public SkCanvas {
866 public:
IsEmptyCanvas(SkBounder * bounder,SkPicture * picture)867     IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
868             mPicture(picture), mEmpty(true) {
869         setBounder(bounder);
870     }
871 
notEmpty()872     void notEmpty() {
873         mEmpty = false;
874         mPicture->abortPlayback();
875     }
876 
clipPath(const SkPath &,SkRegion::Op)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 
commonDrawBitmap(const SkBitmap & bitmap,const SkIRect * rect,const SkMatrix &,const SkPaint &)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 
drawPaint(const SkPaint & paint)891     virtual void drawPaint(const SkPaint& paint) {
892     }
893 
drawPath(const SkPath &,const SkPaint & paint)894     virtual void drawPath(const SkPath& , const SkPaint& paint) {
895         DBG_SET_LOG("abort");
896         notEmpty();
897     }
898 
drawPoints(PointMode,size_t,const SkPoint[],const SkPaint & paint)899     virtual void drawPoints(PointMode , size_t , const SkPoint [],
900                             const SkPaint& paint) {
901     }
902 
drawRect(const SkRect &,const SkPaint & paint)903     virtual void drawRect(const SkRect& , const SkPaint& paint) {
904         // wait for visual content
905         if (paint.getColor() != SK_ColorWHITE)
906             notEmpty();
907     }
908 
drawSprite(const SkBitmap &,int,int,const SkPaint * paint=NULL)909     virtual void drawSprite(const SkBitmap& , int , int ,
910                             const SkPaint* paint = NULL) {
911         DBG_SET_LOG("abort");
912         notEmpty();
913     }
914 
drawText(const void *,size_t byteLength,SkScalar,SkScalar,const SkPaint & paint)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 
drawPosText(const void *,size_t byteLength,const SkPoint[],const SkPaint & paint)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 
drawPosTextH(const void *,size_t byteLength,const SkScalar[],SkScalar,const SkPaint & paint)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 
drawTextOnPath(const void *,size_t byteLength,const SkPath &,const SkMatrix *,const SkPaint & paint)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 
drawPicture(SkPicture & picture)941     virtual void drawPicture(SkPicture& picture) {
942         SkCanvas::drawPicture(picture);
943     }
944 
945     SkPicture* mPicture;
946     bool mEmpty;
947 };
948 
emptyPicture(SkPicture * picture) const949 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 
isEmpty() const960 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 
set(const PictureSet & src)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 
reuseSubdivided(const SkRegion & inval)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 
setDrawTimes(const PictureSet & src)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 
setPicture(size_t i,SkPicture * p)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 
split(PictureSet * out) const1088 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 
validate(const char * funct) const1171 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