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