OSDN Git Service

3f40174fe7e7e980a1a1e634b82355edc3ead9fa
[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 #define BUCKET_SIZE 256
47 #define MAX_BUCKET_COUNT_X 16
48 #define MAX_BUCKET_COUNT_Y 64
49
50 #include <wtf/CurrentTime.h>
51
52 #include <cutils/log.h>
53 #include <wtf/text/CString.h>
54
55 #undef XLOGC
56 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
57
58 #ifdef DEBUG
59
60 #undef XLOG
61 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
62
63 #else
64
65 #undef XLOG
66 #define XLOG(...)
67
68 #endif // DEBUG
69
70 #if PICTURE_SET_DEBUG
71 class MeasureStream : public SkWStream {
72 public:
73     MeasureStream() : mTotal(0) {}
74     virtual bool write(const void* , size_t size) {
75         mTotal += size;
76         return true;
77     }
78     size_t mTotal;
79 };
80 #endif
81
82 namespace android {
83
84 PictureSet::PictureSet()
85     : mBucketSizeX(0), mBucketSizeY(0), mBucketCountX(0), mBucketCountY(0),
86       mHeight(0), mWidth(0)
87 {
88     setDimensions(0, 0);
89     mBaseArea = mAdditionalArea = 0;
90 }
91
92 PictureSet::PictureSet(SkPicture* picture)
93     : mBucketSizeX(0), mBucketSizeY(0), mBucketCountX(0), mBucketCountY(0),
94       mHeight(0), mWidth(0)
95 {
96     mBaseArea = mAdditionalArea = 0;
97     if (!picture) {
98         setDimensions(0, 0);
99         return;
100     }
101     setDimensions(picture->width(), picture->height());
102     mBaseArea = mWidth * mHeight;
103 #ifdef FAST_PICTURESET
104     SkIRect area;
105     area.set(0, 0, mWidth, mHeight);
106     splitAdd(area);
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;
122         }
123     }
124     buckets->clear();
125 #else
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
137 }
138
139 PictureSet::~PictureSet()
140 {
141     clear();
142 }
143
144 #ifdef FAST_PICTURESET
145 #else
146 void PictureSet::add(const Pictures* temp)
147 {
148     Pictures pictureAndBounds = *temp;
149     SkSafeRef(pictureAndBounds.mPicture);
150     pictureAndBounds.mWroteElapsed = false;
151     mPictures.append(pictureAndBounds);
152 }
153 #endif // FAST_PICTURESET
154
155 void PictureSet::add(const SkRegion& area, SkPicture* picture,
156                      uint32_t elapsed, bool split)
157 {
158     if (area.isRect()) {
159 #ifdef FAST_PICTURESET
160         splitAdd(area.getBounds());
161 #else
162         add(area, picture, elapsed, split, false);
163 #endif // FAST_PICTURESET
164     } else {
165         SkRegion::Iterator cliperator(area);
166         while (!cliperator.done()) {
167             SkIRect ir = cliperator.rect();
168 #ifdef FAST_PICTURESET
169             splitAdd(ir);
170 #else
171             SkRegion newArea;
172             newArea.setRect(ir);
173             add(newArea, picture, elapsed, split, false);
174 #endif // FAST_PICTURESET
175             cliperator.next();
176         }
177     }
178 }
179
180 #ifdef FAST_PICTURESET
181
182 Bucket* PictureSet::getBucket(int x, int y)
183 {
184     // only create buckets for valid, positive coordinates, ignore and return
185     // NULL otherwise
186     if (x < 0 || y < 0)
187         return 0;
188
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);
194     }
195     return mBuckets.get(position);
196 }
197
198 void PictureSet::displayBucket(Bucket* bucket)
199 {
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",
204               bucket,
205               current->mArea.fLeft,
206               current->mArea.fTop,
207               current->mArea.fRight,
208               current->mArea.fBottom,
209               current->mArea.width(),
210               current->mArea.height(),
211               current->mPicture,
212               current->mBase);
213     }
214 }
215
216 void PictureSet::displayBuckets()
217 {
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);
222     }
223     XLOGC("\n****** END OF DISPLAY BUCKETS ******\n\n");
224 }
225
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)
229 {
230     bool resetBase = false;
231
232     SkIRect totalArea = rect;
233     BucketPicture* first = bucket->begin();
234     BucketPicture* last = bucket->end();
235
236     // If the inval covers a large area of the base inval, let's repaint the
237     // entire bucket.
238     if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * mBucketSizeX * mBucketSizeY)
239         resetBase = true;
240
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;
246
247         if (!remove)
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)
251             remove = true;
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))
255             remove = true;
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)) {
261             remove = true;
262         }
263
264         if (remove) {
265             totalArea.join(current->mArea);
266             current->mBase = false;
267             current->mArea.setEmpty();
268             SkSafeUnref(current->mPicture);
269             current->mPicture = 0;
270         }
271     }
272
273     // Now, let's add the new BucketPicture to the list, with the correct
274     // area that needs to be repainted
275     SkRegion region;
276     SkIRect area = totalArea;
277     area.offset(dx, dy);
278     BucketPicture picture = { 0, totalArea, area, false };
279
280     bucket->append(picture);
281
282     first = bucket->begin();
283     last = bucket->end();
284
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())
289             continue;
290         *writer++ = *current;
291     }
292
293     bucket->shrink(writer - first);
294
295     // let's recompute the bases
296     first = bucket->begin();
297     last = bucket->end();
298     SkRegion drawn;
299     drawn.setEmpty();
300     for (BucketPicture* current = first; current != last; current++) {
301         if (drawn.contains(current->mArea) == false) {
302             current->mBase = true;
303         }
304         drawn.op(current->mArea, SkRegion::kUnion_Op);
305     }
306 }
307
308 void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect)
309 {
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());
313
314     int x = rect.fLeft;
315     int y = rect.fTop;
316     int firstTileX = rect.fLeft / mBucketSizeX;
317     int firstTileY = rect.fTop / mBucketSizeY;
318     int lastTileX = rect.fRight / mBucketSizeX;
319     int lastTileY = rect.fBottom / mBucketSizeY;
320
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);
325             if (bucket)
326                 list.append(bucket);
327         }
328     }
329 }
330
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)
335 {
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());
339
340     // TODO: reuse gatherBucketsForArea() (change Bucket to be a class)
341     int x = rect.fLeft;
342     int y = rect.fTop;
343     int firstTileX = rect.fLeft / mBucketSizeX;
344     int firstTileY = rect.fTop / mBucketSizeY;
345     int lastTileX = rect.fRight / mBucketSizeX;
346     int lastTileY = rect.fBottom / mBucketSizeY;
347
348     XLOG("--- firstTile(%d, %d) lastTile(%d, %d)",
349           firstTileX, firstTileY,
350           lastTileX, lastTileY);
351
352     for (int i = firstTileX; i <= lastTileX; i++) {
353         for (int j = firstTileY; j <= lastTileY; j++) {
354             Bucket* bucket = getBucket(i, j);
355             if (!bucket)
356                 continue;
357
358             SkIRect newRect;
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;
365
366             newRect.set(left, top, right, bottom);
367             addToBucket(bucket, deltaX, deltaY, newRect);
368             mUpdatedBuckets.append(bucket);
369         }
370     }
371
372     XLOG("--- splitAdd DONE\n");
373 }
374
375 #endif // FAST_PICTURESET
376
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
380 // underneath them.
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
387 // contains it.
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
401 #else
402 void PictureSet::add(const SkRegion& area, SkPicture* picture,
403     uint32_t elapsed, bool split, bool empty)
404 {
405     bool checkForNewBases = false;
406
407     Pictures* first = mPictures.begin();
408     Pictures* last = mPictures.end();
409 #ifdef DEBUG
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",
414              working - first,
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');
419     }
420     XLOG("----------------------------------");
421 #endif
422
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();
428         bool remove = false;
429         if (!working->mBase && working->mArea.intersects(inval))
430             remove = true;
431         if (working->mBase) {
432             SkIRect baseArea = working->mArea.getBounds();
433             if (area.contains(baseArea)) {
434                 remove = true;
435                 checkForNewBases = true;
436             }
437         }
438
439         if (remove) {
440             SkIRect currentArea = working->mArea.getBounds();
441             if (working->mBase)
442                 mBaseArea -= currentArea.width() * currentArea.height();
443             else
444                 mAdditionalArea -= currentArea.width() * currentArea.height();
445
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",
448                  working - first,
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;
458         }
459     }
460
461     // Now we can add the new Picture to the list, with the correct area
462     // that need to be repainted
463     SkRegion collect;
464     collect.setRect(totalArea);
465     Pictures pictureAndBounds = {collect, 0, collect.getBounds(),
466         elapsed, split, false, false, empty};
467
468 #ifdef FAST_PICTURESET
469     if (mPictures.size() == 0)
470         checkForNewBases = true;
471 #endif
472
473     mPictures.append(pictureAndBounds);
474     mAdditionalArea += totalArea.width() * totalArea.height();
475     last = mPictures.end();
476     first = mPictures.begin();
477
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);
483         clearUp = true;
484     }
485
486     if (!clearUp) {
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);
490             clearUp = true;
491         }
492     }
493
494     if (clearUp) {
495         for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
496             if (!working->mBase)
497                 working->mArea.setEmpty();
498             SkSafeUnref(working->mPicture);
499             working->mPicture = 0;
500         }
501     }
502
503 #ifdef DEBUG
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",
508              working - first,
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');
513     }
514     XLOG("----------------------------------");
515     XLOG("let's collapse...");
516 #endif
517
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())
522             continue;
523         *writer++ = *working;
524     }
525     XLOG("shiking of %d elements", writer - first);
526     mPictures.shrink(writer - first);
527
528 #ifdef DEBUG
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",
533              working - first,
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);
538     }
539     XLOG("----------------------------------");
540 #endif
541
542     // Base pictures might have been removed/added -- let's recompute them
543     SkRegion drawn;
544     if (checkForNewBases) {
545         drawn.setEmpty();
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();
554                 mBaseArea += area;
555                 mAdditionalArea -= area;
556             }
557             drawn.op(working->mArea, SkRegion::kUnion_Op);
558         }
559     }
560 }
561 #endif // FAST_PICTURESET
562
563 void PictureSet::setDimensions(int width, int height, SkRegion* inval)
564 {
565     // Note that setDimensions() may be called by our ctor and should behave accordingly
566     if (mWidth == width && mHeight == height)
567         return;
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;
571     if (inval) {
572         if (mWidth == width && height > mHeight) { // only grew vertically
573             SkIRect rect;
574             rect.set(0, mHeight, width, height);
575             inval->op(rect, SkRegion::kUnion_Op);
576         } else {
577             clearCache = true;
578             inval->setRect(0, 0, width, height);
579         }
580     }
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;
585
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;
589
590     int bucketCountX = (width + bucketSizeX - 1) / bucketSizeX;
591     int bucketCountY = (height + bucketSizeY - 1) / bucketSizeY;
592
593     // Clear the cache if the horizontal bucket count changed or the vertical
594     // count shrank
595     if (bucketCountX != mBucketCountX || bucketCountY < mBucketCountY)
596         clearCache = true;
597
598     // Or if the bucket size changed
599     if (bucketSizeX != mBucketSizeX || bucketSizeY != mBucketSizeY)
600         clearCache = true;
601
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);
606 #endif
607     if (clearCache)
608         clear();
609     mWidth = width;
610     mHeight = height;
611 #ifdef FAST_PICTURESET
612     mBucketSizeX = bucketSizeX;
613     mBucketSizeY = bucketSizeY;
614     mBucketCountX = bucketCountX;
615     mBucketCountY = bucketCountY;
616 #endif
617 }
618
619 void PictureSet::clear()
620 {
621     DBG_SET_LOG("");
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;
630          }
631          bucket->clear();
632     }
633     mBuckets.clear();
634 #else
635     Pictures* last = mPictures.end();
636     for (Pictures* working = mPictures.begin(); working != last; working++) {
637         working->mArea.setEmpty();
638         SkSafeUnref(working->mPicture);
639     }
640     mPictures.clear();
641 #endif // FAST_PICTURESET
642     mWidth = mHeight = 0;
643     mBucketSizeX = mBucketSizeY = 0;
644 }
645
646 bool PictureSet::draw(SkCanvas* canvas)
647 {
648 #ifdef FAST_PICTURESET
649     XLOG("PictureSet %x draw on canvas %x", this, canvas);
650     SkRect bounds;
651     if (canvas->getClipBounds(&bounds) == false)
652         return false;
653     SkIRect irect;
654     bounds.roundOut(&irect);
655
656     WTF::Vector<Bucket*> list;
657     gatherBucketsForArea(list, irect);
658
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)
666                 continue;
667             int saved = canvas->save();
668             SkRect pathBounds;
669             pathBounds.set(picture.mRealArea);
670             XLOG("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d",
671                   j, bucket->size(),
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);
680             canvas->save();
681             canvas->drawPicture(*picture.mPicture);
682             canvas->restoreToCount(saved);
683         }
684     }
685     return false;
686
687 #else
688
689     validate(__FUNCTION__);
690     Pictures* first = mPictures.begin();
691     Pictures* last = mPictures.end();
692     Pictures* working;
693     SkRect bounds;
694     if (canvas->getClipBounds(&bounds) == false)
695         return false;
696     SkIRect irect;
697     bounds.roundOut(&irect);
698     for (working = last; working != first; ) {
699         --working;
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);
706 #endif
707             first = working;
708             break;
709         }
710     }
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);
723 #endif
724             working->mElapsed = 0;
725             continue;
726         }
727         int saved = canvas->save();
728         SkRect pathBounds;
729         if (area.isComplex()) {
730             SkPath pathClip;
731             area.getBoundaryPath(&pathClip);
732             canvas->clipPath(pathClip);
733             pathBounds = pathClip.getBounds();
734         } else {
735             pathBounds.set(area.getBounds());
736             canvas->clipRect(pathBounds);
737         }
738         canvas->translate(pathBounds.fLeft, pathBounds.fTop);
739         canvas->save();
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);
752         SkPaint paint;
753         color ^= 0x00ffffff;
754         paint.setColor(color);
755         char location[256];
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);
762             }
763         }
764 #endif
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");
770     }
771  //   dump(__FUNCTION__);
772     return maxElapsed >= MAX_DRAW_TIME;
773 #endif // FAST_PICTURESET
774 }
775
776 void PictureSet::dump(const char* label) const
777 {
778 #if PICTURE_SET_DUMP
779     DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
780         mWidth, mHeight);
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);
788         LOGD(" [%d]"
789             " mArea.bounds={%d,%d,r=%d,b=%d}"
790             " mPicture=%p"
791             " mUnsplit={%d,%d,r=%d,b=%d}"
792             " mElapsed=%d"
793             " mSplit=%s"
794             " mWroteElapsed=%s"
795             " mBase=%s"
796             " pict-size=%d",
797             working - mPictures.begin(),
798             bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
799             working->mPicture,
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",
804             measure.mTotal);
805     }
806 #endif
807 }
808
809 class IsEmptyBounder : public SkBounder {
810     virtual bool onIRect(const SkIRect& rect) {
811         return false;
812     }
813 };
814
815 class IsEmptyCanvas : public SkCanvas {
816 public:
817     IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) : 
818             mPicture(picture), mEmpty(true) {
819         setBounder(bounder);
820     }
821     
822     void notEmpty() {
823         mEmpty = false;
824         mPicture->abortPlayback();    
825     }
826
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
830         return true;
831     }
832
833     virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
834             const SkMatrix& , const SkPaint& ) {
835         if (bitmap.width() <= 1 || bitmap.height() <= 1)
836             return;
837         DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
838         notEmpty();
839     }
840
841     virtual void drawPaint(const SkPaint& paint) {
842     }
843
844     virtual void drawPath(const SkPath& , const SkPaint& paint) {
845         DBG_SET_LOG("abort");
846         notEmpty();
847     }
848
849     virtual void drawPoints(PointMode , size_t , const SkPoint [],
850                             const SkPaint& paint) {
851     }
852     
853     virtual void drawRect(const SkRect& , const SkPaint& paint) {
854         // wait for visual content
855         if (paint.getColor() != SK_ColorWHITE)
856             notEmpty();
857     }
858
859     virtual void drawSprite(const SkBitmap& , int , int ,
860                             const SkPaint* paint = NULL) {
861         DBG_SET_LOG("abort");
862         notEmpty();
863     }
864     
865     virtual void drawText(const void* , size_t byteLength, SkScalar , 
866                           SkScalar , const SkPaint& paint) {
867         DBG_SET_LOGD("abort %d", byteLength);
868         notEmpty();
869     }
870
871     virtual void drawPosText(const void* , size_t byteLength, 
872                              const SkPoint [], const SkPaint& paint) {
873         DBG_SET_LOGD("abort %d", byteLength);
874         notEmpty();
875     }
876
877     virtual void drawPosTextH(const void* , size_t byteLength,
878                               const SkScalar [], SkScalar ,
879                               const SkPaint& paint) {
880         DBG_SET_LOGD("abort %d", byteLength);
881         notEmpty();
882     }
883
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);
888         notEmpty();
889     }
890
891     virtual void drawPicture(SkPicture& picture) {
892         SkCanvas::drawPicture(picture);
893     }
894     
895     SkPicture* mPicture;
896     bool mEmpty;
897 };
898
899 bool PictureSet::emptyPicture(SkPicture* picture) const
900 {
901     IsEmptyBounder isEmptyBounder;
902     IsEmptyCanvas checker(&isEmptyBounder, picture);
903     SkBitmap bitmap;
904     bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
905     checker.setBitmapDevice(bitmap);
906     checker.drawPicture(*picture);
907     return checker.mEmpty;
908 }
909
910 bool PictureSet::isEmpty() const
911 {
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)
917             return false;
918     }
919     return true;
920 #else
921     const Pictures* last = mPictures.end();
922     for (const Pictures* working = mPictures.begin(); working != last; working++) {
923         if (!working->mEmpty)
924             return false;
925     }
926     return true;
927 #endif // FAST_PICTURESET
928 }
929
930 void PictureSet::set(const PictureSet& src)
931 {
932     DBG_SET_LOGD("start %p src=%p", this, &src);
933     clear();
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);
953          }
954     }
955     XLOG("--- DONE set picture ---\n");
956 #else
957     const Pictures* last = src.mPictures.end();
958     for (const Pictures* working = src.mPictures.begin(); working != last; working++)
959         add(working);
960  //   dump(__FUNCTION__);
961     validate(__FUNCTION__);
962     DBG_SET_LOG("end");
963 #endif // FAST_PICTURESET
964 }
965
966 #ifdef FAST_PICTURESET
967 #else
968
969 bool PictureSet::reuseSubdivided(const SkRegion& inval)
970 {
971     validate(__FUNCTION__);
972
973     if (inval.isComplex())
974         return false;
975     Pictures* working, * last = mPictures.end();
976     const SkIRect& invalBounds = inval.getBounds();
977     bool steal = false;
978     for (working = mPictures.begin(); working != last; working++) {
979         if (working->mSplit && invalBounds == working->mUnsplit) {
980             steal = true;
981             continue;
982         }
983         if (steal == false)
984             continue;
985         SkRegion temp = SkRegion(inval);
986         temp.op(working->mArea, SkRegion::kIntersect_Op);
987         if (temp.isEmpty() || temp == working->mArea)
988             continue;
989         return false;
990     }
991     if (steal == false)
992         return false;
993     for (working = mPictures.begin(); working != last; working++) {
994         if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
995                 inval.contains(working->mArea) == false)
996             continue;
997         SkSafeUnref(working->mPicture);
998         working->mPicture = NULL;
999     }
1000     return true;
1001 }
1002
1003 void PictureSet::setDrawTimes(const PictureSet& src)
1004 {
1005     validate(__FUNCTION__);
1006     if (mWidth != src.mWidth || mHeight != src.mHeight)
1007         return;
1008     Pictures* last = mPictures.end();
1009     Pictures* working = mPictures.begin();
1010     if (working == last)
1011         return;
1012     const Pictures* srcLast = src.mPictures.end();
1013     const Pictures* srcWorking = src.mPictures.begin();
1014     for (; srcWorking != srcLast; srcWorking++) {
1015         if (srcWorking->mWroteElapsed == false)
1016             continue;
1017         while ((srcWorking->mArea != working->mArea ||
1018                 srcWorking->mPicture != working->mPicture)) {
1019             if (++working == last)
1020                 return;
1021         }
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;
1028     }
1029 }
1030
1031 void PictureSet::setPicture(size_t i, SkPicture* p)
1032 {
1033     SkSafeUnref(mPictures[i].mPicture);
1034     mPictures[i].mPicture = p;
1035     mPictures[i].mEmpty = emptyPicture(p);
1036 }
1037
1038 void PictureSet::split(PictureSet* out) const
1039 {
1040     dump(__FUNCTION__);
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)
1053             continue;
1054         if (++multiUnsplitFastPictures > 1)
1055             break;
1056     }
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,
1069                     working->mEmpty);
1070             } else if (balance < elapsed)
1071                 balance = elapsed;
1072             continue;
1073         }
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();
1078         int across = 1;
1079         int down = 1;
1080         while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
1081             if (height >= width) {
1082                 height >>= 1;
1083                 down <<= 1;
1084             } else {
1085                 width >>= 1;
1086                 across <<= 1 ;
1087             }
1088             if ((elapsed >>= 1) < MAX_DRAW_TIME)
1089                 break;
1090         }
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;
1099                 SkIRect cBounds;
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);
1104                 left = right;
1105             }
1106             top = bottom;
1107         }
1108     }
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);
1114     delete total;
1115     validate(__FUNCTION__);
1116     out->dump("split-out");
1117 }
1118
1119 #endif // FAST_PICTURESET
1120
1121 bool PictureSet::validate(const char* funct) const
1122 {
1123 #ifdef FAST_PICTURESET
1124     return true;
1125 #else
1126     bool valid = true;
1127 #if PICTURE_SET_VALIDATE
1128     SkRegion all;
1129     const Pictures* first = mPictures.begin();
1130     for (const Pictures* working = mPictures.end(); working != first; ) {
1131         --working;
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);
1155             else
1156                 localValid = true;
1157         } else
1158             localValid = true;
1159         working->mArea.validate();
1160         if (localValid == false) {
1161             if (all.contains(area) == true)
1162                 LOGD("%s all.contains(area) == true", funct);
1163             else
1164                 localValid = true;
1165         }
1166         valid &= localValid;
1167         all.op(area, SkRegion::kUnion_Op);
1168     }
1169     const SkIRect& allBounds = all.getBounds();
1170     if (valid) {
1171         valid = false;
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);
1176         else
1177             valid = true;
1178     }
1179     while (valid == false)
1180         ;
1181 #endif
1182     return valid;
1183 #endif // FAST_PICTURESET
1184 }
1185
1186 } /* namespace android */