diff options
Diffstat (limited to 'Source/WebKit/android')
-rw-r--r-- | Source/WebKit/android/AndroidLog.h | 7 | ||||
-rw-r--r-- | Source/WebKit/android/jni/PicturePile.cpp | 309 | ||||
-rw-r--r-- | Source/WebKit/android/jni/PicturePile.h | 121 | ||||
-rw-r--r-- | Source/WebKit/android/jni/PictureSet.cpp | 1244 | ||||
-rw-r--r-- | Source/WebKit/android/jni/PictureSet.h | 152 | ||||
-rw-r--r-- | Source/WebKit/android/jni/ViewStateSerializer.cpp | 1 | ||||
-rw-r--r-- | Source/WebKit/android/jni/WebCoreViewBridge.h | 2 | ||||
-rw-r--r-- | Source/WebKit/android/jni/WebViewCore.cpp | 248 | ||||
-rw-r--r-- | Source/WebKit/android/jni/WebViewCore.h | 18 | ||||
-rw-r--r-- | Source/WebKit/android/nav/WebView.cpp | 35 |
10 files changed, 509 insertions, 1628 deletions
diff --git a/Source/WebKit/android/AndroidLog.h b/Source/WebKit/android/AndroidLog.h index e483169..f034d35 100644 --- a/Source/WebKit/android/AndroidLog.h +++ b/Source/WebKit/android/AndroidLog.h @@ -53,7 +53,12 @@ extern FILE* gRenderTreeFile; #define DISPLAY_TREE_LOG_FILE "/sdcard/displayTree.txt" #define LAYERS_TREE_LOG_FILE "/sdcard/layersTree.plist" -#define TRACE_METHOD() ScopedTrace __st(ATRACE_TAG_WEBVIEW, __func__); +#define FLOAT_RECT_FORMAT "[x=%.2f,y=%.2f,w=%.2f,h=%.2f]" +#define FLOAT_RECT_ARGS(fr) fr.x(), fr.y(), fr.width(), fr.height() +#define INT_RECT_FORMAT "[x=%d,y=%d,w=%d,h=%d]" +#define INT_RECT_ARGS(ir) ir.x(), ir.y(), ir.width(), ir.height() + +#define TRACE_METHOD() android::ScopedTrace __st(ATRACE_TAG_WEBVIEW, __func__); #define TIME_METHOD() MethodTimer __method_timer(__func__) class MethodTimer { diff --git a/Source/WebKit/android/jni/PicturePile.cpp b/Source/WebKit/android/jni/PicturePile.cpp new file mode 100644 index 0000000..9ca3588 --- /dev/null +++ b/Source/WebKit/android/jni/PicturePile.cpp @@ -0,0 +1,309 @@ +/* + * Copyright 2012, 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 "PicturePile" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "PicturePile.h" + +#include "AndroidLog.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContextSkia.h" +#include "SkCanvas.h" +#include "SkNWayCanvas.h" +#include "SkPicture.h" +#include "SkPixelRef.h" +#include "SkRect.h" +#include "SkRegion.h" + +#define ENABLE_PRERENDERED_INVALS true +#define MAX_OVERLAP_COUNT 2 +#define MAX_OVERLAP_AREA .7 + +namespace WebCore { + +static SkIRect toSkIRect(const IntRect& rect) { + return SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); +} + +static IntRect extractClipBounds(SkCanvas* canvas, const IntSize& size) { + SkRect clip; + canvas->getClipBounds(&clip); + clip.intersect(0, 0, size.width(), size.height()); + return enclosingIntRect(clip); +} + +PicturePile::PicturePile(const PicturePile& other) + : m_size(other.m_size) + , m_pile(other.m_pile) + , m_webkitInvals(other.m_webkitInvals) +{ +} + +PicturePile::PicturePile(SkPicture* picture) +{ + m_size = IntSize(picture->width(), picture->height()); + PictureContainer pc(IntRect(0, 0, m_size.width(), m_size.height())); + pc.picture = picture; + pc.dirty = false; + m_pile.append(pc); +} + +void PicturePile::draw(SkCanvas* canvas) +{ + /* Loop down recursively, subtracting the previous clip from the SkRegion, + * stopping when the SkRegion is empty. This will still draw back-to-front, + * but it will clip out anything obscured. For performance reasons we use + * the rect bounds of the SkRegion for the clip, so this still can't be + * used for translucent surfaces + */ + TRACE_METHOD(); + IntRect clipBounds = extractClipBounds(canvas, m_size); + SkRegion clipRegion(toSkIRect(clipBounds)); + drawWithClipRecursive(canvas, clipRegion, m_pile.size() - 1); +} + +void PicturePile::clearPrerenders() +{ + for (size_t i = 0; i < m_pile.size(); i++) + m_pile[i].prerendered.clear(); +} + +void PicturePile::drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion, + int index) +{ + // TODO: Add some debug visualizations of this + if (index < 0 || clipRegion.isEmpty()) + return; + PictureContainer& pc = m_pile[index]; + IntRect intersection = clipRegion.getBounds(); + intersection.intersect(pc.area); + if (pc.picture && !intersection.isEmpty()) { + clipRegion.op(intersection, SkRegion::kDifference_Op); + drawWithClipRecursive(canvas, clipRegion, index - 1); + int saved = canvas->save(); + canvas->clipRect(intersection); + canvas->translate(pc.area.x(), pc.area.y()); + canvas->drawPicture(*pc.picture); + canvas->restoreToCount(saved); + } else + drawWithClipRecursive(canvas, clipRegion, index - 1); +} + +// Used by WebViewCore +void PicturePile::invalidate(const IntRect& dirtyRect) +{ + // This will typically happen if the document has been resized but we haven't + // drawn yet. As the first draw after a size change will do a full inval anyway, + // don't bother tracking individual rects + // TODO: Instead of clipping here, we should take the invals as given + // and when the size changes just inval the deltas. This prevents a full + // redraw for a page that grows + IntRect inval = dirtyRect; + inval.intersect(IntRect(0, 0, m_size.width(), m_size.height())); + if (inval.isEmpty()) { + ALOGV("Rejecting inval " INT_RECT_FORMAT, INT_RECT_ARGS(dirtyRect)); + return; + } + // TODO: Support multiple non-intersecting webkit invals + if (m_webkitInvals.size()) + m_webkitInvals[0].unite(inval); + else + m_webkitInvals.append(inval); +} + +void PicturePile::setSize(const IntSize& size) +{ + if (m_size == size) + return; + m_size = size; + // TODO: See above about just adding invals for new content + m_pile.clear(); + m_webkitInvals.clear(); + IntRect area(0, 0, size.width(), size.height()); + m_webkitInvals.append(area); + m_pile.append(area); +} + +void PicturePile::updatePicturesIfNeeded(PicturePainter* painter) +{ + applyWebkitInvals(); + for (size_t i = 0; i < m_pile.size(); i++) { + PictureContainer& pc = m_pile[i]; + if (pc.dirty) + updatePicture(painter, pc); + } +} + +void PicturePile::updatePicture(PicturePainter* painter, PictureContainer& pc) +{ + /* The ref counting here is a bit unusual. What happens is begin/end recording + * will ref/unref the recording canvas. However, 'canvas' might be pointing + * at an SkNWayCanvas instead of the recording canvas, which needs to be + * unref'd. Thus what we do is ref the recording canvas so that we can + * always unref whatever canvas we have at the end. + */ + TRACE_METHOD(); + SkPicture* picture = new SkPicture(); + SkCanvas* canvas = picture->beginRecording(pc.area.width(), pc.area.height(), + SkPicture::kUsePathBoundsForClip_RecordingFlag); + SkSafeRef(canvas); + canvas->translate(-pc.area.x(), -pc.area.y()); + IntRect drawArea = pc.area; + if (pc.prerendered.get()) { + SkCanvas* prerender = painter->createPrerenderCanvas(pc.prerendered.get()); + if (!prerender) { + ALOGV("Failed to create prerendered for " INT_RECT_FORMAT, + INT_RECT_ARGS(pc.prerendered->area)); + pc.prerendered.clear(); + } else { + drawArea.unite(pc.prerendered->area); + SkNWayCanvas* nwayCanvas = new SkNWayCanvas(drawArea.width(), drawArea.height()); + nwayCanvas->addCanvas(canvas); + nwayCanvas->addCanvas(prerender); + SkSafeUnref(canvas); + SkSafeUnref(prerender); + canvas = nwayCanvas; + } + } + WebCore::PlatformGraphicsContextSkia pgc(canvas); + WebCore::GraphicsContext gc(&pgc); + ALOGV("painting picture: " INT_RECT_FORMAT, INT_RECT_ARGS(drawArea)); + painter->paintContents(&gc, drawArea); + SkSafeUnref(canvas); + picture->endRecording(); + + SkSafeUnref(pc.picture); + pc.picture = picture; + pc.dirty = false; +} + +void PicturePile::reset() +{ + m_size = IntSize(0,0); + m_pile.clear(); + m_webkitInvals.clear(); +} + +void PicturePile::applyWebkitInvals() +{ + m_dirtyRegion.setEmpty(); + if (!m_webkitInvals.size()) + return; + // Build the invals (TODO: Support multiple inval regions) + IntRect inval = m_webkitInvals[0]; + m_dirtyRegion.setRect(toSkIRect(inval)); + for (size_t i = 1; i < m_webkitInvals.size(); i++) { + inval.unite(m_webkitInvals[i]); + m_dirtyRegion.op(toSkIRect(m_webkitInvals[i]), SkRegion::kUnion_Op); + } + m_webkitInvals.clear(); + ALOGV("Webkit inval: " INT_RECT_FORMAT, INT_RECT_ARGS(inval)); + if (inval.isEmpty()) + return; + + // Find the overlaps + Vector<int> overlaps; + for (size_t i = 0; i < m_pile.size(); i++) { + PictureContainer& pc = m_pile[i]; + if (pc.area.contains(inval)) { + if (pc.dirty) { + ALOGV("Found already dirty intersection"); + return; + } + if (pc.area == inval) { + appendToPile(inval); + return; + } + // Don't count the base surface as an overlap + if (pc.area.size() != m_size) + overlaps.append(i); + } else if (pc.area.intersects(inval)) + overlaps.append(i); + } + + if (overlaps.size() >= MAX_OVERLAP_COUNT) { + ALOGV("Exceeds overlap count"); + IntRect overlap = inval; + for (int i = (int) overlaps.size() - 1; i >= 0; i--) { + overlap.unite(m_pile[overlaps[i]].area); + m_pile.remove(overlaps[i]); + } + float overlapArea = overlap.width() * overlap.height(); + float totalArea = m_size.width() * m_size.height(); + if (overlapArea / totalArea > MAX_OVERLAP_AREA) + overlap = IntRect(0, 0, m_size.width(), m_size.height()); + appendToPile(overlap, inval); + return; + } + + // Append! + appendToPile(inval); +} + +void PicturePile::appendToPile(const IntRect& inval, const IntRect& originalInval) +{ + ALOGV("Adding inval " INT_RECT_FORMAT " for original inval " INT_RECT_FORMAT, + INT_RECT_ARGS(inval), INT_RECT_ARGS(originalInval)); + // Remove any entries this obscures + for (int i = (int) m_pile.size() - 1; i >= 0; i--) { + if (inval.contains(m_pile[i].area)) + m_pile.remove(i); + } + PictureContainer container(inval); + if (ENABLE_PRERENDERED_INVALS) { + container.prerendered = PrerenderedInval::create(originalInval.isEmpty() + ? inval : originalInval); + } + m_pile.append(container); +} + +PrerenderedInval* PicturePile::prerenderedInvalForArea(const IntRect& area) +{ + ALOGV("Checking for prerendered inval for area " INT_RECT_FORMAT, + INT_RECT_ARGS(area)); + for (int i = (int) m_pile.size() - 1; i >= 0; i--) { + if (m_pile[i].area.intersects(area)) { + RefPtr<PrerenderedInval> inval = m_pile[i].prerendered; + if (inval.get() && inval->area.contains(area)) { + ALOGV("Returning prerendered %p for area " INT_RECT_FORMAT, + m_pile[i].prerendered.get(), INT_RECT_ARGS(area)); + return inval.get(); + } + if (inval.get()) { + ALOGV("Prerendered area doesn't contain requested area; prerendered=" + INT_RECT_FORMAT, INT_RECT_ARGS(inval->area)); + } else + ALOGV("No prerendered in intersection"); + return 0; + } + } + ALOGV("No containers found"); + return 0; +} + +} // namespace WebCore diff --git a/Source/WebKit/android/jni/PicturePile.h b/Source/WebKit/android/jni/PicturePile.h new file mode 100644 index 0000000..b28a792 --- /dev/null +++ b/Source/WebKit/android/jni/PicturePile.h @@ -0,0 +1,121 @@ +/* + * Copyright 2012, 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. + */ + +#ifndef PicturePile_h +#define PicturePile_h + +#include "IntRect.h" +#include "IntSize.h" +#include "PrerenderedInval.h" +#include "SkBitmap.h" +#include "SkRegion.h" +#include "SkRefCnt.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/Vector.h> + +class SkPicture; +class SkCanvas; + +namespace WebCore { + +class GraphicsContext; + +class PicturePainter { +public: + virtual void paintContents(GraphicsContext* gc, IntRect& dirty) = 0; + virtual SkCanvas* createPrerenderCanvas(PrerenderedInval* prerendered) + { + return 0; + } + virtual ~PicturePainter() {} +}; + +class PictureContainer { +public: + SkPicture* picture; + IntRect area; + bool dirty; + RefPtr<PrerenderedInval> prerendered; + + PictureContainer(const IntRect& area) + : picture(0) + , area(area) + , dirty(true) + {} + + PictureContainer(const PictureContainer& other) + : picture(other.picture) + , area(other.area) + , dirty(other.dirty) + , prerendered(other.prerendered) + { + SkSafeRef(picture); + } + + ~PictureContainer() + { + SkSafeUnref(picture); + } +}; + +class PicturePile { +public: + PicturePile() {} + PicturePile(const PicturePile& other); + PicturePile(SkPicture* picture); + + const IntSize& size() { return m_size; } + + void clearPrerenders(); + + // used by PicturePileLayerContents + void draw(SkCanvas* canvas); + + // Used by WebViewCore + void invalidate(const IntRect& dirtyRect); + void setSize(const IntSize& size); + void updatePicturesIfNeeded(PicturePainter* painter); + void reset(); + SkRegion& dirtyRegion() { return m_dirtyRegion; } + PrerenderedInval* prerenderedInvalForArea(const IntRect& area); + +private: + void applyWebkitInvals(); + void updatePicture(PicturePainter* painter, PictureContainer& container); + void appendToPile(const IntRect& inval, const IntRect& originalInval = IntRect()); + void drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion, int index); + + IntSize m_size; + Vector<PictureContainer> m_pile; + Vector<IntRect> m_webkitInvals; + SkRegion m_dirtyRegion; +}; + +} // namespace android + +#endif // PicturePile_h 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 */ diff --git a/Source/WebKit/android/jni/PictureSet.h b/Source/WebKit/android/jni/PictureSet.h deleted file mode 100644 index 1e639da..0000000 --- a/Source/WebKit/android/jni/PictureSet.h +++ /dev/null @@ -1,152 +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. - */ - -#ifndef PictureSet_h -#define PictureSet_h - -#define PICTURE_SET_DUMP 0 -#define PICTURE_SET_DEBUG 0 -#define PICTURE_SET_VALIDATE 0 - -#if PICTURE_SET_DEBUG -#define DBG_SET_LOG(message) ALOGD("%s %s", __FUNCTION__, message) -#define DBG_SET_LOGD(format, ...) ALOGD("%s " format, __FUNCTION__, __VA_ARGS__) -#define DEBUG_SET_UI_LOGD(...) ALOGD(__VA_ARGS__) -#else -#define DBG_SET_LOG(message) ((void)0) -#define DBG_SET_LOGD(format, ...) ((void)0) -#define DEBUG_SET_UI_LOGD(...) ((void)0) -#endif - -#include "jni.h" -#include "SkRegion.h" -#include <wtf/Vector.h> -#include <wtf/HashMap.h> - -#include "GraphicsOperationCollection.h" - -// #define FAST_PICTURESET // use a hierarchy of pictures -// #define CONTEXT_RECORDING // use the new PlatformGraphicsContextRecording - -class SkCanvas; -class SkPicture; -class SkIRect; - -namespace android { - -#ifdef FAST_PICTURESET - struct BucketPicture { - SkPicture* mPicture; - SkIRect mArea; - SkIRect mRealArea; - bool mBase; - }; - - typedef std::pair<int, int> BucketPosition; - typedef WTF::Vector<BucketPicture> Bucket; - typedef WTF::HashMap<BucketPosition , Bucket* > BucketMap; -#endif - - class PictureSet { - public: - PictureSet(); - PictureSet(const PictureSet& src) { set(src); } - PictureSet(SkPicture* picture); - virtual ~PictureSet(); - -#ifdef FAST_PICTURESET - void displayBucket(Bucket* bucket); - void displayBuckets(); - WTF::Vector<Bucket*>* bucketsToUpdate() { return &mUpdatedBuckets; } - Bucket* getBucket(int x, int y); - void addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect); - void gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect); - void splitAdd(const SkIRect& rect); -#endif - - void add(const SkRegion& area, uint32_t elapsed, bool split); - - // Update mWidth/mHeight, and adds any additional inval region - void setDimensions(int width, int height, SkRegion* inval = 0); - void clear(); - bool draw(SkCanvas* ); - static PictureSet* GetNativePictureSet(JNIEnv* env, jobject jpic); - int height() const { return mHeight; } - bool isEmpty() const; // returns true if empty or only trivial content - void set(const PictureSet& ); - -#ifdef FAST_PICTURESET -#else - void add(const SkIRect& area, uint32_t elapsed, bool split, bool empty); - const SkIRect& bounds(size_t i) const { - return mPictures[i].mArea; } - void setPicture(size_t i, SkPicture* p); -#ifdef CONTEXT_RECORDING - void setGraphicsOperationCollection(size_t i, WebCore::GraphicsOperationCollection* p); -#endif - void setDrawTimes(const PictureSet& ); - size_t size() const { return mPictures.size(); } - void split(PictureSet* result) const; - bool upToDate(size_t i) const { return mPictures[i].mPicture != NULL; } -#endif - int width() const { return mWidth; } - void dump(const char* label) const; - bool validate(const char* label) const; - private: - bool emptyPicture(SkPicture* ) const; // true if no text, images, paths - -#ifdef FAST_PICTURESET - BucketMap mBuckets; - WTF::Vector<Bucket*> mUpdatedBuckets; - int mBucketSizeX; - int mBucketSizeY; - int mBucketCountX; - int mBucketCountY; -#else - - struct Pictures { - SkIRect mArea; - SkPicture* mPicture; -#ifdef CONTEXT_RECORDING - WebCore::GraphicsOperationCollection* mGraphicsOperationCollection; -#endif - SkIRect mUnsplit; - uint32_t mElapsed; - bool mSplit : 8; - bool mWroteElapsed : 8; - bool mBase : 8; // true if nothing is drawn underneath this - bool mEmpty : 8; // true if the picture only draws white - }; - void add(const Pictures* temp); - WTF::Vector<Pictures> mPictures; -#endif - float mBaseArea; - float mAdditionalArea; - int mHeight; - int mWidth; - }; -} - -#endif diff --git a/Source/WebKit/android/jni/ViewStateSerializer.cpp b/Source/WebKit/android/jni/ViewStateSerializer.cpp index 5f2480a..02ddca6 100644 --- a/Source/WebKit/android/jni/ViewStateSerializer.cpp +++ b/Source/WebKit/android/jni/ViewStateSerializer.cpp @@ -38,7 +38,6 @@ #include "LayerAndroid.h" #include "LayerContent.h" #include "PictureLayerContent.h" -#include "PictureSet.h" #include "ScrollableLayerAndroid.h" #include "SkFlattenable.h" #include "SkPicture.h" diff --git a/Source/WebKit/android/jni/WebCoreViewBridge.h b/Source/WebKit/android/jni/WebCoreViewBridge.h index b04a0f9..cca4ca5 100644 --- a/Source/WebKit/android/jni/WebCoreViewBridge.h +++ b/Source/WebKit/android/jni/WebCoreViewBridge.h @@ -48,7 +48,7 @@ public: virtual ~WebCoreViewBridge() { } - virtual void draw(WebCore::GraphicsContext* ctx, + virtual void draw(WebCore::GraphicsContext* ctx, const WebCore::IntRect& rect) = 0; const WebCore::IntRect& getBounds() const diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index e0379a8..4e6ffc6 100644 --- a/Source/WebKit/android/jni/WebViewCore.cpp +++ b/Source/WebKit/android/jni/WebViewCore.cpp @@ -89,7 +89,7 @@ #include "Page.h" #include "PageGroup.h" #include "PictureLayerContent.h" -#include "PictureSetLayerContent.h" +#include "PicturePileLayerContent.h" #include "PlatformKeyboardEvent.h" #include "PlatformString.h" #include "PluginWidgetAndroid.h" @@ -169,17 +169,6 @@ FILE* gRenderTreeFile = 0; #include "RenderLayerCompositor.h" #endif -// In some cases, too many invalidations passed to the UI will slow us down. -// Limit ourselves to 32 rectangles, past this just send the area bounds to the UI. -// see WebViewCore::recordPictureSet(). -#define MAX_INVALIDATIONS 32 - -/* We pass this flag when recording the actual content, so that we don't spend - time actually regionizing complex path clips, when all we really want to do - is record them. - */ -#define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag - //////////////////////////////////////////////////////////////////////////////////////////////// namespace android { @@ -430,7 +419,6 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m , m_scrollOffsetX(0) , m_scrollOffsetY(0) , m_mousePos(WebCore::IntPoint(0,0)) - , m_progressDone(false) , m_screenWidth(320) , m_screenHeight(240) , m_textWrapWidth(320) @@ -610,18 +598,8 @@ static bool layoutIfNeededRecursive(WebCore::Frame* f) WebCore::FrameView* v = f->view(); if (!v) return true; - - if (v->needsLayout()) - v->layout(f->tree()->parent()); - - WebCore::Frame* child = f->tree()->firstChild(); - bool success = true; - while (child) { - success &= layoutIfNeededRecursive(child); - child = child->tree()->nextSibling(); - } - - return success && !v->needsLayout(); + v->updateLayoutAndStyleIfNeededRecursive(); + return !v->needsLayout(); } WebCore::Node* WebViewCore::currentFocus() @@ -629,19 +607,16 @@ WebCore::Node* WebViewCore::currentFocus() return focusedFrame()->document()->focusedNode(); } -void WebViewCore::recordPictureSet(PictureSet* content) +void WebViewCore::recordPicturePile() { TRACE_METHOD(); // if there is no document yet, just return if (!m_mainFrame->document()) { - DBG_SET_LOG("!m_mainFrame->document()"); - return; - } - if (m_addInval.isEmpty()) { - DBG_SET_LOG("m_addInval.isEmpty()"); + ALOGV("!m_mainFrame->document()"); return; } + // Call layout to ensure that the contentWidth and contentHeight are correct // it's fine for layout to gather invalidates, but defeat sending a message // back to java to call webkitDraw, since we're already in the middle of @@ -704,6 +679,8 @@ void WebViewCore::recordPictureSet(PictureSet* content) // If the new total is larger than the content, resize the view to include // all the content. if (!contentRect.contains(total)) { + // TODO: Does this ever happen? Is this needed now that we don't flatten + // frames? // Resize the view to change the overflow clip. view->resize(total.fRight, total.fBottom); @@ -723,51 +700,15 @@ void WebViewCore::recordPictureSet(PictureSet* content) height = view->contentsHeight(); } -#if USE(ACCELERATED_COMPOSITING) - // The invals are not always correct when the content size has changed. For - // now, let's just reset the inval so that it invalidates the entire content - // -- the pictureset will be fully repainted, tiles will be marked dirty and - // will have to be repainted. - - // FIXME: the webkit invals ought to have been enough... - if (content->width() != width || content->height() != height) { - SkIRect r; - r.fLeft = 0; - r.fTop = 0; - r.fRight = width; - r.fBottom = height; - m_addInval.setRect(r); - } -#endif - - content->setDimensions(width, height, &m_addInval); + m_content.setSize(IntSize(width, height)); - // Add the current inval rects to the PictureSet, and rebuild it. - content->add(m_addInval, 0, false); - - // If we have too many invalidations, just get the area bounds - SkRegion::Iterator iterator(m_addInval); - int nbInvals = 0; - while (!iterator.done()) { - iterator.next(); - nbInvals++; - if (nbInvals > MAX_INVALIDATIONS) - break; - } - if (nbInvals > MAX_INVALIDATIONS) { - SkIRect r = m_addInval.getBounds(); - m_addInval.setRect(r); - } // Rebuild the pictureset (webkit repaint) - rebuildPictureSet(content); + m_content.updatePicturesIfNeeded(this); } void WebViewCore::clearContent() { - DBG_SET_LOG(""); - m_content.clear(); - m_addInval.setEmpty(); - m_rebuildInval.setEmpty(); + m_content.reset(); updateLocale(); } @@ -778,86 +719,51 @@ bool WebViewCore::focusBoundsChanged() return result; } -SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval) +void WebViewCore::paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty) { WebCore::FrameView* view = m_mainFrame->view(); - int width = view->contentsWidth(); - int height = view->contentsHeight(); - SkPicture* picture = new SkPicture(); - SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS); - SkAutoMemoryUsageProbe mup(__FUNCTION__); - SkCanvas* recordingCanvas = arp.getRecordingCanvas(); - WebCore::PlatformGraphicsContextSkia pgc(recordingCanvas); - WebCore::GraphicsContext gc(&pgc); IntPoint origin = view->minimumScrollPosition(); - WebCore::IntRect drawArea(inval.fLeft + origin.x(), inval.fTop + origin.y(), - inval.width(), inval.height()); - recordingCanvas->translate(-drawArea.x(), -drawArea.y()); - recordingCanvas->save(); - view->platformWidget()->draw(&gc, drawArea); - m_rebuildInval.op(inval, SkRegion::kUnion_Op); - DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}", - m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop, - m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom); - - return picture; + IntRect drawArea = dirty; + gc->translate(-origin.x(), -origin.y()); + drawArea.move(origin.x(), origin.y()); + view->platformWidget()->draw(gc, drawArea); } -#ifdef CONTEXT_RECORDING -GraphicsOperationCollection* WebViewCore::rebuildGraphicsOperationCollection(const SkIRect& inval) +SkCanvas* WebViewCore::createPrerenderCanvas(PrerenderedInval* prerendered) { - WebCore::FrameView* view = m_mainFrame->view(); - int width = view->contentsWidth(); - int height = view->contentsHeight(); - - IntPoint origin = view->minimumScrollPosition(); - WebCore::IntRect drawArea(inval.fLeft + origin.x(), inval.fTop + origin.y(), - inval.width(), inval.height()); - - AutoGraphicsOperationCollection autoPicture(drawArea); - view->platformWidget()->draw(autoPicture.context(), drawArea); - - m_rebuildInval.op(inval, SkRegion::kUnion_Op); - - SkSafeRef(autoPicture.picture()); - return autoPicture.picture(); -} -#endif - -void WebViewCore::rebuildPictureSet(PictureSet* pictureSet) -{ -#ifdef FAST_PICTURESET - WTF::Vector<Bucket*>* buckets = pictureSet->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* picture = rebuildPicture(inval); - SkSafeUnref(bucketPicture.mPicture); - bucketPicture.mPicture = picture; - } - } - buckets->clear(); -#else - size_t size = pictureSet->size(); - for (size_t index = 0; index < size; index++) { - if (pictureSet->upToDate(index)) - continue; - const SkIRect& inval = pictureSet->bounds(index); - DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index, - inval.fLeft, inval.fTop, inval.width(), inval.height()); - pictureSet->setPicture(index, rebuildPicture(inval)); -#ifdef CONTEXT_RECORDING - pictureSet->setGraphicsOperationCollection(index, - rebuildGraphicsOperationCollection(inval)); -#endif + IntRect screen(m_scrollOffsetX, m_scrollOffsetY, m_screenWidth, m_screenHeight); + if (prerendered->area.isEmpty() || !prerendered->area.intersects(screen)) + return 0; + FloatRect scaledArea = prerendered->area; + scaledArea.scale(m_scale); + IntRect enclosingScaledArea = enclosingIntRect(scaledArea); + if (enclosingScaledArea.isEmpty()) + return 0; + prerendered->screenArea = enclosingScaledArea; + FloatRect enclosingDocArea(enclosingScaledArea); + enclosingDocArea.scale(1 / m_scale); + prerendered->area = enclosingIntRect(enclosingDocArea); + if (prerendered->area.isEmpty()) + return 0; + // TODO: We need a better heuristic for this. We should change this to: + // 1) Limit by area, not width/height (as we care more about the RAM than size) + // 2) Clip by the screen, but "round out" to make sure we cover partially + // visible tiles + int maxWidth = ceilf(m_screenWidth * m_scale); + int maxHeight = ceilf(m_screenHeight * m_scale); + if (enclosingScaledArea.width() <= maxWidth + && enclosingScaledArea.height() <= maxHeight) { + prerendered->bitmap.setConfig(SkBitmap::kARGB_8888_Config, + enclosingScaledArea.width(), + enclosingScaledArea.height()); + prerendered->bitmap.allocPixels(); + SkCanvas* bitmapCanvas = new SkCanvas(prerendered->bitmap); + bitmapCanvas->scale(m_scale, m_scale); + bitmapCanvas->translate(-enclosingDocArea.x(), -enclosingDocArea.y()); + return bitmapCanvas; } - - pictureSet->validate(__FUNCTION__); -#endif + return 0; } void WebViewCore::notifyAnimationStarted() @@ -896,7 +802,8 @@ BaseLayerAndroid* WebViewCore::createBaseLayer() bodyHasFixedBackgroundImage = style->hasFixedBackgroundImage(); } - PictureSetLayerContent* content = new PictureSetLayerContent(m_content); + PicturePileLayerContent* content = new PicturePileLayerContent(m_content); + m_content.clearPrerenders(); BaseLayerAndroid* realBase = 0; LayerAndroid* base = 0; @@ -952,46 +859,22 @@ BaseLayerAndroid* WebViewCore::createBaseLayer() BaseLayerAndroid* WebViewCore::recordContent(SkIPoint* point) { - // If there is a pending style recalculation, just return. - if (m_mainFrame->document()->isPendingStyleRecalc()) { - DBG_SET_LOG("recordContent: pending style recalc, ignoring."); - return 0; - } - float progress = (float) m_mainFrame->page()->progress()->estimatedProgress(); - m_progressDone = progress <= 0.0f || progress >= 1.0f; - recordPictureSet(&m_content); - if (!m_progressDone && m_content.isEmpty()) { - DBG_SET_LOGD("empty (progress=%g)", progress); - return 0; - } + recordPicturePile(); BaseLayerAndroid* baseLayer = createBaseLayer(); - baseLayer->markAsDirty(m_addInval); - m_addInval.setEmpty(); + baseLayer->markAsDirty(m_content.dirtyRegion()); + m_content.dirtyRegion().setEmpty(); #if USE(ACCELERATED_COMPOSITING) #else baseLayer->markAsDirty(m_rebuildInval); #endif - m_rebuildInval.setEmpty(); - point->fX = m_content.width(); - point->fY = m_content.height(); + point->fX = m_content.size().width(); + point->fY = m_content.size().height(); return baseLayer; } -void WebViewCore::splitContent(PictureSet* content) -{ -#ifdef FAST_PICTURESET -#else - bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame); - ALOG_ASSERT(layoutSucceeded, "Can never be called recursively"); - content->split(&m_content); - rebuildPictureSet(&m_content); - content->set(m_content); -#endif // FAST_PICTURESET -} - void WebViewCore::scrollTo(int x, int y, bool animate) { ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); @@ -1041,14 +924,10 @@ void WebViewCore::contentDraw() void WebViewCore::contentInvalidate(const WebCore::IntRect &r) { - DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height()); - SkIRect rect(r); - if (!rect.intersect(0, 0, INT_MAX, INT_MAX)) - return; - m_addInval.op(rect, SkRegion::kUnion_Op); - DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}", - m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop, - m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom); + IntPoint origin = m_mainFrame->view()->minimumScrollPosition(); + IntRect dirty = r; + dirty.move(-origin.x(), -origin.y()); + m_content.invalidate(dirty); if (!m_skipContentDraw) contentDraw(); } @@ -4560,13 +4439,6 @@ static jint RecordContent(JNIEnv* env, jobject obj, jint nativeClass, jobject pt return reinterpret_cast<jint>(result); } -static void SplitContent(JNIEnv* env, jobject obj, jint nativeClass, - jint content) -{ - WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass); - viewImpl->splitContent(reinterpret_cast<PictureSet*>(content)); -} - static void SendListBoxChoice(JNIEnv* env, jobject obj, jint nativeClass, jint choice) { @@ -5074,8 +4946,6 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) RecordContent }, { "setViewportSettingsFromNative", "(I)V", (void*) SetViewportSettingsFromNative }, - { "nativeSplitContent", "(II)V", - (void*) SplitContent }, { "nativeSetBackgroundColor", "(II)V", (void*) SetBackgroundColor }, { "nativeRegisterURLSchemeAsLocal", "(ILjava/lang/String;)V", diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h index 3e1dbab..8dc1d07 100644 --- a/Source/WebKit/android/jni/WebViewCore.h +++ b/Source/WebKit/android/jni/WebViewCore.h @@ -31,7 +31,7 @@ #include "FileChooser.h" #include "FocusDirection.h" #include "HitTestResult.h" -#include "PictureSet.h" +#include "PicturePile.h" #include "PlatformGraphicsContext.h" #include "Position.h" #include "ScrollTypes.h" @@ -121,7 +121,7 @@ namespace android { }; // one instance of WebViewCore per page for calling into Java's WebViewCore - class WebViewCore : public WebCoreRefObject { + class WebViewCore : public WebCoreRefObject, public WebCore::PicturePainter { public: /** * Initialize the native WebViewCore with a JNI environment, a Java @@ -526,9 +526,6 @@ namespace android { WebCore::Frame* mainFrame() const { return m_mainFrame; } WebCore::Frame* focusedFrame() const; - // utility to split slow parts of the picture set - void splitContent(PictureSet*); - void notifyWebAppCanBeInstalled(); void deleteText(int startX, int startY, int endX, int endY); @@ -625,13 +622,13 @@ namespace android { WebCore::Node* currentFocus(); // Create a set of pictures to represent the drawn DOM, driven by // the invalidated region and the time required to draw (used to draw) - void recordPictureSet(PictureSet* master); + void recordPicturePile(); - SkPicture* rebuildPicture(const SkIRect& inval); + virtual void paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty); + virtual SkCanvas* createPrerenderCanvas(WebCore::PrerenderedInval* prerendered); #ifdef CONTEXT_RECORDING WebCore::GraphicsOperationCollection* rebuildGraphicsOperationCollection(const SkIRect& inval); #endif - void rebuildPictureSet(PictureSet* ); void sendNotifyProgressFinished(); /* * Handle a mouse click, either from a touch or trackball press. @@ -750,9 +747,7 @@ namespace android { struct TextFieldInitDataGlue* m_textFieldInitDataGlue; WebCore::Frame* m_mainFrame; WebCoreReply* m_popupReply; - PictureSet m_content; // the set of pictures to draw - SkRegion m_addInval; // the accumulated inval region (not yet drawn) - SkRegion m_rebuildInval; // the accumulated region for rebuilt pictures + WebCore::PicturePile m_content; // the set of pictures to draw // Used in passToJS to avoid updating the UI text field until after the // key event has been processed. bool m_blockTextfieldUpdates; @@ -766,7 +761,6 @@ namespace android { int m_scrollOffsetX; // webview.java's current scroll in X int m_scrollOffsetY; // webview.java's current scroll in Y WebCore::IntPoint m_mousePos; - bool m_progressDone; int m_screenWidth; // width of the visible rect in document coordinates int m_screenHeight;// height of the visible rect in document coordinates int m_textWrapWidth; diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp index 51ffdfe..9d4d7a4 100644 --- a/Source/WebKit/android/nav/WebView.cpp +++ b/Source/WebKit/android/nav/WebView.cpp @@ -293,12 +293,11 @@ int drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, return 0; } -PictureSet* draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras, bool split) +void draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras) { - PictureSet* ret = 0; if (!m_baseLayer) { canvas->drawColor(bgColor); - return ret; + return; } // draw the content of the base layer first @@ -327,8 +326,6 @@ PictureSet* draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras, bool spli m_baseLayer->setMatrix(canvas->getTotalMatrix()); canvas->resetMatrix(); m_baseLayer->draw(canvas, getDrawExtra(extras)); - - return ret; } int getScaledMaxXScroll() @@ -543,14 +540,6 @@ bool setBaseLayer(BaseLayerAndroid* newBaseLayer, bool showVisualIndicator, return queueFull; } -void replaceBaseContent(PictureSet* set) -{ - if (!m_baseLayer) - return; - // TODO: remove the split picture codepath - delete set; -} - void copyBaseContentToPicture(SkPicture* picture) { if (!m_baseLayer) @@ -805,16 +794,14 @@ static SkRect jrectf_to_rect(JNIEnv* env, jobject obj) return rect; } -static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, +static void nativeDraw(JNIEnv *env, jobject obj, jobject canv, jobject visible, jint color, - jint extras, jboolean split) { + jint extras) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); WebView* webView = GET_NATIVE_VIEW(env, obj); SkRect visibleRect = jrectf_to_rect(env, visible); webView->setVisibleRect(visibleRect); - PictureSet* pictureSet = webView->draw(canvas, color, - static_cast<WebView::DrawExtras>(extras), split); - return reinterpret_cast<jint>(pictureSet); + webView->draw(canvas, color, static_cast<WebView::DrawExtras>(extras)); } static jint nativeCreateDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView, @@ -898,12 +885,6 @@ static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj) return GET_NATIVE_VIEW(env, obj)->getBaseLayer(); } -static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content) -{ - PictureSet* set = reinterpret_cast<PictureSet*>(content); - GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set); -} - static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict) { SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); @@ -1110,7 +1091,7 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) SkDumpCanvas canvas(&dumper); // this will playback the picture into the canvas, which will // spew its contents to the dumper - view->draw(&canvas, 0, WebView::DrawExtrasNone, false); + view->draw(&canvas, 0, WebView::DrawExtrasNone); // we're done with the file now fwrite("\n", 1, 1, file); fclose(file); @@ -1249,7 +1230,7 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeCreate }, { "nativeDestroy", "()V", (void*) nativeDestroy }, - { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;IIZ)I", + { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;II)V", (void*) nativeDraw }, { "nativeCreateDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I", (void*) nativeCreateDrawGLFunction }, @@ -1271,8 +1252,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeSetBaseLayer }, { "nativeGetBaseLayer", "()I", (void*) nativeGetBaseLayer }, - { "nativeReplaceBaseContent", "(I)V", - (void*) nativeReplaceBaseContent }, { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V", (void*) nativeCopyBaseContentToPicture }, { "nativeHasContent", "()Z", |