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 #include <wtf/CurrentTime.h>
51 #include <cutils/log.h>
52 #include <wtf/text/CString.h>
55 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
65 class MeasureStream : public SkWStream {
67 MeasureStream() : mTotal(0) {}
68 virtual bool write(const void* , size_t size) {
78 PictureSet::PictureSet()
81 mBaseArea = mAdditionalArea = 0;
84 PictureSet::PictureSet(SkPicture* picture)
88 Pictures pictureAndBounds;
89 pictureAndBounds.mPicture = picture;
90 SkSafeRef(pictureAndBounds.mPicture);
91 pictureAndBounds.mEmpty = false;
92 pictureAndBounds.mArea.setRect(0, 0, picture->width(), picture->height());
93 pictureAndBounds.mSplit = false;
94 pictureAndBounds.mBase = true;
95 pictureAndBounds.mElapsed = 0;
96 pictureAndBounds.mWroteElapsed = false;
97 mPictures.append(pictureAndBounds);
100 PictureSet::~PictureSet()
105 void PictureSet::add(const Pictures* temp)
107 Pictures pictureAndBounds = *temp;
108 SkSafeRef(pictureAndBounds.mPicture);
109 pictureAndBounds.mWroteElapsed = false;
110 mPictures.append(pictureAndBounds);
113 // This function is used to maintain the list of Pictures.
114 // Pictures contain an SkPicture covering a specific area; some
115 // Pictures are "base" Pictures -- i.e. there is no Pictures
117 // The idea here is to keep a balance between the number of Pictures
118 // we have (more Pictures slow us down) and the area of Pictures that
119 // need to be repainted (obviously, smaller areas are better).
120 // To do so, we try to not update/repaint the base pictures -- by
121 // construction, they usually cover a large area (the entire page).
122 // We only reset a base picture if the new invalidated area entirely
124 // Most of the time we thus work on smaller pictures on top of the
125 // base ones; We compute the total area of all pictures intersecting
126 // with the passed invalidated area (as they would need to be invalidated),
127 // and use that as the basis for the correct area we want to invalidate
128 // (we then can simply delete the pictures we intersect with).
129 // In addition, we do a couple of things to limit the total number of pictures
130 // we keep in the list:
131 // - if the total area of additional textures reach 65% of the base pictures,
132 // we delete the additional pictures and mark the base pictures as
133 // needing a full repaint
134 // - we limit the number of pictures to 32 -- above that, we do the same
135 // things (deleting additional pictures + full repaint of base pictures)
136 void PictureSet::add(const SkRegion& area, SkPicture* picture,
137 uint32_t elapsed, bool split, bool empty)
139 bool checkForNewBases = false;
141 Pictures* first = mPictures.begin();
142 Pictures* last = mPictures.end();
144 XLOG("--- before adding the new inval ---");
145 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
146 SkIRect currentArea = working->mArea.getBounds();
147 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
149 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
150 currentArea.width(), currentArea.height(),
151 working->mArea.isRect() ? 'Y' : 'N',
152 working->mBase ? 'Y' : 'N');
154 XLOG("----------------------------------");
157 // let's gather all the Pictures intersecting with the new invalidated
158 // area, collect their area and remove their picture
159 SkIRect totalArea = area.getBounds();
160 for (Pictures* working = first; working != last; working++) {
161 SkIRect inval = area.getBounds();
163 if (!working->mBase && working->mArea.intersects(inval))
165 if (working->mBase) {
166 SkIRect baseArea = working->mArea.getBounds();
167 if (area.contains(baseArea)) {
169 checkForNewBases = true;
174 SkIRect currentArea = working->mArea.getBounds();
176 mBaseArea -= currentArea.width() * currentArea.height();
178 mAdditionalArea -= currentArea.width() * currentArea.height();
180 totalArea.join(currentArea);
181 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",
183 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
184 currentArea.width(), currentArea.height(),
185 working->mArea.isRect() ? 'Y' : 'N',
186 inval.fLeft, inval.fTop, inval.fRight, inval.fBottom,
187 inval.width(), inval.height(),
188 area.isRect() ? 'Y' : 'N');
189 working->mArea.setEmpty();
190 SkSafeUnref(working->mPicture);
191 working->mPicture = 0;
196 // Now we can add the new Picture to the list, with the correct area
197 // that need to be repainted
199 collect.setRect(totalArea);
200 Pictures pictureAndBounds = {collect, 0, collect.getBounds(),
201 elapsed, split, false, false, empty};
202 mPictures.append(pictureAndBounds);
203 mAdditionalArea += totalArea.width() * totalArea.height();
204 last = mPictures.end();
205 first = mPictures.begin();
207 // Then, let's see if we have to clear up the pictures in order to keep
208 // the total number of pictures under our limit
209 bool clearUp = false;
210 if (last - first > MAX_ADDITIONAL_PICTURES) {
211 XLOG("--- too many pictures, only keeping the bases : %d", last - first);
216 if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) {
217 XLOG("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f",
218 MAX_ADDITIONAL_AREA * 100, baseArea * 0.65, baseArea, addArea);
224 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
226 working->mArea.setEmpty();
227 SkSafeUnref(working->mPicture);
228 working->mPicture = 0;
233 XLOG("--- after adding the new inval, but before collapsing ---");
234 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
235 SkIRect currentArea = working->mArea.getBounds();
236 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
238 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
239 currentArea.width(), currentArea.height(),
240 working->mArea.isRect() ? 'Y' : 'N',
241 working->mBase ? 'Y' : 'N');
243 XLOG("----------------------------------");
244 XLOG("let's collapse...");
247 // Finally, let's do a pass to collapse out empty regions
248 Pictures* writer = first;
249 for (Pictures* working = first; working != last; working++) {
250 if (working && working->mArea.isEmpty())
252 *writer++ = *working;
254 XLOG("shiking of %d elements", writer - first);
255 mPictures.shrink(writer - first);
258 XLOG("--- after adding the new inval ---");
259 for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
260 SkIRect currentArea = working->mArea.getBounds();
261 XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c picture %x",
263 currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
264 currentArea.width(), currentArea.height(),
265 working->mArea.isRect() ? 'Y' : 'N',
266 working->mBase ? 'Y' : 'N', working->mPicture);
268 XLOG("----------------------------------");
271 // Base pictures might have been removed/added -- let's recompute them
273 if (checkForNewBases) {
275 Pictures* last = mPictures.end();
276 XLOG("checkForNewBases...");
277 for (Pictures* working = mPictures.begin(); working != last; working++) {
278 SkRegion& area = working->mArea;
279 const SkIRect& a = area.getBounds();
280 if (drawn.contains(working->mArea) == false) {
281 working->mBase = true;
282 float area = a.width() * a.height();
284 mAdditionalArea -= area;
286 drawn.op(working->mArea, SkRegion::kUnion_Op);
291 void PictureSet::checkDimensions(int width, int height, SkRegion* inval)
293 if (mWidth == width && mHeight == height)
295 DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
296 mWidth, mHeight, width, height);
297 if (mWidth == width && height > mHeight) { // only grew vertically
299 rect.set(0, mHeight, width, height - mHeight);
300 inval->op(rect, SkRegion::kUnion_Op);
302 clear(); // if both width/height changed, clear the old cache
303 inval->setRect(0, 0, width, height);
309 void PictureSet::clear()
312 Pictures* last = mPictures.end();
313 for (Pictures* working = mPictures.begin(); working != last; working++) {
314 working->mArea.setEmpty();
315 SkSafeUnref(working->mPicture);
318 mWidth = mHeight = 0;
321 bool PictureSet::draw(SkCanvas* canvas)
323 validate(__FUNCTION__);
324 Pictures* first = mPictures.begin();
325 Pictures* last = mPictures.end();
328 if (canvas->getClipBounds(&bounds) == false)
331 bounds.roundOut(&irect);
332 for (working = last; working != first; ) {
334 if (working->mArea.contains(irect)) {
335 #if PICTURE_SET_DEBUG
336 const SkIRect& b = working->mArea.getBounds();
337 DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
338 " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
339 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
345 DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
346 last - mPictures.begin());
347 uint32_t maxElapsed = 0;
348 for (working = first; working != last; working++) {
349 const SkRegion& area = working->mArea;
350 if (area.quickReject(irect)) {
351 #if PICTURE_SET_DEBUG
352 const SkIRect& b = area.getBounds();
353 DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
354 " irect={%d,%d,%d,%d}", working - first, working,
355 b.fLeft, b.fTop, b.fRight, b.fBottom,
356 irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
358 working->mElapsed = 0;
361 int saved = canvas->save();
363 if (area.isComplex()) {
365 area.getBoundaryPath(&pathClip);
366 canvas->clipPath(pathClip);
367 pathBounds = pathClip.getBounds();
369 pathBounds.set(area.getBounds());
370 canvas->clipRect(pathBounds);
372 canvas->translate(pathBounds.fLeft, pathBounds.fTop);
374 uint32_t startTime = getThreadMsec();
375 canvas->drawPicture(*working->mPicture);
376 size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
377 working->mWroteElapsed = true;
378 if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
379 pathBounds.height() >= MIN_SPLITTABLE))
380 maxElapsed = elapsed;
381 canvas->restoreToCount(saved);
382 #define DRAW_TEST_IMAGE 01
383 #if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
384 SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
385 canvas->drawColor(color);
388 paint.setColor(color);
390 for (int x = area.getBounds().fLeft & ~0x3f;
391 x < area.getBounds().fRight; x += 0x40) {
392 for (int y = area.getBounds().fTop & ~0x3f;
393 y < area.getBounds().fBottom; y += 0x40) {
394 int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
395 canvas->drawText(location, len, x, y, paint);
399 DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
400 working - first, working,
401 area.getBounds().fLeft, area.getBounds().fTop,
402 area.getBounds().fRight, area.getBounds().fBottom,
403 working->mElapsed, working->mBase ? "true" : "false");
405 // dump(__FUNCTION__);
406 return maxElapsed >= MAX_DRAW_TIME;
409 void PictureSet::dump(const char* label) const
412 DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
414 const Pictures* last = mPictures.end();
415 for (const Pictures* working = mPictures.begin(); working != last; working++) {
416 const SkIRect& bounds = working->mArea.getBounds();
417 const SkIRect& unsplit = working->mUnsplit;
418 MeasureStream measure;
419 if (working->mPicture != NULL)
420 working->mPicture->serialize(&measure);
422 " mArea.bounds={%d,%d,r=%d,b=%d}"
424 " mUnsplit={%d,%d,r=%d,b=%d}"
430 working - mPictures.begin(),
431 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
433 unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
434 working->mElapsed, working->mSplit ? "true" : "false",
435 working->mWroteElapsed ? "true" : "false",
436 working->mBase ? "true" : "false",
442 class IsEmptyBounder : public SkBounder {
443 virtual bool onIRect(const SkIRect& rect) {
448 class IsEmptyCanvas : public SkCanvas {
450 IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
451 mPicture(picture), mEmpty(true) {
457 mPicture->abortPlayback();
460 virtual bool clipPath(const SkPath&, SkRegion::Op) {
461 // this can be expensive to actually do, and doesn't affect the
462 // question of emptiness, so we make it a no-op
466 virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
467 const SkMatrix& , const SkPaint& ) {
468 if (bitmap.width() <= 1 || bitmap.height() <= 1)
470 DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
474 virtual void drawPaint(const SkPaint& paint) {
477 virtual void drawPath(const SkPath& , const SkPaint& paint) {
478 DBG_SET_LOG("abort");
482 virtual void drawPoints(PointMode , size_t , const SkPoint [],
483 const SkPaint& paint) {
486 virtual void drawRect(const SkRect& , const SkPaint& paint) {
487 // wait for visual content
488 if (paint.getColor() != SK_ColorWHITE)
492 virtual void drawSprite(const SkBitmap& , int , int ,
493 const SkPaint* paint = NULL) {
494 DBG_SET_LOG("abort");
498 virtual void drawText(const void* , size_t byteLength, SkScalar ,
499 SkScalar , const SkPaint& paint) {
500 DBG_SET_LOGD("abort %d", byteLength);
504 virtual void drawPosText(const void* , size_t byteLength,
505 const SkPoint [], const SkPaint& paint) {
506 DBG_SET_LOGD("abort %d", byteLength);
510 virtual void drawPosTextH(const void* , size_t byteLength,
511 const SkScalar [], SkScalar ,
512 const SkPaint& paint) {
513 DBG_SET_LOGD("abort %d", byteLength);
517 virtual void drawTextOnPath(const void* , size_t byteLength,
518 const SkPath& , const SkMatrix* ,
519 const SkPaint& paint) {
520 DBG_SET_LOGD("abort %d", byteLength);
524 virtual void drawPicture(SkPicture& picture) {
525 SkCanvas::drawPicture(picture);
532 bool PictureSet::emptyPicture(SkPicture* picture) const
534 IsEmptyBounder isEmptyBounder;
535 IsEmptyCanvas checker(&isEmptyBounder, picture);
537 bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
538 checker.setBitmapDevice(bitmap);
539 checker.drawPicture(*picture);
540 return checker.mEmpty;
543 bool PictureSet::isEmpty() const
545 const Pictures* last = mPictures.end();
546 for (const Pictures* working = mPictures.begin(); working != last; working++) {
547 if (!working->mEmpty)
553 bool PictureSet::reuseSubdivided(const SkRegion& inval)
555 validate(__FUNCTION__);
556 if (inval.isComplex())
558 Pictures* working, * last = mPictures.end();
559 const SkIRect& invalBounds = inval.getBounds();
561 for (working = mPictures.begin(); working != last; working++) {
562 if (working->mSplit && invalBounds == working->mUnsplit) {
568 SkRegion temp = SkRegion(inval);
569 temp.op(working->mArea, SkRegion::kIntersect_Op);
570 if (temp.isEmpty() || temp == working->mArea)
576 for (working = mPictures.begin(); working != last; working++) {
577 if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
578 inval.contains(working->mArea) == false)
580 SkSafeUnref(working->mPicture);
581 working->mPicture = NULL;
586 void PictureSet::set(const PictureSet& src)
588 DBG_SET_LOGD("start %p src=%p", this, &src);
591 mHeight = src.mHeight;
592 const Pictures* last = src.mPictures.end();
593 for (const Pictures* working = src.mPictures.begin(); working != last; working++)
595 // dump(__FUNCTION__);
596 validate(__FUNCTION__);
600 void PictureSet::setDrawTimes(const PictureSet& src)
602 validate(__FUNCTION__);
603 if (mWidth != src.mWidth || mHeight != src.mHeight)
605 Pictures* last = mPictures.end();
606 Pictures* working = mPictures.begin();
609 const Pictures* srcLast = src.mPictures.end();
610 const Pictures* srcWorking = src.mPictures.begin();
611 for (; srcWorking != srcLast; srcWorking++) {
612 if (srcWorking->mWroteElapsed == false)
614 while ((srcWorking->mArea != working->mArea ||
615 srcWorking->mPicture != working->mPicture)) {
616 if (++working == last)
619 DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
620 this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
621 working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
622 working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
623 working->mElapsed, srcWorking->mElapsed);
624 working->mElapsed = srcWorking->mElapsed;
628 void PictureSet::setPicture(size_t i, SkPicture* p)
630 SkSafeUnref(mPictures[i].mPicture);
631 mPictures[i].mPicture = p;
632 mPictures[i].mEmpty = emptyPicture(p);
635 void PictureSet::split(PictureSet* out) const
638 DBG_SET_LOGD("%p", this);
640 out->mWidth = mWidth;
641 out->mHeight = mHeight;
642 totalBounds.set(0, 0, mWidth, mHeight);
643 SkRegion* total = new SkRegion(totalBounds);
644 const Pictures* last = mPictures.end();
645 const Pictures* working;
646 uint32_t balance = 0;
647 int multiUnsplitFastPictures = 0; // > 1 has more than 1
648 for (working = mPictures.begin(); working != last; working++) {
649 if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
651 if (++multiUnsplitFastPictures > 1)
654 for (working = mPictures.begin(); working != last; working++) {
655 uint32_t elapsed = working->mElapsed;
656 if (elapsed < MAX_DRAW_TIME) {
657 bool split = working->mSplit;
658 DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
659 "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
660 total->getBounds().fLeft, total->getBounds().fTop,
661 total->getBounds().fRight, total->getBounds().fBottom,
662 split ? "true" : "false");
663 if (multiUnsplitFastPictures <= 1 || split) {
664 total->op(working->mArea, SkRegion::kDifference_Op);
665 out->add(working->mArea, working->mPicture, elapsed, split,
667 } else if (balance < elapsed)
671 total->op(working->mArea, SkRegion::kDifference_Op);
672 const SkIRect& bounds = working->mArea.getBounds();
673 int width = bounds.width();
674 int height = bounds.height();
677 while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
678 if (height >= width) {
685 if ((elapsed >>= 1) < MAX_DRAW_TIME)
688 width = bounds.width();
689 height = bounds.height();
690 int top = bounds.fTop;
691 for (int indexY = 0; indexY < down; ) {
692 int bottom = bounds.fTop + height * ++indexY / down;
693 int left = bounds.fLeft;
694 for (int indexX = 0; indexX < across; ) {
695 int right = bounds.fLeft + width * ++indexX / across;
697 cBounds.set(left, top, right, bottom);
698 out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
699 working->mPicture, elapsed, true,
700 (across | down) != 1 ? false : working->mEmpty);
706 DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
707 this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
708 multiUnsplitFastPictures);
709 if (!total->isEmpty() && multiUnsplitFastPictures > 1)
710 out->add(*total, NULL, balance, false, false);
712 validate(__FUNCTION__);
713 out->dump("split-out");
716 bool PictureSet::validate(const char* funct) const
719 #if PICTURE_SET_VALIDATE
721 const Pictures* first = mPictures.begin();
722 for (const Pictures* working = mPictures.end(); working != first; ) {
724 const SkPicture* pict = working->mPicture;
725 const SkRegion& area = working->mArea;
726 const SkIRect& bounds = area.getBounds();
727 bool localValid = false;
728 if (working->mUnsplit.isEmpty())
729 LOGD("%s working->mUnsplit.isEmpty()", funct);
730 else if (working->mUnsplit.contains(bounds) == false)
731 LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
732 else if (working->mElapsed >= 1000)
733 LOGD("%s working->mElapsed >= 1000", funct);
734 else if ((working->mSplit & 0xfe) != 0)
735 LOGD("%s (working->mSplit & 0xfe) != 0", funct);
736 else if ((working->mWroteElapsed & 0xfe) != 0)
737 LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
738 else if (pict != NULL) {
739 int pictWidth = pict->width();
740 int pictHeight = pict->height();
741 if (pictWidth < bounds.width())
742 LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
743 else if (pictHeight < bounds.height())
744 LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
745 else if (working->mArea.isEmpty())
746 LOGD("%s working->mArea.isEmpty()", funct);
751 working->mArea.validate();
752 if (localValid == false) {
753 if (all.contains(area) == true)
754 LOGD("%s all.contains(area) == true", funct);
759 all.op(area, SkRegion::kUnion_Op);
761 const SkIRect& allBounds = all.getBounds();
764 if (allBounds.width() != mWidth)
765 LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
766 else if (allBounds.height() != mHeight)
767 LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
771 while (valid == false)
777 } /* namespace android */