OSDN Git Service

Initial support for serializing the view state
[android-x86/external-webkit.git] / Source / WebKit / android / jni / PictureSet.cpp
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 #include <wtf/CurrentTime.h>
47
48 //#define DEBUG
49 #ifdef DEBUG
50
51 #include <cutils/log.h>
52 #include <wtf/text/CString.h>
53
54 #undef XLOG
55 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
56
57 #else
58
59 #undef XLOG
60 #define XLOG(...)
61
62 #endif // DEBUG
63
64 #if PICTURE_SET_DEBUG
65 class MeasureStream : public SkWStream {
66 public:
67     MeasureStream() : mTotal(0) {}
68     virtual bool write(const void* , size_t size) {
69         mTotal += size;
70         return true;
71     }
72     size_t mTotal;
73 };
74 #endif
75
76 namespace android {
77
78 PictureSet::PictureSet()
79 {
80     mWidth = mHeight = 0;
81     mBaseArea = mAdditionalArea = 0;
82 }
83
84 PictureSet::PictureSet(SkPicture* picture)
85 {
86     if (!picture)
87         return;
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);
98 }
99
100 PictureSet::~PictureSet()
101 {
102     clear();
103 }
104
105 void PictureSet::add(const Pictures* temp)
106 {
107     Pictures pictureAndBounds = *temp;
108     SkSafeRef(pictureAndBounds.mPicture);
109     pictureAndBounds.mWroteElapsed = false;
110     mPictures.append(pictureAndBounds);
111 }
112
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
116 // underneath them.
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
123 // contains it.
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)
138 {
139     bool checkForNewBases = false;
140
141     Pictures* first = mPictures.begin();
142     Pictures* last = mPictures.end();
143 #ifdef DEBUG
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",
148              working - first,
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');
153     }
154     XLOG("----------------------------------");
155 #endif
156
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();
162         bool remove = false;
163         if (!working->mBase && working->mArea.intersects(inval))
164             remove = true;
165         if (working->mBase) {
166             SkIRect baseArea = working->mArea.getBounds();
167             if (area.contains(baseArea)) {
168                 remove = true;
169                 checkForNewBases = true;
170             }
171         }
172
173         if (remove) {
174             SkIRect currentArea = working->mArea.getBounds();
175             if (working->mBase)
176                 mBaseArea -= currentArea.width() * currentArea.height();
177             else
178                 mAdditionalArea -= currentArea.width() * currentArea.height();
179
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",
182                  working - first,
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;
192
193         }
194     }
195
196     // Now we can add the new Picture to the list, with the correct area
197     // that need to be repainted
198     SkRegion collect;
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();
206
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);
212         clearUp = true;
213     }
214
215     if (!clearUp) {
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);
219             clearUp = true;
220         }
221     }
222
223     if (clearUp) {
224         for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
225             if (!working->mBase)
226                 working->mArea.setEmpty();
227             SkSafeUnref(working->mPicture);
228             working->mPicture = 0;
229         }
230     }
231
232 #ifdef DEBUG
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",
237              working - first,
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');
242     }
243     XLOG("----------------------------------");
244     XLOG("let's collapse...");
245 #endif
246
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())
251             continue;
252         *writer++ = *working;
253     }
254     XLOG("shiking of %d elements", writer - first);
255     mPictures.shrink(writer - first);
256
257 #ifdef DEBUG
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",
262              working - first,
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);
267     }
268     XLOG("----------------------------------");
269 #endif
270
271     // Base pictures might have been removed/added -- let's recompute them
272     SkRegion drawn;
273     if (checkForNewBases) {
274         drawn.setEmpty();
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();
283                 mBaseArea += area;
284                 mAdditionalArea -= area;
285             }
286             drawn.op(working->mArea, SkRegion::kUnion_Op);
287         }
288     }
289 }
290
291 void PictureSet::checkDimensions(int width, int height, SkRegion* inval)
292 {
293     if (mWidth == width && mHeight == height)
294         return;
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
298         SkIRect rect;
299         rect.set(0, mHeight, width, height - mHeight);
300         inval->op(rect, SkRegion::kUnion_Op);
301     } else {
302         clear(); // if both width/height changed, clear the old cache
303         inval->setRect(0, 0, width, height);
304     }
305     mWidth = width;
306     mHeight = height;
307 }
308
309 void PictureSet::clear()
310 {
311     DBG_SET_LOG("");
312     Pictures* last = mPictures.end();
313     for (Pictures* working = mPictures.begin(); working != last; working++) {
314         working->mArea.setEmpty();
315         SkSafeUnref(working->mPicture);
316     }
317     mPictures.clear();
318     mWidth = mHeight = 0;
319 }
320
321 bool PictureSet::draw(SkCanvas* canvas)
322 {
323     validate(__FUNCTION__);
324     Pictures* first = mPictures.begin();
325     Pictures* last = mPictures.end();
326     Pictures* working;
327     SkRect bounds;
328     if (canvas->getClipBounds(&bounds) == false)
329         return false;
330     SkIRect irect;
331     bounds.roundOut(&irect);
332     for (working = last; working != first; ) {
333         --working;
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);
340 #endif
341             first = working;
342             break;
343         }
344     }
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);
357 #endif
358             working->mElapsed = 0;
359             continue;
360         }
361         int saved = canvas->save();
362         SkRect pathBounds;
363         if (area.isComplex()) {
364             SkPath pathClip;
365             area.getBoundaryPath(&pathClip);
366             canvas->clipPath(pathClip);
367             pathBounds = pathClip.getBounds();
368         } else {
369             pathBounds.set(area.getBounds());
370             canvas->clipRect(pathBounds);
371         }
372         canvas->translate(pathBounds.fLeft, pathBounds.fTop);
373         canvas->save();
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);
386         SkPaint paint;
387         color ^= 0x00ffffff;
388         paint.setColor(color);
389         char location[256];
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);
396             }
397         }
398 #endif
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");
404     }
405  //   dump(__FUNCTION__);
406     return maxElapsed >= MAX_DRAW_TIME;
407 }
408
409 void PictureSet::dump(const char* label) const
410 {
411 #if PICTURE_SET_DUMP
412     DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
413         mWidth, mHeight);
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);
421         LOGD(" [%d]"
422             " mArea.bounds={%d,%d,r=%d,b=%d}"
423             " mPicture=%p"
424             " mUnsplit={%d,%d,r=%d,b=%d}"
425             " mElapsed=%d"
426             " mSplit=%s"
427             " mWroteElapsed=%s"
428             " mBase=%s"
429             " pict-size=%d",
430             working - mPictures.begin(),
431             bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
432             working->mPicture,
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",
437             measure.mTotal);
438     }
439 #endif
440 }
441
442 class IsEmptyBounder : public SkBounder {
443     virtual bool onIRect(const SkIRect& rect) {
444         return false;
445     }
446 };
447
448 class IsEmptyCanvas : public SkCanvas {
449 public:
450     IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) : 
451             mPicture(picture), mEmpty(true) {
452         setBounder(bounder);
453     }
454     
455     void notEmpty() {
456         mEmpty = false;
457         mPicture->abortPlayback();    
458     }
459
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
463         return true;
464     }
465
466     virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
467             const SkMatrix& , const SkPaint& ) {
468         if (bitmap.width() <= 1 || bitmap.height() <= 1)
469             return;
470         DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
471         notEmpty();
472     }
473
474     virtual void drawPaint(const SkPaint& paint) {
475     }
476
477     virtual void drawPath(const SkPath& , const SkPaint& paint) {
478         DBG_SET_LOG("abort");
479         notEmpty();
480     }
481
482     virtual void drawPoints(PointMode , size_t , const SkPoint [],
483                             const SkPaint& paint) {
484     }
485     
486     virtual void drawRect(const SkRect& , const SkPaint& paint) {
487         // wait for visual content
488         if (paint.getColor() != SK_ColorWHITE)
489             notEmpty();
490     }
491
492     virtual void drawSprite(const SkBitmap& , int , int ,
493                             const SkPaint* paint = NULL) {
494         DBG_SET_LOG("abort");
495         notEmpty();
496     }
497     
498     virtual void drawText(const void* , size_t byteLength, SkScalar , 
499                           SkScalar , const SkPaint& paint) {
500         DBG_SET_LOGD("abort %d", byteLength);
501         notEmpty();
502     }
503
504     virtual void drawPosText(const void* , size_t byteLength, 
505                              const SkPoint [], const SkPaint& paint) {
506         DBG_SET_LOGD("abort %d", byteLength);
507         notEmpty();
508     }
509
510     virtual void drawPosTextH(const void* , size_t byteLength,
511                               const SkScalar [], SkScalar ,
512                               const SkPaint& paint) {
513         DBG_SET_LOGD("abort %d", byteLength);
514         notEmpty();
515     }
516
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);
521         notEmpty();
522     }
523
524     virtual void drawPicture(SkPicture& picture) {
525         SkCanvas::drawPicture(picture);
526     }
527     
528     SkPicture* mPicture;
529     bool mEmpty;
530 };
531
532 bool PictureSet::emptyPicture(SkPicture* picture) const
533 {
534     IsEmptyBounder isEmptyBounder;
535     IsEmptyCanvas checker(&isEmptyBounder, picture);
536     SkBitmap bitmap;
537     bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
538     checker.setBitmapDevice(bitmap);
539     checker.drawPicture(*picture);
540     return checker.mEmpty;
541 }
542
543 bool PictureSet::isEmpty() const
544 {
545     const Pictures* last = mPictures.end();
546     for (const Pictures* working = mPictures.begin(); working != last; working++) {
547         if (!working->mEmpty)
548             return false;
549     }
550     return true;
551 }
552
553 bool PictureSet::reuseSubdivided(const SkRegion& inval)
554 {
555     validate(__FUNCTION__);
556     if (inval.isComplex())
557         return false;
558     Pictures* working, * last = mPictures.end();
559     const SkIRect& invalBounds = inval.getBounds();
560     bool steal = false;
561     for (working = mPictures.begin(); working != last; working++) {
562         if (working->mSplit && invalBounds == working->mUnsplit) {
563             steal = true;
564             continue;
565         }
566         if (steal == false)
567             continue;
568         SkRegion temp = SkRegion(inval);
569         temp.op(working->mArea, SkRegion::kIntersect_Op);
570         if (temp.isEmpty() || temp == working->mArea)
571             continue;
572         return false;
573     }
574     if (steal == false)
575         return false;
576     for (working = mPictures.begin(); working != last; working++) {
577         if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
578                 inval.contains(working->mArea) == false)
579             continue;
580         SkSafeUnref(working->mPicture);
581         working->mPicture = NULL;
582     }
583     return true;
584 }
585
586 void PictureSet::set(const PictureSet& src)
587 {
588     DBG_SET_LOGD("start %p src=%p", this, &src);
589     clear();
590     mWidth = src.mWidth;
591     mHeight = src.mHeight;
592     const Pictures* last = src.mPictures.end();
593     for (const Pictures* working = src.mPictures.begin(); working != last; working++)
594         add(working);
595  //   dump(__FUNCTION__);
596     validate(__FUNCTION__);
597     DBG_SET_LOG("end");
598 }
599
600 void PictureSet::setDrawTimes(const PictureSet& src)
601 {
602     validate(__FUNCTION__);
603     if (mWidth != src.mWidth || mHeight != src.mHeight)
604         return;
605     Pictures* last = mPictures.end();
606     Pictures* working = mPictures.begin();
607     if (working == last)
608         return;
609     const Pictures* srcLast = src.mPictures.end();
610     const Pictures* srcWorking = src.mPictures.begin();
611     for (; srcWorking != srcLast; srcWorking++) {
612         if (srcWorking->mWroteElapsed == false)
613             continue;
614         while ((srcWorking->mArea != working->mArea ||
615                 srcWorking->mPicture != working->mPicture)) {
616             if (++working == last)
617                 return;
618         }
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;
625     }
626 }
627
628 void PictureSet::setPicture(size_t i, SkPicture* p)
629 {
630     SkSafeUnref(mPictures[i].mPicture);
631     mPictures[i].mPicture = p;
632     mPictures[i].mEmpty = emptyPicture(p);
633 }
634
635 void PictureSet::split(PictureSet* out) const
636 {
637     dump(__FUNCTION__);
638     DBG_SET_LOGD("%p", this);
639     SkIRect totalBounds;
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)
650             continue;
651         if (++multiUnsplitFastPictures > 1)
652             break;
653     }
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,
666                     working->mEmpty);
667             } else if (balance < elapsed)
668                 balance = elapsed;
669             continue;
670         }
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();
675         int across = 1;
676         int down = 1;
677         while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
678             if (height >= width) {
679                 height >>= 1;
680                 down <<= 1;
681             } else {
682                 width >>= 1;
683                 across <<= 1 ;
684             }
685             if ((elapsed >>= 1) < MAX_DRAW_TIME)
686                 break;
687         }
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;
696                 SkIRect cBounds;
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);
701                 left = right;
702             }
703             top = bottom;
704         }
705     }
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);
711     delete total;
712     validate(__FUNCTION__);
713     out->dump("split-out");
714 }
715
716 bool PictureSet::validate(const char* funct) const
717 {
718     bool valid = true;
719 #if PICTURE_SET_VALIDATE
720     SkRegion all;
721     const Pictures* first = mPictures.begin();
722     for (const Pictures* working = mPictures.end(); working != first; ) {
723         --working;
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);
747             else
748                 localValid = true;
749         } else
750             localValid = true;
751         working->mArea.validate();
752         if (localValid == false) {
753             if (all.contains(area) == true)
754                 LOGD("%s all.contains(area) == true", funct);
755             else
756                 localValid = true;
757         }
758         valid &= localValid;
759         all.op(area, SkRegion::kUnion_Op);
760     }
761     const SkIRect& allBounds = all.getBounds();
762     if (valid) {
763         valid = false;
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);
768         else
769             valid = true;
770     }
771     while (valid == false)
772         ;
773 #endif
774     return valid;
775 }
776
777 } /* namespace android */