Author: stippi Date: Tue Mar 2 21:26:45 2010 New Revision: 272 URL: http://mmlr.dyndns.org/changeset/272 Log: Implemented completely smooth scrolling by moving the bitmap contents. In ChromeClientHaiku::paint(), the contentChanged flag actually tells us whether the paint is used for scrolling. Since we now implement scroll(), we can ignore these events (confirmed with Gtk port). I did see some glitches, so something with the calculation of the dirty region in BWebPage::scroll() could be wrong still. Otherwise it works mighty fine. Modified: webkit/trunk/WebKit/haiku/API/WebPage.cpp webkit/trunk/WebKit/haiku/API/WebPage.h webkit/trunk/WebKit/haiku/API/WebView.cpp webkit/trunk/WebKit/haiku/API/WebView.h webkit/trunk/WebKit/haiku/WebCoreSupport/ChromeClientHaiku.cpp Modified: webkit/trunk/WebKit/haiku/API/WebPage.cpp ============================================================================== --- webkit/trunk/WebKit/haiku/API/WebPage.cpp Tue Mar 2 21:24:06 2010 (r271) +++ webkit/trunk/WebKit/haiku/API/WebPage.cpp Tue Mar 2 21:26:45 2010 (r272) @@ -519,6 +519,8 @@ void BWebPage::paint(BRect rect, bool contentChanged, bool immediate, bool repaintContentOnly) { + if (!rect.IsValid()) + return; // Block any drawing as long as the BWebView is hidden // (should be extended to when the containing BWebWindow is not // currently on screen either...) @@ -558,15 +560,11 @@ } m_webView->UnlockLooper(); - WebCore::GraphicsContext context(offscreenView); - - offscreenView->PushState(); if (!rect.IsValid()) rect = offscreenView->Bounds(); BRegion region(rect); - offscreenView->ConstrainClippingRegion(®ion); - view->paint(&context, IntRect(rect)); - offscreenView->PopState(); + internalPaint(offscreenView, ®ion); + offscreenView->UnlockLooper(); m_pageDirty = false; @@ -575,6 +573,113 @@ m_webView->SetOffscreenViewClean(rect, immediate); } +void BWebPage::scroll(int xOffset, int yOffset, const BRect& rectToScroll, + const BRect& clipRect) +{ + if (!m_webView->LockLooper()) + return; + BBitmap* bitmap = m_webView->OffscreenBitmap(); + BView* offscreenView = m_webView->OffscreenView(); + + // Lock the offscreen bitmap while we still have the + // window locked. This cannot deadlock and makes sure + // the window is not deleting the offscreen view right + // after we unlock it and before locking the bitmap. + if (!bitmap->Lock()) { + m_webView->UnlockLooper(); + return; + } + m_webView->UnlockLooper(); + + BRect clip = offscreenView->Bounds(); + BRect rectAtSrc = rectToScroll; + BRect rectAtDst = rectAtSrc.OffsetByCopy(xOffset, yOffset); + BRegion repaintRegion(rectAtSrc); + if (clip.Intersects(rectAtSrc) && clip.Intersects(rectAtDst)) { + uint8* src = reinterpret_cast<uint8*>(bitmap->Bits()); + uint32 bytesPerRow = bitmap->BytesPerRow(); + // clip source rect + rectAtSrc = rectAtSrc & clip; + // clip dest rect + rectAtDst = rectAtDst & clip; + // move dest back over source and clip source to dest + rectAtDst.OffsetBy(-xOffset, -yOffset); + rectAtSrc = rectAtSrc & rectAtDst; + // remember the part that will be clean + rectAtDst.OffsetBy(xOffset, yOffset); + repaintRegion.Exclude(rectAtDst); + + // calc offset in buffer + src += (int32)rectAtSrc.left * 4 + + (int32)rectAtSrc.top * bytesPerRow; + + uint32 width = rectAtSrc.IntegerWidth() + 1; + uint32 height = rectAtSrc.IntegerHeight() + 1; + + int32 xIncrement; + int32 yIncrement; + + if (yOffset == 0 && xOffset > 0) { + // copy from right to left + xIncrement = -1; + src += (width - 1) * 4; + } else { + // copy from left to right + xIncrement = 1; + } + + if (yOffset > 0) { + // copy from bottom to top + yIncrement = -bytesPerRow; + src += (height - 1) * bytesPerRow; + } else { + // copy from top to bottom + yIncrement = bytesPerRow; + } + + uint8* dst = src + yOffset * bytesPerRow + xOffset * 4; + + if (xIncrement == 1) { + for (uint32 y = 0; y < height; y++) { + memcpy(dst, src, width * 4); + src += yIncrement; + dst += yIncrement; + } + } else { + for (uint32 y = 0; y < height; y++) { + uint32* srcHandle = (uint32*)src; + uint32* dstHandle = (uint32*)dst; + for (uint32 x = 0; x < width; x++) { + *dstHandle = *srcHandle; + srcHandle += xIncrement; + dstHandle += xIncrement; + } + src += yIncrement; + dst += yIncrement; + } + } + } + + if (repaintRegion.Frame().IsValid()) + internalPaint(offscreenView, &repaintRegion); + + bitmap->Unlock(); + + // Notify the view that it can now pull the bitmap in its own thread + m_webView->SetOffscreenViewClean(rectToScroll, false); +} + +void BWebPage::internalPaint(BView* offscreenView, BRegion* dirty) +{ + WebCore::Frame* frame = m_mainFrame->Frame(); + WebCore::FrameView* view = frame->view(); + offscreenView->PushState(); + offscreenView->ConstrainClippingRegion(dirty); + WebCore::GraphicsContext context(offscreenView); + view->paint(&context, IntRect(dirty->Frame())); + offscreenView->PopState(); +} + // #pragma mark - private void BWebPage::MessageReceived(BMessage* message) Modified: webkit/trunk/WebKit/haiku/API/WebPage.h ============================================================================== --- webkit/trunk/WebKit/haiku/API/WebPage.h Tue Mar 2 21:24:06 2010 (r271) +++ webkit/trunk/WebKit/haiku/API/WebPage.h Tue Mar 2 21:26:45 2010 (r272) @@ -34,6 +34,8 @@ #include <Rect.h> #include <String.h> +class BRegion; +class BView; class BWebDownload; class BWebFrame; class BWebSettings; @@ -164,6 +166,9 @@ void paint(BRect rect, bool contentChanged, bool immediate, bool repaintContentOnly); + void scroll(int scrollDeltaX, int scrollDeltaY, const BRect& rectToScroll, + const BRect& clipRect); + void internalPaint(BView* view, BRegion* dirty); private: virtual ~BWebPage(); Modified: webkit/trunk/WebKit/haiku/API/WebView.cpp ============================================================================== --- webkit/trunk/WebKit/haiku/API/WebView.cpp Tue Mar 2 21:24:06 2010 (r271) +++ webkit/trunk/WebKit/haiku/API/WebView.cpp Tue Mar 2 21:26:45 2010 (r272) @@ -66,6 +66,10 @@ BWebView::~BWebView() { + if (fOffscreenBitmap) { + fOffscreenBitmap->Lock(); + delete fOffscreenBitmap; + } } // #pragma mark - BView hooks @@ -146,15 +150,15 @@ GetMouse(&where, &buttons); BPoint screenWhere = ConvertToScreen(where); fWebPage->mouseWheelChanged(message, where, screenWhere); - // NOTE: This solves a bug in WebKit itself, it should issue a mouse - // event when scrolling by wheel event. The effects of the this bug - // can be witnessed in Safari as well. - BMessage mouseMessage(B_MOUSE_MOVED); - mouseMessage.AddPoint("be:view_where", where); - mouseMessage.AddPoint("screen_where", screenWhere); - mouseMessage.AddInt32("buttons", buttons); - mouseMessage.AddInt32("modifiers", modifiers()); - fWebPage->mouseEvent(&mouseMessage, where, screenWhere); +// // NOTE: This solves a bug in WebKit itself, it should issue a mouse +// // event when scrolling by wheel event. The effects of the this bug +// // can be witnessed in Safari as well. +// BMessage mouseMessage(B_MOUSE_MOVED); +// mouseMessage.AddPoint("be:view_where", where); +// mouseMessage.AddPoint("screen_where", screenWhere); +// mouseMessage.AddInt32("buttons", buttons); +// mouseMessage.AddInt32("modifiers", modifiers()); +// fWebPage->mouseEvent(&mouseMessage, where, screenWhere); break; } @@ -279,6 +283,19 @@ wrapSelection, startInSelection); } +void BWebView::SendFakeMouseMovedEvent() +{ + if (!LockLooper()) + return; + + BPoint where; + uint32 buttons; + GetMouse(&where, &buttons); + BPoint screenWhere = ConvertToScreen(where); + _DispatchFakeMouseMovedEvent(where, screenWhere, buttons); + UnlockLooper(); +} + // #pragma mark - API for WebPage only void BWebView::SetOffscreenViewClean(BRect cleanRect, bool immediate) @@ -367,6 +384,20 @@ fWebPage->mouseEvent(message, where, ConvertToScreen(where)); } +void BWebView::_DispatchFakeMouseMovedEvent(const BPoint& where, + const BPoint& screenWhere, uint32 buttons) +{ + // NOTE: This solves a bug in WebKit itself, it should issue a mouse + // event when scrolling by wheel event. The effects of the this bug + // can be witnessed in Safari as well. + BMessage mouseMessage(B_MOUSE_MOVED); + mouseMessage.AddPoint("be:view_where", where); + mouseMessage.AddPoint("screen_where", screenWhere); + mouseMessage.AddInt32("buttons", buttons); + mouseMessage.AddInt32("modifiers", modifiers()); + fWebPage->mouseEvent(&mouseMessage, where, screenWhere); +} + void BWebView::_DispatchKeyEvent(uint32 sanityWhat) { BMessage* message = Looper()->CurrentMessage(); Modified: webkit/trunk/WebKit/haiku/API/WebView.h ============================================================================== --- webkit/trunk/WebKit/haiku/API/WebView.h Tue Mar 2 21:24:06 2010 (r271) +++ webkit/trunk/WebKit/haiku/API/WebView.h Tue Mar 2 21:26:45 2010 (r272) @@ -86,8 +86,12 @@ bool wrapSelection = true, bool startInSelection = false); + void SendFakeMouseMovedEvent(); + private: friend class BWebPage; + inline BBitmap* OffscreenBitmap() const + { return fOffscreenBitmap; } inline BView* OffscreenView() const { return fOffscreenView; } void SetOffscreenViewClean(BRect cleanRect, @@ -98,8 +102,9 @@ void _ResizeOffscreenView(int width, int height); void _DispatchMouseEvent(const BPoint& where, uint32 sanityWhat); + void _DispatchFakeMouseMovedEvent(const BPoint& where, + const BPoint& screenWhere, uint32 buttons); void _DispatchKeyEvent(uint32 sanityWhat); - private: uint32 fLastMouseButtons; Modified: webkit/trunk/WebKit/haiku/WebCoreSupport/ChromeClientHaiku.cpp ============================================================================== --- webkit/trunk/WebKit/haiku/WebCoreSupport/ChromeClientHaiku.cpp Tue Mar 2 21:24:06 2010 (r271) +++ webkit/trunk/WebKit/haiku/WebCoreSupport/ChromeClientHaiku.cpp Tue Mar 2 21:26:45 2010 (r272) @@ -283,16 +283,27 @@ void ChromeClientHaiku::repaint(const IntRect& rect, bool contentChanged, bool immediate, bool repaintContentOnly) { +//printf("ChromeClientHaiku::repaint(rect(%d, %d, %d, %d), contentChanged: %d, " +//"immediate: %d, repaintContentOnly: %d)\n", +//rect.x(), rect.y(), rect.right(), rect.bottom(), contentChanged, immediate, repaintContentOnly); // TODO: This deadlocks when called from the app thread during init (fortunately immediate is false then) - if (immediate) - m_webPage->paint(BRect(rect), contentChanged, immediate, repaintContentOnly); - else - m_webPage->draw(BRect(rect)); + // contentChanged == false is used for scrolling. + if (contentChanged) { + if (immediate) + m_webPage->paint(BRect(rect), contentChanged, immediate, repaintContentOnly); + else + m_webPage->draw(BRect(rect)); + } } void ChromeClientHaiku::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) { - // TODO: Find out how to tell WebCore not to repaint the area that we were able to scroll. +//printf("ChromeClientHaiku::scroll(%d x %d, rectToScroll(%d, %d, %d, %d), " +//"clipRect(%d, %d, %d, %d))\n", scrollDelta.width(), scrollDelta.height(), +//rectToScroll.x(), rectToScroll.y(), rectToScroll.right(), rectToScroll.bottom(), +//clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); + m_webPage->scroll(scrollDelta.width(), scrollDelta.height(), rectToScroll, clipRect); + m_webView->SendFakeMouseMovedEvent(); } IntPoint ChromeClientHaiku::screenToWindow(const IntPoint& point) const