2 * Copyright 2008, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #define LOG_TAG "pictureset"
30 #include "CachedPrefix.h"
31 #include "android_graphics.h"
32 #include "PictureSet.h"
33 #include "SkBounder.h"
35 #include "SkPicture.h"
39 #include "TimeCounter.h"
41 #define MAX_DRAW_TIME 100
42 #define MIN_SPLITTABLE 400
43 #define MAX_ADDITIONAL_AREA 0.65
44 #define MAX_ADDITIONAL_PICTURES 32
46 #define BUCKET_SIZE 256
47 #define MAX_BUCKET_COUNT_X 16
48 #define MAX_BUCKET_COUNT_Y 64
50 #include <wtf/CurrentTime.h>
52 #include <cutils/log.h>
53 #include <wtf/text/CString.h>
56 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
61 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
71 class MeasureStream : public SkWStream {
73 MeasureStream() : mTotal(0) {}
74 virtual bool write(const void* , size_t size) {
84 PictureSet::PictureSet()
85 : mBucketSizeX(0), mBucketSizeY(0), mBucketCountX(0), mBucketCountY(0),
89 mBaseArea = mAdditionalArea = 0;
92 PictureSet::PictureSet(SkPicture* picture)
93 : mBucketSizeX(0), mBucketSizeY(0), mBucketCountX(0), mBucketCountY(0),
96 mBaseArea = mAdditionalArea = 0;
101 setDimensions(picture->width(), picture->height());
102 mBaseArea = mWidth * mHeight;
103 #ifdef FAST_PICTURESET
105 area.set(0, 0, mWidth, mHeight);
107 WTF::Vector<Bucket*>* buckets = bucketsToUpdate();
108 for (unsigned int i = 0; i < buckets->size(); i++) {
109 Bucket* bucket = (*buckets)[i];
110 for (unsigned int j = 0; j < bucket->size(); j++) {
111 BucketPicture& bucketPicture = (*bucket)[j];
112 const SkIRect& inval = bucketPicture.mRealArea;
113 SkPicture *splitPicture = new SkPicture();
114 SkCanvas *canvas = splitPicture->beginRecording(
115 inval.width(), inval.height(),
116 SkPicture::kUsePathBoundsForClip_RecordingFlag);
117 canvas->translate(-inval.fLeft, -inval.fTop);
118 picture->draw(canvas);
119 splitPicture->endRecording();
120 SkSafeUnref(bucketPicture.mPicture);
121 bucketPicture.mPicture = splitPicture;
126 Pictures pictureAndBounds;
127 pictureAndBounds.mPicture = picture;
128 SkSafeRef(pictureAndBounds.mPicture);
129 pictureAndBounds.mEmpty = false;
130 pictureAndBounds.mArea.setRect(0, 0, mWidth, mHeight);
131 pictureAndBounds.mSplit = false;
132 pictureAndBounds.mBase = true;
133 pictureAndBounds.mElapsed = 0;
134 pictureAndBounds.mWroteElapsed = false;
135 mPictures.append(pictureAndBounds);
136 #endif // FAST_PICTURESET
139 PictureSet::~PictureSet()
144 #ifdef FAST_PICTURESET
146 void PictureSet::add(const Pictures* temp)
148 Pictures pictureAndBounds = *temp;
149 SkSafeRef(pictureAndBounds.mPicture);
150 pictureAndBounds.mWroteElapsed = false;
151 mPictures.append(pictureAndBounds);
153 #endif // FAST_PICTURESET
155 void PictureSet::add(const SkRegion& area, SkPicture* picture,
156 uint32_t elapsed, bool split)
159 #ifdef FAST_PICTURESET
160 splitAdd(area.getBounds());
162 add(area, picture, elapsed, split, false);
163 #endif // FAST_PICTURESET
165 SkRegion::Iterator cliperator(area);
166 while (!cliperator.done()) {
167 SkIRect ir = cliperator.rect();
168 #ifdef FAST_PICTURESET
173 add(newArea, picture, elapsed, split, false);
174 #endif // FAST_PICTURESET
180 #ifdef FAST_PICTURESET
182 Bucket* PictureSet::getBucket(int x, int y)
184 // only create buckets for valid, positive coordinates, ignore and return
189 BucketPosition position(x+1, y+1);
190 if (!mBuckets.contains(position)) {
191 XLOG("PictureSet::getBucket(%d, %d) adding new bucket", x, y);
192 Bucket* bucket = new Bucket();
193 mBuckets.add(position, bucket);
195 return mBuckets.get(position);
198 void PictureSet::displayBucket(Bucket* bucket)
200 BucketPicture* first = bucket->begin();
201 BucketPicture* last = bucket->end();
202 for (BucketPicture* current = first; current != last; current++) {
203 XLOGC("- in %x, bucketPicture %d,%d,%d,%d - %dx%d, picture: %x, base: %x",
205 current->mArea.fLeft,
207 current->mArea.fRight,
208 current->mArea.fBottom,
209 current->mArea.width(),
210 current->mArea.height(),
216 void PictureSet::displayBuckets()
218 XLOGC("\n\n****** DISPLAY BUCKETS ON PictureSet %x ******", this);
219 for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
220 XLOGC("\n*** Bucket %x for %d, %d", iter->second, iter->first.first, iter->first.second);
221 displayBucket(iter->second);
223 XLOGC("\n****** END OF DISPLAY BUCKETS ******\n\n");
226 // When we receive an inval in a Bucket, we try to see if we intersect with
227 // existing invals/pictures in the Bucket.
228 void PictureSet::addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect)
230 bool resetBase = false;
232 SkIRect totalArea = rect;
233 BucketPicture* first = bucket->begin();
234 BucketPicture* last = bucket->end();
236 // If the inval covers a large area of the base inval, let's repaint the
238 if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * mBucketSizeX * mBucketSizeY)
241 // let's gather all the BucketPicture intersecting with the new invalidated
242 // area, collect their area and remove their picture
243 for (BucketPicture* current = first; current != last; current++) {
244 bool remove = resetBase;
245 bool intersect = false;
248 intersect = SkIRect::Intersects(current->mArea, rect);
249 // If the current picture is not a base, and we intersect, remove it
250 if (!remove && !current->mBase && intersect)
252 // If the current picture is a base, check if the new inval completely
253 // contains the base, and if so remove it.
254 if (!remove && current->mBase && rect.contains(current->mArea))
256 // If the current picture is a base and it intersects,
257 // also check that it fully covers the bucket -- otherwise,
258 // let's aggregate it with the new inval.
259 if (!remove && current->mBase && intersect
260 && (current->mArea.width() < mBucketSizeX || current->mArea.height() < mBucketSizeY)) {
265 totalArea.join(current->mArea);
266 current->mBase = false;
267 current->mArea.setEmpty();
268 SkSafeUnref(current->mPicture);
269 current->mPicture = 0;
273 // Now, let's add the new BucketPicture to the list, with the correct
274 // area that needs to be repainted
276 SkIRect area = totalArea;
278 BucketPicture picture = { 0, totalArea, area, false };
280 bucket->append(picture);
282 first = bucket->begin();
283 last = bucket->end();
285 // let's do a pass to collapse out empty areas
286 BucketPicture* writer = first;
287 for (BucketPicture* current = first; current != last; current++) {
288 if (current && current->mArea.isEmpty())
290 *writer++ = *current;
293 bucket->shrink(writer - first);
295 // let's recompute the bases
296 first = bucket->begin();
297 last = bucket->end();
300 for (BucketPicture* current = first; current != last; current++) {
301 if (drawn.contains(current->mArea) == false) {
302 current->mBase = true;
304 drawn.op(current->mArea, SkRegion::kUnion_Op);
308 void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect)
310 XLOG("\n--- gatherBucketsForArea for rect %d, %d, %d, %d (%d x %d)",
311 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
312 rect.width(), rect.height());
316 int firstTileX = rect.fLeft / mBucketSizeX;
317 int firstTileY = rect.fTop / mBucketSizeY;
318 int lastTileX = rect.fRight / mBucketSizeX;
319 int lastTileY = rect.fBottom / mBucketSizeY;
321 for (int i = firstTileX; i <= lastTileX; i++) {
322 for (int j = firstTileY; j <= lastTileY; j++) {
323 Bucket* bucket = getBucket(i, j);
324 XLOG("gather bucket %x for %d, %d", bucket, i+1, j+1);
331 // When we receive a new inval rect, we first find the Buckets that intersect
332 // with it; then we split the original inval into a serie of invals (one for
333 // each Bucket we intersect with). We then send that inval to the Bucket.
334 void PictureSet::splitAdd(const SkIRect& rect)
336 XLOG("\n--- splitAdd for rect %d, %d, %d, %d (%d x %d)",
337 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
338 rect.width(), rect.height());
340 // TODO: reuse gatherBucketsForArea() (change Bucket to be a class)
343 int firstTileX = rect.fLeft / mBucketSizeX;
344 int firstTileY = rect.fTop / mBucketSizeY;
345 int lastTileX = rect.fRight / mBucketSizeX;
346 int lastTileY = rect.fBottom / mBucketSizeY;
348 XLOG("--- firstTile(%d, %d) lastTile(%d, %d)",
349 firstTileX, firstTileY,
350 lastTileX, lastTileY);
352 for (int i = firstTileX; i <= lastTileX; i++) {
353 for (int j = firstTileY; j <= lastTileY; j++) {
354 Bucket* bucket = getBucket(i, j);
359 int deltaX = i * mBucketSizeX;
360 int deltaY = j * mBucketSizeY;
361 int left = (i == firstTileX) ? rect.fLeft - deltaX : 0;
362 int top = (j == firstTileY) ? rect.fTop - deltaY : 0;
363 int right = (i == lastTileX) ? rect.fRight % mBucketSizeX : mBucketSizeX;
364 int bottom = (j == lastTileY) ? rect.fBottom % mBucketSizeY : mBucketSizeY;
366 newRect.set(left, top, right, bottom);
367 addToBucket(bucket, deltaX, deltaY, newRect);
368 mUpdatedBuckets.append(bucket);
372 XLOG("--- splitAdd DONE\n");
375 #endif // FAST_PICTURESET
377 // This function is used to maintain the list of Pictures.
378 // Pictures contain an SkPicture covering a specific area; some
379 // Pictures are "base" Pictures -- i.e. there is no Pictures
381 // The idea here is to keep a balance between the number of Pictures
382 // we have (more Pictures slow us down) and the area of Pictures that
383 // need to be repainted (obviously, smaller areas are better).
384 // To do so, we try to not update/repaint the base pictures -- by
385 // construction, they usually cover a large area (the entire page).
386 // We only reset a base picture if the new invalidated area entirely
388 // Most of the time we thus work on smaller pictures on top of the
389 // base ones; We compute the total area of all pictures intersecting
390 // with the passed invalidated area (as they would need to be invalidated),
391 // and use that as the basis for the correct area we want to invalidate
392 // (we then can simply delete the pictures we intersect with).
393 // In addition, we do a couple of things to limit the total number of pictures
394 // we keep in the list:
395 // - if the total area of additional textures reach 65% of the base pictures,
396 // we delete the additional pictures and mark the base pictures as
397 // needing a full repaint
398 // - we limit the number of pictures to 32 -- above that, we do the same
399 // things (deleting additional pictures + full repaint of base pictures)
400 #ifdef FAST_PICTURESET
402 void PictureSet::add(const SkRegion& area, SkPicture* picture,
403 uint32_t elapsed, bool split, bool empty)
405 bool checkForNewBases = false;
407 Pictures* first = mPictures.begin();
408 Pictures* last = mPictures.end();
410 XLOG("--- before adding the new inval ---");
411 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
412 SkIRect currentArea = working->mArea.getBounds();
413 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
415 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
416 currentArea.width(), currentArea.height(),
417 working->mArea.isRect() ? 'Y' : 'N',
418 working->mBase ? 'Y' : 'N');
420 XLOG("----------------------------------");
423 // let's gather all the Pictures intersecting with the new invalidated
424 // area, collect their area and remove their picture
425 SkIRect totalArea = area.getBounds();
426 for (Pictures* working = first; working != last; working++) {
427 SkIRect inval = area.getBounds();
429 if (!working->mBase && working->mArea.intersects(inval))
431 if (working->mBase) {
432 SkIRect baseArea = working->mArea.getBounds();
433 if (area.contains(baseArea)) {
435 checkForNewBases = true;
440 SkIRect currentArea = working->mArea.getBounds();
442 mBaseArea -= currentArea.width() * currentArea.height();
444 mAdditionalArea -= currentArea.width() * currentArea.height();
446 totalArea.join(currentArea);
447 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",
449 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
450 currentArea.width(), currentArea.height(),
451 working->mArea.isRect() ? 'Y' : 'N',
452 inval.fLeft, inval.fTop, inval.fRight, inval.fBottom,
453 inval.width(), inval.height(),
454 area.isRect() ? 'Y' : 'N');
455 working->mArea.setEmpty();
456 SkSafeUnref(working->mPicture);
457 working->mPicture = 0;
461 // Now we can add the new Picture to the list, with the correct area
462 // that need to be repainted
464 collect.setRect(totalArea);
465 Pictures pictureAndBounds = {collect, 0, collect.getBounds(),
466 elapsed, split, false, false, empty};
468 #ifdef FAST_PICTURESET
469 if (mPictures.size() == 0)
470 checkForNewBases = true;
473 mPictures.append(pictureAndBounds);
474 mAdditionalArea += totalArea.width() * totalArea.height();
475 last = mPictures.end();
476 first = mPictures.begin();
478 // Then, let's see if we have to clear up the pictures in order to keep
479 // the total number of pictures under our limit
480 bool clearUp = false;
481 if (last - first > MAX_ADDITIONAL_PICTURES) {
482 XLOG("--- too many pictures, only keeping the bases : %d", last - first);
487 if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) {
488 XLOG("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f",
489 MAX_ADDITIONAL_AREA * 100, mBaseArea * 0.65, mBaseArea, mAdditionalArea);
495 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
497 working->mArea.setEmpty();
498 SkSafeUnref(working->mPicture);
499 working->mPicture = 0;
504 XLOG("--- after adding the new inval, but before collapsing ---");
505 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
506 SkIRect currentArea = working->mArea.getBounds();
507 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
509 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
510 currentArea.width(), currentArea.height(),
511 working->mArea.isRect() ? 'Y' : 'N',
512 working->mBase ? 'Y' : 'N');
514 XLOG("----------------------------------");
515 XLOG("let's collapse...");
518 // Finally, let's do a pass to collapse out empty regions
519 Pictures* writer = first;
520 for (Pictures* working = first; working != last; working++) {
521 if (working && working->mArea.isEmpty())
523 *writer++ = *working;
525 XLOG("shiking of %d elements", writer - first);
526 mPictures.shrink(writer - first);
529 XLOG("--- after adding the new inval ---");
530 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
531 SkIRect currentArea = working->mArea.getBounds();
532 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c picture %x",
534 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
535 currentArea.width(), currentArea.height(),
536 working->mArea.isRect() ? 'Y' : 'N',
537 working->mBase ? 'Y' : 'N', working->mPicture);
539 XLOG("----------------------------------");
542 // Base pictures might have been removed/added -- let's recompute them
544 if (checkForNewBases) {
546 Pictures* last = mPictures.end();
547 XLOG("checkForNewBases...");
548 for (Pictures* working = mPictures.begin(); working != last; working++) {
549 SkRegion& area = working->mArea;
550 const SkIRect& a = area.getBounds();
551 if (drawn.contains(working->mArea) == false) {
552 working->mBase = true;
553 float area = a.width() * a.height();
555 mAdditionalArea -= area;
557 drawn.op(working->mArea, SkRegion::kUnion_Op);
561 #endif // FAST_PICTURESET
563 void PictureSet::setDimensions(int width, int height, SkRegion* inval)
565 // Note that setDimensions() may be called by our ctor and should behave accordingly
566 if (mWidth == width && mHeight == height)
568 DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
569 mWidth, mHeight, width, height);
570 bool clearCache = false;
572 if (mWidth == width && height > mHeight) { // only grew vertically
574 rect.set(0, mHeight, width, height);
575 inval->op(rect, SkRegion::kUnion_Op);
578 inval->setRect(0, 0, width, height);
581 #ifdef FAST_PICTURESET
582 // First figure out how large each bucket would be if we used all of the buckets
583 int tmpSizeX = (width + MAX_BUCKET_COUNT_X - 1) / MAX_BUCKET_COUNT_X;
584 int tmpSizeY = (height + MAX_BUCKET_COUNT_Y - 1) / MAX_BUCKET_COUNT_Y;
586 // Then round the bucket size up to the nearest chunk
587 int bucketSizeX = ((tmpSizeX - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE;
588 int bucketSizeY = ((tmpSizeY - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE;
590 int bucketCountX = (width + bucketSizeX - 1) / bucketSizeX;
591 int bucketCountY = (height + bucketSizeY - 1) / bucketSizeY;
593 // Clear the cache if the horizontal bucket count changed or the vertical
595 if (bucketCountX != mBucketCountX || bucketCountY < mBucketCountY)
598 // Or if the bucket size changed
599 if (bucketSizeX != mBucketSizeX || bucketSizeY != mBucketSizeY)
602 XLOG("old width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d",
603 mWidth, mHeight, mBucketSizeX, mBucketSizeY, mBucketCountX, mBucketCountY, clearCache);
604 XLOG("new width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d",
605 width, height, bucketSizeX, bucketSizeY, bucketCountX, bucketCountY, clearCache);
611 #ifdef FAST_PICTURESET
612 mBucketSizeX = bucketSizeX;
613 mBucketSizeY = bucketSizeY;
614 mBucketCountX = bucketCountX;
615 mBucketCountY = bucketCountY;
619 void PictureSet::clear()
622 #ifdef FAST_PICTURESET
623 for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
624 Bucket* bucket = iter->second;
625 BucketPicture* first = bucket->begin();
626 BucketPicture* last = bucket->end();
627 for (BucketPicture* current = first; current != last; current++) {
628 SkSafeUnref(current->mPicture);
629 current->mPicture = 0;
635 Pictures* last = mPictures.end();
636 for (Pictures* working = mPictures.begin(); working != last; working++) {
637 working->mArea.setEmpty();
638 SkSafeUnref(working->mPicture);
641 #endif // FAST_PICTURESET
642 mWidth = mHeight = 0;
643 mBucketSizeX = mBucketSizeY = 0;
646 bool PictureSet::draw(SkCanvas* canvas)
648 #ifdef FAST_PICTURESET
649 XLOG("PictureSet %x draw on canvas %x", this, canvas);
651 if (canvas->getClipBounds(&bounds) == false)
654 bounds.roundOut(&irect);
656 WTF::Vector<Bucket*> list;
657 gatherBucketsForArea(list, irect);
659 XLOG("PictureSet draw on canvas %x, we have %d buckets", canvas, list.size());
660 for (unsigned int i = 0; i < list.size(); i++) {
661 Bucket* bucket = list[i];
662 XLOG("We paint using bucket %x with %d pictures", bucket, bucket->size());
663 for (unsigned int j = 0; j < bucket->size(); j++) {
664 BucketPicture& picture = bucket->at(j);
665 if (!picture.mPicture)
667 int saved = canvas->save();
669 pathBounds.set(picture.mRealArea);
670 XLOG("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d",
672 picture.mRealArea.fLeft,
673 picture.mRealArea.fTop,
674 picture.mRealArea.fRight,
675 picture.mRealArea.fBottom,
676 picture.mRealArea.width(),
677 picture.mRealArea.height());
678 canvas->clipRect(pathBounds);
679 canvas->translate(pathBounds.fLeft, pathBounds.fTop);
681 canvas->drawPicture(*picture.mPicture);
682 canvas->restoreToCount(saved);
689 validate(__FUNCTION__);
690 Pictures* first = mPictures.begin();
691 Pictures* last = mPictures.end();
694 if (canvas->getClipBounds(&bounds) == false)
697 bounds.roundOut(&irect);
698 for (working = last; working != first; ) {
700 if (working->mArea.contains(irect)) {
701 #if PICTURE_SET_DEBUG
702 const SkIRect& b = working->mArea.getBounds();
703 DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
704 " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
705 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
711 DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
712 last - mPictures.begin());
713 uint32_t maxElapsed = 0;
714 for (working = first; working != last; working++) {
715 const SkRegion& area = working->mArea;
716 if (area.quickReject(irect)) {
717 #if PICTURE_SET_DEBUG
718 const SkIRect& b = area.getBounds();
719 DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
720 " irect={%d,%d,%d,%d}", working - first, working,
721 b.fLeft, b.fTop, b.fRight, b.fBottom,
722 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
724 working->mElapsed = 0;
727 int saved = canvas->save();
729 if (area.isComplex()) {
731 area.getBoundaryPath(&pathClip);
732 canvas->clipPath(pathClip);
733 pathBounds = pathClip.getBounds();
735 pathBounds.set(area.getBounds());
736 canvas->clipRect(pathBounds);
738 canvas->translate(pathBounds.fLeft, pathBounds.fTop);
740 uint32_t startTime = getThreadMsec();
741 canvas->drawPicture(*working->mPicture);
742 size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
743 working->mWroteElapsed = true;
744 if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
745 pathBounds.height() >= MIN_SPLITTABLE))
746 maxElapsed = elapsed;
747 canvas->restoreToCount(saved);
748 #define DRAW_TEST_IMAGE 01
749 #if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
750 SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
751 canvas->drawColor(color);
754 paint.setColor(color);
756 for (int x = area.getBounds().fLeft & ~0x3f;
757 x < area.getBounds().fRight; x += 0x40) {
758 for (int y = area.getBounds().fTop & ~0x3f;
759 y < area.getBounds().fBottom; y += 0x40) {
760 int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
761 canvas->drawText(location, len, x, y, paint);
765 DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
766 working - first, working,
767 area.getBounds().fLeft, area.getBounds().fTop,
768 area.getBounds().fRight, area.getBounds().fBottom,
769 working->mElapsed, working->mBase ? "true" : "false");
771 // dump(__FUNCTION__);
772 return maxElapsed >= MAX_DRAW_TIME;
773 #endif // FAST_PICTURESET
776 void PictureSet::dump(const char* label) const
779 DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
781 const Pictures* last = mPictures.end();
782 for (const Pictures* working = mPictures.begin(); working != last; working++) {
783 const SkIRect& bounds = working->mArea.getBounds();
784 const SkIRect& unsplit = working->mUnsplit;
785 MeasureStream measure;
786 if (working->mPicture != NULL)
787 working->mPicture->serialize(&measure);
789 " mArea.bounds={%d,%d,r=%d,b=%d}"
791 " mUnsplit={%d,%d,r=%d,b=%d}"
797 working - mPictures.begin(),
798 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
800 unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
801 working->mElapsed, working->mSplit ? "true" : "false",
802 working->mWroteElapsed ? "true" : "false",
803 working->mBase ? "true" : "false",
809 class IsEmptyBounder : public SkBounder {
810 virtual bool onIRect(const SkIRect& rect) {
815 class IsEmptyCanvas : public SkCanvas {
817 IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
818 mPicture(picture), mEmpty(true) {
824 mPicture->abortPlayback();
827 virtual bool clipPath(const SkPath&, SkRegion::Op) {
828 // this can be expensive to actually do, and doesn't affect the
829 // question of emptiness, so we make it a no-op
833 virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
834 const SkMatrix& , const SkPaint& ) {
835 if (bitmap.width() <= 1 || bitmap.height() <= 1)
837 DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
841 virtual void drawPaint(const SkPaint& paint) {
844 virtual void drawPath(const SkPath& , const SkPaint& paint) {
845 DBG_SET_LOG("abort");
849 virtual void drawPoints(PointMode , size_t , const SkPoint [],
850 const SkPaint& paint) {
853 virtual void drawRect(const SkRect& , const SkPaint& paint) {
854 // wait for visual content
855 if (paint.getColor() != SK_ColorWHITE)
859 virtual void drawSprite(const SkBitmap& , int , int ,
860 const SkPaint* paint = NULL) {
861 DBG_SET_LOG("abort");
865 virtual void drawText(const void* , size_t byteLength, SkScalar ,
866 SkScalar , const SkPaint& paint) {
867 DBG_SET_LOGD("abort %d", byteLength);
871 virtual void drawPosText(const void* , size_t byteLength,
872 const SkPoint [], const SkPaint& paint) {
873 DBG_SET_LOGD("abort %d", byteLength);
877 virtual void drawPosTextH(const void* , size_t byteLength,
878 const SkScalar [], SkScalar ,
879 const SkPaint& paint) {
880 DBG_SET_LOGD("abort %d", byteLength);
884 virtual void drawTextOnPath(const void* , size_t byteLength,
885 const SkPath& , const SkMatrix* ,
886 const SkPaint& paint) {
887 DBG_SET_LOGD("abort %d", byteLength);
891 virtual void drawPicture(SkPicture& picture) {
892 SkCanvas::drawPicture(picture);
899 bool PictureSet::emptyPicture(SkPicture* picture) const
901 IsEmptyBounder isEmptyBounder;
902 IsEmptyCanvas checker(&isEmptyBounder, picture);
904 bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
905 checker.setBitmapDevice(bitmap);
906 checker.drawPicture(*picture);
907 return checker.mEmpty;
910 bool PictureSet::isEmpty() const
912 #ifdef FAST_PICTURESET
913 // For now, just assume the pictureset is *not* empty
914 // if the hashmap contains something
915 for (BucketMap::const_iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
916 if (iter->second->size() > 0)
921 const Pictures* last = mPictures.end();
922 for (const Pictures* working = mPictures.begin(); working != last; working++) {
923 if (!working->mEmpty)
927 #endif // FAST_PICTURESET
930 void PictureSet::set(const PictureSet& src)
932 DBG_SET_LOGD("start %p src=%p", this, &src);
934 setDimensions(src.mWidth, src.mHeight);
935 #ifdef FAST_PICTURESET
936 XLOG("\n--- set picture ---");
937 for (BucketMap::const_iterator iter = src.mBuckets.begin();
938 iter != src.mBuckets.end(); ++iter) {
939 Bucket* sourceBucket = iter->second;
940 Bucket* targetBucket = getBucket(iter->first.first-1, iter->first.second-1);
941 BucketPicture* first = sourceBucket->begin();
942 BucketPicture* last = sourceBucket->end();
943 XLOG("set from bucket %x (%d, %d), %d pictures", sourceBucket,
944 iter->first.first, iter->first.second, sourceBucket->size());
945 for (BucketPicture* current = first; current != last; current++) {
946 XLOG("set picture %x from bucket %x in bucket %x (%d, %d)",
947 current->mPicture, sourceBucket, targetBucket,
948 iter->first.first, iter->first.second);
949 SkSafeRef(current->mPicture);
950 BucketPicture picture = { current->mPicture, current->mArea,
951 current->mRealArea, current->mBase };
952 targetBucket->append(picture);
955 XLOG("--- DONE set picture ---\n");
957 const Pictures* last = src.mPictures.end();
958 for (const Pictures* working = src.mPictures.begin(); working != last; working++)
960 // dump(__FUNCTION__);
961 validate(__FUNCTION__);
963 #endif // FAST_PICTURESET
966 #ifdef FAST_PICTURESET
969 bool PictureSet::reuseSubdivided(const SkRegion& inval)
971 validate(__FUNCTION__);
973 if (inval.isComplex())
975 Pictures* working, * last = mPictures.end();
976 const SkIRect& invalBounds = inval.getBounds();
978 for (working = mPictures.begin(); working != last; working++) {
979 if (working->mSplit && invalBounds == working->mUnsplit) {
985 SkRegion temp = SkRegion(inval);
986 temp.op(working->mArea, SkRegion::kIntersect_Op);
987 if (temp.isEmpty() || temp == working->mArea)
993 for (working = mPictures.begin(); working != last; working++) {
994 if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
995 inval.contains(working->mArea) == false)
997 SkSafeUnref(working->mPicture);
998 working->mPicture = NULL;
1003 void PictureSet::setDrawTimes(const PictureSet& src)
1005 validate(__FUNCTION__);
1006 if (mWidth != src.mWidth || mHeight != src.mHeight)
1008 Pictures* last = mPictures.end();
1009 Pictures* working = mPictures.begin();
1010 if (working == last)
1012 const Pictures* srcLast = src.mPictures.end();
1013 const Pictures* srcWorking = src.mPictures.begin();
1014 for (; srcWorking != srcLast; srcWorking++) {
1015 if (srcWorking->mWroteElapsed == false)
1017 while ((srcWorking->mArea != working->mArea ||
1018 srcWorking->mPicture != working->mPicture)) {
1019 if (++working == last)
1022 DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
1023 this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
1024 working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
1025 working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
1026 working->mElapsed, srcWorking->mElapsed);
1027 working->mElapsed = srcWorking->mElapsed;
1031 void PictureSet::setPicture(size_t i, SkPicture* p)
1033 SkSafeUnref(mPictures[i].mPicture);
1034 mPictures[i].mPicture = p;
1035 mPictures[i].mEmpty = emptyPicture(p);
1038 void PictureSet::split(PictureSet* out) const
1041 DBG_SET_LOGD("%p", this);
1042 SkIRect totalBounds;
1043 out->mWidth = mWidth;
1044 out->mHeight = mHeight;
1045 totalBounds.set(0, 0, mWidth, mHeight);
1046 SkRegion* total = new SkRegion(totalBounds);
1047 const Pictures* last = mPictures.end();
1048 const Pictures* working;
1049 uint32_t balance = 0;
1050 int multiUnsplitFastPictures = 0; // > 1 has more than 1
1051 for (working = mPictures.begin(); working != last; working++) {
1052 if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
1054 if (++multiUnsplitFastPictures > 1)
1057 for (working = mPictures.begin(); working != last; working++) {
1058 uint32_t elapsed = working->mElapsed;
1059 if (elapsed < MAX_DRAW_TIME) {
1060 bool split = working->mSplit;
1061 DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
1062 "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
1063 total->getBounds().fLeft, total->getBounds().fTop,
1064 total->getBounds().fRight, total->getBounds().fBottom,
1065 split ? "true" : "false");
1066 if (multiUnsplitFastPictures <= 1 || split) {
1067 total->op(working->mArea, SkRegion::kDifference_Op);
1068 out->add(working->mArea, working->mPicture, elapsed, split,
1070 } else if (balance < elapsed)
1074 total->op(working->mArea, SkRegion::kDifference_Op);
1075 const SkIRect& bounds = working->mArea.getBounds();
1076 int width = bounds.width();
1077 int height = bounds.height();
1080 while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
1081 if (height >= width) {
1088 if ((elapsed >>= 1) < MAX_DRAW_TIME)
1091 width = bounds.width();
1092 height = bounds.height();
1093 int top = bounds.fTop;
1094 for (int indexY = 0; indexY < down; ) {
1095 int bottom = bounds.fTop + height * ++indexY / down;
1096 int left = bounds.fLeft;
1097 for (int indexX = 0; indexX < across; ) {
1098 int right = bounds.fLeft + width * ++indexX / across;
1100 cBounds.set(left, top, right, bottom);
1101 out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
1102 working->mPicture, elapsed, true,
1103 (across | down) != 1 ? false : working->mEmpty);
1109 DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
1110 this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
1111 multiUnsplitFastPictures);
1112 if (!total->isEmpty() && multiUnsplitFastPictures > 1)
1113 out->add(*total, NULL, balance, false, false);
1115 validate(__FUNCTION__);
1116 out->dump("split-out");
1119 #endif // FAST_PICTURESET
1121 bool PictureSet::validate(const char* funct) const
1123 #ifdef FAST_PICTURESET
1127 #if PICTURE_SET_VALIDATE
1129 const Pictures* first = mPictures.begin();
1130 for (const Pictures* working = mPictures.end(); working != first; ) {
1132 const SkPicture* pict = working->mPicture;
1133 const SkRegion& area = working->mArea;
1134 const SkIRect& bounds = area.getBounds();
1135 bool localValid = false;
1136 if (working->mUnsplit.isEmpty())
1137 LOGD("%s working->mUnsplit.isEmpty()", funct);
1138 else if (working->mUnsplit.contains(bounds) == false)
1139 LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
1140 else if (working->mElapsed >= 1000)
1141 LOGD("%s working->mElapsed >= 1000", funct);
1142 else if ((working->mSplit & 0xfe) != 0)
1143 LOGD("%s (working->mSplit & 0xfe) != 0", funct);
1144 else if ((working->mWroteElapsed & 0xfe) != 0)
1145 LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
1146 else if (pict != NULL) {
1147 int pictWidth = pict->width();
1148 int pictHeight = pict->height();
1149 if (pictWidth < bounds.width())
1150 LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
1151 else if (pictHeight < bounds.height())
1152 LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
1153 else if (working->mArea.isEmpty())
1154 LOGD("%s working->mArea.isEmpty()", funct);
1159 working->mArea.validate();
1160 if (localValid == false) {
1161 if (all.contains(area) == true)
1162 LOGD("%s all.contains(area) == true", funct);
1166 valid &= localValid;
1167 all.op(area, SkRegion::kUnion_Op);
1169 const SkIRect& allBounds = all.getBounds();
1172 if (allBounds.width() != mWidth)
1173 LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
1174 else if (allBounds.height() != mHeight)
1175 LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
1179 while (valid == false)
1183 #endif // FAST_PICTURESET
1186 } /* namespace android */