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 512
48 #include <wtf/CurrentTime.h>
50 #include <cutils/log.h>
51 #include <wtf/text/CString.h>
54 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
59 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
69 class MeasureStream : public SkWStream {
71 MeasureStream() : mTotal(0) {}
72 virtual bool write(const void* , size_t size) {
82 PictureSet::PictureSet()
85 mBaseArea = mAdditionalArea = 0;
88 PictureSet::PictureSet(SkPicture* picture)
90 mBaseArea = mAdditionalArea = 0;
95 mWidth = picture->width();
96 mHeight = picture->height();
97 mBaseArea = mWidth * mHeight;
98 #ifdef FAST_PICTURESET
100 Pictures pictureAndBounds;
101 pictureAndBounds.mPicture = picture;
102 SkSafeRef(pictureAndBounds.mPicture);
103 pictureAndBounds.mEmpty = false;
104 pictureAndBounds.mArea.setRect(0, 0, mWidth, mHeight);
105 pictureAndBounds.mSplit = false;
106 pictureAndBounds.mBase = true;
107 pictureAndBounds.mElapsed = 0;
108 pictureAndBounds.mWroteElapsed = false;
109 mPictures.append(pictureAndBounds);
110 #endif // FAST_PICTURESET
113 PictureSet::~PictureSet()
118 #ifdef FAST_PICTURESET
120 void PictureSet::add(const Pictures* temp)
122 Pictures pictureAndBounds = *temp;
123 SkSafeRef(pictureAndBounds.mPicture);
124 pictureAndBounds.mWroteElapsed = false;
125 mPictures.append(pictureAndBounds);
127 #endif // FAST_PICTURESET
129 void PictureSet::add(const SkRegion& area, SkPicture* picture,
130 uint32_t elapsed, bool split)
133 #ifdef FAST_PICTURESET
134 splitAdd(area.getBounds());
136 add(area, picture, elapsed, split, false);
137 #endif // FAST_PICTURESET
139 SkRegion::Iterator cliperator(area);
140 while (!cliperator.done()) {
141 SkIRect ir = cliperator.rect();
142 #ifdef FAST_PICTURESET
147 add(newArea, picture, elapsed, split, false);
148 #endif // FAST_PICTURESET
154 #ifdef FAST_PICTURESET
156 Bucket* PictureSet::getBucket(int x, int y)
158 BucketPosition position(x+1, y+1);
159 if (!mBuckets.contains(position)) {
160 Bucket* bucket = new Bucket();
161 mBuckets.add(position, bucket);
163 return mBuckets.get(position);
166 void PictureSet::displayBucket(Bucket* bucket)
168 BucketPicture* first = bucket->begin();
169 BucketPicture* last = bucket->end();
170 for (BucketPicture* current = first; current != last; current++) {
171 XLOGC("- in %x, bucketPicture %d,%d,%d,%d - %dx%d, picture: %x, base: %x",
173 current->mArea.fLeft,
175 current->mArea.fRight,
176 current->mArea.fBottom,
177 current->mArea.width(),
178 current->mArea.height(),
184 void PictureSet::displayBuckets()
186 XLOGC("\n\n****** DISPLAY BUCKETS ON PictureSet %x ******", this);
187 for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
188 XLOGC("\n*** Bucket %x for %d, %d", iter->second, iter->first.first, iter->first.second);
189 displayBucket(iter->second);
191 XLOGC("\n****** END OF DISPLAY BUCKETS ******\n\n");
194 // When we receive an inval in a Bucket, we try to see if we intersect with
195 // existing invals/pictures in the Bucket.
196 void PictureSet::addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect)
198 bool resetBase = false;
200 SkIRect totalArea = rect;
201 BucketPicture* first = bucket->begin();
202 BucketPicture* last = bucket->end();
204 // If the inval covers a large area of the base inval, let's repaint the
206 if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * BUCKET_SIZE * BUCKET_SIZE)
209 // let's gather all the BucketPicture intersecting with the new invalidated
210 // area, collect their area and remove their picture
211 for (BucketPicture* current = first; current != last; current++) {
212 bool remove = resetBase;
213 bool intersect = false;
216 intersect = SkIRect::Intersects(current->mArea, rect);
217 // If the current picture is not a base, and we intersect, remove it
218 if (!remove && !current->mBase && intersect)
220 // If the current picture is a base, check if the new inval completely
221 // contains the base, and if so remove it.
222 if (!remove && current->mBase && rect.contains(current->mArea))
224 // If the current picture is a base and it intersects,
225 // also check that it fully covers the bucket -- otherwise,
226 // let's aggregate it with the new inval.
227 if (!remove && current->mBase && intersect
228 && (current->mArea.width() < BUCKET_SIZE || current->mArea.height() < BUCKET_SIZE)) {
233 totalArea.join(current->mArea);
234 current->mBase = false;
235 current->mArea.setEmpty();
236 SkSafeUnref(current->mPicture);
237 current->mPicture = 0;
241 // Now, let's add the new BucketPicture to the list, with the correct
242 // area that needs to be repainted
244 SkIRect area = totalArea;
246 BucketPicture picture = { 0, totalArea, area, false };
248 bucket->append(picture);
250 first = bucket->begin();
251 last = bucket->end();
253 // let's do a pass to collapse out empty areas
254 BucketPicture* writer = first;
255 for (BucketPicture* current = first; current != last; current++) {
256 if (current && current->mArea.isEmpty())
258 *writer++ = *current;
261 bucket->shrink(writer - first);
263 // let's recompute the bases
264 first = bucket->begin();
265 last = bucket->end();
268 for (BucketPicture* current = first; current != last; current++) {
269 if (drawn.contains(current->mArea) == false) {
270 current->mBase = true;
272 drawn.op(current->mArea, SkRegion::kUnion_Op);
276 void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect)
278 int maxSize = BUCKET_SIZE;
282 int firstTileX = rect.fLeft / maxSize;
283 int firstTileY = rect.fTop / maxSize;
284 int lastTileX = rect.fRight / maxSize;
285 int lastTileY = rect.fBottom / maxSize;
287 for (int i = firstTileX; i <= lastTileX; i++) {
288 for (int j = firstTileY; j <= lastTileY; j++) {
289 Bucket* bucket = getBucket(i, j);
290 XLOG("gather bucket %x for %d, %d", bucket, i+1, j+1);
296 // When we receive a new inval rect, we first find the Buckets that intersect
297 // with it; then we split the original inval into a serie of invals (one for
298 // each Bucket we intersect with). We then send that inval to the Bucket.
299 void PictureSet::splitAdd(const SkIRect& rect)
301 XLOG("\n--- splitAdd for rect %d, %d, %d, %d (%d x %d)",
302 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
303 rect.width(), rect.height());
305 int maxSize = BUCKET_SIZE;
307 // TODO: reuse gatherBucketsForArea() (change Bucket to be a class)
310 int firstTileX = rect.fLeft / maxSize;
311 int firstTileY = rect.fTop / maxSize;
312 int lastTileX = rect.fRight / maxSize;
313 int lastTileY = rect.fBottom / maxSize;
315 XLOG("--- firstTile(%d, %d) lastTile(%d, %d)",
316 firstTileX, firstTileY,
317 lastTileX, lastTileY);
319 for (int i = firstTileX; i <= lastTileX; i++) {
320 for (int j = firstTileY; j <= lastTileY; j++) {
321 Bucket* bucket = getBucket(i, j);
323 int deltaX = i * maxSize;
324 int deltaY = j * maxSize;
325 int left, top, right, bottom;
335 right = rect.fRight % maxSize;
339 bottom = rect.fBottom % maxSize;
343 newRect.set(left, top, right, bottom);
344 addToBucket(bucket, deltaX, deltaY, newRect);
345 mUpdatedBuckets.append(bucket);
349 XLOG("--- splitAdd DONE\n");
352 #endif // FAST_PICTURESET
354 // This function is used to maintain the list of Pictures.
355 // Pictures contain an SkPicture covering a specific area; some
356 // Pictures are "base" Pictures -- i.e. there is no Pictures
358 // The idea here is to keep a balance between the number of Pictures
359 // we have (more Pictures slow us down) and the area of Pictures that
360 // need to be repainted (obviously, smaller areas are better).
361 // To do so, we try to not update/repaint the base pictures -- by
362 // construction, they usually cover a large area (the entire page).
363 // We only reset a base picture if the new invalidated area entirely
365 // Most of the time we thus work on smaller pictures on top of the
366 // base ones; We compute the total area of all pictures intersecting
367 // with the passed invalidated area (as they would need to be invalidated),
368 // and use that as the basis for the correct area we want to invalidate
369 // (we then can simply delete the pictures we intersect with).
370 // In addition, we do a couple of things to limit the total number of pictures
371 // we keep in the list:
372 // - if the total area of additional textures reach 65% of the base pictures,
373 // we delete the additional pictures and mark the base pictures as
374 // needing a full repaint
375 // - we limit the number of pictures to 32 -- above that, we do the same
376 // things (deleting additional pictures + full repaint of base pictures)
377 #ifdef FAST_PICTURESET
379 void PictureSet::add(const SkRegion& area, SkPicture* picture,
380 uint32_t elapsed, bool split, bool empty)
382 bool checkForNewBases = false;
384 Pictures* first = mPictures.begin();
385 Pictures* last = mPictures.end();
387 XLOG("--- before adding the new inval ---");
388 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
389 SkIRect currentArea = working->mArea.getBounds();
390 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
392 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
393 currentArea.width(), currentArea.height(),
394 working->mArea.isRect() ? 'Y' : 'N',
395 working->mBase ? 'Y' : 'N');
397 XLOG("----------------------------------");
400 // let's gather all the Pictures intersecting with the new invalidated
401 // area, collect their area and remove their picture
402 SkIRect totalArea = area.getBounds();
403 for (Pictures* working = first; working != last; working++) {
404 SkIRect inval = area.getBounds();
406 if (!working->mBase && working->mArea.intersects(inval))
408 if (working->mBase) {
409 SkIRect baseArea = working->mArea.getBounds();
410 if (area.contains(baseArea)) {
412 checkForNewBases = true;
417 SkIRect currentArea = working->mArea.getBounds();
419 mBaseArea -= currentArea.width() * currentArea.height();
421 mAdditionalArea -= currentArea.width() * currentArea.height();
423 totalArea.join(currentArea);
424 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",
426 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
427 currentArea.width(), currentArea.height(),
428 working->mArea.isRect() ? 'Y' : 'N',
429 inval.fLeft, inval.fTop, inval.fRight, inval.fBottom,
430 inval.width(), inval.height(),
431 area.isRect() ? 'Y' : 'N');
432 working->mArea.setEmpty();
433 SkSafeUnref(working->mPicture);
434 working->mPicture = 0;
438 // Now we can add the new Picture to the list, with the correct area
439 // that need to be repainted
441 collect.setRect(totalArea);
442 Pictures pictureAndBounds = {collect, 0, collect.getBounds(),
443 elapsed, split, false, false, empty};
445 if (mPictures.size() == 0)
446 checkForNewBases = true;
448 mPictures.append(pictureAndBounds);
449 mAdditionalArea += totalArea.width() * totalArea.height();
450 last = mPictures.end();
451 first = mPictures.begin();
453 // Then, let's see if we have to clear up the pictures in order to keep
454 // the total number of pictures under our limit
455 bool clearUp = false;
456 if (last - first > MAX_ADDITIONAL_PICTURES) {
457 XLOG("--- too many pictures, only keeping the bases : %d", last - first);
462 if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) {
463 XLOG("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f",
464 MAX_ADDITIONAL_AREA * 100, mBaseArea * 0.65, mBaseArea, mAdditionalArea);
470 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
472 working->mArea.setEmpty();
473 SkSafeUnref(working->mPicture);
474 working->mPicture = 0;
479 XLOG("--- after adding the new inval, but before collapsing ---");
480 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
481 SkIRect currentArea = working->mArea.getBounds();
482 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
484 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
485 currentArea.width(), currentArea.height(),
486 working->mArea.isRect() ? 'Y' : 'N',
487 working->mBase ? 'Y' : 'N');
489 XLOG("----------------------------------");
490 XLOG("let's collapse...");
493 // Finally, let's do a pass to collapse out empty regions
494 Pictures* writer = first;
495 for (Pictures* working = first; working != last; working++) {
496 if (working && working->mArea.isEmpty())
498 *writer++ = *working;
500 XLOG("shiking of %d elements", writer - first);
501 mPictures.shrink(writer - first);
504 XLOG("--- after adding the new inval ---");
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 picture %x",
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', working->mPicture);
514 XLOG("----------------------------------");
517 // Base pictures might have been removed/added -- let's recompute them
519 if (checkForNewBases) {
521 Pictures* last = mPictures.end();
522 XLOG("checkForNewBases...");
523 for (Pictures* working = mPictures.begin(); working != last; working++) {
524 SkRegion& area = working->mArea;
525 const SkIRect& a = area.getBounds();
526 if (drawn.contains(working->mArea) == false) {
527 working->mBase = true;
528 float area = a.width() * a.height();
530 mAdditionalArea -= area;
532 drawn.op(working->mArea, SkRegion::kUnion_Op);
536 #endif // FAST_PICTURESET
538 void PictureSet::checkDimensions(int width, int height, SkRegion* inval)
540 if (mWidth == width && mHeight == height)
542 DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
543 mWidth, mHeight, width, height);
544 if (mWidth == width && height > mHeight) { // only grew vertically
546 rect.set(0, mHeight, width, height);
547 inval->op(rect, SkRegion::kUnion_Op);
549 clear(); // if both width/height changed, clear the old cache
550 inval->setRect(0, 0, width, height);
556 void PictureSet::clear()
559 #ifdef FAST_PICTURESET
560 for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
561 Bucket* bucket = iter->second;
562 BucketPicture* first = bucket->begin();
563 BucketPicture* last = bucket->end();
564 for (BucketPicture* current = first; current != last; current++) {
565 SkSafeUnref(current->mPicture);
566 current->mPicture = 0;
572 Pictures* last = mPictures.end();
573 for (Pictures* working = mPictures.begin(); working != last; working++) {
574 working->mArea.setEmpty();
575 SkSafeUnref(working->mPicture);
578 #endif // FAST_PICTURESET
579 mWidth = mHeight = 0;
582 bool PictureSet::draw(SkCanvas* canvas)
584 #ifdef FAST_PICTURESET
585 XLOG("PictureSet %x draw on canvas %x", this, canvas);
587 if (canvas->getClipBounds(&bounds) == false)
590 bounds.roundOut(&irect);
592 WTF::Vector<Bucket*> list;
593 gatherBucketsForArea(list, irect);
595 XLOG("PictureSet draw on canvas %x, we have %d buckets", canvas, list.size());
596 for (unsigned int i = 0; i < list.size(); i++) {
597 Bucket* bucket = list[i];
598 XLOG("We paint using bucket %x with %d pictures", bucket, bucket->size());
599 for (unsigned int j = 0; j < bucket->size(); j++) {
600 BucketPicture& picture = bucket->at(j);
601 if (!picture.mPicture)
603 int saved = canvas->save();
605 pathBounds.set(picture.mRealArea);
606 XLOG("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d",
608 picture.mRealArea.fLeft,
609 picture.mRealArea.fTop,
610 picture.mRealArea.fRight,
611 picture.mRealArea.fBottom,
612 picture.mRealArea.width(),
613 picture.mRealArea.height());
614 canvas->clipRect(pathBounds);
615 canvas->translate(pathBounds.fLeft, pathBounds.fTop);
617 canvas->drawPicture(*picture.mPicture);
618 canvas->restoreToCount(saved);
625 validate(__FUNCTION__);
626 Pictures* first = mPictures.begin();
627 Pictures* last = mPictures.end();
630 if (canvas->getClipBounds(&bounds) == false)
633 bounds.roundOut(&irect);
634 for (working = last; working != first; ) {
636 if (working->mArea.contains(irect)) {
637 #if PICTURE_SET_DEBUG
638 const SkIRect& b = working->mArea.getBounds();
639 DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
640 " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
641 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
647 DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
648 last - mPictures.begin());
649 uint32_t maxElapsed = 0;
650 for (working = first; working != last; working++) {
651 const SkRegion& area = working->mArea;
652 if (area.quickReject(irect)) {
653 #if PICTURE_SET_DEBUG
654 const SkIRect& b = area.getBounds();
655 DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
656 " irect={%d,%d,%d,%d}", working - first, working,
657 b.fLeft, b.fTop, b.fRight, b.fBottom,
658 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
660 working->mElapsed = 0;
663 int saved = canvas->save();
665 if (area.isComplex()) {
667 area.getBoundaryPath(&pathClip);
668 canvas->clipPath(pathClip);
669 pathBounds = pathClip.getBounds();
671 pathBounds.set(area.getBounds());
672 canvas->clipRect(pathBounds);
674 canvas->translate(pathBounds.fLeft, pathBounds.fTop);
676 uint32_t startTime = getThreadMsec();
677 canvas->drawPicture(*working->mPicture);
678 size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
679 working->mWroteElapsed = true;
680 if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
681 pathBounds.height() >= MIN_SPLITTABLE))
682 maxElapsed = elapsed;
683 canvas->restoreToCount(saved);
684 #define DRAW_TEST_IMAGE 01
685 #if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
686 SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
687 canvas->drawColor(color);
690 paint.setColor(color);
692 for (int x = area.getBounds().fLeft & ~0x3f;
693 x < area.getBounds().fRight; x += 0x40) {
694 for (int y = area.getBounds().fTop & ~0x3f;
695 y < area.getBounds().fBottom; y += 0x40) {
696 int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
697 canvas->drawText(location, len, x, y, paint);
701 DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
702 working - first, working,
703 area.getBounds().fLeft, area.getBounds().fTop,
704 area.getBounds().fRight, area.getBounds().fBottom,
705 working->mElapsed, working->mBase ? "true" : "false");
707 // dump(__FUNCTION__);
708 return maxElapsed >= MAX_DRAW_TIME;
709 #endif // FAST_PICTURESET
712 void PictureSet::dump(const char* label) const
715 DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
717 const Pictures* last = mPictures.end();
718 for (const Pictures* working = mPictures.begin(); working != last; working++) {
719 const SkIRect& bounds = working->mArea.getBounds();
720 const SkIRect& unsplit = working->mUnsplit;
721 MeasureStream measure;
722 if (working->mPicture != NULL)
723 working->mPicture->serialize(&measure);
725 " mArea.bounds={%d,%d,r=%d,b=%d}"
727 " mUnsplit={%d,%d,r=%d,b=%d}"
733 working - mPictures.begin(),
734 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
736 unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
737 working->mElapsed, working->mSplit ? "true" : "false",
738 working->mWroteElapsed ? "true" : "false",
739 working->mBase ? "true" : "false",
745 class IsEmptyBounder : public SkBounder {
746 virtual bool onIRect(const SkIRect& rect) {
751 class IsEmptyCanvas : public SkCanvas {
753 IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
754 mPicture(picture), mEmpty(true) {
760 mPicture->abortPlayback();
763 virtual bool clipPath(const SkPath&, SkRegion::Op) {
764 // this can be expensive to actually do, and doesn't affect the
765 // question of emptiness, so we make it a no-op
769 virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
770 const SkMatrix& , const SkPaint& ) {
771 if (bitmap.width() <= 1 || bitmap.height() <= 1)
773 DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
777 virtual void drawPaint(const SkPaint& paint) {
780 virtual void drawPath(const SkPath& , const SkPaint& paint) {
781 DBG_SET_LOG("abort");
785 virtual void drawPoints(PointMode , size_t , const SkPoint [],
786 const SkPaint& paint) {
789 virtual void drawRect(const SkRect& , const SkPaint& paint) {
790 // wait for visual content
791 if (paint.getColor() != SK_ColorWHITE)
795 virtual void drawSprite(const SkBitmap& , int , int ,
796 const SkPaint* paint = NULL) {
797 DBG_SET_LOG("abort");
801 virtual void drawText(const void* , size_t byteLength, SkScalar ,
802 SkScalar , const SkPaint& paint) {
803 DBG_SET_LOGD("abort %d", byteLength);
807 virtual void drawPosText(const void* , size_t byteLength,
808 const SkPoint [], const SkPaint& paint) {
809 DBG_SET_LOGD("abort %d", byteLength);
813 virtual void drawPosTextH(const void* , size_t byteLength,
814 const SkScalar [], SkScalar ,
815 const SkPaint& paint) {
816 DBG_SET_LOGD("abort %d", byteLength);
820 virtual void drawTextOnPath(const void* , size_t byteLength,
821 const SkPath& , const SkMatrix* ,
822 const SkPaint& paint) {
823 DBG_SET_LOGD("abort %d", byteLength);
827 virtual void drawPicture(SkPicture& picture) {
828 SkCanvas::drawPicture(picture);
835 bool PictureSet::emptyPicture(SkPicture* picture) const
837 IsEmptyBounder isEmptyBounder;
838 IsEmptyCanvas checker(&isEmptyBounder, picture);
840 bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
841 checker.setBitmapDevice(bitmap);
842 checker.drawPicture(*picture);
843 return checker.mEmpty;
846 bool PictureSet::isEmpty() const
848 #ifdef FAST_PICTURESET
849 // For now, just assume the pictureset is *not* empty
850 // if the hashmap contains something
851 for (BucketMap::const_iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
852 if (iter->second->size() > 0)
857 const Pictures* last = mPictures.end();
858 for (const Pictures* working = mPictures.begin(); working != last; working++) {
859 if (!working->mEmpty)
863 #endif // FAST_PICTURESET
866 void PictureSet::set(const PictureSet& src)
868 DBG_SET_LOGD("start %p src=%p", this, &src);
871 mHeight = src.mHeight;
872 #ifdef FAST_PICTURESET
873 XLOG("\n--- set picture ---");
874 for (BucketMap::const_iterator iter = src.mBuckets.begin();
875 iter != src.mBuckets.end(); ++iter) {
876 Bucket* sourceBucket = iter->second;
877 Bucket* targetBucket = getBucket(iter->first.first-1, iter->first.second-1);
878 BucketPicture* first = sourceBucket->begin();
879 BucketPicture* last = sourceBucket->end();
880 XLOG("set from bucket %x (%d, %d), %d pictures", sourceBucket,
881 iter->first.first, iter->first.second, sourceBucket->size());
882 for (BucketPicture* current = first; current != last; current++) {
883 XLOG("set picture %x from bucket %x in bucket %x (%d, %d)",
884 current->mPicture, sourceBucket, targetBucket,
885 iter->first.first, iter->first.second);
886 SkSafeRef(current->mPicture);
887 BucketPicture picture = { current->mPicture, current->mArea,
888 current->mRealArea, current->mBase };
889 targetBucket->append(picture);
892 XLOG("--- DONE set picture ---\n");
894 const Pictures* last = src.mPictures.end();
895 for (const Pictures* working = src.mPictures.begin(); working != last; working++)
897 // dump(__FUNCTION__);
898 validate(__FUNCTION__);
900 #endif // FAST_PICTURESET
903 #ifdef FAST_PICTURESET
906 bool PictureSet::reuseSubdivided(const SkRegion& inval)
908 validate(__FUNCTION__);
910 if (inval.isComplex())
912 Pictures* working, * last = mPictures.end();
913 const SkIRect& invalBounds = inval.getBounds();
915 for (working = mPictures.begin(); working != last; working++) {
916 if (working->mSplit && invalBounds == working->mUnsplit) {
922 SkRegion temp = SkRegion(inval);
923 temp.op(working->mArea, SkRegion::kIntersect_Op);
924 if (temp.isEmpty() || temp == working->mArea)
930 for (working = mPictures.begin(); working != last; working++) {
931 if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
932 inval.contains(working->mArea) == false)
934 SkSafeUnref(working->mPicture);
935 working->mPicture = NULL;
940 void PictureSet::setDrawTimes(const PictureSet& src)
942 validate(__FUNCTION__);
943 if (mWidth != src.mWidth || mHeight != src.mHeight)
945 Pictures* last = mPictures.end();
946 Pictures* working = mPictures.begin();
949 const Pictures* srcLast = src.mPictures.end();
950 const Pictures* srcWorking = src.mPictures.begin();
951 for (; srcWorking != srcLast; srcWorking++) {
952 if (srcWorking->mWroteElapsed == false)
954 while ((srcWorking->mArea != working->mArea ||
955 srcWorking->mPicture != working->mPicture)) {
956 if (++working == last)
959 DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
960 this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
961 working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
962 working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
963 working->mElapsed, srcWorking->mElapsed);
964 working->mElapsed = srcWorking->mElapsed;
968 void PictureSet::setPicture(size_t i, SkPicture* p)
970 SkSafeUnref(mPictures[i].mPicture);
971 mPictures[i].mPicture = p;
972 mPictures[i].mEmpty = emptyPicture(p);
975 void PictureSet::split(PictureSet* out) const
978 DBG_SET_LOGD("%p", this);
980 out->mWidth = mWidth;
981 out->mHeight = mHeight;
982 totalBounds.set(0, 0, mWidth, mHeight);
983 SkRegion* total = new SkRegion(totalBounds);
984 const Pictures* last = mPictures.end();
985 const Pictures* working;
986 uint32_t balance = 0;
987 int multiUnsplitFastPictures = 0; // > 1 has more than 1
988 for (working = mPictures.begin(); working != last; working++) {
989 if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
991 if (++multiUnsplitFastPictures > 1)
994 for (working = mPictures.begin(); working != last; working++) {
995 uint32_t elapsed = working->mElapsed;
996 if (elapsed < MAX_DRAW_TIME) {
997 bool split = working->mSplit;
998 DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
999 "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
1000 total->getBounds().fLeft, total->getBounds().fTop,
1001 total->getBounds().fRight, total->getBounds().fBottom,
1002 split ? "true" : "false");
1003 if (multiUnsplitFastPictures <= 1 || split) {
1004 total->op(working->mArea, SkRegion::kDifference_Op);
1005 out->add(working->mArea, working->mPicture, elapsed, split,
1007 } else if (balance < elapsed)
1011 total->op(working->mArea, SkRegion::kDifference_Op);
1012 const SkIRect& bounds = working->mArea.getBounds();
1013 int width = bounds.width();
1014 int height = bounds.height();
1017 while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
1018 if (height >= width) {
1025 if ((elapsed >>= 1) < MAX_DRAW_TIME)
1028 width = bounds.width();
1029 height = bounds.height();
1030 int top = bounds.fTop;
1031 for (int indexY = 0; indexY < down; ) {
1032 int bottom = bounds.fTop + height * ++indexY / down;
1033 int left = bounds.fLeft;
1034 for (int indexX = 0; indexX < across; ) {
1035 int right = bounds.fLeft + width * ++indexX / across;
1037 cBounds.set(left, top, right, bottom);
1038 out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
1039 working->mPicture, elapsed, true,
1040 (across | down) != 1 ? false : working->mEmpty);
1046 DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
1047 this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
1048 multiUnsplitFastPictures);
1049 if (!total->isEmpty() && multiUnsplitFastPictures > 1)
1050 out->add(*total, NULL, balance, false, false);
1052 validate(__FUNCTION__);
1053 out->dump("split-out");
1056 #endif // FAST_PICTURESET
1058 bool PictureSet::validate(const char* funct) const
1060 #ifdef FAST_PICTURESET
1064 #if PICTURE_SET_VALIDATE
1066 const Pictures* first = mPictures.begin();
1067 for (const Pictures* working = mPictures.end(); working != first; ) {
1069 const SkPicture* pict = working->mPicture;
1070 const SkRegion& area = working->mArea;
1071 const SkIRect& bounds = area.getBounds();
1072 bool localValid = false;
1073 if (working->mUnsplit.isEmpty())
1074 LOGD("%s working->mUnsplit.isEmpty()", funct);
1075 else if (working->mUnsplit.contains(bounds) == false)
1076 LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
1077 else if (working->mElapsed >= 1000)
1078 LOGD("%s working->mElapsed >= 1000", funct);
1079 else if ((working->mSplit & 0xfe) != 0)
1080 LOGD("%s (working->mSplit & 0xfe) != 0", funct);
1081 else if ((working->mWroteElapsed & 0xfe) != 0)
1082 LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
1083 else if (pict != NULL) {
1084 int pictWidth = pict->width();
1085 int pictHeight = pict->height();
1086 if (pictWidth < bounds.width())
1087 LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
1088 else if (pictHeight < bounds.height())
1089 LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
1090 else if (working->mArea.isEmpty())
1091 LOGD("%s working->mArea.isEmpty()", funct);
1096 working->mArea.validate();
1097 if (localValid == false) {
1098 if (all.contains(area) == true)
1099 LOGD("%s all.contains(area) == true", funct);
1103 valid &= localValid;
1104 all.op(area, SkRegion::kUnion_Op);
1106 const SkIRect& allBounds = all.getBounds();
1109 if (allBounds.width() != mWidth)
1110 LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
1111 else if (allBounds.height() != mHeight)
1112 LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
1116 while (valid == false)
1120 #endif // FAST_PICTURESET
1123 } /* namespace android */