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