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