diff options
Diffstat (limited to 'Source/WebKit/android/jni/PictureSet.cpp')
-rw-r--r-- | Source/WebKit/android/jni/PictureSet.cpp | 1244 |
1 files changed, 0 insertions, 1244 deletions
diff --git a/Source/WebKit/android/jni/PictureSet.cpp b/Source/WebKit/android/jni/PictureSet.cpp deleted file mode 100644 index 8beb0ef..0000000 --- a/Source/WebKit/android/jni/PictureSet.cpp +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright 2008, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LOG_TAG "PictureSet" -#define LOG_NDEBUG 1 - -#include "config.h" -#include "PictureSet.h" - -#include "AndroidLog.h" -#include "android_graphics.h" -#include "SkBounder.h" -#include "SkCanvas.h" -#include "SkPicture.h" -#include "SkRect.h" -#include "SkRegion.h" -#include "SkStream.h" - -#include "PlatformGraphicsContext.h" - -#define MAX_DRAW_TIME 100 -#define MIN_SPLITTABLE 400 -#define MAX_ADDITIONAL_AREA 0.65 -#define MAX_ADDITIONAL_PICTURES 32 - -#define BUCKET_SIZE 1024 -#define MAX_BUCKET_COUNT_X 16 -#define MAX_BUCKET_COUNT_Y 64 - -#if PICTURE_SET_DEBUG -class MeasureStream : public SkWStream { -public: - MeasureStream() : mTotal(0) {} - virtual bool write(const void* , size_t size) { - mTotal += size; - return true; - } - size_t mTotal; -}; -#endif - -namespace android { - -PictureSet::PictureSet() : -#ifdef FAST_PICTURESET - mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE), - mBucketCountX(0), mBucketCountY(0), -#endif - mHeight(0), mWidth(0) -{ - setDimensions(0, 0); - mBaseArea = mAdditionalArea = 0; -} - -PictureSet::PictureSet(SkPicture* picture) : -#ifdef FAST_PICTURESET - mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE), - mBucketCountX(0), mBucketCountY(0), -#endif - mHeight(0), mWidth(0) -{ - mBaseArea = mAdditionalArea = 0; - if (!picture) { - setDimensions(0, 0); - return; - } - setDimensions(picture->width(), picture->height()); - mBaseArea = mWidth * mHeight; -#ifdef FAST_PICTURESET - SkIRect area; - area.set(0, 0, mWidth, mHeight); - splitAdd(area); - WTF::Vector<Bucket*>* buckets = bucketsToUpdate(); - for (unsigned int i = 0; i < buckets->size(); i++) { - Bucket* bucket = (*buckets)[i]; - for (unsigned int j = 0; j < bucket->size(); j++) { - BucketPicture& bucketPicture = (*bucket)[j]; - const SkIRect& inval = bucketPicture.mRealArea; - SkPicture *splitPicture = new SkPicture(); - SkCanvas *canvas = splitPicture->beginRecording( - inval.width(), inval.height(), - SkPicture::kUsePathBoundsForClip_RecordingFlag); - canvas->translate(-inval.fLeft, -inval.fTop); - picture->draw(canvas); - splitPicture->endRecording(); - SkSafeUnref(bucketPicture.mPicture); - bucketPicture.mPicture = splitPicture; - } - } - buckets->clear(); -#else - Pictures pictureAndBounds; - pictureAndBounds.mPicture = picture; - SkSafeRef(pictureAndBounds.mPicture); - pictureAndBounds.mEmpty = false; - pictureAndBounds.mArea.set(0, 0, mWidth, mHeight); - pictureAndBounds.mSplit = false; - pictureAndBounds.mBase = true; - pictureAndBounds.mElapsed = 0; - pictureAndBounds.mWroteElapsed = false; - mPictures.append(pictureAndBounds); -#endif // FAST_PICTURESET -} - -PictureSet::~PictureSet() -{ - clear(); -} - -#ifdef FAST_PICTURESET -#else -void PictureSet::add(const Pictures* temp) -{ - Pictures pictureAndBounds = *temp; - SkSafeRef(pictureAndBounds.mPicture); -#ifdef CONTEXT_RECORDING - SkSafeRef(pictureAndBounds.mGraphicsOperationCollection); -#endif - pictureAndBounds.mWroteElapsed = false; - mPictures.append(pictureAndBounds); -} -#endif // FAST_PICTURESET - -void PictureSet::add(const SkRegion& area, uint32_t elapsed, bool split) -{ - if (area.isRect()) { -#ifdef FAST_PICTURESET - splitAdd(area.getBounds()); -#else - add(area.getBounds(), elapsed, split, false); -#endif // FAST_PICTURESET - } else { - SkRegion::Iterator cliperator(area); - while (!cliperator.done()) { - SkIRect ir = cliperator.rect(); -#ifdef FAST_PICTURESET - splitAdd(ir); -#else - add(ir, elapsed, split, false); -#endif // FAST_PICTURESET - cliperator.next(); - } - } -} - -#ifdef FAST_PICTURESET - -Bucket* PictureSet::getBucket(int x, int y) -{ - // only create buckets for valid, positive coordinates, ignore and return - // NULL otherwise - if (x < 0 || y < 0) - return 0; - - BucketPosition position(x+1, y+1); - if (!mBuckets.contains(position)) { - ALOGV("PictureSet::getBucket(%d, %d) adding new bucket", x, y); - Bucket* bucket = new Bucket(); - mBuckets.add(position, bucket); - } - return mBuckets.get(position); -} - -void PictureSet::displayBucket(Bucket* bucket) -{ - BucketPicture* first = bucket->begin(); - BucketPicture* last = bucket->end(); - for (BucketPicture* current = first; current != last; current++) { - ALOGD("- in %x, bucketPicture %d,%d,%d,%d - %dx%d, picture: %x, base: %x", - bucket, - current->mArea.fLeft, - current->mArea.fTop, - current->mArea.fRight, - current->mArea.fBottom, - current->mArea.width(), - current->mArea.height(), - current->mPicture, - current->mBase); - } -} - -void PictureSet::displayBuckets() -{ - ALOGD("\n\n****** DISPLAY BUCKETS ON PictureSet %x ******", this); - for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) { - ALOGD("\n*** Bucket %x for %d, %d", iter->second, iter->first.first, iter->first.second); - displayBucket(iter->second); - } - ALOGD("\n****** END OF DISPLAY BUCKETS ******\n\n"); -} - -// When we receive an inval in a Bucket, we try to see if we intersect with -// existing invals/pictures in the Bucket. -void PictureSet::addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect) -{ - bool resetBase = false; - - SkIRect totalArea = rect; - BucketPicture* first = bucket->begin(); - BucketPicture* last = bucket->end(); - - // If the inval covers a large area of the base inval, let's repaint the - // entire bucket. - if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * mBucketSizeX * mBucketSizeY) - resetBase = true; - - // let's gather all the BucketPicture intersecting with the new invalidated - // area, collect their area and remove their picture - for (BucketPicture* current = first; current != last; current++) { - bool remove = resetBase; - bool intersect = false; - - if (!remove) - intersect = SkIRect::Intersects(current->mArea, rect); - // If the current picture is not a base, and we intersect, remove it - if (!remove && !current->mBase && intersect) - remove = true; - // If the current picture is a base, check if the new inval completely - // contains the base, and if so remove it. - if (!remove && current->mBase && rect.contains(current->mArea)) - remove = true; - // If the current picture is a base and it intersects, - // also check that it fully covers the bucket -- otherwise, - // let's aggregate it with the new inval. - if (!remove && current->mBase && intersect - && (current->mArea.width() < mBucketSizeX || current->mArea.height() < mBucketSizeY)) { - remove = true; - } - - if (remove) { - totalArea.join(current->mArea); - current->mBase = false; - current->mArea.setEmpty(); - SkSafeUnref(current->mPicture); - current->mPicture = 0; - } - } - - // Now, let's add the new BucketPicture to the list, with the correct - // area that needs to be repainted - SkRegion region; - SkIRect area = totalArea; - area.offset(dx, dy); - BucketPicture picture = { 0, totalArea, area, false }; - - bucket->append(picture); - - first = bucket->begin(); - last = bucket->end(); - - bool clearUp = false; - if (last - first > MAX_ADDITIONAL_PICTURES) { - // too many pictures in the bucket, let's collapse - clearUp = true; - } - - float bucketBaseArea = 0; - float bucketAdditionalArea = 0; - for (BucketPicture* current = first; current != last; current++) { - float area = current->mArea.width() * current->mArea.height(); - if (current->mBase) - bucketBaseArea += area; - else - bucketAdditionalArea += area; - } - - if (bucketBaseArea > 0 && bucketBaseArea * MAX_ADDITIONAL_AREA <= bucketAdditionalArea) { - // additional area too large, not worth maintaining - clearUp = true; - } - - // To clear things up, we just need to mark the pictures' area as empty - // We only keep the base surface. - if (clearUp) { - for (BucketPicture* current = first; current != last; current++) { - if (!current->mBase) - current->mArea.setEmpty(); - SkSafeUnref(current->mPicture); - current->mPicture = 0; - } - } - - // let's do a pass to collapse out empty areas - BucketPicture* writer = first; - for (BucketPicture* current = first; current != last; current++) { - if (current && current->mArea.isEmpty()) - continue; - *writer++ = *current; - } - - bucket->shrink(writer - first); - - // let's recompute the bases - first = bucket->begin(); - last = bucket->end(); - SkRegion drawn; - drawn.setEmpty(); - for (BucketPicture* current = first; current != last; current++) { - if (drawn.contains(current->mArea) == false) { - current->mBase = true; - } - drawn.op(current->mArea, SkRegion::kUnion_Op); - } -} - -void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect) -{ - ALOGV("\n--- gatherBucketsForArea for rect %d, %d, %d, %d (%d x %d)", - rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - rect.width(), rect.height()); - - if (!mBucketSizeX || !mBucketSizeY) { - ALOGD("PictureSet::gatherBucketsForArea() called with bad bucket size: x=%d y=%d", - mBucketSizeX, mBucketSizeY); - return; - } - - int x = rect.fLeft; - int y = rect.fTop; - int firstTileX = rect.fLeft / mBucketSizeX; - int firstTileY = rect.fTop / mBucketSizeY; - int lastTileX = rect.fRight / mBucketSizeX; - int lastTileY = rect.fBottom / mBucketSizeY; - - for (int i = firstTileX; i <= lastTileX; i++) { - for (int j = firstTileY; j <= lastTileY; j++) { - Bucket* bucket = getBucket(i, j); - ALOGV("gather bucket %x for %d, %d", bucket, i+1, j+1); - if (bucket) - list.append(bucket); - } - } -} - -// When we receive a new inval rect, we first find the Buckets that intersect -// with it; then we split the original inval into a serie of invals (one for -// each Bucket we intersect with). We then send that inval to the Bucket. -void PictureSet::splitAdd(const SkIRect& rect) -{ - ALOGV("\n--- splitAdd for rect %d, %d, %d, %d (%d x %d)", - rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - rect.width(), rect.height()); - - if (!mBucketSizeX || !mBucketSizeY) { - ALOGD("PictureSet::splitAdd() called with bad bucket size: x=%d y=%d", - mBucketSizeX, mBucketSizeY); - return; - } - - // TODO: reuse gatherBucketsForArea() (change Bucket to be a class) - int x = rect.fLeft; - int y = rect.fTop; - int firstTileX = rect.fLeft / mBucketSizeX; - int firstTileY = rect.fTop / mBucketSizeY; - int lastTileX = rect.fRight / mBucketSizeX; - int lastTileY = rect.fBottom / mBucketSizeY; - - ALOGV("--- firstTile(%d, %d) lastTile(%d, %d)", - firstTileX, firstTileY, - lastTileX, lastTileY); - - for (int i = firstTileX; i <= lastTileX; i++) { - for (int j = firstTileY; j <= lastTileY; j++) { - Bucket* bucket = getBucket(i, j); - if (!bucket) - continue; - - SkIRect newRect; - int deltaX = i * mBucketSizeX; - int deltaY = j * mBucketSizeY; - int left = (i == firstTileX) ? rect.fLeft - deltaX : 0; - int top = (j == firstTileY) ? rect.fTop - deltaY : 0; - int right = (i == lastTileX) ? rect.fRight % mBucketSizeX : mBucketSizeX; - int bottom = (j == lastTileY) ? rect.fBottom % mBucketSizeY : mBucketSizeY; - - newRect.set(left, top, right, bottom); - addToBucket(bucket, deltaX, deltaY, newRect); - mUpdatedBuckets.append(bucket); - } - } - - ALOGV("--- splitAdd DONE\n"); -} - -#endif // FAST_PICTURESET - -// This function is used to maintain the list of Pictures. -// Pictures contain an SkPicture covering a specific area; some -// Pictures are "base" Pictures -- i.e. there is no Pictures -// underneath them. -// The idea here is to keep a balance between the number of Pictures -// we have (more Pictures slow us down) and the area of Pictures that -// need to be repainted (obviously, smaller areas are better). -// To do so, we try to not update/repaint the base pictures -- by -// construction, they usually cover a large area (the entire page). -// We only reset a base picture if the new invalidated area entirely -// contains it. -// Most of the time we thus work on smaller pictures on top of the -// base ones; We compute the total area of all pictures intersecting -// with the passed invalidated area (as they would need to be invalidated), -// and use that as the basis for the correct area we want to invalidate -// (we then can simply delete the pictures we intersect with). -// In addition, we do a couple of things to limit the total number of pictures -// we keep in the list: -// - if the total area of additional textures reach 65% of the base pictures, -// we delete the additional pictures and mark the base pictures as -// needing a full repaint -// - we limit the number of pictures to 32 -- above that, we do the same -// things (deleting additional pictures + full repaint of base pictures) -#ifdef FAST_PICTURESET -#else -void PictureSet::add(const SkIRect& area, uint32_t elapsed, bool split, bool empty) -{ - bool checkForNewBases = false; - - Pictures* first = mPictures.begin(); - Pictures* last = mPictures.end(); -#ifdef DEBUG - ALOGV("--- before adding the new inval ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N'); - } - ALOGV("----------------------------------"); -#endif - - // let's gather all the Pictures intersecting with the new invalidated - // area, collect their area and remove their picture - SkIRect totalArea = area; - for (Pictures* working = first; working != last; working++) { - SkIRect inval = area; - bool remove = false; - if (!working->mBase && SkIRect::Intersects(working->mArea, inval)) - remove = true; - if (working->mBase) { - SkIRect baseArea = working->mArea; - if (area.contains(baseArea)) { - remove = true; - checkForNewBases = true; - } - } - - if (remove) { - SkIRect currentArea = working->mArea; - if (working->mBase) - mBaseArea -= currentArea.width() * currentArea.height(); - else - mAdditionalArea -= currentArea.width() * currentArea.height(); - - totalArea.join(currentArea); - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) intersects with the new inval area (%d, %d, %d, %d - %d x %d)", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - inval.fLeft, inval.fTop, inval.fRight, inval.fBottom, - inval.width(), inval.height()); - working->mArea.setEmpty(); - SkSafeUnref(working->mPicture); - working->mPicture = 0; -#ifdef CONTEXT_RECORDING - SkSafeUnref(working->mGraphicsOperationCollection); - working->mGraphicsOperationCollection = 0; -#endif - } - } - - // Now we can add the new Picture to the list, with the correct area - // that need to be repainted - Pictures pictureAndBounds = {totalArea, 0, -#ifdef CONTEXT_RECORDING - 0, -#endif - totalArea, elapsed, split, false, false, empty}; - -#ifdef FAST_PICTURESET - if (mPictures.size() == 0) - checkForNewBases = true; -#endif - - if (!size()) { - pictureAndBounds.mBase = true; - mBaseArea = totalArea.width() * totalArea.height(); - mAdditionalArea = 0; - mPictures.append(pictureAndBounds); - return; - } - mPictures.append(pictureAndBounds); - mAdditionalArea += totalArea.width() * totalArea.height(); - last = mPictures.end(); - first = mPictures.begin(); - - // Then, let's see if we have to clear up the pictures in order to keep - // the total number of pictures under our limit - bool clearUp = false; - if (last - first > MAX_ADDITIONAL_PICTURES) { - ALOGV("--- too many pictures, only keeping the bases : %d", last - first); - clearUp = true; - } - - if (!clearUp) { - if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) { - ALOGV("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f", - MAX_ADDITIONAL_AREA * 100, mBaseArea * 0.65, mBaseArea, mAdditionalArea); - clearUp = true; - } - } - - if (clearUp) { - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - if (!working->mBase) - working->mArea.setEmpty(); - SkSafeUnref(working->mPicture); - working->mPicture = 0; -#ifdef CONTEXT_RECORDING - SkSafeUnref(working->mGraphicsOperationCollection); - working->mGraphicsOperationCollection = 0; -#endif - } - } - -#ifdef DEBUG - ALOGV("--- after adding the new inval, but before collapsing ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N'); - } - ALOGV("----------------------------------"); - ALOGV("let's collapse..."); -#endif - - // Finally, let's do a pass to collapse out empty regions - Pictures* writer = first; - for (Pictures* working = first; working != last; working++) { - if (working && working->mArea.isEmpty()) - continue; - *writer++ = *working; - } - ALOGV("shiking of %d elements", writer - first); - mPictures.shrink(writer - first); - -#ifdef DEBUG - ALOGV("--- after adding the new inval ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c picture %x", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N', working->mPicture); - } - ALOGV("----------------------------------"); -#endif - - // Base pictures might have been removed/added -- let's recompute them - SkRegion drawn; - if (checkForNewBases) { - drawn.setEmpty(); - Pictures* last = mPictures.end(); - ALOGV("checkForNewBases..."); - for (Pictures* working = mPictures.begin(); working != last; working++) { - SkRegion area; - area.setRect(working->mArea); - const SkIRect& a = area.getBounds(); - if (drawn.contains(working->mArea) == false) { - working->mBase = true; - float area = a.width() * a.height(); - mBaseArea += area; - mAdditionalArea -= area; - } - drawn.op(working->mArea, SkRegion::kUnion_Op); - } - } - -#ifdef DEBUG - ALOGV("--- after checking for bases ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c picture %x", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N', working->mPicture); - } - ALOGV("----------------------------------"); -#endif -} -#endif // FAST_PICTURESET - -void PictureSet::setDimensions(int width, int height, SkRegion* inval) -{ - // Note that setDimensions() may be called by our ctor and should behave accordingly - if (mWidth == width && mHeight == height) - return; - DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this, - mWidth, mHeight, width, height); - bool clearCache = false; - if (inval) { - if (mWidth == width && height > mHeight) { // only grew vertically - SkIRect rect; - rect.set(0, mHeight, width, height); - inval->op(rect, SkRegion::kUnion_Op); - } else { - clearCache = true; - inval->setRect(0, 0, width, height); - } - } -#ifdef FAST_PICTURESET - // First figure out how large each bucket would be if we used all of the buckets - int tmpSizeX = (width + MAX_BUCKET_COUNT_X - 1) / MAX_BUCKET_COUNT_X; - int tmpSizeY = (height + MAX_BUCKET_COUNT_Y - 1) / MAX_BUCKET_COUNT_Y; - - // Then round the bucket size up to the nearest chunk - int bucketSizeX = ((tmpSizeX - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE; - int bucketSizeY = ((tmpSizeY - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE; - - int bucketCountX = (width + bucketSizeX - 1) / bucketSizeX; - int bucketCountY = (height + bucketSizeY - 1) / bucketSizeY; - - // Clear the cache if the horizontal bucket count changed or the vertical - // count shrank - if (bucketCountX != mBucketCountX || bucketCountY < mBucketCountY) - clearCache = true; - - // Or if the bucket size changed - if (bucketSizeX != mBucketSizeX || bucketSizeY != mBucketSizeY) - clearCache = true; - - ALOGV("old width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d", - mWidth, mHeight, mBucketSizeX, mBucketSizeY, mBucketCountX, mBucketCountY, clearCache); - ALOGV("new width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d", - width, height, bucketSizeX, bucketSizeY, bucketCountX, bucketCountY, clearCache); -#endif - if (clearCache) - clear(); - mWidth = width; - mHeight = height; -#ifdef FAST_PICTURESET - mBucketSizeX = bucketSizeX; - mBucketSizeY = bucketSizeY; - mBucketCountX = bucketCountX; - mBucketCountY = bucketCountY; -#endif -} - -void PictureSet::clear() -{ - DBG_SET_LOG(""); -#ifdef FAST_PICTURESET - for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) { - Bucket* bucket = iter->second; - BucketPicture* first = bucket->begin(); - BucketPicture* last = bucket->end(); - for (BucketPicture* current = first; current != last; current++) { - SkSafeUnref(current->mPicture); - current->mPicture = 0; - } - bucket->clear(); - } - mBuckets.clear(); - mBucketSizeX = mBucketSizeY = BUCKET_SIZE; -#else - Pictures* last = mPictures.end(); - for (Pictures* working = mPictures.begin(); working != last; working++) { - working->mArea.setEmpty(); - SkSafeUnref(working->mPicture); -#ifdef CONTEXT_RECORDING - SkSafeUnref(working->mGraphicsOperationCollection); -#endif - } - mPictures.clear(); -#endif // FAST_PICTURESET - mWidth = mHeight = 0; -} - -uint32_t getThreadMsec() -{ -#if defined(HAVE_POSIX_CLOCKS) - struct timespec tm; - - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); - return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; -#else - struct timeval now; - struct timezone zone; - - gettimeofday(&now, &zone); - return now.tv_sec * 1000LL + now.tv_usec / 1000; -#endif -} - -bool PictureSet::draw(SkCanvas* canvas) -{ -#ifdef FAST_PICTURESET - ALOGV("PictureSet %x draw on canvas %x", this, canvas); - SkRect bounds; - if (canvas->getClipBounds(&bounds) == false) - return false; - SkIRect irect; - bounds.roundOut(&irect); - - WTF::Vector<Bucket*> list; - gatherBucketsForArea(list, irect); - - ALOGV("PictureSet draw on canvas %x, we have %d buckets", canvas, list.size()); - for (unsigned int i = 0; i < list.size(); i++) { - Bucket* bucket = list[i]; - ALOGV("We paint using bucket %x with %d pictures", bucket, bucket->size()); - for (unsigned int j = 0; j < bucket->size(); j++) { - BucketPicture& picture = bucket->at(j); - if (!picture.mPicture) - continue; - int saved = canvas->save(); - SkRect pathBounds; - pathBounds.set(picture.mRealArea); - ALOGV("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d", - j, bucket->size(), - picture.mRealArea.fLeft, - picture.mRealArea.fTop, - picture.mRealArea.fRight, - picture.mRealArea.fBottom, - picture.mRealArea.width(), - picture.mRealArea.height()); - canvas->clipRect(pathBounds); - canvas->translate(pathBounds.fLeft, pathBounds.fTop); - canvas->save(); - canvas->drawPicture(*picture.mPicture); - canvas->restoreToCount(saved); - } - } - return false; - -#else - - validate(__FUNCTION__); - Pictures* first = mPictures.begin(); - Pictures* last = mPictures.end(); - Pictures* working; - SkRect bounds; - if (canvas->getClipBounds(&bounds) == false) - return false; - SkIRect irect; - bounds.roundOut(&irect); - if (!irect.intersect(0, 0, width(), height())) - return false; - for (working = last; working != first; ) { - --working; - if (working->mArea.contains(irect)) { -#if PICTURE_SET_DEBUG - const SkIRect& b = working->mArea; - DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}" - " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom, - irect.fLeft, irect.fTop, irect.fRight, irect.fBottom); -#endif - first = working; - break; - } - } - DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(), - last - mPictures.begin()); - uint32_t maxElapsed = 0; - for (working = first; working != last; working++) { - SkRegion area; - area.setRect(working->mArea); - if (area.quickReject(irect)) { -#if PICTURE_SET_DEBUG - const SkIRect& b = area.getBounds(); - DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}" - " irect={%d,%d,%d,%d}", working - first, working, - b.fLeft, b.fTop, b.fRight, b.fBottom, - irect.fLeft, irect.fTop, irect.fRight, irect.fBottom); -#endif - working->mElapsed = 0; - continue; - } - int saved = canvas->save(); - SkRect pathBounds; - if (area.isComplex()) { - SkPath pathClip; - area.getBoundaryPath(&pathClip); - canvas->clipPath(pathClip); - pathBounds = pathClip.getBounds(); - } else { - pathBounds.set(area.getBounds()); - canvas->clipRect(pathBounds); - } - canvas->translate(pathBounds.fLeft, pathBounds.fTop); - canvas->save(); - uint32_t startTime = getThreadMsec(); - -#ifdef CONTEXT_RECORDING - WebCore::PlatformGraphicsContextSkia context(canvas); - working->mGraphicsOperationCollection->apply(&context); -#else - canvas->drawPicture(*working->mPicture); -#endif - - size_t elapsed = working->mElapsed = getThreadMsec() - startTime; - working->mWroteElapsed = true; - if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE || - pathBounds.height() >= MIN_SPLITTABLE)) - maxElapsed = elapsed; - canvas->restoreToCount(saved); -#define DRAW_TEST_IMAGE 01 -#if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG - SkColor color = 0x3f000000 | (0xffffff & (unsigned) working); - canvas->drawColor(color); - SkPaint paint; - color ^= 0x00ffffff; - paint.setColor(color); - char location[256]; - for (int x = area.getBounds().fLeft & ~0x3f; - x < area.getBounds().fRight; x += 0x40) { - for (int y = area.getBounds().fTop & ~0x3f; - y < area.getBounds().fBottom; y += 0x40) { - int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y); - canvas->drawText(location, len, x, y, paint); - } - } -#endif - DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s", - working - first, working, - area.getBounds().fLeft, area.getBounds().fTop, - area.getBounds().fRight, area.getBounds().fBottom, - working->mElapsed, working->mBase ? "true" : "false"); - } - // dump(__FUNCTION__); - return maxElapsed >= MAX_DRAW_TIME; -#endif // FAST_PICTURESET -} - -void PictureSet::dump(const char* label) const -{ -#if PICTURE_SET_DUMP - DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(), - mWidth, mHeight); - const Pictures* last = mPictures.end(); - for (const Pictures* working = mPictures.begin(); working != last; working++) { - const SkIRect& bounds = working->mArea.getBounds(); - const SkIRect& unsplit = working->mUnsplit; - MeasureStream measure; - if (working->mPicture != NULL) - working->mPicture->serialize(&measure); - ALOGD(" [%d]" - " mArea.bounds={%d,%d,r=%d,b=%d}" - " mPicture=%p" - " mUnsplit={%d,%d,r=%d,b=%d}" - " mElapsed=%d" - " mSplit=%s" - " mWroteElapsed=%s" - " mBase=%s" - " pict-size=%d", - working - mPictures.begin(), - bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, - working->mPicture, - unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom, - working->mElapsed, working->mSplit ? "true" : "false", - working->mWroteElapsed ? "true" : "false", - working->mBase ? "true" : "false", - measure.mTotal); - } -#endif -} - -class IsEmptyBounder : public SkBounder { - virtual bool onIRect(const SkIRect& rect) { - return false; - } -}; - -class IsEmptyCanvas : public SkCanvas { -public: - IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) : - mPicture(picture), mEmpty(true) { - setBounder(bounder); - } - - void notEmpty() { - mEmpty = false; - mPicture->abortPlayback(); - } - - virtual bool clipPath(const SkPath&, SkRegion::Op) { - // this can be expensive to actually do, and doesn't affect the - // question of emptiness, so we make it a no-op - return true; - } - - virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect, - const SkMatrix& , const SkPaint& ) { - if (bitmap.width() <= 1 || bitmap.height() <= 1) - return; - DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height()); - notEmpty(); - } - - virtual void drawPaint(const SkPaint& paint) { - } - - virtual void drawPath(const SkPath& , const SkPaint& paint) { - DBG_SET_LOG("abort"); - notEmpty(); - } - - virtual void drawPoints(PointMode , size_t , const SkPoint [], - const SkPaint& paint) { - } - - virtual void drawRect(const SkRect& , const SkPaint& paint) { - // wait for visual content - if (paint.getColor() != SK_ColorWHITE) - notEmpty(); - } - - virtual void drawSprite(const SkBitmap& , int , int , - const SkPaint* paint = NULL) { - DBG_SET_LOG("abort"); - notEmpty(); - } - - virtual void drawText(const void* , size_t byteLength, SkScalar , - SkScalar , const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawPosText(const void* , size_t byteLength, - const SkPoint [], const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawPosTextH(const void* , size_t byteLength, - const SkScalar [], SkScalar , - const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawTextOnPath(const void* , size_t byteLength, - const SkPath& , const SkMatrix* , - const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawPicture(SkPicture& picture) { - SkCanvas::drawPicture(picture); - } - - SkPicture* mPicture; - bool mEmpty; -}; - -bool PictureSet::emptyPicture(SkPicture* picture) const -{ - IsEmptyBounder isEmptyBounder; - IsEmptyCanvas checker(&isEmptyBounder, picture); - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight); - checker.setBitmapDevice(bitmap); - checker.drawPicture(*picture); - return checker.mEmpty; -} - -bool PictureSet::isEmpty() const -{ -#ifdef FAST_PICTURESET - // For now, just assume the pictureset is *not* empty - // if the hashmap contains something - for (BucketMap::const_iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) { - if (iter->second->size() > 0) - return false; - } - return true; -#else - const Pictures* last = mPictures.end(); - for (const Pictures* working = mPictures.begin(); working != last; working++) { - if (!working->mEmpty) - return false; - } - return true; -#endif // FAST_PICTURESET -} - -void PictureSet::set(const PictureSet& src) -{ - DBG_SET_LOGD("start %p src=%p", this, &src); - clear(); - setDimensions(src.mWidth, src.mHeight); -#ifdef FAST_PICTURESET - ALOGV("\n--- set picture ---"); - for (BucketMap::const_iterator iter = src.mBuckets.begin(); - iter != src.mBuckets.end(); ++iter) { - Bucket* sourceBucket = iter->second; - Bucket* targetBucket = getBucket(iter->first.first-1, iter->first.second-1); - BucketPicture* first = sourceBucket->begin(); - BucketPicture* last = sourceBucket->end(); - ALOGV("set from bucket %x (%d, %d), %d pictures", sourceBucket, - iter->first.first, iter->first.second, sourceBucket->size()); - for (BucketPicture* current = first; current != last; current++) { - ALOGV("set picture %x from bucket %x in bucket %x (%d, %d)", - current->mPicture, sourceBucket, targetBucket, - iter->first.first, iter->first.second); - SkSafeRef(current->mPicture); - BucketPicture picture = { current->mPicture, current->mArea, - current->mRealArea, current->mBase }; - targetBucket->append(picture); - } - } - ALOGV("--- DONE set picture ---\n"); -#else - const Pictures* last = src.mPictures.end(); - for (const Pictures* working = src.mPictures.begin(); working != last; working++) - add(working); - // dump(__FUNCTION__); - validate(__FUNCTION__); - DBG_SET_LOG("end"); -#endif // FAST_PICTURESET -} - -#ifdef FAST_PICTURESET -#else - -void PictureSet::setDrawTimes(const PictureSet& src) -{ - validate(__FUNCTION__); - if (mWidth != src.mWidth || mHeight != src.mHeight) - return; - Pictures* last = mPictures.end(); - Pictures* working = mPictures.begin(); - if (working == last) - return; - const Pictures* srcLast = src.mPictures.end(); - const Pictures* srcWorking = src.mPictures.begin(); - for (; srcWorking != srcLast; srcWorking++) { - if (srcWorking->mWroteElapsed == false) - continue; - while ((srcWorking->mArea != working->mArea || - srcWorking->mPicture != working->mPicture)) { - if (++working == last) - return; - } - DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d", - this, working - mPictures.begin(), srcWorking - src.mPictures.begin(), - working->mArea.fLeft, working->mArea.fTop, - working->mArea.fRight, working->mArea.fBottom, - working->mElapsed, srcWorking->mElapsed); - working->mElapsed = srcWorking->mElapsed; - } -} - -void PictureSet::setPicture(size_t i, SkPicture* p) -{ - SkSafeUnref(mPictures[i].mPicture); - mPictures[i].mPicture = p; - mPictures[i].mEmpty = emptyPicture(p); -} - -#ifdef CONTEXT_RECORDING -void PictureSet::setGraphicsOperationCollection(size_t i, WebCore::GraphicsOperationCollection* p) -{ - SkSafeUnref(mPictures[i].mGraphicsOperationCollection); - mPictures[i].mGraphicsOperationCollection = p; -} -#endif - -void PictureSet::split(PictureSet* out) const -{ - dump(__FUNCTION__); - DBG_SET_LOGD("%p", this); - SkIRect totalBounds; - out->mWidth = mWidth; - out->mHeight = mHeight; - totalBounds.set(0, 0, mWidth, mHeight); - SkRegion* total = new SkRegion(totalBounds); - const Pictures* last = mPictures.end(); - const Pictures* working; - uint32_t balance = 0; - int multiUnsplitFastPictures = 0; // > 1 has more than 1 - for (working = mPictures.begin(); working != last; working++) { - if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit) - continue; - if (++multiUnsplitFastPictures > 1) - break; - } - for (working = mPictures.begin(); working != last; working++) { - uint32_t elapsed = working->mElapsed; - if (elapsed < MAX_DRAW_TIME) { - bool split = working->mSplit; - DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()=" - "{%d,%d,r=%d,b=%d} split=%s", elapsed, working, - total->getBounds().fLeft, total->getBounds().fTop, - total->getBounds().fRight, total->getBounds().fBottom, - split ? "true" : "false"); - if (multiUnsplitFastPictures <= 1 || split) { - total->op(working->mArea, SkRegion::kDifference_Op); - out->add(working->mArea, elapsed, split, - working->mEmpty); - } else if (balance < elapsed) - balance = elapsed; - continue; - } - total->op(working->mArea, SkRegion::kDifference_Op); - const SkIRect& bounds = working->mArea; - int width = bounds.width(); - int height = bounds.height(); - int across = 1; - int down = 1; - while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) { - if (height >= width) { - height >>= 1; - down <<= 1; - } else { - width >>= 1; - across <<= 1 ; - } - if ((elapsed >>= 1) < MAX_DRAW_TIME) - break; - } - width = bounds.width(); - height = bounds.height(); - int top = bounds.fTop; - for (int indexY = 0; indexY < down; ) { - int bottom = bounds.fTop + height * ++indexY / down; - int left = bounds.fLeft; - for (int indexX = 0; indexX < across; ) { - int right = bounds.fLeft + width * ++indexX / across; - SkIRect cBounds; - cBounds.set(left, top, right, bottom); - out->add(cBounds, elapsed, true, - (across | down) != 1 ? false : working->mEmpty); - left = right; - } - top = bottom; - } - } - DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d", - this, mWidth, mHeight, total->isEmpty() ? "true" : "false", - multiUnsplitFastPictures); - if (!total->isEmpty() && multiUnsplitFastPictures > 1) - out->add(*total, balance, false); - delete total; - validate(__FUNCTION__); - out->dump("split-out"); -} - -#endif // FAST_PICTURESET - -bool PictureSet::validate(const char* funct) const -{ -#ifdef FAST_PICTURESET - return true; -#else - bool valid = true; -#if PICTURE_SET_VALIDATE - SkRegion all; - const Pictures* first = mPictures.begin(); - for (const Pictures* working = mPictures.end(); working != first; ) { - --working; - const SkPicture* pict = working->mPicture; - const SkRegion& area = working->mArea; - const SkIRect& bounds = area.getBounds(); - bool localValid = false; - if (working->mUnsplit.isEmpty()) - ALOGD("%s working->mUnsplit.isEmpty()", funct); - else if (working->mUnsplit.contains(bounds) == false) - ALOGD("%s working->mUnsplit.contains(bounds) == false", funct); - else if (working->mElapsed >= 1000) - ALOGD("%s working->mElapsed >= 1000", funct); - else if ((working->mSplit & 0xfe) != 0) - ALOGD("%s (working->mSplit & 0xfe) != 0", funct); - else if ((working->mWroteElapsed & 0xfe) != 0) - ALOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct); - else if (pict != NULL) { - int pictWidth = pict->width(); - int pictHeight = pict->height(); - if (pictWidth < bounds.width()) - ALOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width()); - else if (pictHeight < bounds.height()) - ALOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height()); - else if (working->mArea.isEmpty()) - ALOGD("%s working->mArea.isEmpty()", funct); - else - localValid = true; - } else - localValid = true; - working->mArea.validate(); - if (localValid == false) { - if (all.contains(area) == true) - ALOGD("%s all.contains(area) == true", funct); - else - localValid = true; - } - valid &= localValid; - all.op(area, SkRegion::kUnion_Op); - } - const SkIRect& allBounds = all.getBounds(); - if (valid) { - valid = false; - if (allBounds.width() != mWidth) - ALOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth); - else if (allBounds.height() != mHeight) - ALOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight); - else - valid = true; - } - while (valid == false) - ; -#endif - return valid; -#endif // FAST_PICTURESET -} - -} /* namespace android */ |