diff options
author | Steve Block <steveblock@google.com> | 2010-04-27 16:23:55 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-04-27 17:07:03 +0100 |
commit | 692e5dbf12901edacf14812a6fae25462920af42 (patch) | |
tree | d62802373a429e0a9dc093b6046c166b2c514285 /WebCore/platform | |
parent | e24bea4efef1c414137d36a9778aa4e142e10c7d (diff) | |
download | external_webkit-692e5dbf12901edacf14812a6fae25462920af42.zip external_webkit-692e5dbf12901edacf14812a6fae25462920af42.tar.gz external_webkit-692e5dbf12901edacf14812a6fae25462920af42.tar.bz2 |
Merge webkit.org at r55033 : Initial merge by git
Change-Id: I98a4af828067cc243ec3dc5e5826154dd88074b5
Diffstat (limited to 'WebCore/platform')
80 files changed, 2188 insertions, 1249 deletions
diff --git a/WebCore/platform/FileChooser.cpp b/WebCore/platform/FileChooser.cpp index 9fad392..a2d4770 100644 --- a/WebCore/platform/FileChooser.cpp +++ b/WebCore/platform/FileChooser.cpp @@ -39,9 +39,9 @@ FileChooserClient::~FileChooserClient() inline FileChooser::FileChooser(FileChooserClient* client, const Vector<String>& initialFilenames) : m_client(client) - , m_icon(Icon::createIconForFiles(initialFilenames)) { m_filenames = initialFilenames; + loadIcon(); } PassRefPtr<FileChooser> FileChooser::create(FileChooserClient* client, const Vector<String>& initialFilenames) @@ -71,9 +71,24 @@ void FileChooser::chooseFiles(const Vector<String>& filenames) if (m_filenames == filenames) return; m_filenames = filenames; - m_icon = Icon::createIconForFiles(filenames); + loadIcon(); if (m_client) m_client->valueChanged(); } +void FileChooser::loadIcon() +{ + m_icon = Icon::createIconForFiles(m_filenames); + // If synchronous icon loading failed, try asynchronous loading. + if (!m_icon && m_filenames.size() && m_client) + m_client->iconForFiles(m_filenames); +} + +void FileChooser::iconLoaded(PassRefPtr<Icon> icon) +{ + m_icon = icon; + if (m_icon && m_client) + m_client->repaint(); +} + } diff --git a/WebCore/platform/FileChooser.h b/WebCore/platform/FileChooser.h index 0764a6a..e7feb3e 100644 --- a/WebCore/platform/FileChooser.h +++ b/WebCore/platform/FileChooser.h @@ -41,8 +41,10 @@ class Icon; class FileChooserClient { public: virtual void valueChanged() = 0; + virtual void repaint() = 0; virtual bool allowsMultipleFiles() = 0; virtual String acceptTypes() = 0; + virtual void iconForFiles(const Vector<String>&) = 0; virtual ~FileChooserClient(); }; @@ -63,13 +65,16 @@ public: void chooseFile(const String& path); void chooseFiles(const Vector<String>& paths); - + // Called when FileChooserClient finishes to load an icon requested by iconForFiles(). + void iconLoaded(PassRefPtr<Icon>); + bool allowsMultipleFiles() const { return m_client ? m_client->allowsMultipleFiles() : false; } // Acceptable MIME types. It's an 'accept' attribute value of the corresponding INPUT element. String acceptTypes() const { return m_client ? m_client->acceptTypes() : String(); } private: FileChooser(FileChooserClient*, const Vector<String>& initialFilenames); + void loadIcon(); FileChooserClient* m_client; Vector<String> m_filenames; diff --git a/WebCore/platform/GeolocationService.cpp b/WebCore/platform/GeolocationService.cpp index be9553b..7e1f755 100644 --- a/WebCore/platform/GeolocationService.cpp +++ b/WebCore/platform/GeolocationService.cpp @@ -25,12 +25,13 @@ #include "config.h" #include "GeolocationService.h" -#include "Geoposition.h" + #include "GeolocationServiceMock.h" +#include "Geoposition.h" #include "PositionError.h" -#include <wtf/CurrentTime.h> #include <wtf/Assertions.h> +#include <wtf/CurrentTime.h> namespace WebCore { diff --git a/WebCore/platform/GeolocationService.h b/WebCore/platform/GeolocationService.h index f991f67..3fcec47 100644 --- a/WebCore/platform/GeolocationService.h +++ b/WebCore/platform/GeolocationService.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GeolocationService_h @@ -46,15 +46,19 @@ class GeolocationService : public Noncopyable { public: static GeolocationService* create(GeolocationServiceClient*); virtual ~GeolocationService() { } +<<<<<<< HEAD #if PLATFORM(ANDROID) // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082 virtual bool startUpdating(PositionOptions*, bool suspend) { return false; } #else +======= + +>>>>>>> webkit.org at r55033 virtual bool startUpdating(PositionOptions*) { return false; } #endif virtual void stopUpdating() { } - + virtual void suspend() { } virtual void resume() { } @@ -68,6 +72,7 @@ public: protected: GeolocationService(GeolocationServiceClient*); + GeolocationServiceClient* geolocationServiceClient() const { return m_geolocationServiceClient; } private: GeolocationServiceClient* m_geolocationServiceClient; diff --git a/WebCore/platform/Logging.cpp b/WebCore/platform/Logging.cpp index f3c6f1c..2358d41 100644 --- a/WebCore/platform/Logging.cpp +++ b/WebCore/platform/Logging.cpp @@ -59,6 +59,7 @@ WTFLogChannel LogMedia = { 0x01000000, "WebCoreLogLevel", WTFLogChan WTFLogChannel LogPlugins = { 0x02000000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogArchives = { 0x04000000, "WebCoreLogLevel", WTFLogChannelOff }; +WTFLogChannel LogProgress = { 0x08000000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel* getChannelFromName(const String& channelName) { @@ -110,6 +111,9 @@ WTFLogChannel* getChannelFromName(const String& channelName) if (equalIgnoringCase(channelName, String("PopupBlocking"))) return &LogPopupBlocking; + if (equalIgnoringCase(channelName, String("Progress"))) + return &LogProgress; + if (equalIgnoringCase(channelName, String("SpellingAndGrammar"))) return &LogSpellingAndGrammar; diff --git a/WebCore/platform/Logging.h b/WebCore/platform/Logging.h index 5c958fe..a3dfe62 100644 --- a/WebCore/platform/Logging.h +++ b/WebCore/platform/Logging.h @@ -57,6 +57,7 @@ namespace WebCore { extern WTFLogChannel LogMedia; extern WTFLogChannel LogPlugins; extern WTFLogChannel LogArchives; + extern WTFLogChannel LogProgress; void InitializeLoggingChannelsIfNecessary(); WTFLogChannel* getChannelFromName(const String& channelName); diff --git a/WebCore/platform/PlatformKeyboardEvent.h b/WebCore/platform/PlatformKeyboardEvent.h index 2b94cce..b2dfe03 100644 --- a/WebCore/platform/PlatformKeyboardEvent.h +++ b/WebCore/platform/PlatformKeyboardEvent.h @@ -63,6 +63,12 @@ class wxKeyEvent; class BMessage; #endif +#if PLATFORM(BREWMP) +typedef unsigned short uint16; +typedef unsigned long int uint32; +#define AEEEvent uint16 +#endif + namespace WebCore { class PlatformKeyboardEvent : public FastAllocBase { @@ -164,6 +170,10 @@ namespace WebCore { PlatformKeyboardEvent(BMessage*); #endif +#if PLATFORM(BREWMP) + PlatformKeyboardEvent(AEEEvent, uint16, uint32, Type); +#endif + #if PLATFORM(WIN) || PLATFORM(CHROMIUM) bool isSystemKey() const { return m_isSystemKey; } #endif diff --git a/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp b/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp new file mode 100644 index 0000000..758d15c --- /dev/null +++ b/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 Company 100, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "PlatformKeyboardEvent.h" + +#include "KeyboardCodes.h" +#include "NotImplemented.h" + +#include <AEEEvent.h> +#include <AEEStdDef.h> +#include <AEEVCodes.h> + +namespace WebCore { + +static String keyIdentifierForBrewKeyCode(int16 keyCode) +{ + switch (keyCode) { + case AVK_LALT: + case AVK_RALT: + return "Alt"; + case AVK_LCTRL: + case AVK_RCTRL: + return "Control"; + case AVK_LSHIFT: + case AVK_RSHIFT: + return "Shift"; + case AVK_CAPLK: + return "CapsLock"; + case AVK_FUNCTION1: + return "F1"; + case AVK_FUNCTION2: + return "F2"; + case AVK_FUNCTION3: + return "F3"; + case AVK_FUNCTION4: + return "F4"; + case AVK_FUNCTION5: + return "F5"; + case AVK_FUNCTION6: + return "F6"; + case AVK_FUNCTION7: + return "F7"; + case AVK_FUNCTION8: + return "F8"; + case AVK_FUNCTION9: + return "F9"; + case AVK_FUNCTION10: + return "F10"; + case AVK_FUNCTION11: + return "F11"; + case AVK_FUNCTION12: + return "F12"; + case AVK_PRSCRN: + return "PrintScreen"; + case AVK_LEFT: + return "Left"; + case AVK_RIGHT: + return "Right"; + case AVK_UP: + return "Up"; + case AVK_DOWN: + return "Down"; + case AVK_TXINSERT: + return "Insert"; + case AVK_ENTER: + return "Enter"; + case AVK_TXHOME: + return "Home"; + case AVK_TXDELETE: + // Standard says that DEL becomes U+007F. + return "U+007F"; + case AVK_TXEND: + return "End"; + case AVK_TXPGUP: + return "PageUp"; + case AVK_TXPGDOWN: + return "PageDown"; + default: + return String::format("U+%04X", toASCIIUpper(keyCode)); + } +} + +static int windowsKeyCodeForKeyEvent(uint16 code) +{ + switch (code) { + case AVK_A: + return VK_BACK; // (08) BACKSPACE key + case AVK_ENTER: + return VK_RETURN; // (0D) Return key + case AVK_SPACE: + return VK_SPACE; // (20) SPACEBAR + case AVK_TXPGUP: + return VK_PRIOR; // (21) PAGE UP key + case AVK_TXPGDOWN: + return VK_NEXT; // (22) PAGE DOWN key + case AVK_TXEND: + return VK_END; // (23) END key + case AVK_TXHOME: + return VK_HOME; // (24) HOME key + case AVK_LEFT: + return VK_LEFT; // (25) LEFT ARROW key + case AVK_UP: + return VK_UP; // (26) UP ARROW key + case AVK_RIGHT: + return VK_RIGHT; // (27) RIGHT ARROW key + case AVK_DOWN: + return VK_DOWN; // (28) DOWN ARROW key + case AVK_TXINSERT: + return VK_INSERT; // (2D) INS key + case AVK_TXDELETE: + return VK_DELETE; // (2E) DEL key + default: + return 0; + } +} + +static inline String singleCharacterString(UChar c) +{ + return String(&c, 1); +} + +PlatformKeyboardEvent::PlatformKeyboardEvent(AEEEvent event, uint16 code, uint32 modifiers, Type type) + : m_type(type) + , m_text((type == Char) ? singleCharacterString(code) : String()) + , m_unmodifiedText((type == Char) ? singleCharacterString(code) : String()) + , m_keyIdentifier((type == Char) ? String() : keyIdentifierForBrewKeyCode(code)) + , m_autoRepeat(modifiers & KB_AUTOREPEAT) + , m_windowsVirtualKeyCode((type == RawKeyDown || type == KeyUp) ? windowsKeyCodeForKeyEvent(code) : 0) + , m_nativeVirtualKeyCode(code) + , m_isKeypad(false) + , m_shiftKey(modifiers & (KB_LSHIFT | KB_RSHIFT)) + , m_ctrlKey(modifiers & (KB_LCTRL | KB_RCTRL)) + , m_altKey(modifiers & (KB_LALT | KB_RALT)) + , m_metaKey(false) +{ +} + +void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool) +{ + // No KeyDown events on BREW to disambiguate. + ASSERT_NOT_REACHED(); +} + +bool PlatformKeyboardEvent::currentCapsLockState() +{ + notImplemented(); + return false; +} + +} // namespace WebCore + diff --git a/WebCore/platform/brew/SystemTimeBrew.cpp b/WebCore/platform/brew/SystemTimeBrew.cpp new file mode 100644 index 0000000..c1e39fb --- /dev/null +++ b/WebCore/platform/brew/SystemTimeBrew.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Company 100. + * + * 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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "SystemTime.h" + +#include <float.h> + +namespace WebCore { + +float userIdleTime() +{ + // return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed + return FLT_MAX; +} + +} + diff --git a/WebCore/platform/chromium/ChromiumBridge.h b/WebCore/platform/chromium/ChromiumBridge.h index 83f9c81..5a085be 100644 --- a/WebCore/platform/chromium/ChromiumBridge.h +++ b/WebCore/platform/chromium/ChromiumBridge.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -53,6 +53,8 @@ namespace WebCore { class Cursor; class Document; class Frame; + class GeolocationServiceBridge; + class GeolocationServiceChromium; class GraphicsContext; class Image; class IntRect; @@ -83,12 +85,12 @@ namespace WebCore { static void clipboardWriteImage(NativeImagePtr, const KURL&, const String&); // Cookies ------------------------------------------------------------ - static void setCookies(const KURL& url, const KURL& firstPartyForCookies, const String& value); - static String cookies(const KURL& url, const KURL& firstPartyForCookies); - static String cookieRequestHeaderFieldValue(const KURL& url, const KURL& firstPartyForCookies); - static bool rawCookies(const KURL& url, const KURL& firstPartyForCookies, Vector<Cookie>*); - static void deleteCookie(const KURL& url, const String& cookieName); - static bool cookiesEnabled(const KURL& url, const KURL& firstPartyForCookies); + static void setCookies(const Document*, const KURL&, const String& value); + static String cookies(const Document*, const KURL&); + static String cookieRequestHeaderFieldValue(const Document*, const KURL&); + static bool rawCookies(const Document*, const KURL& url, Vector<Cookie>&); + static void deleteCookie(const Document*, const KURL& url, const String& cookieName); + static bool cookiesEnabled(const Document*); // DNS ---------------------------------------------------------------- static void prefetchDNS(const String& hostname); @@ -117,6 +119,9 @@ namespace WebCore { // Forms -------------------------------------------------------------- static void notifyFormStateChanged(const Document*); + // Geolocation -------------------------------------------------------- + static GeolocationServiceBridge* createGeolocationServiceBridge(GeolocationServiceChromium*); + // HTML5 DB ----------------------------------------------------------- #if ENABLE(DATABASE) // Returns a handle to the DB file and ooptionally a handle to its containing directory diff --git a/WebCore/platform/chromium/ChromiumDataObject.cpp b/WebCore/platform/chromium/ChromiumDataObject.cpp index 695da9f..77a5f0f 100644 --- a/WebCore/platform/chromium/ChromiumDataObject.cpp +++ b/WebCore/platform/chromium/ChromiumDataObject.cpp @@ -37,7 +37,6 @@ void ChromiumDataObject::clear() { url = KURL(); urlTitle = ""; - downloadURL = KURL(); downloadMetadata = ""; fileExtension = ""; filenames.clear(); @@ -52,7 +51,6 @@ void ChromiumDataObject::clear() bool ChromiumDataObject::hasData() const { return !url.isEmpty() - || !downloadURL.isEmpty() || !downloadMetadata.isEmpty() || !fileExtension.isEmpty() || !filenames.isEmpty() @@ -64,7 +62,6 @@ bool ChromiumDataObject::hasData() const ChromiumDataObject::ChromiumDataObject(const ChromiumDataObject& other) : url(other.url) , urlTitle(other.urlTitle) - , downloadURL(other.downloadURL) , downloadMetadata(other.downloadMetadata) , fileExtension(other.fileExtension) , filenames(other.filenames) diff --git a/WebCore/platform/chromium/ChromiumDataObject.h b/WebCore/platform/chromium/ChromiumDataObject.h index 186a1a0..5fae3e3 100644 --- a/WebCore/platform/chromium/ChromiumDataObject.h +++ b/WebCore/platform/chromium/ChromiumDataObject.h @@ -59,7 +59,6 @@ namespace WebCore { KURL url; String urlTitle; - KURL downloadURL; String downloadMetadata; String fileExtension; diff --git a/WebCore/platform/chromium/ClipboardChromium.cpp b/WebCore/platform/chromium/ClipboardChromium.cpp index 933d839..a1f60a6 100644 --- a/WebCore/platform/chromium/ClipboardChromium.cpp +++ b/WebCore/platform/chromium/ClipboardChromium.cpp @@ -161,9 +161,6 @@ bool ClipboardChromium::setData(const String& type, const String& data) if (winType == ClipboardDataTypeDownloadURL) { m_dataObject->downloadMetadata = data; - KURL url = KURL(ParsedURLString, data); - if (url.isValid()) - m_dataObject->downloadURL = url; return true; } diff --git a/WebCore/platform/chromium/GeolocationServiceChromium.cpp b/WebCore/platform/chromium/GeolocationServiceChromium.cpp index 65886b0..4e00908 100644 --- a/WebCore/platform/chromium/GeolocationServiceChromium.cpp +++ b/WebCore/platform/chromium/GeolocationServiceChromium.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,27 +29,79 @@ */ #include "config.h" -#include "GeolocationService.h" +#include "GeolocationServiceChromium.h" + +#include "ChromiumBridge.h" namespace WebCore { -class GeolocationServiceChromium : public GeolocationService { -public: - GeolocationServiceChromium(GeolocationServiceClient* c) - : GeolocationService(c) - { - } - // FIXME: Implement. https://bugs.webkit.org/show_bug.cgi?id=32068 -}; +GeolocationServiceChromium::GeolocationServiceChromium(GeolocationServiceClient* c) + : GeolocationService(c), + m_geolocation(reinterpret_cast<Geolocation*>(c)), + m_geolocationServiceBridge(ChromiumBridge::createGeolocationServiceBridge(this)), + m_lastPosition(Geoposition::create(Coordinates::create(0.0, 0.0, false, 0.0, 0.0, false, 0.0, false, 0.0, false, 0.0), 0)), + m_lastError(PositionError::create(PositionError::POSITION_UNAVAILABLE, "")) +{ +} + +void GeolocationServiceChromium::setIsAllowed(bool allowed) +{ + m_geolocation->setIsAllowed(allowed); +} + +void GeolocationServiceChromium::setLastPosition(double latitude, double longitude, bool providesAltitude, double altitude, double accuracy, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed, long long timestamp) +{ + m_lastPosition = Geoposition::create(Coordinates::create(latitude, longitude, providesAltitude, altitude, accuracy, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed), timestamp); + positionChanged(); +} + +void GeolocationServiceChromium::setLastError(int errorCode, const String& message) +{ + m_lastError = PositionError::create(static_cast<PositionError::ErrorCode>(errorCode), message); + errorOccurred(); +} + +Frame* GeolocationServiceChromium::frame() +{ + return m_geolocation->frame(); +} + +bool GeolocationServiceChromium::startUpdating(PositionOptions* options) +{ + return m_geolocationServiceBridge->startUpdating(options); +} + +void GeolocationServiceChromium::stopUpdating() +{ + return m_geolocationServiceBridge->stopUpdating(); +} + +void GeolocationServiceChromium::suspend() +{ + return m_geolocationServiceBridge->suspend(); +} + +void GeolocationServiceChromium::resume() +{ + return m_geolocationServiceBridge->resume(); +} + +Geoposition* GeolocationServiceChromium::lastPosition() const +{ + return m_lastPosition.get(); +} + +PositionError* GeolocationServiceChromium::lastError() const +{ + return m_lastError.get(); +} -// This guard is the counterpart of the one in WebCore/platform/GeolocationService.cpp -#if ENABLE(GEOLOCATION) -static GeolocationService* createGeolocationService(GeolocationServiceClient* c) +static GeolocationService* createGeolocationServiceChromium(GeolocationServiceClient* c) { return new GeolocationServiceChromium(c); } -GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &createGeolocationService; -#endif +// Sets up the factory function for GeolocationService. +GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &createGeolocationServiceChromium; } // namespace WebCore diff --git a/WebCore/platform/chromium/GeolocationServiceChromium.h b/WebCore/platform/chromium/GeolocationServiceChromium.h new file mode 100644 index 0000000..e32de8b --- /dev/null +++ b/WebCore/platform/chromium/GeolocationServiceChromium.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 GeolocationServiceChromium_h +#define GeolocationServiceChromium_h + +#include "Geolocation.h" +#include "GeolocationService.h" +#include "Geoposition.h" +#include "PlatformString.h" +#include "PositionError.h" + +namespace WebCore { + +// Provides an interface for GeolocationServiceChromium to call into the embedder. +class GeolocationServiceBridge { +public: + // Called by GeolocationServiceChromium. + virtual bool startUpdating(PositionOptions*) = 0; + virtual void stopUpdating() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + + // Called by the embedder, to identify this bridge. + virtual int getBridgeId() const = 0; +}; + +// This class extends GeolocationService, and uses GeolocationServiceBridge to +// call into the embedder, as well as provides a few extra methods so that the +// embedder can notify permission, position, error, etc. +class GeolocationServiceChromium : public GeolocationService { +public: + explicit GeolocationServiceChromium(GeolocationServiceClient*); + + GeolocationServiceBridge* geolocationServiceBridge() const { return m_geolocationServiceBridge.get(); } + void setIsAllowed(bool allowed); + void setLastPosition(double latitude, double longitude, bool providesAltitude, double altitude, double accuracy, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed, long long timestamp); + void setLastError(int errorCode, const String& message); + Frame* frame(); + + // From GeolocationService. + virtual bool startUpdating(PositionOptions*); + virtual void stopUpdating(); + virtual void suspend(); + virtual void resume(); + virtual Geoposition* lastPosition() const; + virtual PositionError* lastError() const; + +private: + Geolocation* m_geolocation; + OwnPtr<GeolocationServiceBridge> m_geolocationServiceBridge; + RefPtr<Geoposition> m_lastPosition; + RefPtr<PositionError> m_lastError; +}; + +} // namespace WebCore + +#endif // GeolocationServiceChromium_h diff --git a/WebCore/platform/chromium/PasteboardChromium.cpp b/WebCore/platform/chromium/PasteboardChromium.cpp index d4f9a27..6904050 100644 --- a/WebCore/platform/chromium/PasteboardChromium.cpp +++ b/WebCore/platform/chromium/PasteboardChromium.cpp @@ -125,7 +125,8 @@ void Pasteboard::writeImage(Node* node, const KURL&, const String& title) ASSERT(node->renderer()->isImage()); RenderImage* renderer = toRenderImage(node->renderer()); CachedImage* cachedImage = renderer->cachedImage(); - ASSERT(cachedImage); + if (!cachedImage || cachedImage->errorOccurred()) + return; Image* image = cachedImage->image(); ASSERT(image); diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 0b94efb..0cd3907 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -270,6 +270,18 @@ void BitmapImage::startAnimation(bool catchUpIfNecessary) if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) return; + // Don't advance the animation to an incomplete frame. + size_t nextFrame = (m_currentFrame + 1) % frameCount(); + if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame)) + return; + + // Don't advance past the last frame if we haven't decoded the whole image + // yet and our repetition count is potentially unset. The repetition count + // in a GIF can potentially come after all the rest of the image data, so + // wait on it. + if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1)) + return; + // Determine time for next frame to start. By ignoring paint and timer lag // in this calculation, we make the animation appear to run at its desired // rate regardless of how fast it's being repainted. @@ -288,18 +300,6 @@ void BitmapImage::startAnimation(bool catchUpIfNecessary) m_desiredFrameStartTime = time + currentDuration; } - // Don't advance the animation to an incomplete frame. - size_t nextFrame = (m_currentFrame + 1) % frameCount(); - if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame)) - return; - - // Don't advance past the last frame if we haven't decoded the whole image - // yet and our repetition count is potentially unset. The repetition count - // in a GIF can potentially come after all the rest of the image data, so - // wait on it. - if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1)) - return; - // The image may load more slowly than it's supposed to animate, so that by // the time we reach the end of the first repetition, we're well behind. // Clamp the desired frame start time in this case, so that we don't skip diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index 74a6293..b265121 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -99,6 +99,8 @@ public: float right() const { return x() + width(); } float bottom() const { return y() + height(); } + FloatPoint center() const { return FloatPoint(x() + width() / 2, y() + height() / 2); } + void move(const FloatSize& delta) { m_location += delta; } void move(float dx, float dy) { m_location.move(dx, dy); } diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h index e1be1fe..0efd3bf 100644 --- a/WebCore/platform/graphics/Gradient.h +++ b/WebCore/platform/graphics/Gradient.h @@ -37,7 +37,9 @@ #if PLATFORM(CG) -#ifdef BUILDING_ON_TIGER +#define USE_CG_SHADING defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + +#if USE_CG_SHADING typedef struct CGShading* CGShadingRef; typedef CGShadingRef PlatformGradient; #else diff --git a/WebCore/platform/graphics/GraphicsContext3D.cpp b/WebCore/platform/graphics/GraphicsContext3D.cpp new file mode 100644 index 0000000..3eb9818 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsContext3D.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" + +#if ENABLE(3D_CANVAS) + +#include "GraphicsContext3D.h" + +#include "Image.h" + +namespace WebCore { + +bool GraphicsContext3D::extractImageData(Image* image, + bool flipY, + bool premultiplyAlpha, + Vector<uint8_t>& imageData, + unsigned int* format, + unsigned int* internalFormat) +{ + if (!image) + return false; + AlphaOp alphaOp = kAlphaDoNothing; + bool hasAlphaChannel = true; + if (!getImageData(image, imageData, premultiplyAlpha, + &hasAlphaChannel, &alphaOp, format)) + return false; + processImageData(imageData.data(), + image->width(), + image->height(), + flipY, + alphaOp); + *internalFormat = (hasAlphaChannel ? RGBA : RGB); + return true; +} + +void GraphicsContext3D::processImageData(uint8_t* imageData, + unsigned width, + unsigned height, + bool flipVertically, + AlphaOp alphaOp) +{ + switch (alphaOp) { + case kAlphaDoPremultiply: + premultiplyAlpha(imageData, width * height); + break; + case kAlphaDoUnmultiply: + unmultiplyAlpha(imageData, width * height); + break; + default: + break; + } + + if (flipVertically && width && height) { + int rowBytes = width * 4; + uint8_t* tempRow = new uint8_t[rowBytes]; + for (unsigned i = 0; i < height / 2; i++) { + uint8_t* lowRow = imageData + (rowBytes * i); + uint8_t* highRow = imageData + (rowBytes * (height - i - 1)); + memcpy(tempRow, lowRow, rowBytes); + memcpy(lowRow, highRow, rowBytes); + memcpy(highRow, tempRow, rowBytes); + } + delete[] tempRow; + } +} + +// Premultiply alpha into color channels. +void GraphicsContext3D::premultiplyAlpha(unsigned char* rgbaData, int numPixels) +{ + for (int j = 0; j < numPixels; j++) { + float r = rgbaData[4*j+0] / 255.0f; + float g = rgbaData[4*j+1] / 255.0f; + float b = rgbaData[4*j+2] / 255.0f; + float a = rgbaData[4*j+3] / 255.0f; + rgbaData[4*j+0] = (unsigned char) (r * a * 255.0f); + rgbaData[4*j+1] = (unsigned char) (g * a * 255.0f); + rgbaData[4*j+2] = (unsigned char) (b * a * 255.0f); + } +} + +// Remove premultiplied alpha from color channels. +// FIXME: this is lossy. Must retrieve original values from HTMLImageElement. +void GraphicsContext3D::unmultiplyAlpha(unsigned char* rgbaData, int numPixels) +{ + for (int j = 0; j < numPixels; j++) { + float r = rgbaData[4*j+0] / 255.0f; + float g = rgbaData[4*j+1] / 255.0f; + float b = rgbaData[4*j+2] / 255.0f; + float a = rgbaData[4*j+3] / 255.0f; + if (a > 0.0f) { + r /= a; + g /= a; + b /= a; + r = (r > 1.0f) ? 1.0f : r; + g = (g > 1.0f) ? 1.0f : g; + b = (b > 1.0f) ? 1.0f : b; + rgbaData[4*j+0] = (unsigned char) (r * 255.0f); + rgbaData[4*j+1] = (unsigned char) (g * 255.0f); + rgbaData[4*j+2] = (unsigned char) (b * 255.0f); + } + } +} + +} // namespace WebCore + +#endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/GraphicsContext3D.h b/WebCore/platform/graphics/GraphicsContext3D.h index b7be8fc..0a41dc6 100644 --- a/WebCore/platform/graphics/GraphicsContext3D.h +++ b/WebCore/platform/graphics/GraphicsContext3D.h @@ -419,6 +419,43 @@ namespace WebCore { // like GL_FLOAT, GL_INT, etc. int sizeInBytes(int type); + //---------------------------------------------------------------------- + // Helpers for texture uploading. + // + + // Extracts the contents of the given Image into the passed + // Vector, obeying the flipY and premultiplyAlpha flags. + // Returns the applicable OpenGL format and internalFormat for + // the subsequent glTexImage2D or glTexSubImage2D call. + // Returns true upon success. + bool extractImageData(Image* image, + bool flipY, + bool premultiplyAlpha, + Vector<uint8_t>& imageData, + unsigned int* format, + unsigned int* internalFormat); + + // Processes the given image data in preparation for uploading + // via texImage2D or texSubImage2D. The input data must be in + // 4-component format with the alpha channel last (i.e., RGBA + // or BGRA), tightly packed, with no space between rows. + + enum AlphaOp { + kAlphaDoNothing = 0, + kAlphaDoPremultiply = 1, + kAlphaDoUnmultiply = 2 + }; + + void processImageData(uint8_t* imageData, + unsigned width, + unsigned height, + bool flipVertically, + AlphaOp alphaOp); + + //---------------------------------------------------------------------- + // Entry points for WebGL. + // + void activeTexture(unsigned long texture); void attachShader(WebGLProgram* program, WebGLShader* shader); void bindAttribLocation(WebGLProgram*, unsigned long index, const String& name); @@ -624,6 +661,34 @@ namespace WebCore { private: GraphicsContext3D(Attributes attrs); + // Helpers for texture uploading. + void premultiplyAlpha(unsigned char* rgbaData, int numPixels); + void unmultiplyAlpha(uint8_t* imageData, int numPixels); + + // Each platform must provide an implementation of this method. + // + // Gets the data for the given Image into outputVector, + // without doing any processing of the data (vertical flip or + // otherwise). + // + // premultiplyAlpha indicates whether the user will eventually + // want the alpha channel multiplied into the color channels. + // + // The out parameter hasAlphaChannel indicates whether the + // image actually had an alpha channel. + // + // The out parameter neededAlphaOp should be passed to a + // subsequent call of processImageData. + // + // The out parameter format should be passed to subsequent + // invocations of texImage2D and texSubImage2D. + bool getImageData(Image* image, + Vector<uint8_t>& outputVector, + bool premultiplyAlpha, + bool* hasAlphaChannel, + AlphaOp* neededAlphaOp, + unsigned int* format); + int m_currentWidth, m_currentHeight; #if PLATFORM(MAC) diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h index 844301e..a097620 100644 --- a/WebCore/platform/graphics/GraphicsLayer.h +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -74,7 +74,7 @@ class FloatPoint3D; class GraphicsContext; class Image; class TextStream; -class TimingFunction; +struct TimingFunction; // Base class for animation values (also used for transitions). Here to // represent values for properties being animated via the GraphicsLayer, diff --git a/WebCore/platform/graphics/Icon.h b/WebCore/platform/graphics/Icon.h index d7d694a..e9f2dc7 100644 --- a/WebCore/platform/graphics/Icon.h +++ b/WebCore/platform/graphics/Icon.h @@ -51,6 +51,8 @@ class String; class Icon : public RefCounted<Icon> { public: + // Deprecated. This function will be removed. + // FIXME: Remove it when all implementations are moved to ChromeClient::iconForFiles(). static PassRefPtr<Icon> createIconForFiles(const Vector<String>& filenames); ~Icon(); diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index 2b09885..8a9a264 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -69,6 +69,9 @@ public: virtual void pause() { } virtual PlatformMedia platformMedia() const { return NoPlatformMedia; } +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const { return 0; } +#endif virtual IntSize naturalSize() const { return IntSize(0, 0); } @@ -364,6 +367,13 @@ PlatformMedia MediaPlayer::platformMedia() const return m_private->platformMedia(); } +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* MediaPlayer::platformLayer() const +{ + return m_private->platformLayer(); +} +#endif + MediaPlayer::NetworkState MediaPlayer::networkState() { return m_private->networkState(); diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 40ed8ae..1ca4576 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -39,6 +39,10 @@ #include <wtf/Noncopyable.h> #include <wtf/PassOwnPtr.h> +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayer.h" +#endif + #ifdef __OBJC__ @class QTMovie; #else @@ -67,10 +71,6 @@ class MediaPlayerPrivateInterface; class String; class TimeRanges; -#if USE(ACCELERATED_COMPOSITING) -class GraphicsLayer; -#endif - class MediaPlayerClient { public: virtual ~MediaPlayerClient() { } @@ -112,8 +112,9 @@ public: // whether the rendering system can accelerate the display of this MediaPlayer. virtual bool mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) { return false; } - // return the GraphicsLayer that will host the presentation for this MediaPlayer. - virtual GraphicsLayer* mediaPlayerGraphicsLayer(MediaPlayer*) { return 0; } + // called when the media player's rendering mode changed, which indicates a change in the + // availability of the platformLayer(). + virtual void mediaPlayerRenderingModeChanged(MediaPlayer*) { } #endif }; @@ -135,6 +136,9 @@ public: bool supportsFullscreen() const; bool supportsSave() const; PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer() const; +#endif IntSize naturalSize(); bool hasVideo() const; diff --git a/WebCore/platform/graphics/MediaPlayerPrivate.h b/WebCore/platform/graphics/MediaPlayerPrivate.h index 3b7c4d4..3bb8475 100644 --- a/WebCore/platform/graphics/MediaPlayerPrivate.h +++ b/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -45,6 +45,9 @@ public: virtual void prepareToPlay() { } virtual PlatformMedia platformMedia() const { return NoPlatformMedia; } +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const { return 0; } +#endif virtual void play() = 0; virtual void pause() = 0; diff --git a/WebCore/platform/graphics/cg/GradientCG.cpp b/WebCore/platform/graphics/cg/GradientCG.cpp index e9b5de7..9c91700 100644 --- a/WebCore/platform/graphics/cg/GradientCG.cpp +++ b/WebCore/platform/graphics/cg/GradientCG.cpp @@ -36,7 +36,7 @@ namespace WebCore { void Gradient::platformDestroy() { -#ifdef BUILDING_ON_TIGER +#if USE_CG_SHADING CGShadingRelease(m_gradient); #else CGGradientRelease(m_gradient); @@ -44,7 +44,7 @@ void Gradient::platformDestroy() m_gradient = 0; } -#ifdef BUILDING_ON_TIGER +#if USE_CG_SHADING static void gradientCallback(void* info, const CGFloat* in, CGFloat* out) { float r, g, b, a; @@ -114,7 +114,7 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect) void Gradient::paint(GraphicsContext* context) { -#ifdef BUILDING_ON_TIGER +#if USE_CG_SHADING CGContextDrawShading(context->platformContext(), platformGradient()); #else CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; diff --git a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp new file mode 100644 index 0000000..c6a8f83 --- /dev/null +++ b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" + +#if ENABLE(3D_CANVAS) + +#include "GraphicsContext3D.h" + +#include "Image.h" + +#include <CoreGraphics/CGBitmapContext.h> +#include <CoreGraphics/CGContext.h> +#include <CoreGraphics/CGImage.h> + +namespace WebCore { + +bool GraphicsContext3D::getImageData(Image* image, + Vector<uint8_t>& outputVector, + bool premultiplyAlpha, + bool* hasAlphaChannel, + AlphaOp* neededAlphaOp, + unsigned int* format) +{ + if (!image) + return false; + CGImageRef cgImage = image->nativeImageForCurrentFrame(); + if (!cgImage) + return false; + int width = CGImageGetWidth(cgImage); + int height = CGImageGetHeight(cgImage); + int rowBytes = width * 4; + CGImageAlphaInfo info = CGImageGetAlphaInfo(cgImage); + *hasAlphaChannel = (info != kCGImageAlphaNone + && info != kCGImageAlphaNoneSkipLast + && info != kCGImageAlphaNoneSkipFirst); + if (!premultiplyAlpha && *hasAlphaChannel) + // FIXME: must fetch the image data before the premultiplication step + *neededAlphaOp = kAlphaDoUnmultiply; + *format = RGBA; + outputVector.resize(height * rowBytes); + // Try to reuse the color space from the image to preserve its colors. + // Some images use a color space (such as indexed) unsupported by the bitmap context. + CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); + bool releaseColorSpace = false; + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); + switch (colorSpaceModel) { + case kCGColorSpaceModelMonochrome: + case kCGColorSpaceModelRGB: + case kCGColorSpaceModelCMYK: + case kCGColorSpaceModelLab: + case kCGColorSpaceModelDeviceN: + break; + default: + colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear); + releaseColorSpace = true; + break; + } + CGContextRef tmpContext = CGBitmapContextCreate(outputVector.data(), + width, height, 8, rowBytes, + colorSpace, + kCGImageAlphaPremultipliedLast); + if (releaseColorSpace) + CGColorSpaceRelease(colorSpace); + if (!tmpContext) + return false; + CGContextSetBlendMode(tmpContext, kCGBlendModeCopy); + CGContextDrawImage(tmpContext, + CGRectMake(0, 0, static_cast<CGFloat>(width), static_cast<CGFloat>(height)), + cgImage); + CGContextRelease(tmpContext); + return true; +} + + +} // namespace WebCore + +#endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp index e2c47c1..e71f66a 100644 --- a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -134,7 +134,8 @@ static bool fillBMPGlyphs(unsigned offset, // Copy the output to the GlyphPage bool haveGlyphs = false; int invalidGlyph = 0xFFFF; - if (!isVistaOrNewer() && !(tm.tmPitchAndFamily & TMPF_TRUETYPE)) + const DWORD cffTableTag = 0x20464643; // 4-byte identifier for OpenType CFF table ('CFF '). + if (!isVistaOrNewer() && !(tm.tmPitchAndFamily & TMPF_TRUETYPE) && (GetFontData(dc, cffTableTag, 0, 0, 0) == GDI_ERROR)) invalidGlyph = 0x1F; Glyph spaceGlyph = 0; // Glyph for a space. Lazily filled. diff --git a/WebCore/platform/graphics/gtk/IconGtk.cpp b/WebCore/platform/graphics/gtk/IconGtk.cpp index 3563a59..71b897e 100644 --- a/WebCore/platform/graphics/gtk/IconGtk.cpp +++ b/WebCore/platform/graphics/gtk/IconGtk.cpp @@ -87,6 +87,7 @@ static String lookupIconName(String MIMEType) return GTK_STOCK_FILE; } +// FIXME: Move the code to ChromeClient::iconForFiles(). PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) { if (filenames.isEmpty()) diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index 1866c36..e1c9fd2 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> + * Copyright (C) 2009, 2010 Igalia S.L * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -52,20 +53,44 @@ #include <gst/video/video.h> #include <limits> #include <math.h> -#include <webkit/webkitwebview.h> #include <wtf/gtk/GOwnPtr.h> +// GstPlayFlags flags from playbin2. It is the policy of GStreamer to +// not publicly expose element-specific enums. That's why this +// GstPlayFlags enum has been copied here. +typedef enum { + GST_PLAY_FLAG_VIDEO = 0x00000001, + GST_PLAY_FLAG_AUDIO = 0x00000002, + GST_PLAY_FLAG_TEXT = 0x00000004, + GST_PLAY_FLAG_VIS = 0x00000008, + GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, + GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, + GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, + GST_PLAY_FLAG_DOWNLOAD = 0x00000080, + GST_PLAY_FLAG_BUFFERING = 0x000000100 +} GstPlayFlags; + using namespace std; namespace WebCore { +static int greatestCommonDivisor(int a, int b) +{ + while (b) { + int temp = a; + a = b; + b = temp % b; + } + + return ABS(a); +} + gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data) { GOwnPtr<GError> err; GOwnPtr<gchar> debug; MediaPlayer::NetworkState error; MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); - gint percent = 0; bool issueError = true; bool attemptNextLocation = false; @@ -115,8 +140,7 @@ gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpo mp->updateStates(); break; case GST_MESSAGE_BUFFERING: - gst_message_parse_buffering(message, &percent); - LOG_VERBOSE(Media, "Buffering %d", percent); + mp->processBufferingStats(message); break; case GST_MESSAGE_DURATION: LOG_VERBOSE(Media, "Duration changed"); @@ -174,6 +198,12 @@ gboolean notifyMuteIdleCallback(gpointer data) return FALSE; } +gboolean bufferingTimeoutCallback(gpointer data) +{ + MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); + return mp->queryBufferingStats(); +} + static float playbackPosition(GstElement* playbin) { @@ -278,14 +308,24 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_playbackRate(1) , m_errorOccured(false) , m_volumeIdleId(0) - , m_mediaDuration(0.0) + , m_mediaDuration(0) , m_muteIdleId(0) + , m_startedBuffering(false) + , m_fillTimeoutId(0) + , m_maxTimeLoaded(0) + , m_fillStatus(0) { - doGstInit(); + if (doGstInit()) + createGSTPlayBin(); } MediaPlayerPrivate::~MediaPlayerPrivate() { + if (m_fillTimeoutId) { + g_source_remove(m_fillTimeoutId); + m_fillTimeoutId = 0; + } + if (m_volumeIdleId) { g_source_remove(m_volumeIdleId); m_volumeIdleId = 0; @@ -338,7 +378,7 @@ void MediaPlayerPrivate::load(const String& url) m_player->readyStateChanged(); } - createGSTPlayBin(url); + g_object_set(m_playBin, "uri", url.utf8().data(), NULL); pause(); } @@ -422,9 +462,6 @@ void MediaPlayerPrivate::seek(float time) if (!m_playBin) return; - if (m_isStreaming) - return; - if (m_errorOccured) return; @@ -473,30 +510,60 @@ IntSize MediaPlayerPrivate::naturalSize() const if (!hasVideo()) return IntSize(); + GstPad* pad = gst_element_get_static_pad(m_videoSink, "sink"); + if (!pad) + return IntSize(); + + int width = 0, height = 0; + GstCaps* caps = GST_PAD_CAPS(pad); + int pixelAspectRatioNumerator, pixelAspectRatioDenominator; + int displayWidth, displayHeight, displayAspectRatioGCD; + int originalWidth = 0, originalHeight = 0; + // TODO: handle possible clean aperture data. See // https://bugzilla.gnome.org/show_bug.cgi?id=596571 // TODO: handle possible transformation matrix. See // https://bugzilla.gnome.org/show_bug.cgi?id=596326 - int width = 0, height = 0; - if (GstPad* pad = gst_element_get_static_pad(m_videoSink, "sink")) { - GstCaps* caps = GST_PAD_CAPS(pad); - gfloat pixelAspectRatio; - gint pixelAspectRatioNumerator, pixelAspectRatioDenominator; - - if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps) - || !gst_video_format_parse_caps(caps, 0, &width, &height) - || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, - &pixelAspectRatioDenominator)) { - gst_object_unref(GST_OBJECT(pad)); - return IntSize(); - } - pixelAspectRatio = (gfloat) pixelAspectRatioNumerator / (gfloat) pixelAspectRatioDenominator; - width *= pixelAspectRatio; - height /= pixelAspectRatio; + // Get the video PAR and original size. + if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps) + || !gst_video_format_parse_caps(caps, 0, &originalWidth, &originalHeight) + || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, + &pixelAspectRatioDenominator)) { gst_object_unref(GST_OBJECT(pad)); + return IntSize(); } + gst_object_unref(GST_OBJECT(pad)); + + LOG_VERBOSE(Media, "Original video size: %dx%d", originalWidth, originalHeight); + LOG_VERBOSE(Media, "Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator); + + // Calculate DAR based on PAR and video size. + displayWidth = originalWidth * pixelAspectRatioNumerator; + displayHeight = originalHeight * pixelAspectRatioDenominator; + + // Divide display width and height by their GCD to avoid possible overflows. + displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight); + displayWidth /= displayAspectRatioGCD; + displayHeight /= displayAspectRatioGCD; + + // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function. + if (!(originalHeight % displayHeight)) { + LOG_VERBOSE(Media, "Keeping video original height"); + width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight); + height = originalHeight; + } else if (!(originalWidth % displayWidth)) { + LOG_VERBOSE(Media, "Keeping video original width"); + height = gst_util_uint64_scale_int(originalWidth, displayHeight, displayWidth); + width = originalWidth; + } else { + LOG_VERBOSE(Media, "Approximating while keeping original video height"); + width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight); + height = originalHeight; + } + + LOG_VERBOSE(Media, "Natural size: %dx%d", width, height); return IntSize(width, height); } @@ -610,16 +677,79 @@ PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const return timeRanges.release(); } +void MediaPlayerPrivate::processBufferingStats(GstMessage* message) +{ + GstBufferingMode mode; + + gst_message_parse_buffering_stats(message, &mode, 0, 0, 0); + if (mode != GST_BUFFERING_DOWNLOAD) + return; + + if (!m_startedBuffering) { + m_startedBuffering = true; + + if (m_fillTimeoutId > 0) + g_source_remove(m_fillTimeoutId); + + m_fillTimeoutId = g_timeout_add(200, (GSourceFunc) bufferingTimeoutCallback, this); + } +} + +bool MediaPlayerPrivate::queryBufferingStats() +{ + GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); + + if (!gst_element_query(m_playBin, query)) { + gst_query_unref(query); + return TRUE; + } + + gint64 start, stop; + + gst_query_parse_buffering_range(query, 0, &start, &stop, 0); + gst_query_unref(query); + + if (stop != -1) + m_fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX; + else + m_fillStatus = 100.0; + + LOG_VERBOSE(Media, "Download buffer filled up to %f%%", m_fillStatus); + + if (!m_mediaDuration) + durationChanged(); + + // Update maxTimeLoaded only if the media duration is + // available. Otherwise we can't compute it. + if (m_mediaDuration) { + m_maxTimeLoaded = static_cast<float>((m_fillStatus * m_mediaDuration) / 100.0); + LOG_VERBOSE(Media, "Updated maxTimeLoaded: %f", m_maxTimeLoaded); + } + + if (m_fillStatus != 100.0) { + updateStates(); + return TRUE; + } + + // Media is now fully loaded. It will play even if network + // connection is cut. Buffering is done, remove the fill source + // from the main loop. + m_fillTimeoutId = 0; + m_startedBuffering = false; + updateStates(); + return FALSE; +} + float MediaPlayerPrivate::maxTimeSeekable() const { if (m_errorOccured) return 0.0; - // TODO LOG_VERBOSE(Media, "maxTimeSeekable"); - if (m_isStreaming) - return numeric_limits<float>::infinity(); // infinite duration means live stream + if (isinf(duration())) + return 0.0; + return maxTimeLoaded(); } @@ -628,29 +758,28 @@ float MediaPlayerPrivate::maxTimeLoaded() const if (m_errorOccured) return 0.0; - // TODO - LOG_VERBOSE(Media, "maxTimeLoaded"); - notImplemented(); - return duration(); + float loaded = m_maxTimeLoaded; + if (!loaded && !m_fillTimeoutId) + loaded = duration(); + LOG_VERBOSE(Media, "maxTimeLoaded: %f", loaded); + return loaded; } unsigned MediaPlayerPrivate::bytesLoaded() const { - notImplemented(); - LOG_VERBOSE(Media, "bytesLoaded"); - /*if (!m_playBin) + if (!m_playBin) return 0; - float dur = duration(); - float maxTime = maxTimeLoaded(); - if (!dur) - return 0;*/ - return 1; // totalBytes() * maxTime / dur; + if (!m_mediaDuration) + return 0; + + unsigned loaded = totalBytes() * maxTimeLoaded() / m_mediaDuration; + LOG_VERBOSE(Media, "bytesLoaded: %d", loaded); + return loaded; } unsigned MediaPlayerPrivate::totalBytes() const { - LOG_VERBOSE(Media, "totalBytes"); if (!m_source) return 0; @@ -660,6 +789,7 @@ unsigned MediaPlayerPrivate::totalBytes() const GstFormat fmt = GST_FORMAT_BYTES; gint64 length = 0; gst_element_query_duration(m_source, &fmt, &length); + LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length); return length; } @@ -710,6 +840,7 @@ void MediaPlayerPrivate::updateStates() if (state == GST_STATE_PLAYING) { m_readyState = MediaPlayer::HaveEnoughData; m_paused = false; + m_startedPlaying = true; if (!m_mediaDuration) { float newDuration = duration(); if (!isinf(newDuration)) @@ -718,6 +849,28 @@ void MediaPlayerPrivate::updateStates() } else m_paused = true; + // Is on-disk buffering in progress? + if (m_fillTimeoutId) { + m_networkState = MediaPlayer::Loading; + // Buffering has just started, we should now have enough + // data to restart playback if it was internally paused by + // GStreamer. + if (m_paused && !m_startedPlaying) + gst_element_set_state(m_playBin, GST_STATE_PLAYING); + } + + if (maxTimeLoaded() == duration()) { + m_networkState = MediaPlayer::Loaded; + if (state == GST_STATE_READY) + m_readyState = MediaPlayer::HaveNothing; + else if (state == GST_STATE_PAUSED) + m_readyState = MediaPlayer::HaveEnoughData; + } else + if (state == GST_STATE_READY) + m_readyState = MediaPlayer::HaveNothing; + else if (m_paused) + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; + if (m_changingRate) { m_player->rateChanged(); m_changingRate = false; @@ -728,14 +881,24 @@ void MediaPlayerPrivate::updateStates() m_seeking = false; } - m_networkState = MediaPlayer::Loaded; break; case GST_STATE_CHANGE_ASYNC: LOG_VERBOSE(Media, "Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); // Change in progress - return; + + if (!m_isStreaming) + return; + + // Resume playback if a seek was performed in a live pipeline. + if (m_seeking) { + shouldUpdateAfterSeek = true; + m_seeking = false; + if (m_paused) + gst_element_set_state(m_playBin, GST_STATE_PLAYING); + } + break; case GST_STATE_CHANGE_FAILURE: LOG_VERBOSE(Media, "Failure: State: %s, pending: %s", gst_element_state_get_name(state), @@ -749,8 +912,25 @@ void MediaPlayerPrivate::updateStates() if (state == GST_STATE_READY) m_readyState = MediaPlayer::HaveNothing; - else if (state == GST_STATE_PAUSED) - m_readyState = MediaPlayer::HaveCurrentData; + else if (state == GST_STATE_PAUSED) { + m_readyState = MediaPlayer::HaveEnoughData; + m_paused = true; + // Live pipelines go in PAUSED without prerolling. + m_isStreaming = true; + } else if (state == GST_STATE_PLAYING) { + m_startedPlaying = true; + m_paused = false; + } + + if (m_paused && !m_startedPlaying) + gst_element_set_state(m_playBin, GST_STATE_PLAYING); + + if (m_seeking) { + shouldUpdateAfterSeek = true; + m_seeking = false; + if (m_paused) + gst_element_set_state(m_playBin, GST_STATE_PLAYING); + } m_networkState = MediaPlayer::Loading; break; @@ -898,8 +1078,11 @@ void MediaPlayerPrivate::didEnd() // not always 0. So to not confuse the HTMLMediaElement we // synchronize position and duration values. float now = currentTime(); - if (now > 0) + if (now > 0) { m_mediaDuration = now; + m_player->durationChanged(); + } + gst_element_set_state(m_playBin, GST_STATE_PAUSED); timeChanged(); @@ -1150,7 +1333,19 @@ bool MediaPlayerPrivate::supportsFullscreen() const return true; } -void MediaPlayerPrivate::createGSTPlayBin(String url) +void MediaPlayerPrivate::setAutobuffer(bool autoBuffer) +{ + ASSERT(m_playBin); + + GstPlayFlags flags; + g_object_get(m_playBin, "flags", &flags, NULL); + if (autoBuffer) + g_object_set(m_playBin, "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL); + else + g_object_set(m_playBin, "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL); +} + +void MediaPlayerPrivate::createGSTPlayBin() { ASSERT(!m_playBin); m_playBin = gst_element_factory_make("playbin2", "play"); @@ -1160,8 +1355,6 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); gst_object_unref(bus); - g_object_set(m_playBin, "uri", url.utf8().data(), NULL); - g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this); g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this); g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this); @@ -1180,7 +1373,7 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) } else { m_fpsSink = 0; g_object_set(m_playBin, "video-sink", m_videoSink, NULL); - LOG(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18"); + LOG_VERBOSE(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18"); } } else g_object_set(m_playBin, "video-sink", m_videoSink, NULL); diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h index 34257ca..e19b686 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h @@ -2,6 +2,7 @@ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009, 2010 Igalia S.L * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -86,6 +87,9 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface { void muteChanged(); void muteChangedCallback(); + void setAutobuffer(bool); + bool queryBufferingStats(); + MediaPlayer::NetworkState networkState() const; MediaPlayer::ReadyState readyState() const; @@ -128,9 +132,11 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface { float maxTimeLoaded() const; void startEndPointTimerIfNeeded(); - void createGSTPlayBin(String url); + void createGSTPlayBin(); bool changePipelineState(GstState state); + void processBufferingStats(GstMessage* message); + private: MediaPlayer* m_player; GstElement* m_playBin; @@ -157,6 +163,10 @@ class MediaPlayerPrivate : public MediaPlayerPrivateInterface { guint m_volumeIdleId; gfloat m_mediaDuration; guint m_muteIdleId; + bool m_startedBuffering; + guint m_fillTimeoutId; + float m_maxTimeLoaded; + gdouble m_fillStatus; }; } diff --git a/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.cpp b/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.cpp index 390c0ec..74a7852 100644 --- a/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -79,6 +79,10 @@ struct _WebKitWebSrcPrivate { gchar* iradioGenre; gchar* iradioUrl; gchar* iradioTitle; + + // TRUE if appsrc's version is >= 0.10.27, see + // https://bugzilla.gnome.org/show_bug.cgi?id=609423 + gboolean haveAppSrc27; }; enum { @@ -109,7 +113,7 @@ static void webKitWebSrcNeedDataCb(GstAppSrc* appsrc, guint length, gpointer use static void webKitWebSrcEnoughDataCb(GstAppSrc* appsrc, gpointer userData); static gboolean webKitWebSrcSeekDataCb(GstAppSrc* appsrc, guint64 offset, gpointer userData); -static void webKitWebSrcStop(WebKitWebSrc* src, bool resetRequestedOffset); +static void webKitWebSrcStop(WebKitWebSrc* src, bool seeking); static GstAppSrcCallbacks appsrcCallbacks = { webKitWebSrcNeedDataCb, @@ -222,6 +226,9 @@ static void webkit_web_src_init(WebKitWebSrc* src, return; } + GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(priv->appsrc)->elementfactory); + priv->haveAppSrc27 = gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 27); + gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc)); targetpad = gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src"); @@ -238,7 +245,20 @@ static void webkit_web_src_init(WebKitWebSrc* src, // GStreamer to handle. gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024); - webKitWebSrcStop(src, true); + // Emit the need-data signal if the queue contains less + // than 20% of data. Without this the need-data signal + // is emitted when the queue is empty, we then dispatch + // the soup message unpausing to the main loop and from + // there unpause the soup message. This already takes + // quite some time and libsoup even needs some more time + // to actually provide data again. If we do all this + // already if the queue is 20% empty, it's much more + // likely that libsoup already provides new data before + // the queue is really empty. + if (priv->haveAppSrc27) + g_object_set(priv->appsrc, "min-percent", 20, NULL); + + webKitWebSrcStop(src, false); } static void webKitWebSrcFinalize(GObject* object) @@ -296,7 +316,7 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value } -static void webKitWebSrcStop(WebKitWebSrc* src, bool resetRequestedOffset) +static void webKitWebSrcStop(WebKitWebSrc* src, bool seeking) { WebKitWebSrcPrivate* priv = src->priv; @@ -335,15 +355,19 @@ static void webKitWebSrcStop(WebKitWebSrc* src, bool resetRequestedOffset) g_free(priv->iradioTitle); priv->iradioTitle = 0; - if (priv->appsrc) + if (priv->appsrc) { gst_app_src_set_caps(priv->appsrc, 0); + if (!seeking) + gst_app_src_set_size(priv->appsrc, -1); + } priv->offset = 0; - priv->size = 0; priv->seekable = FALSE; - if (resetRequestedOffset) + if (!seeking) { + priv->size = 0; priv->requestedOffset = 0; + } GST_DEBUG_OBJECT(src, "Stopped request"); } @@ -434,7 +458,7 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG_OBJECT(src, "PAUSED->READY"); - webKitWebSrcStop(src, true); + webKitWebSrcStop(src, false); break; default: break; @@ -558,7 +582,7 @@ static void webKitWebSrcEnoughDataCb(GstAppSrc* appsrc, gpointer userData) static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src) { - webKitWebSrcStop(src, false); + webKitWebSrcStop(src, true); webKitWebSrcStart(src); return FALSE; @@ -617,7 +641,8 @@ void StreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse // If we seeked we need 206 == PARTIAL_CONTENT if (priv->requestedOffset && response.httpStatusCode() != 206) { GST_ELEMENT_ERROR(m_src, RESOURCE, READ, (0), (0)); - webKitWebSrcStop(m_src, true); + gst_app_src_end_of_stream(priv->appsrc); + webKitWebSrcStop(m_src, false); return; } @@ -625,6 +650,12 @@ void StreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse if (length > 0) { length += priv->requestedOffset; gst_app_src_set_size(priv->appsrc, length); + if (!priv->haveAppSrc27) { + gst_segment_set_duration(&GST_BASE_SRC(priv->appsrc)->segment, GST_FORMAT_BYTES, length); + gst_element_post_message(GST_ELEMENT(priv->appsrc), + gst_message_new_duration(GST_OBJECT(priv->appsrc), + GST_FORMAT_BYTES, length)); + } } priv->size = length >= 0 ? length : 0; diff --git a/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.h b/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.h index 045e7d7..ae19640 100644 --- a/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.h +++ b/WebCore/platform/graphics/gtk/WebKitWebSourceGStreamer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * Copyright (C) 2009,2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp index 99ad130..096cdbd 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp @@ -1126,31 +1126,6 @@ long GraphicsContext3D::getVertexAttribOffset(unsigned long index, unsigned long return reinterpret_cast<long>(pointer); } -// Returned pointer must be freed by fastFree() -static bool imageToTexture(Image* image, GLubyte*& buffer, size_t& width, size_t& height) -{ - if (!image) - return false; - - CGImageRef textureImage = image->getCGImageRef(); - if (!textureImage) - return false; - - width = CGImageGetWidth(textureImage); - height = CGImageGetHeight(textureImage); - - buffer = (GLubyte*) fastMalloc(width * height * 4); - if (!buffer) - return false; - - CGContextRef textureContext = CGBitmapContextCreate(buffer, width, height, 8, width * 4, - CGImageGetColorSpace(textureImage), kCGImageAlphaPremultipliedLast); - CGContextSetBlendMode(textureContext, kCGBlendModeCopy); - CGContextDrawImage(textureContext, CGRectMake(0, 0, (CGFloat)width, (CGFloat)height), textureImage); - CGContextRelease(textureContext); - return true; -} - int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, void* pixels) { // FIXME: Need to do bounds checking on the buffer here. @@ -1160,20 +1135,12 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned inte int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, bool flipY, bool premultiplyAlpha) { - // FIXME: need to support flipY and premultiplyAlpha - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - ASSERT(image); - ensureContext(m_contextObj); - GLubyte* buffer; - size_t width; - size_t height; - if (!imageToTexture(image, buffer, width, height)) + Vector<uint8_t> imageData; + unsigned int format, internalFormat; + if (!extractImageData(image, flipY, premultiplyAlpha, imageData, &format, &internalFormat)) return -1; - - ::glTexImage2D(target, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - fastFree(buffer); + ::glTexImage2D(target, level, internalFormat, image->width(), image->height(), 0, format, GL_UNSIGNED_BYTE, imageData.data()); return 0; } @@ -1188,20 +1155,12 @@ int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned x int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, Image* image, bool flipY, bool premultiplyAlpha) { // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size - // FIXME: need to support flipY and premultiplyAlpha - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - ASSERT(image); - ensureContext(m_contextObj); - GLubyte* buffer; - size_t width; - size_t height; - if (!imageToTexture(image, buffer, width, height)) + Vector<uint8_t> imageData; + unsigned int format, internalFormat; + if (!extractImageData(image, flipY, premultiplyAlpha, imageData, &format, &internalFormat)) return -1; - - ::glTexSubImage2D(target, level, xoff, yoff, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - fastFree(buffer); + ::glTexSubImage2D(target, level, xoff, yoff, image->width(), image->height(), format, GL_UNSIGNED_BYTE, imageData.data()); return 0; } diff --git a/WebCore/platform/graphics/mac/IconMac.mm b/WebCore/platform/graphics/mac/IconMac.mm index aee7234..bc8c312 100644 --- a/WebCore/platform/graphics/mac/IconMac.mm +++ b/WebCore/platform/graphics/mac/IconMac.mm @@ -39,6 +39,7 @@ Icon::~Icon() { } +// FIXME: Move the code to ChromeClient::iconForFiles(). PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) { if (filenames.isEmpty()) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index e9f64be..355aa68 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -78,6 +78,9 @@ private: static bool isAvailable(); PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer() const; +#endif IntSize naturalSize() const; bool hasVideo() const; diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index dd87bb5..2b90f7a 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -445,12 +445,7 @@ void MediaPlayerPrivate::createQTMovieLayer() #ifndef NDEBUG [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"]; #endif - - // Hang the video layer from the render layer, if we have one yet. If not, we'll do this - // later via acceleratedRenderingStateChanged(). - GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); - if (videoGraphicsLayer) - videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer.get()); + // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration(). } #endif } @@ -522,6 +517,11 @@ void MediaPlayerPrivate::setUpVideoRendering() createQTMovieLayer(); break; } + +#if USE(ACCELERATED_COMPOSITING) + if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) + m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); +#endif } void MediaPlayerPrivate::tearDownVideoRendering() @@ -576,6 +576,13 @@ PlatformMedia MediaPlayerPrivate::platformMedia() const return plaftformMedia; } +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* MediaPlayerPrivate::platformLayer() const +{ + return m_qtVideoLayer.get(); +} +#endif + void MediaPlayerPrivate::play() { if (!metaDataAvailable()) @@ -1406,12 +1413,6 @@ void MediaPlayerPrivate::acceleratedRenderingStateChanged() { // Set up or change the rendering path if necessary. setUpVideoRendering(); - - if (currentRenderingMode() == MediaRenderingMovieLayer) { - GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); - if (videoGraphicsLayer) - videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer.get()); - } } #endif diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index ef7c58f..09947d8 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -273,37 +273,54 @@ void SimpleFontData::platformInit() } else m_xHeight = [m_platformData.font() xHeight]; } + +static CFDataRef copyFontTableForTag(FontPlatformData platformData, FourCharCode tableName) +{ +#ifdef BUILDING_ON_TIGER + ATSFontRef atsFont = FMGetATSFontRefFromFont(platformData.m_atsuFontID); + + ByteCount tableSize; + if (ATSFontGetTable(atsFont, tableName, 0, 0, NULL, &tableSize) != noErr) + return 0; + + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, tableSize); + if (!data) + return 0; + + CFDataIncreaseLength(data, tableSize); + if (ATSFontGetTable(atsFont, tableName, 0, tableSize, CFDataGetMutableBytePtr(data), &tableSize) != noErr) { + CFRelease(data); + return 0; + } + + return data; +#else + return CGFontCopyTableForTag(platformData.cgFont(), tableName); +#endif +} void SimpleFontData::platformCharWidthInit() { - m_avgCharWidth = 0.f; - - // Calculate avgCharWidth according to http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6OS2.html - // We can try grabbing it out of the OS/2 table or via ATSFontGetHorizontalMetrics, but - // ATSFontGetHorizontalMetrics never seems to return a non-zero value and the OS/2 table - // contains zero for a large number of fonts. - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - if (glyphPageZero) { - static int weights[] = { 64, 14, 27, 35, 100, 20, 14, 42, 63, 3, 6, 35, 20, 56, 56, 17, 4, 49, 56, 71, 31, 10, 18, 3, 18, 2, 166 }; - int numGlyphs = 27; - ASSERT(numGlyphs == sizeof(weights) / sizeof(int)); - // Compute the weighted sum of the space character and the lowercase letters in the Latin alphabet. - float sum = 0.f; - int totalWeight = 0; - for (int i = 0; i < numGlyphs; i++) { - Glyph glyph = glyphPageZero->glyphDataForCharacter((i < 26 ? i + 'a' : ' ')).glyph; - if (glyph) { - totalWeight += weights[i]; - sum += widthForGlyph(glyph) * weights[i]; - } - } - if (sum > 0.f && totalWeight > 0) - m_avgCharWidth = sum / totalWeight; + m_avgCharWidth = 0; + m_maxCharWidth = 0; + + RetainPtr<CFDataRef> os2Table(AdoptCF, copyFontTableForTag(m_platformData, 'OS/2')); + if (os2Table && CFDataGetLength(os2Table.get()) >= 4) { + const UInt8* os2 = CFDataGetBytePtr(os2Table.get()); + SInt16 os2AvgCharWidth = os2[2] * 256 + os2[3]; + m_avgCharWidth = scaleEmToUnits(os2AvgCharWidth, m_unitsPerEm) * m_platformData.m_size; } - m_maxCharWidth = 0.f; - if (m_platformData.font()) - m_maxCharWidth = [m_platformData.font() maximumAdvancement].width; + RetainPtr<CFDataRef> headTable(AdoptCF, copyFontTableForTag(m_platformData, 'head')); + if (headTable && CFDataGetLength(headTable.get()) >= 42) { + const UInt8* head = CFDataGetBytePtr(headTable.get()); + ushort uxMin = head[36] * 256 + head[37]; + ushort uxMax = head[40] * 256 + head[41]; + SInt16 xMin = static_cast<SInt16>(uxMin); + SInt16 xMax = static_cast<SInt16>(uxMax); + float diff = static_cast<float>(xMax - xMin); + m_maxCharWidth = scaleEmToUnits(diff, m_unitsPerEm) * m_platformData.m_size; + } // Fallback to a cross-platform estimate, which will populate these values if they are non-positive. initCharWidths(); diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 105d866..8bcda2e 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -219,6 +219,7 @@ public: QStack<TransparencyLayer*> layers; QPainter* redirect; + // reuse this brush for solid color (to prevent expensive QBrush construction) QBrush solidColor; InterpolationQuality imageInterpolationQuality; @@ -760,11 +761,30 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int, b FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) { - QRectF rect(frect); - rect = m_data->p()->deviceMatrix().mapRect(rect); + // It is not enough just to round to pixels in device space. The rotation part of the + // affine transform matrix to device space can mess with this conversion if we have a + // rotating image like the hands of the world clock widget. We just need the scale, so + // we get the affine transform matrix and extract the scale. + QPainter* painter = platformContext(); + QTransform deviceTransform = painter->deviceTransform(); + if (deviceTransform.isIdentity()) + return frect; - QRect result = rect.toRect(); //round it - return FloatRect(QRectF(result)); + qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12()); + qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22()); + + QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY); + QPoint deviceLowerRight(frect.right() * deviceScaleX, frect.bottom() * deviceScaleY); + + // Don't let the height or width round to 0 unless either was originally 0 + if (deviceOrigin.y() == deviceLowerRight.y() && frect.height()) + deviceLowerRight.setY(deviceLowerRight.y() + 1); + if (deviceOrigin.x() == deviceLowerRight.x() && frect.width()) + deviceLowerRight.setX(deviceLowerRight.x() + 1); + + FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY); + FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY); + return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); } void GraphicsContext::setPlatformShadow(const IntSize& size, int, const Color&, ColorSpace) @@ -1112,7 +1132,8 @@ void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colo return; QPainter* p = m_data->p(); QPen newPen(p->pen()); - newPen.setColor(color); + m_data->solidColor.setColor(color); + newPen.setBrush(m_data->solidColor); p->setPen(newPen); } diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index 11f7384..0fd0f1a 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -136,7 +136,9 @@ public: virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); // we manage transforms ourselves because transform-origin acts differently in webkit and in Qt - void setBaseTransform(const QTransform&); + void setBaseTransform(const TransformationMatrix&); + QTransform computeTransform(const TransformationMatrix& baseTransform) const; + void updateTransform(); // let the compositor-API tell us which properties were changed void notifyChange(ChangeMask); @@ -145,7 +147,7 @@ public: // this is called indirectly from ChromeClientQt::setNeedsOneShotDrawingSynchronization // (meaning the sync would happen together with the next draw) // or ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP) - void flushChanges(bool recursive = true); + void flushChanges(bool recursive = true, bool forceTransformUpdate = false); // optimization: when we have an animation running on an element with no contents, that has child-elements with contents, // ALL of them have to have ItemCoordinateCache and not DeviceCoordinateCache @@ -166,7 +168,7 @@ signals: public: GraphicsLayerQt* m_layer; - QTransform m_baseTransform; + TransformationMatrix m_baseTransform; bool m_transformAnimationRunning; bool m_opacityAnimationRunning; QWeakPointer<MaskEffectQt> m_maskEffect; @@ -237,7 +239,10 @@ GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) { // we use graphics-view for compositing, not for interactivity setAcceptedMouseButtons(Qt::NoButton); - setEnabled(false); + // we need to have the item enabled, or else wheel events are not + // passed to the parent class implementation of wheelEvent, where + // they are ignored and passed to the item below. + setEnabled(true); // we'll set the cache when we know what's going on setCacheMode(NoCache); @@ -280,18 +285,54 @@ void GraphicsLayerQtImpl::adjustCachingRecursively(bool animationIsRunning) } } -void GraphicsLayerQtImpl::setBaseTransform(const QTransform& transform) +void GraphicsLayerQtImpl::updateTransform() +{ + setBaseTransform(isTransformAnimationRunning() ? m_baseTransform : m_layer->transform()); +} + +void GraphicsLayerQtImpl::setBaseTransform(const TransformationMatrix& baseTransform) +{ + m_baseTransform = baseTransform; + setTransform(computeTransform(baseTransform)); +} + +QTransform GraphicsLayerQtImpl::computeTransform(const TransformationMatrix& baseTransform) const { if (!m_layer) - return; + return baseTransform; + + TransformationMatrix computedTransform; + + // The origin for childrenTransform is always the center of the ancestor which contains the childrenTransform. + // this has to do with how WebCore implements -webkit-perspective and -webkit-perspective-origin, which are the CSS + // attribute that call setChildrenTransform + QPointF offset = -pos() - boundingRect().bottomRight() / 2; + const GraphicsLayerQtImpl* ancestor = this; + while ((ancestor = qobject_cast<GraphicsLayerQtImpl*>(ancestor->parentObject()))) { + if (!ancestor->m_state.childrenTransform.isIdentity()) { + offset += ancestor->boundingRect().bottomRight() / 2; + computedTransform + .translate(offset.x(), offset.y()) + .multLeft(ancestor->m_state.childrenTransform) + .translate(-offset.x(), -offset.y()); + break; + } + offset -= ancestor->pos(); + } + + computedTransform.multLeft(baseTransform); + // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint, here we convert // we have to manage this ourselves because QGraphicsView's transformOrigin is incompatible - const qreal x = m_layer->anchorPoint().x() * m_layer->size().width(); - const qreal y = m_layer->anchorPoint().y() * m_layer->size().height(); - setTransform(QTransform::fromTranslate(x, y)); - setTransform(transform, true); - translate(-x, -y); - m_baseTransform = transform; + const qreal originX = m_state.anchorPoint.x() * m_size.width(); + const qreal originY = m_state.anchorPoint.y() * m_size.height(); + computedTransform = TransformationMatrix() + .translate(originX, originY) + .multiply(computedTransform) + .translate(-originX, -originY); + + // now we project to 2D + return QTransform(computedTransform); } bool GraphicsLayerQtImpl::isTransformAnimationRunning() const @@ -357,7 +398,7 @@ void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) m_layer->client()->notifySyncRequired(m_layer); } -void GraphicsLayerQtImpl::flushChanges(bool recursive) +void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform) { // this is the bulk of the work. understanding what the compositor is trying to achieve, // what graphics-view can do, and trying to find a sane common-grounds @@ -425,12 +466,15 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive) } } - if (m_changeMask & (TransformChange | AnchorPointChange | SizeChange)) { - // since we convert a percentage-based origin-point to a pixel-based one, - // the anchor-point, transform and size from WebCore all affect the one - // that we give Qt - if (m_state.transform != m_layer->transform() || m_state.anchorPoint != m_layer->anchorPoint() || m_state.size != m_layer->size()) - setBaseTransform(m_layer->transform()); + // FIXME: this is a hack, due to a probable QGraphicsScene bug when rapidly modifying the perspective + // but without this line we get graphic artifacts + if ((m_changeMask & ChildrenTransformChange) && m_state.childrenTransform != m_layer->childrenTransform()) + scene()->update(); + + if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange)) { + // due to the differences between the way WebCore handles transforms and the way Qt handles transforms, + // all these elements affect the transforms of all the descendants. + forceUpdateTransform = true; } if (m_changeMask & (ContentChange | DrawsContentChange | MaskLayerChange)) { @@ -439,8 +483,6 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive) update(); setFlag(ItemHasNoContents, false); - // we only use ItemUsesExtendedStyleOption for HTML content - pixmap can be handled better with regular clipping - setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false); break; case ColorContentType: @@ -524,6 +566,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive) m_state.drawsContent = m_layer->drawsContent(); m_state.contentsOpaque = m_layer->contentsOpaque(); m_state.backfaceVisibility = m_layer->backfaceVisibility(); + m_state.childrenTransform = m_layer->childrenTransform(); m_currentContent.pixmap = m_pendingContent.pixmap; m_currentContent.contentType = m_pendingContent.contentType; m_currentContent.backgroundColor = m_pendingContent.backgroundColor; @@ -534,6 +577,9 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive) afterLayerChanges: + if (forceUpdateTransform) + updateTransform(); + if (!recursive) return; @@ -544,7 +590,7 @@ afterLayerChanges: for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) { if (QGraphicsItem* item = *it) if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject())) - layer->flushChanges(true); + layer->flushChanges(true, forceUpdateTransform); } } @@ -1013,7 +1059,7 @@ public: // this came up during the compositing/animation LayoutTests // when the animation dies, the transform has to go back to default if (m_layer) - m_layer.data()->setBaseTransform(m_layer.data()->m_layer->transform()); + m_layer.data()->updateTransform(); } // the idea is that we let WebCore manage the transform-operations diff --git a/WebCore/platform/graphics/qt/IconQt.cpp b/WebCore/platform/graphics/qt/IconQt.cpp index a9870fc..eb09eda 100644 --- a/WebCore/platform/graphics/qt/IconQt.cpp +++ b/WebCore/platform/graphics/qt/IconQt.cpp @@ -40,6 +40,7 @@ Icon::~Icon() { } +// FIXME: Move the code to ChromeClient::iconForFiles(). PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) { if (filenames.isEmpty()) diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index 234f78b..18e7f08 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -102,7 +102,7 @@ size_t ImageDecoderQt::frameCount() // Fixup for Qt decoders... imageCount() is wrong // and jumpToNextImage does not work either... so // we will have to parse everything... - if (imageCount == 0) + if (!imageCount) forceLoadEverything(); else m_frameBufferCache.resize(imageCount); @@ -132,13 +132,13 @@ RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index) // In case the ImageDecoderQt got recreated we don't know // yet how many images we are going to have and need to // find that out now. - int count = m_frameBufferCache.size(); - if (!m_failed && count == 0) { + size_t count = m_frameBufferCache.size(); + if (!m_failed && !count) { internalDecodeSize(); count = frameCount(); } - if (index >= static_cast<size_t>(count)) + if (index >= count) return 0; RGBA32Buffer& frame = m_frameBufferCache[index]; @@ -215,7 +215,7 @@ void ImageDecoderQt::forceLoadEverything() do { m_frameBufferCache.resize(++imageCount); internalHandleCurrentImage(imageCount - 1); - } while(!m_failed); + } while (!m_failed); // If we failed decoding the first image we actually // have no images and need to keep m_failed set to diff --git a/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp new file mode 100644 index 0000000..cd86e6d --- /dev/null +++ b/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" + +#if ENABLE(3D_CANVAS) + +#include "GraphicsContext3D.h" + +#include "Image.h" +#include "NativeImageSkia.h" + +namespace WebCore { + +bool GraphicsContext3D::getImageData(Image* image, + Vector<uint8_t>& outputVector, + bool premultiplyAlpha, + bool* hasAlphaChannel, + AlphaOp* neededAlphaOp, + unsigned int* format) +{ + if (!image) + return false; + NativeImageSkia* skiaImage = image->nativeImageForCurrentFrame(); + if (!skiaImage) + return false; + SkBitmap::Config skiaConfig = skiaImage->config(); + // FIXME: must support more image configurations. + if (skiaConfig != SkBitmap::kARGB_8888_Config) + return false; + SkBitmap& skiaImageRef = *skiaImage; + SkAutoLockPixels lock(skiaImageRef); + int width = skiaImage->width(); + int height = skiaImage->height(); + int rowBytes = skiaImage->rowBytes(); + ASSERT(rowBytes == width * 4); + uint8_t* pixels = reinterpret_cast<uint8_t*>(skiaImage->getPixels()); + outputVector.resize(rowBytes * height); + memcpy(outputVector.data(), pixels, rowBytes * height); + *hasAlphaChannel = true; + if (!premultiplyAlpha) + // FIXME: must fetch the image data before the premultiplication step + *neededAlphaOp = kAlphaDoUnmultiply; + // FIXME: remove this dependency on desktop OpenGL + *format = 0x80E1; // GL_BGRA + return true; +} + +} // namespace WebCore + +#endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 92a1870..e0f6f5d 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -344,15 +344,6 @@ void PlatformContextSkia::setupPaintForFilling(SkPaint* paint) const paint->setShader(m_state->m_fillShader); } -static SkScalar scalarBound(SkScalar v, SkScalar min, SkScalar max) -{ - if (v < min) - return min; - if (v > max) - return max; - return v; -} - float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, int length) const { setupPaintCommon(paint); @@ -361,13 +352,10 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); paint->setShader(m_state->m_strokeShader); paint->setStyle(SkPaint::kStroke_Style); - // The limits here (512 and 256) were made up but are hopefully large - // enough to be reasonable. They are, empirically, small enough not to - // cause overflows in Skia. - paint->setStrokeWidth(scalarBound(SkFloatToScalar(width), 0, 512)); + paint->setStrokeWidth(SkFloatToScalar(width)); paint->setStrokeCap(m_state->m_lineCap); paint->setStrokeJoin(m_state->m_lineJoin); - paint->setStrokeMiter(scalarBound(SkFloatToScalar(m_state->m_miterLimit), 0, 256)); + paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); if (m_state->m_dash) paint->setPathEffect(m_state->m_dash); diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index 56b46de..cc9343a 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -47,6 +47,7 @@ Icon::~Icon() DestroyIcon(m_hIcon); } +// FIXME: Move the code to ChromeClient::iconForFiles(). PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) { if (filenames.isEmpty()) diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index b2fe069..1df73a7 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -108,6 +108,13 @@ PlatformMedia MediaPlayerPrivate::platformMedia() const return p; } +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* MediaPlayerPrivate::platformLayer() const +{ + return m_qtVideoLayer->platformLayer(); +} +#endif + class TaskTimer : TimerBase { public: static void initialize(); @@ -745,6 +752,11 @@ void MediaPlayerPrivate::setUpVideoRendering() if (preferredMode == MediaRenderingMovieLayer) createLayerForMovie(); + +#if USE(ACCELERATED_COMPOSITING) + if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) + m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); +#endif } void MediaPlayerPrivate::tearDownVideoRendering() @@ -810,11 +822,6 @@ void MediaPlayerPrivate::createLayerForMovie() if (!m_qtMovie || m_qtVideoLayer) return; - // Do nothing if the parent layer hasn't been set up yet. - GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); - if (!videoGraphicsLayer) - return; - // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used // as a wrapper for a WKCACFLayer which gets inserted as the content layer of the video // renderer's GraphicsLayer. @@ -829,9 +836,7 @@ void MediaPlayerPrivate::createLayerForMovie() #ifndef NDEBUG m_qtVideoLayer->setName("Video layer"); #endif - - // Hang the video layer from the render layer. - videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer->platformLayer()); + // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration(). #endif } @@ -858,7 +863,7 @@ void MediaPlayerPrivate::acceleratedRenderingStateChanged() void MediaPlayerPrivate::notifySyncRequired(const GraphicsLayer*) { - GraphicsLayerCACF* videoGraphicsLayer = static_cast<GraphicsLayerCACF*>(m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player)); + GraphicsLayerCACF* videoGraphicsLayer = static_cast<GraphicsLayerCACF*>(m_qtVideoLayer.get()); if (videoGraphicsLayer) videoGraphicsLayer->notifySyncRequired(); } diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index d58f44f..029a520 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -76,6 +76,9 @@ private: virtual bool supportsFullscreen() const; virtual PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer() const; +#endif IntSize naturalSize() const; bool hasVideo() const; diff --git a/WebCore/platform/gtk/PasteboardGtk.cpp b/WebCore/platform/gtk/PasteboardGtk.cpp index 0b4d356..5c7d9a7 100644 --- a/WebCore/platform/gtk/PasteboardGtk.cpp +++ b/WebCore/platform/gtk/PasteboardGtk.cpp @@ -134,7 +134,8 @@ void Pasteboard::writeImage(Node* node, const KURL&, const String&) ASSERT(node && node->renderer() && node->renderer()->isImage()); RenderImage* renderer = toRenderImage(node->renderer()); CachedImage* cachedImage = renderer->cachedImage(); - ASSERT(cachedImage); + if (!cachedImage || cachedImage->errorOccurred()) + return; Image* image = cachedImage->image(); ASSERT(image); diff --git a/WebCore/platform/gtk/RenderThemeGtk.cpp b/WebCore/platform/gtk/RenderThemeGtk.cpp index 727788a..e19e2fa 100644 --- a/WebCore/platform/gtk/RenderThemeGtk.cpp +++ b/WebCore/platform/gtk/RenderThemeGtk.cpp @@ -27,9 +27,11 @@ #include "AffineTransform.h" #include "CString.h" #include "GOwnPtr.h" +#include "Gradient.h" #include "GraphicsContext.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" +#include "MediaControlElements.h" #include "NotImplemented.h" #include "RenderBox.h" #include "RenderObject.h" @@ -686,9 +688,46 @@ bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* o, const RenderOb bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) { - paintInfo.context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace); - paintInfo.context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2, - r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace); + GraphicsContext* context = paintInfo.context; + + context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace); + context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2, + r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace); + + RenderStyle* style = o->style(); + HTMLMediaElement* mediaElement = toParentMediaElement(o); + + if (!mediaElement) + return false; + + // Draw the buffered ranges. This code is highly inspired from + // Chrome. + // FIXME: Draw multiple ranges if there are multiple buffered + // ranges. The current implementation of the player is always + // buffering a single range anyway. + IntRect bufferedRect = r; + bufferedRect.inflate(-style->borderLeftWidth()); + bufferedRect.setWidth((bufferedRect.width() * mediaElement->percentLoaded())); + + // Don't bother drawing an empty area. + if (bufferedRect.isEmpty()) + return false; + + IntPoint sliderTopLeft = bufferedRect.location(); + IntPoint sliderTopRight = sliderTopLeft; + sliderTopRight.move(0, bufferedRect.height()); + + RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight); + Color startColor = m_panelColor; + gradient->addColorStop(0.0, startColor); + gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha())); + + context->save(); + context->setStrokeStyle(NoStroke); + context->setFillGradient(gradient); + context->fillRect(bufferedRect); + context->restore(); + return false; } diff --git a/WebCore/platform/gtk/WidgetGtk.cpp b/WebCore/platform/gtk/WidgetGtk.cpp index 53c10f1..834c21a 100644 --- a/WebCore/platform/gtk/WidgetGtk.cpp +++ b/WebCore/platform/gtk/WidgetGtk.cpp @@ -82,16 +82,18 @@ void Widget::setCursor(const Cursor& cursor) void Widget::show() { - if (!platformWidget()) - return; - gtk_widget_show(platformWidget()); + setSelfVisible(true); + + if (isParentVisible() && platformWidget()) + gtk_widget_show(platformWidget()); } void Widget::hide() { - if (!platformWidget()) - return; - gtk_widget_hide(platformWidget()); + setSelfVisible(false); + + if (isParentVisible() && platformWidget()) + gtk_widget_hide(platformWidget()); } void Widget::paint(GraphicsContext* context, const IntRect& rect) diff --git a/WebCore/platform/haiku/FileSystemHaiku.cpp b/WebCore/platform/haiku/FileSystemHaiku.cpp index 7400cd1..3d9161a 100644 --- a/WebCore/platform/haiku/FileSystemHaiku.cpp +++ b/WebCore/platform/haiku/FileSystemHaiku.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com> + * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> * * All rights reserved. * @@ -29,9 +30,14 @@ #include "FileSystem.h" #include "CString.h" +#include "NotImplemented.h" #include "PlatformString.h" -#include "NotImplemented.h" +#include <Directory.h> +#include <Entry.h> +#include <File.h> +#include <FindDirectory.h> +#include <Path.h> namespace WebCore { @@ -43,8 +49,11 @@ CString fileSystemRepresentation(const String& string) String homeDirectoryPath() { - notImplemented(); - return String(); + BPath path; + if (find_directory(B_USER_DIRECTORY, &path) != B_OK) + return String(); + + return String(path.Path()); } CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) @@ -74,7 +83,10 @@ bool unloadModule(PlatformModule) Vector<String> listDirectory(const String& path, const String& filter) { Vector<String> entries; - notImplemented(); + BDirectory directory(path.utf8().data()); + entry_ref ref; + while (directory.GetNextRef(&ref) == B_OK) + entries.append(ref.name); return entries; } diff --git a/WebCore/platform/image-decoders/ImageDecoder.cpp b/WebCore/platform/image-decoders/ImageDecoder.cpp index ed13048..86bcb45 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.cpp +++ b/WebCore/platform/image-decoders/ImageDecoder.cpp @@ -58,30 +58,24 @@ static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const // TODO: Find a better fix. ImageDecoder* ImageDecoder::create(const SharedBuffer& data) { - // We need at least 4 bytes to figure out what kind of image we're dealing with. + // We need at least 4 bytes to figure out what kind of image we're dealing + // with. static const unsigned maxMarkerLength = 4; char contents[maxMarkerLength]; unsigned length = copyFromSharedBuffer(contents, maxMarkerLength, data, 0); if (length < maxMarkerLength) return 0; - const unsigned char* uContents = reinterpret_cast<const unsigned char*>(contents); - // GIFs begin with GIF8(7 or 9). if (strncmp(contents, "GIF8", 4) == 0) return new GIFImageDecoder(); // Test for PNG. - if (uContents[0]==0x89 && - uContents[1]==0x50 && - uContents[2]==0x4E && - uContents[3]==0x47) + if (!memcmp(contents, "\x89\x50\x4E\x47", 4)) return new PNGImageDecoder(); // JPEG - if (uContents[0]==0xFF && - uContents[1]==0xD8 && - uContents[2]==0xFF) + if (!memcmp(contents, "\xFF\xD8\xFF", 3)) return new JPEGImageDecoder(); // BMP @@ -90,8 +84,7 @@ ImageDecoder* ImageDecoder::create(const SharedBuffer& data) // ICOs always begin with a 2-byte 0 followed by a 2-byte 1. // CURs begin with 2-byte 0 followed by 2-byte 2. - if (!memcmp(contents, "\000\000\001\000", 4) || - !memcmp(contents, "\000\000\002\000", 4)) + if (!memcmp(contents, "\x00\x00\x01\x00", 4) || !memcmp(contents, "\x00\x00\x02\x00", 4)) return new ICOImageDecoder(); // Give up. We don't know what the heck this is. @@ -109,14 +102,27 @@ RGBA32Buffer::RGBA32Buffer() { } +RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) +{ + if (this == &other) + return *this; + + copyBitmapData(other); + setRect(other.rect()); + setStatus(other.status()); + setDuration(other.duration()); + setDisposalMethod(other.disposalMethod()); + return *this; +} + void RGBA32Buffer::clear() { m_bytes.clear(); m_status = FrameEmpty; - // NOTE: Do not reset other members here; clearFrameBufferCache() - // calls this to free the bitmap data, but other functions like - // initFrameBuffer() and frameComplete() may still need to read - // other metadata out of this frame later. + // NOTE: Do not reset other members here; clearFrameBufferCache() calls this + // to free the bitmap data, but other functions like initFrameBuffer() and + // frameComplete() may still need to read other metadata out of this frame + // later. } void RGBA32Buffer::zeroFill() @@ -137,8 +143,8 @@ void RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other) bool RGBA32Buffer::setSize(int newWidth, int newHeight) { - // NOTE: This has no way to check for allocation failure if the - // requested size was too big... + // NOTE: This has no way to check for allocation failure if the requested + // size was too big... m_bytes.resize(newWidth * newHeight); m_size = IntSize(newWidth, newHeight); @@ -163,19 +169,6 @@ void RGBA32Buffer::setStatus(FrameStatus status) m_status = status; } -RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) -{ - if (this == &other) - return *this; - - copyBitmapData(other); - setRect(other.rect()); - setStatus(other.status()); - setDuration(other.duration()); - setDisposalMethod(other.disposalMethod()); - return *this; -} - int RGBA32Buffer::width() const { return m_size.width(); @@ -200,13 +193,11 @@ inline void fillScaledValues(Vector<int>& scaledValues, double scaleRate, int le { double inflateRate = 1. / scaleRate; scaledValues.reserveCapacity(static_cast<int>(length * scaleRate + 0.5)); - for (int scaledIndex = 0;;) { + for (int scaledIndex = 0; ; ++scaledIndex) { int index = static_cast<int>(scaledIndex * inflateRate + 0.5); - if (index < length) { - scaledValues.append(index); - ++scaledIndex; - } else + if (index >= length) break; + scaledValues.append(index); } } diff --git a/WebCore/platform/image-decoders/ImageDecoder.h b/WebCore/platform/image-decoders/ImageDecoder.h index 8d27072..002395b 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.h +++ b/WebCore/platform/image-decoders/ImageDecoder.h @@ -46,18 +46,21 @@ namespace WebCore { - // The RGBA32Buffer object represents the decoded image data in RGBA32 format. This buffer is what all - // decoders write a single frame into. Frames are then instantiated for drawing by being handed this buffer. + // The RGBA32Buffer object represents the decoded image data in RGBA32 + // format. This buffer is what all decoders write a single frame into. + // Frames are then instantiated for drawing by being handed this buffer. class RGBA32Buffer { public: enum FrameStatus { FrameEmpty, FramePartial, FrameComplete }; enum FrameDisposalMethod { - // If you change the numeric values of these, make sure you audit all - // users, as some users may cast raw values to/from these constants. - DisposeNotSpecified, // Leave frame in framebuffer - DisposeKeep, // Leave frame in framebuffer - DisposeOverwriteBgcolor, // Clear frame to transparent - DisposeOverwritePrevious, // Clear frame to previous framebuffer contents + // If you change the numeric values of these, make sure you audit + // all users, as some users may cast raw values to/from these + // constants. + DisposeNotSpecified, // Leave frame in framebuffer + DisposeKeep, // Leave frame in framebuffer + DisposeOverwriteBgcolor, // Clear frame to transparent + DisposeOverwritePrevious, // Clear frame to previous framebuffer + // contents }; #if PLATFORM(SKIA) || PLATFORM(QT) typedef uint32_t PixelData; @@ -67,15 +70,11 @@ namespace WebCore { RGBA32Buffer(); - // For backends which refcount their data, this constructor doesn't need - // to create a new copy of the image data, only increase the ref count. - // - // This exists because ImageDecoder keeps a Vector<RGBA32Buffer>, and - // Vector requires this constructor. - RGBA32Buffer(const RGBA32Buffer& other) - { - operator=(other); - } + RGBA32Buffer(const RGBA32Buffer& other) { operator=(other); } + + // For backends which refcount their data, this operator doesn't need to + // create a new copy of the image data, only increase the ref count. + RGBA32Buffer& operator=(const RGBA32Buffer& other); // Deletes the pixel data entirely; used by ImageDecoder to save memory // when we no longer need to display a frame and only need its metadata. @@ -142,8 +141,6 @@ namespace WebCore { #endif private: - RGBA32Buffer& operator=(const RGBA32Buffer& other); - int width() const; int height() const; @@ -188,26 +185,33 @@ namespace WebCore { Vector<PixelData> m_bytes; IntSize m_size; // The size of the buffer. This should be the // same as ImageDecoder::m_size. - bool m_hasAlpha; // Whether or not any of the pixels in the buffer have transparency. + bool m_hasAlpha; // Whether or not any of the pixels in the buffer + // have transparency. #endif - IntRect m_rect; // The rect of the original specified frame within the overall buffer. - // This will always just be the entire buffer except for GIF frames - // whose original rect was smaller than the overall image size. - FrameStatus m_status; // Whether or not this frame is completely finished decoding. + IntRect m_rect; // The rect of the original specified frame within + // the overall buffer. This will always just be + // the entire buffer except for GIF frames whose + // original rect was smaller than the overall + // image size. + FrameStatus m_status; // Whether or not this frame is completely + // finished decoding. unsigned m_duration; // The animation delay. FrameDisposalMethod m_disposalMethod; - // What to do with this frame's data when initializing the next frame. + // What to do with this frame's data when + // initializing the next frame. }; - // The ImageDecoder class represents a base class for specific image format decoders - // (e.g., GIF, JPG, PNG, ICO) to derive from. All decoders decode into RGBA32 format - // and the base class manages the RGBA32 frame cache. + // The ImageDecoder class represents a base class for specific image format + // decoders (e.g., GIF, JPG, PNG, ICO) to derive from. All decoders decode + // into RGBA32 format and the base class manages the RGBA32 frame cache. + // + // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write + // directly to scaled output buffers by down sampling. Call + // setMaxNumPixels() to specify the biggest size that decoded images can + // have. Image decoders will deflate those images that are bigger than + // m_maxNumPixels. (Not supported by all image decoders yet) class ImageDecoder : public Noncopyable { public: - // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write directly to - // scaled output buffers by down sampling. Call setMaxNumPixels() to specify the - // biggest size that decoded images can have. Image decoders will deflate those - // images that are bigger than m_maxNumPixels. (Not supported by all image decoders yet) ImageDecoder() : m_scaled(false) , m_failed(false) @@ -224,21 +228,22 @@ namespace WebCore { // needing to write a dedicated setData() implementation. static ImageDecoder* create(const SharedBuffer& data); - // The the filename extension usually associated with an undecoded image of this type. + // The the filename extension usually associated with an undecoded image + // of this type. virtual String filenameExtension() const = 0; - // All specific decoder plugins must do something with the data they are given. bool isAllDataReceived() const { return m_isAllDataReceived; } + virtual void setData(SharedBuffer* data, bool allDataReceived) { m_data = data; m_isAllDataReceived = allDataReceived; } - // Whether or not the size information has been decoded yet. This default - // implementation just returns true if the size has been set and we have not - // seen a failure. Decoders may want to override this to lazily decode - // enough of the image to get the size. + // Whether or not the size information has been decoded yet. This + // default implementation just returns true if the size has been set and + // we have not seen a failure. Decoders may want to override this to + // lazily decode enough of the image to get the size. virtual bool isSizeAvailable() { return !m_failed && m_sizeAvailable; @@ -266,10 +271,10 @@ namespace WebCore { return size(); } - // Called by the image decoders to set their decoded size, this also check - // the size for validity. It will return true if the size was set, or false - // if there is an error. On error, the m_failed flag will be set and the - // caller should immediately stop decoding. + // Called by the image decoders to set their decoded size, this also + // checks the size for validity. It will return true if the size was + // set, or false if there is an error. On error, the m_failed flag will + // be set and the caller should immediately stop decoding. virtual bool setSize(unsigned width, unsigned height) { if (isOverSize(width, height)) { @@ -281,29 +286,30 @@ namespace WebCore { return true; } - // The total number of frames for the image. Classes that support multiple frames - // will scan the image data for the answer if they need to (without necessarily - // decoding all of the individual frames). + // The total number of frames for the image. Classes that support + // multiple frames will scan the image data for the answer if they need + // to (without necessarily decoding all of the individual frames). virtual size_t frameCount() { return 1; } // The number of repetitions to perform for an animation loop. virtual int repetitionCount() const { return cAnimationNone; } - // Called to obtain the RGBA32Buffer full of decoded data for rendering. The - // decoder plugin will decode as much of the frame as it can before handing - // back the buffer. + // Called to obtain the RGBA32Buffer full of decoded data for rendering. + // The decoder plugin will decode as much of the frame as it can before + // handing back the buffer. virtual RGBA32Buffer* frameBufferAtIndex(size_t) = 0; - // Whether or not the underlying image format even supports alpha transparency. + // Whether or not the underlying image format even supports alpha + // transparency. virtual bool supportsAlpha() const { return true; } bool failed() const { return m_failed; } void setFailed() { m_failed = true; } // Wipe out frames in the frame buffer cache before |clearBeforeFrame|, - // assuming this can be done without breaking decoding. Different decoders - // place different restrictions on what frames are safe to destroy, so this - // is left to them to implement. + // assuming this can be done without breaking decoding. Different + // decoders place different restrictions on what frames are safe to + // destroy, so this is left to them to implement. // For convenience's sake, we provide a default (empty) implementation, // since in practice only GIFs will ever use this. virtual void clearFrameBufferCache(size_t clearBeforeFrame) { } diff --git a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp index de0690f..fb9f9f2 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp +++ b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp @@ -41,9 +41,7 @@ namespace WebCore { static const size_t sizeOfFileHeader = 14; BMPImageDecoder::BMPImageDecoder() - : ImageDecoder() - , m_allDataReceived(false) - , m_decodedOffset(0) + : m_decodedOffset(0) { } @@ -53,7 +51,6 @@ void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) return; ImageDecoder::setData(data, allDataReceived); - m_allDataReceived = allDataReceived; if (m_reader) m_reader->setData(data); } @@ -61,7 +58,7 @@ void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) bool BMPImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed()) - decodeWithCheckForDataEnded(true); + decode(true); return ImageDecoder::isSizeAvailable(); } @@ -76,31 +73,29 @@ RGBA32Buffer* BMPImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer* buffer = &m_frameBufferCache.first(); if (buffer->status() != RGBA32Buffer::FrameComplete && !failed()) - decodeWithCheckForDataEnded(false); + decode(false); return buffer; } -void BMPImageDecoder::decodeWithCheckForDataEnded(bool onlySize) +void BMPImageDecoder::decode(bool onlySize) { if (failed()) return; // If we couldn't decode the image but we've received all the data, decoding // has failed. - if (!decode(onlySize) && m_allDataReceived) + if (!decodeHelper(onlySize) && isAllDataReceived()) setFailed(); } -bool BMPImageDecoder::decode(bool onlySize) +bool BMPImageDecoder::decodeHelper(bool onlySize) { size_t imgDataOffset = 0; - if ((m_decodedOffset < sizeOfFileHeader) - && !processFileHeader(&imgDataOffset)) + if ((m_decodedOffset < sizeOfFileHeader) && !processFileHeader(&imgDataOffset)) return false; if (!m_reader) { - m_reader.set(new BMPImageReader(this, m_decodedOffset, imgDataOffset, - false)); + m_reader.set(new BMPImageReader(this, m_decodedOffset, imgDataOffset, false)); m_reader->setData(m_data.get()); } @@ -118,8 +113,7 @@ bool BMPImageDecoder::processFileHeader(size_t* imgDataOffset) ASSERT(!m_decodedOffset); if (m_data->size() < sizeOfFileHeader) return false; - const uint16_t fileType = - (m_data->data()[0] << 8) | static_cast<uint8_t>(m_data->data()[1]); + const uint16_t fileType = (m_data->data()[0] << 8) | static_cast<uint8_t>(m_data->data()[1]); *imgDataOffset = readUint32(10); m_decodedOffset = sizeOfFileHeader; diff --git a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h index c793585..15be0a2 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h +++ b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h @@ -50,29 +50,23 @@ namespace WebCore { private: inline uint32_t readUint32(int offset) const { - return BMPImageReader::readUint32(m_data.get(), - m_decodedOffset + offset); + return BMPImageReader::readUint32(m_data.get(), m_decodedOffset + offset); } // Decodes the image. If |onlySize| is true, stops decoding after // calculating the image size. If decoding fails but there is no more // data coming, sets the "decode failure" flag. - void decodeWithCheckForDataEnded(bool onlySize); + void decode(bool onlySize); // Decodes the image. If |onlySize| is true, stops decoding after // calculating the image size. Returns whether decoding succeeded. - // NOTE: Used internally by decodeWithCheckForDataEnded(). Other people - // should not call this. - bool decode(bool onlySize); + bool decodeHelper(bool onlySize); // Processes the file header at the beginning of the data. Sets // |*imgDataOffset| based on the header contents. Returns true if the // file header could be decoded. bool processFileHeader(size_t* imgDataOffset); - // True if we've seen all the data. - bool m_allDataReceived; - // An index into |m_data| representing how much we've already decoded. // Note that this only tracks data _this_ class decodes; once the // BMPImageReader takes over this will not be updated further. diff --git a/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp b/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp index 1936d81..2f3bffa 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp +++ b/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp @@ -33,10 +33,7 @@ namespace WebCore { -BMPImageReader::BMPImageReader(ImageDecoder* parent, - size_t decodedAndHeaderOffset, - size_t imgDataOffset, - bool usesAndMask) +BMPImageReader::BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask) : m_parent(parent) , m_buffer(0) , m_decodedOffset(decodedAndHeaderOffset) @@ -63,8 +60,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) return false; // Read and process info header. - if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) - && !processInfoHeader()) + if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) && !processInfoHeader()) return false; // processInfoHeader() set the size, so if that's all we needed, we're done. @@ -82,8 +78,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) // Initialize the framebuffer if needed. ASSERT(m_buffer); // Parent should set this before asking us to decode! if (m_buffer->status() == RGBA32Buffer::FrameEmpty) { - if (!m_buffer->setSize(m_parent->size().width(), - m_parent->size().height())) + if (!m_buffer->setSize(m_parent->size().width(), m_parent->size().height())) return setFailed(); // Unable to allocate. m_buffer->setStatus(RGBA32Buffer::FramePartial); // setSize() calls eraseARGB(), which resets the alpha flag, so we force @@ -100,9 +95,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) // Decode the data. if ((m_andMaskState != Decoding) && !pastEndOfImage(0)) { - if ((m_infoHeader.biCompression == RLE4) - || (m_infoHeader.biCompression == RLE8) - || (m_infoHeader.biCompression == RLE24)) { + if ((m_infoHeader.biCompression == RLE4) || (m_infoHeader.biCompression == RLE8) || (m_infoHeader.biCompression == RLE24)) { if (!processRLEData()) return false; } else if (!processNonRLEData(false, 0)) @@ -133,8 +126,7 @@ bool BMPImageReader::readInfoHeaderSize() { // Get size of info header. ASSERT(m_decodedOffset == m_headerOffset); - if ((m_decodedOffset > m_data->size()) - || ((m_data->size() - m_decodedOffset) < 4)) + if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < 4)) return false; m_infoHeader.biSize = readUint32(0); // Don't increment m_decodedOffset here, it just makes the code in @@ -143,9 +135,7 @@ bool BMPImageReader::readInfoHeaderSize() // Don't allow the header to overflow (which would be harmless here, but // problematic or at least confusing in other places), or to overrun the // image data. - if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) - || (m_imgDataOffset - && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize)))) + if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize)))) return setFailed(); // See if this is a header size we understand: @@ -156,9 +146,7 @@ bool BMPImageReader::readInfoHeaderSize() else if ((m_infoHeader.biSize == 40) || isWindowsV4Plus()) ; // OS/2 2.x: any multiple of 4 between 16 and 64, inclusive, or 42 or 46 - else if ((m_infoHeader.biSize >= 16) && (m_infoHeader.biSize <= 64) - && (((m_infoHeader.biSize & 3) == 0) || (m_infoHeader.biSize == 42) - || (m_infoHeader.biSize == 46))) + else if ((m_infoHeader.biSize >= 16) && (m_infoHeader.biSize <= 64) && (!(m_infoHeader.biSize & 3) || (m_infoHeader.biSize == 42) || (m_infoHeader.biSize == 46))) m_isOS22x = true; else return setFailed(); @@ -170,9 +158,7 @@ bool BMPImageReader::processInfoHeader() { // Read info header. ASSERT(m_decodedOffset == m_headerOffset); - if ((m_decodedOffset > m_data->size()) - || ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) - || !readInfoHeader()) + if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) || !readInfoHeader()) return false; m_decodedOffset += m_infoHeader.biSize; @@ -188,11 +174,9 @@ bool BMPImageReader::processInfoHeader() // colors", so set it to the maximum number of colors for this bit depth. // Also do this for bitmaps that put too large a value here. if (m_infoHeader.biBitCount < 16) { - const uint32_t maxColors = - static_cast<uint32_t>(1) << m_infoHeader.biBitCount; - if ((m_infoHeader.biClrUsed == 0) - || (m_infoHeader.biClrUsed > maxColors)) - m_infoHeader.biClrUsed = maxColors; + const uint32_t maxColors = static_cast<uint32_t>(1) << m_infoHeader.biBitCount; + if (!m_infoHeader.biClrUsed || (m_infoHeader.biClrUsed > maxColors)) + m_infoHeader.biClrUsed = maxColors; } // For any bitmaps that set their BitCount to the wrong value, reset the @@ -206,7 +190,7 @@ bool BMPImageReader::processInfoHeader() // Tell caller what still needs to be processed. if (m_infoHeader.biBitCount >= 16) m_needToProcessBitmasks = true; - else if (m_infoHeader.biBitCount > 0) + else if (m_infoHeader.biBitCount) m_needToProcessColorTable = true; return true; @@ -246,8 +230,7 @@ bool BMPImageReader::readInfoHeader() } else if (biCompression > 5) return setFailed(); // Some type we don't understand. else - m_infoHeader.biCompression = - static_cast<CompressionType>(biCompression); + m_infoHeader.biCompression = static_cast<CompressionType>(biCompression); } // Read colors used, if present. @@ -288,7 +271,7 @@ bool BMPImageReader::isInfoHeaderValid() const { // Non-positive widths/heights are invalid. (We've already flipped the // sign of the height for top-down bitmaps.) - if ((m_infoHeader.biWidth <= 0) || (m_infoHeader.biHeight == 0)) + if ((m_infoHeader.biWidth <= 0) || !m_infoHeader.biHeight) return false; // Only Windows V3+ has top-down bitmaps. @@ -296,16 +279,10 @@ bool BMPImageReader::isInfoHeaderValid() const return false; // Only bit depths of 1, 4, 8, or 24 are universally supported. - if ((m_infoHeader.biBitCount != 1) && (m_infoHeader.biBitCount != 4) - && (m_infoHeader.biBitCount != 8) - && (m_infoHeader.biBitCount != 24)) { + if ((m_infoHeader.biBitCount != 1) && (m_infoHeader.biBitCount != 4) && (m_infoHeader.biBitCount != 8) && (m_infoHeader.biBitCount != 24)) { // Windows V3+ additionally supports bit depths of 0 (for embedded // JPEG/PNG images), 16, and 32. - if (m_isOS21x || m_isOS22x) - return false; - if ((m_infoHeader.biBitCount != 0) - && (m_infoHeader.biBitCount != 16) - && (m_infoHeader.biBitCount != 32)) + if (m_isOS21x || m_isOS22x || (m_infoHeader.biBitCount && (m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32))) return false; } @@ -314,7 +291,7 @@ bool BMPImageReader::isInfoHeaderValid() const // some compression types. switch (m_infoHeader.biCompression) { case RGB: - if (m_infoHeader.biBitCount == 0) + if (!m_infoHeader.biBitCount) return false; break; @@ -323,46 +300,38 @@ bool BMPImageReader::isInfoHeaderValid() const // Compression = RLE4" (which means "4 bit, but with a 2-color table"), // so also allow the paletted RLE compression types to have too low a // bit count; we'll correct this later. - if (m_infoHeader.biBitCount == 0 || m_infoHeader.biBitCount > 8) + if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 8)) return false; break; case RLE4: // See comments in RLE8. - if (m_infoHeader.biBitCount == 0 || m_infoHeader.biBitCount > 4) + if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 4)) return false; break; case BITFIELDS: // Only valid for Windows V3+. - if (m_isOS21x || m_isOS22x) - return false; - if ((m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32)) + if (m_isOS21x || m_isOS22x || ((m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32))) return false; break; case JPEG: case PNG: // Only valid for Windows V3+. - if (m_isOS21x || m_isOS22x) - return false; - if (m_infoHeader.biBitCount != 0) + if (m_isOS21x || m_isOS22x || m_infoHeader.biBitCount) return false; break; case HUFFMAN1D: // Only valid for OS/2 2.x. - if (!m_isOS22x) - return false; - if (m_infoHeader.biBitCount != 1) + if (!m_isOS22x || (m_infoHeader.biBitCount != 1)) return false; break; case RLE24: // Only valid for OS/2 2.x. - if (!m_isOS22x) - return false; - if (m_infoHeader.biBitCount != 24) + if (!m_isOS22x || (m_infoHeader.biBitCount != 24)) return false; break; @@ -374,8 +343,7 @@ bool BMPImageReader::isInfoHeaderValid() const } // Top-down bitmaps cannot be compressed; they must be RGB or BITFIELDS. - if (m_isTopDown && (m_infoHeader.biCompression != RGB) - && (m_infoHeader.biCompression != BITFIELDS)) + if (m_isTopDown && (m_infoHeader.biCompression != RGB) && (m_infoHeader.biCompression != BITFIELDS)) return false; // Reject the following valid bitmap types that we don't currently bother @@ -385,13 +353,11 @@ bool BMPImageReader::isInfoHeaderValid() const // * Bitmaps larger than 2^16 pixels in either dimension (Windows // probably doesn't draw these well anyway, and the decoded data would // take a lot of memory). - if ((m_infoHeader.biWidth >= (1 << 16)) - || (m_infoHeader.biHeight >= (1 << 16))) + if ((m_infoHeader.biWidth >= (1 << 16)) || (m_infoHeader.biHeight >= (1 << 16))) return false; // * Windows V3+ JPEG-in-BMP and PNG-in-BMP bitmaps (supposedly not found // in the wild, only used to send data to printers?). - if ((m_infoHeader.biCompression == JPEG) - || (m_infoHeader.biCompression == PNG)) + if ((m_infoHeader.biCompression == JPEG) || (m_infoHeader.biCompression == PNG)) return false; // * OS/2 2.x Huffman-encoded monochrome bitmaps (see // http://www.fileformat.info/mirror/egff/ch09_05.htm , re: "G31D" @@ -413,11 +379,8 @@ bool BMPImageReader::processBitmasks() // 16 bits: MSB <- xRRRRRGG GGGBBBBB -> LSB // 24/32 bits: MSB <- [AAAAAAAA] RRRRRRRR GGGGGGGG BBBBBBBB -> LSB const int numBits = (m_infoHeader.biBitCount == 16) ? 5 : 8; - for (int i = 0; i <= 2; ++i) { - m_bitMasks[i] = - ((static_cast<uint32_t>(1) << (numBits * (3 - i))) - 1) ^ - ((static_cast<uint32_t>(1) << (numBits * (2 - i))) - 1); - } + for (int i = 0; i <= 2; ++i) + m_bitMasks[i] = ((static_cast<uint32_t>(1) << (numBits * (3 - i))) - 1) ^ ((static_cast<uint32_t>(1) << (numBits * (2 - i))) - 1); // For Windows V4+ 32-bit RGB, don't overwrite the alpha mask from the // header (see note in readInfoHeader()). @@ -431,10 +394,7 @@ bool BMPImageReader::processBitmasks() // Fail if we don't have enough file space for the bitmasks. static const size_t SIZEOF_BITMASKS = 12; - if (((m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS) < - (m_headerOffset + m_infoHeader.biSize)) - || (m_imgDataOffset && (m_imgDataOffset < - (m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS)))) + if (((m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS) < (m_headerOffset + m_infoHeader.biSize)) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS)))) return setFailed(); // Read bitmasks. @@ -461,8 +421,7 @@ bool BMPImageReader::processBitmasks() // specify a bogus alpha channel in bits that don't exist in the pixel // data (for example, bits 25-31 in a 24-bit RGB format). if (m_infoHeader.biBitCount < 32) - m_bitMasks[i] &= - ((static_cast<uint32_t>(1) << m_infoHeader.biBitCount) - 1); + m_bitMasks[i] &= ((static_cast<uint32_t>(1) << m_infoHeader.biBitCount) - 1); // For empty masks (common on the alpha channel, especially after the // trimming above), quickly clear the shifts and continue, to avoid an @@ -507,15 +466,11 @@ bool BMPImageReader::processColorTable() m_tableSizeInBytes = m_infoHeader.biClrUsed * (m_isOS21x ? 3 : 4); // Fail if we don't have enough file space for the color table. - if (((m_headerOffset + m_infoHeader.biSize + m_tableSizeInBytes) < - (m_headerOffset + m_infoHeader.biSize)) - || (m_imgDataOffset && (m_imgDataOffset < - (m_headerOffset + m_infoHeader.biSize + m_tableSizeInBytes)))) + if (((m_headerOffset + m_infoHeader.biSize + m_tableSizeInBytes) < (m_headerOffset + m_infoHeader.biSize)) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize + m_tableSizeInBytes)))) return setFailed(); // Read color table. - if ((m_decodedOffset > m_data->size()) - || ((m_data->size() - m_decodedOffset) < m_tableSizeInBytes)) + if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < m_tableSizeInBytes)) return false; m_colorTable.resize(m_infoHeader.biClrUsed); for (size_t i = 0; i < m_infoHeader.biClrUsed; ++i) { @@ -573,7 +528,7 @@ bool BMPImageReader::processRLEData() // the image. const uint8_t count = m_data->data()[m_decodedOffset]; const uint8_t code = m_data->data()[m_decodedOffset + 1]; - if (((count != 0) || (code != 1)) && pastEndOfImage(0)) + if ((count || (code != 1)) && pastEndOfImage(0)) return setFailed(); // Decode. @@ -590,10 +545,7 @@ bool BMPImageReader::processRLEData() case 1: // Magic token: EOF // Skip any remaining pixels in the image. - if ((m_coord.x() < m_parent->size().width()) - || (m_isTopDown - ? (m_coord.y() < (m_parent->size().height() - 1)) - : (m_coord.y() > 0))) + if ((m_coord.x() < m_parent->size().width()) || (m_isTopDown ? (m_coord.y() < (m_parent->size().height() - 1)) : (m_coord.y() > 0))) m_buffer->setHasAlpha(true); return true; @@ -607,10 +559,9 @@ bool BMPImageReader::processRLEData() // past the end of the image. const uint8_t dx = m_data->data()[m_decodedOffset + 2]; const uint8_t dy = m_data->data()[m_decodedOffset + 3]; - if ((dx != 0) || (dy != 0)) + if (dx || dy) m_buffer->setHasAlpha(true); - if (((m_coord.x() + dx) > m_parent->size().width()) || - pastEndOfImage(dy)) + if (((m_coord.x() + dx) > m_parent->size().width()) || pastEndOfImage(dy)) return setFailed(); // Skip intervening pixels. @@ -637,8 +588,7 @@ bool BMPImageReader::processRLEData() // The following color data is repeated for |count| total pixels. // Strangely, some BMPs seem to specify excessively large counts // here; ignore pixels past the end of the row. - const int endX = - std::min(m_coord.x() + count, m_parent->size().width()); + const int endX = std::min(m_coord.x() + count, m_parent->size().width()); if (m_infoHeader.biCompression == RLE24) { // Bail if there isn't enough data. @@ -646,8 +596,7 @@ bool BMPImageReader::processRLEData() return false; // One BGR triple that we copy |count| times. - fillRGBA(endX, m_data->data()[m_decodedOffset + 3], - m_data->data()[m_decodedOffset + 2], code, 0xff); + fillRGBA(endX, m_data->data()[m_decodedOffset + 3], m_data->data()[m_decodedOffset + 2], code, 0xff); m_decodedOffset += 4; } else { // RLE8 has one color index that gets repeated; RLE4 has two @@ -658,8 +607,7 @@ bool BMPImageReader::processRLEData() colorIndexes[0] = (colorIndexes[0] >> 4) & 0xf; colorIndexes[1] &= 0xf; } - if ((colorIndexes[0] >= m_infoHeader.biClrUsed) - || (colorIndexes[1] >= m_infoHeader.biClrUsed)) + if ((colorIndexes[0] >= m_infoHeader.biClrUsed) || (colorIndexes[1] >= m_infoHeader.biClrUsed)) return setFailed(); for (int which = 0; m_coord.x() < endX; ) { setI(colorIndexes[which]); @@ -689,9 +637,7 @@ bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) // requires. const size_t pixelsPerByte = 8 / m_infoHeader.biBitCount; const size_t bytesPerPixel = m_infoHeader.biBitCount / 8; - const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) - ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) - : (numPixels * bytesPerPixel); + const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) : (numPixels * bytesPerPixel); // RLE runs are zero-padded at the end to a multiple of 16 bits. Non-RLE // data is in rows and is zero-padded to a multiple of 32 bits. const size_t alignBits = inRLE ? 1 : 3; @@ -711,10 +657,8 @@ bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) const uint8_t mask = (1 << m_infoHeader.biBitCount) - 1; for (size_t byte = 0; byte < unpaddedNumBytes; ++byte) { uint8_t pixelData = m_data->data()[m_decodedOffset + byte]; - for (size_t pixel = 0; - (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) { - const size_t colorIndex = - (pixelData >> (8 - m_infoHeader.biBitCount)) & mask; + for (size_t pixel = 0; (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) { + const size_t colorIndex = (pixelData >> (8 - m_infoHeader.biBitCount)) & mask; if (m_andMaskState == Decoding) { // There's no way to accurately represent an AND + XOR // operation as an RGBA image, so where the AND values @@ -749,7 +693,7 @@ bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) // images where all alpha values are 255; opaque images are // faster to draw. int alpha = getAlpha(pixel); - if (!m_seenNonZeroAlphaPixel && (alpha == 0)) { + if (!m_seenNonZeroAlphaPixel && !alpha) { m_seenZeroAlphaPixel = true; alpha = 255; } else { diff --git a/WebCore/platform/image-decoders/bmp/BMPImageReader.h b/WebCore/platform/image-decoders/bmp/BMPImageReader.h index 3536e3b..a30a721 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageReader.h +++ b/WebCore/platform/image-decoders/bmp/BMPImageReader.h @@ -57,8 +57,7 @@ namespace WebCore { uint32_t result; memcpy(&result, &data->data()[offset], 4); #if CPU(BIG_ENDIAN) - result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) | - ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24); + result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) | ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24); #endif return result; } @@ -67,10 +66,7 @@ namespace WebCore { // |startOffset| points to the start of the BMP within the file. // |buffer| points at an empty RGBA32Buffer that we'll initialize and // fill with decoded data. - BMPImageReader(ImageDecoder* parent, - size_t decodedAndHeaderOffset, - size_t imgDataOffset, - bool usesAndMask); + BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask); void setBuffer(RGBA32Buffer* buffer) { m_buffer = buffer; } void setData(SharedBuffer* data) { m_data = data; } @@ -179,9 +175,7 @@ namespace WebCore { // image", so downwards for m_isTopDown images and upwards otherwise. inline bool pastEndOfImage(int numRows) { - return m_isTopDown - ? ((m_coord.y() + numRows) >= m_parent->size().height()) - : ((m_coord.y() - numRows) < 0); + return m_isTopDown ? ((m_coord.y() + numRows) >= m_parent->size().height()) : ((m_coord.y() - numRows) < 0); } // Returns the pixel data for the current X coordinate in a uint32_t. @@ -203,8 +197,7 @@ namespace WebCore { uint32_t pixel; memcpy(&pixel, &m_data->data()[m_decodedOffset + offset], 3); #if CPU(BIG_ENDIAN) - pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) | - ((pixel & 0xff000000) >> 24); + pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) | ((pixel & 0xff000000) >> 24); #endif return pixel; } @@ -222,17 +215,13 @@ namespace WebCore { // in the given pixel data. inline unsigned getComponent(uint32_t pixel, int component) const { - return ((pixel & m_bitMasks[component]) >> - m_bitShiftsRight[component]) << m_bitShiftsLeft[component]; + return ((pixel & m_bitMasks[component]) >> m_bitShiftsRight[component]) << m_bitShiftsLeft[component]; } inline unsigned getAlpha(uint32_t pixel) const { // For images without alpha, return alpha of 0xff. - if (m_bitMasks[3] == 0) - return 0xff; - - return getComponent(pixel, 3); + return m_bitMasks[3] ? getComponent(pixel, 3) : 0xff; } // Sets the current pixel to the color given by |colorIndex|. This also @@ -240,9 +229,7 @@ namespace WebCore { // right by one. inline void setI(size_t colorIndex) { - setRGBA(m_colorTable[colorIndex].rgbRed, - m_colorTable[colorIndex].rgbGreen, - m_colorTable[colorIndex].rgbBlue, 0xff); + setRGBA(m_colorTable[colorIndex].rgbRed, m_colorTable[colorIndex].rgbGreen, m_colorTable[colorIndex].rgbBlue, 0xff); } // Like setI(), but with the individual component values specified. @@ -251,8 +238,7 @@ namespace WebCore { unsigned blue, unsigned alpha) { - m_buffer->setRGBA(m_coord.x(), m_coord.y(), red, green, blue, - alpha); + m_buffer->setRGBA(m_coord.x(), m_coord.y(), red, green, blue, alpha); m_coord.move(1, 0); } diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index 1124bd2..807d57c 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -30,7 +30,7 @@ namespace WebCore { GIFImageDecoder::GIFImageDecoder() - : m_frameCountValid(true) + : m_alreadyScannedThisDataForFrameCount(true) , m_repetitionCount(cAnimationLoopOnce) , m_readOffset(0) { @@ -40,56 +40,45 @@ GIFImageDecoder::~GIFImageDecoder() { } -// Take the data and store it. void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) { if (m_failed) return; - // Cache our new data. ImageDecoder::setData(data, allDataReceived); - // Our frame count is now unknown. - m_frameCountValid = false; + // We need to rescan the frame count, as the new data may have changed it. + m_alreadyScannedThisDataForFrameCount = false; - // Create the GIF reader. if (!m_reader && !m_failed) m_reader.set(new GIFImageReader(this)); } -// Whether or not the size information has been decoded yet. bool GIFImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) - decode(GIFSizeQuery, 0); + decode(0, GIFSizeQuery); return ImageDecoder::isSizeAvailable(); } -// The total number of frames for the image. Will scan the image data for the answer -// (without necessarily decoding all of the individual frames). size_t GIFImageDecoder::frameCount() { - // If the decoder had an earlier error, we will just return what we had decoded - // so far. - if (!m_frameCountValid) { - // FIXME: Scanning all the data has O(n^2) behavior if the data were to come in really - // slowly. Might be interesting to try to clone our existing read session to preserve - // state, but for now we just crawl all the data. Note that this is no worse than what - // ImageIO does on Mac right now (it also crawls all the data again). + if (!m_alreadyScannedThisDataForFrameCount) { + // FIXME: Scanning all the data has O(n^2) behavior if the data were to + // come in really slowly. Might be interesting to try to clone our + // existing read session to preserve state, but for now we just crawl + // all the data. Note that this is no worse than what ImageIO does on + // Mac right now (it also crawls all the data again). GIFImageReader reader(0); - // This function may fail, but we want to keep any partial data it may - // have decoded, so don't mark it is invalid. If there is an overflow - // or some serious error, m_failed will have gotten set for us. reader.read((const unsigned char*)m_data->data(), m_data->size(), GIFFrameCountQuery, static_cast<unsigned>(-1)); - m_frameCountValid = true; + m_alreadyScannedThisDataForFrameCount = true; m_frameBufferCache.resize(reader.images_count); } return m_frameBufferCache.size(); } -// The number of repetitions to perform for an animation loop. int GIFImageDecoder::repetitionCount() const { // This value can arrive at any point in the image data stream. Most GIFs @@ -120,7 +109,7 @@ RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer& frame = m_frameBufferCache[index]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - decode(GIFFullQuery, index + 1); // Decode this frame. + decode(index + 1, GIFFullQuery); // Decode this frame. return &frame; } @@ -174,23 +163,11 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) } } -// Feed data to the GIF reader. -void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) -{ - if (m_failed) - return; - - m_failed = !m_reader->read((const unsigned char*)m_data->data() + m_readOffset, m_data->size() - m_readOffset, query, haltAtFrame); - - if (m_failed) - m_reader.clear(); -} - -// Callbacks from the GIF reader. bool GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height) { if (!setSize(width, height)) return false; + prepareScaleDataIfNecessary(); return true; } @@ -200,94 +177,7 @@ void GIFImageDecoder::decodingHalted(unsigned bytesLeft) m_readOffset = m_data->size() - bytesLeft; } -bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) -{ - // Initialize the frame rect in our buffer. - const GIFFrameReader* frameReader = m_reader->frame_reader; - IntRect frameRect(frameReader->x_offset, frameReader->y_offset, frameReader->width, frameReader->height); - - // Make sure the frameRect doesn't extend past the bottom-right of the buffer. - if (frameRect.right() > size().width()) - frameRect.setWidth(size().width() - frameReader->x_offset); - if (frameRect.bottom() > size().height()) - frameRect.setHeight(size().height() - frameReader->y_offset); - - RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; - int left = upperBoundScaledX(frameRect.x()); - int right = lowerBoundScaledX(frameRect.right(), left); - int top = upperBoundScaledY(frameRect.y()); - int bottom = lowerBoundScaledY(frameRect.bottom(), top); - buffer->setRect(IntRect(left, top, right - left, bottom - top)); - - if (frameIndex == 0) { - // This is the first frame, so we're not relying on any previous data. - if (!buffer->setSize(scaledSize().width(), scaledSize().height())) { - m_failed = true; - return false; - } - } else { - // The starting state for this frame depends on the previous frame's - // disposal method. - // - // Frames that use the DisposeOverwritePrevious method are effectively - // no-ops in terms of changing the starting state of a frame compared to - // the starting state of the previous frame, so skip over them. (If the - // first frame specifies this method, it will get treated like - // DisposeOverwriteBgcolor below and reset to a completely empty image.) - const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - RGBA32Buffer::FrameDisposalMethod prevMethod = - prevBuffer->disposalMethod(); - while ((frameIndex > 0) - && (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) { - prevBuffer = &m_frameBufferCache[--frameIndex]; - prevMethod = prevBuffer->disposalMethod(); - } - ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete); - - if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || - (prevMethod == RGBA32Buffer::DisposeKeep)) { - // Preserve the last frame as the starting state for this frame. - buffer->copyBitmapData(*prevBuffer); - } else { - // We want to clear the previous frame to transparent, without - // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->rect(); - const IntSize& bufferSize = scaledSize(); - if ((frameIndex == 0) - || prevRect.contains(IntRect(IntPoint(), bufferSize))) { - // Clearing the first frame, or a frame the size of the whole - // image, results in a completely empty image. - if (!buffer->setSize(bufferSize.width(), bufferSize.height())) { - m_failed = true; - return false; - } - } else { - // Copy the whole previous buffer, then clear just its frame. - buffer->copyBitmapData(*prevBuffer); - for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { - for (int x = prevRect.x(); x < prevRect.right(); ++x) - buffer->setRGBA(x, y, 0, 0, 0, 0); - } - if ((prevRect.width() > 0) && (prevRect.height() > 0)) - buffer->setHasAlpha(true); - } - } - } - - // Update our status to be partially complete. - buffer->setStatus(RGBA32Buffer::FramePartial); - - // Reset the alpha pixel tracker for this frame. - m_currentBufferSawAlpha = false; - return true; -} - -bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, - unsigned char* rowBuffer, - unsigned char* rowEnd, - unsigned rowNumber, - unsigned repeatCount, - bool writeTransparentPixels) +bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels) { const GIFFrameReader* frameReader = m_reader->frame_reader; // The pixel data and coordinates supplied to us are relative to the frame's @@ -365,7 +255,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // resulting buffer was non-transparent, and we can setHasAlpha(false). if (buffer.rect().contains(IntRect(IntPoint(), scaledSize()))) buffer.setHasAlpha(false); - else if (frameIndex > 0) { + else if (frameIndex) { // Tricky case. This frame does not have alpha only if everywhere // outside its rect doesn't have alpha. To know whether this is // true, we check the start state of the frame -- if it doesn't have @@ -375,8 +265,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // don't affect the start state of this frame) the same way we do in // initFrameBuffer(). const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - while ((frameIndex > 0) - && (prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)) + while (frameIndex && (prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)) prevBuffer = &m_frameBufferCache[--frameIndex]; // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then @@ -387,8 +276,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // The only remaining case is a DisposeOverwriteBgcolor frame. If // it had no alpha, and its rect is contained in the current frame's // rect, we know the current frame has no alpha. - if ((prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwriteBgcolor) - && !prevBuffer->hasAlpha() && buffer.rect().contains(prevBuffer->rect())) + if ((prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.rect().contains(prevBuffer->rect())) buffer.setHasAlpha(false); } } @@ -401,4 +289,93 @@ void GIFImageDecoder::gifComplete() m_reader.clear(); } +void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) +{ + if (m_failed) + return; + + m_failed = !m_reader->read((const unsigned char*)m_data->data() + m_readOffset, m_data->size() - m_readOffset, query, haltAtFrame); + + if (m_failed) + m_reader.clear(); +} + +bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) +{ + // Initialize the frame rect in our buffer. + const GIFFrameReader* frameReader = m_reader->frame_reader; + IntRect frameRect(frameReader->x_offset, frameReader->y_offset, frameReader->width, frameReader->height); + + // Make sure the frameRect doesn't extend outside the buffer. + if (frameRect.right() > size().width()) + frameRect.setWidth(size().width() - frameReader->x_offset); + if (frameRect.bottom() > size().height()) + frameRect.setHeight(size().height() - frameReader->y_offset); + + RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; + int left = upperBoundScaledX(frameRect.x()); + int right = lowerBoundScaledX(frameRect.right(), left); + int top = upperBoundScaledY(frameRect.y()); + int bottom = lowerBoundScaledY(frameRect.bottom(), top); + buffer->setRect(IntRect(left, top, right - left, bottom - top)); + + if (!frameIndex) { + // This is the first frame, so we're not relying on any previous data. + if (!buffer->setSize(scaledSize().width(), scaledSize().height())) { + m_failed = true; + return false; + } + } else { + // The starting state for this frame depends on the previous frame's + // disposal method. + // + // Frames that use the DisposeOverwritePrevious method are effectively + // no-ops in terms of changing the starting state of a frame compared to + // the starting state of the previous frame, so skip over them. (If the + // first frame specifies this method, it will get treated like + // DisposeOverwriteBgcolor below and reset to a completely empty image.) + const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; + RGBA32Buffer::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod(); + while (frameIndex && (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) { + prevBuffer = &m_frameBufferCache[--frameIndex]; + prevMethod = prevBuffer->disposalMethod(); + } + ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete); + + if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || (prevMethod == RGBA32Buffer::DisposeKeep)) { + // Preserve the last frame as the starting state for this frame. + buffer->copyBitmapData(*prevBuffer); + } else { + // We want to clear the previous frame to transparent, without + // affecting pixels in the image outside of the frame. + const IntRect& prevRect = prevBuffer->rect(); + const IntSize& bufferSize = scaledSize(); + if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) { + // Clearing the first frame, or a frame the size of the whole + // image, results in a completely empty image. + if (!buffer->setSize(bufferSize.width(), bufferSize.height())) { + m_failed = true; + return false; + } + } else { + // Copy the whole previous buffer, then clear just its frame. + buffer->copyBitmapData(*prevBuffer); + for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { + for (int x = prevRect.x(); x < prevRect.right(); ++x) + buffer->setRGBA(x, y, 0, 0, 0, 0); + } + if ((prevRect.width() > 0) && (prevRect.height() > 0)) + buffer->setHasAlpha(true); + } + } + } + + // Update our status to be partially complete. + buffer->setStatus(RGBA32Buffer::FramePartial); + + // Reset the alpha pixel tracker for this frame. + m_currentBufferSawAlpha = false; + return true; +} + } // namespace WebCore diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h index 011ca96..0aa5387 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h @@ -37,48 +37,38 @@ namespace WebCore { class GIFImageDecoder : public ImageDecoder { public: GIFImageDecoder(); - ~GIFImageDecoder(); + virtual ~GIFImageDecoder(); - virtual String filenameExtension() const { return "gif"; } + enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery }; - // Take the data and store it. + // ImageDecoder + virtual String filenameExtension() const { return "gif"; } virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. virtual bool isSizeAvailable(); - - // The total number of frames for the image. Will scan the image data for the answer - // (without necessarily decoding all of the individual frames). virtual size_t frameCount(); - - // The number of repetitions to perform for an animation loop. virtual int repetitionCount() const; - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - virtual void clearFrameBufferCache(size_t clearBeforeFrame); - virtual unsigned frameDurationAtIndex(size_t index) { return 0; } - - enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery }; - - void decode(GIFQuery, unsigned haltAtFrame); - // Callbacks from the GIF reader. bool sizeNowAvailable(unsigned width, unsigned height); void decodingHalted(unsigned bytesLeft); - bool haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, - unsigned repeatCount, bool writeTransparentPixels); + bool haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels); void frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod); void gifComplete(); private: - // Called to initialize the frame buffer with the given index, based on the - // previous frame's disposal method. Returns true on success. On failure, - // this will mark the image as failed. + // If the query is GIFFullQuery, decodes the image up to (but not + // including) |haltAtFrame|. Otherwise, decodes as much as is needed to + // answer the query, ignoring bitmap data. + void decode(unsigned haltAtFrame, GIFQuery); + + // Called to initialize the frame buffer with the given index, based on + // the previous frame's disposal method. Returns true on success. On + // failure, this will mark the image as failed. bool initFrameBuffer(unsigned frameIndex); - bool m_frameCountValid; + bool m_alreadyScannedThisDataForFrameCount; bool m_currentBufferSawAlpha; mutable int m_repetitionCount; OwnPtr<GIFImageReader> m_reader; diff --git a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp index ffb1310..755a48d 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp @@ -290,7 +290,7 @@ bool GIFImageReader::do_lzw(const unsigned char *q) /* Check for explicit end-of-stream code */ if (code == (clear_code + 1)) { /* end-of-stream should only appear after all image data */ - return rows_remaining == 0; + return !rows_remaining; } if (oldcode == -1) { @@ -494,11 +494,11 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, /* All GIF files begin with "GIF87a" or "GIF89a" */ case gif_type: { - if (!strncmp((char*)q, "GIF89a", 6)) { + if (!strncmp((char*)q, "GIF89a", 6)) version = 89; - } else if (!strncmp((char*)q, "GIF87a", 6)) { + else if (!strncmp((char*)q, "GIF87a", 6)) version = 87; - } else { + else { state = gif_error; break; } @@ -712,9 +712,10 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, // and doesn't do anything, as our streaming/buffering takes care of it all... // See: http://semmix.pl/color/exgraf/eeg24.htm GETN(1, gif_netscape_extension_block); - } else - state = gif_error; // 0,3-7 are yet to be defined netscape - // extension codes + } else { + // 0,3-7 are yet to be defined netscape extension codes + state = gif_error; + } break; } @@ -893,8 +894,7 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, // CALLBACK: The frame is now complete. if (clientptr && frame_reader) - clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, - frame_reader->disposal_method); + clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, frame_reader->disposal_method); /* Clear state from this image */ if (frame_reader) { @@ -919,7 +919,6 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, // Handle general errors case gif_error: - // nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count); return false; // We shouldn't ever get here. diff --git a/WebCore/platform/image-decoders/gif/GIFImageReader.h b/WebCore/platform/image-decoders/gif/GIFImageReader.h index 14c2fb4..5982827 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageReader.h +++ b/WebCore/platform/image-decoders/gif/GIFImageReader.h @@ -194,10 +194,6 @@ struct GIFImageReader { } ~GIFImageReader() { - close(); - } - - void close() { delete []global_colormap; global_colormap = 0; delete frame_reader; diff --git a/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp b/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp index a0ec4f7..1a202bc 100644 --- a/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp +++ b/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp @@ -45,9 +45,11 @@ static const size_t sizeOfDirectory = 6; static const size_t sizeOfDirEntry = 16; ICOImageDecoder::ICOImageDecoder() - : ImageDecoder() - , m_allDataReceived(false) - , m_decodedOffset(0) + : m_decodedOffset(0) +{ +} + +ICOImageDecoder::~ICOImageDecoder() { } @@ -57,10 +59,8 @@ void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) return; ImageDecoder::setData(data, allDataReceived); - m_allDataReceived = allDataReceived; - for (BMPReaders::iterator i(m_bmpReaders.begin()); - i != m_bmpReaders.end(); ++i) { + for (BMPReaders::iterator i(m_bmpReaders.begin()); i != m_bmpReaders.end(); ++i) { if (*i) (*i)->setData(data); } @@ -71,7 +71,7 @@ void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) bool ICOImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable()) - decodeWithCheckForDataEnded(0, true); + decode(0, true); return ImageDecoder::isSizeAvailable(); } @@ -83,8 +83,7 @@ IntSize ICOImageDecoder::size() const IntSize ICOImageDecoder::frameSizeAtIndex(size_t index) const { - return (index && (index < m_dirEntries.size())) ? - m_dirEntries[index].m_size : size(); + return (index && (index < m_dirEntries.size())) ? m_dirEntries[index].m_size : size(); } bool ICOImageDecoder::setSize(unsigned width, unsigned height) @@ -101,7 +100,7 @@ bool ICOImageDecoder::setSize(unsigned width, unsigned height) size_t ICOImageDecoder::frameCount() { - decodeWithCheckForDataEnded(0, true); + decode(0, true); if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(m_dirEntries.size()); // CAUTION: We must not resize m_frameBufferCache again after this, as @@ -116,40 +115,19 @@ RGBA32Buffer* ICOImageDecoder::frameBufferAtIndex(size_t index) if (index >= frameCount()) return 0; - // Determine the image type, and if this is a BMP, decode. - decodeWithCheckForDataEnded(index, false); - - // PNGs decode into their own framebuffers, so only use our internal cache - // for non-PNGs (BMP or unknown). - if (!m_pngDecoders[index]) - return &m_frameBufferCache[index]; - - // Fail if the size the PNGImageDecoder calculated does not match the size - // in the directory. - if (m_pngDecoders[index]->isSizeAvailable()) { - const IntSize pngSize(m_pngDecoders[index]->size()); - const IconDirectoryEntry& dirEntry = m_dirEntries[index]; - if (pngSize != dirEntry.m_size) { - setFailed(); - m_pngDecoders[index]->setFailed(); - } - } - - return m_pngDecoders[index]->frameBufferAtIndex(0); + RGBA32Buffer* buffer = &m_frameBufferCache[index]; + if (buffer->status() != RGBA32Buffer::FrameComplete) + decode(index, false); + return buffer; } // static -bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a, - const IconDirectoryEntry& b) +bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a, const IconDirectoryEntry& b) { - // Larger icons are better. + // Larger icons are better. After that, higher bit-depth icons are better. const int aEntryArea = a.m_size.width() * a.m_size.height(); const int bEntryArea = b.m_size.width() * b.m_size.height(); - if (aEntryArea != bEntryArea) - return (aEntryArea > bEntryArea); - - // Higher bit-depth icons are better. - return (a.m_bitCount > b.m_bitCount); + return (aEntryArea == bEntryArea) ? (a.m_bitCount > b.m_bitCount) : (aEntryArea > bEntryArea); } void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t index) @@ -161,21 +139,18 @@ void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t index) // Copy out PNG data to a separate vector and send to the PNG decoder. // FIXME: Save this copy by making the PNG decoder able to take an // optional offset. - RefPtr<SharedBuffer> pngData( - SharedBuffer::create(&m_data->data()[dirEntry.m_imageOffset], - m_data->size() - dirEntry.m_imageOffset)); - m_pngDecoders[index]->setData(pngData.get(), m_allDataReceived); + RefPtr<SharedBuffer> pngData(SharedBuffer::create(&m_data->data()[dirEntry.m_imageOffset], m_data->size() - dirEntry.m_imageOffset)); + m_pngDecoders[index]->setData(pngData.get(), isAllDataReceived()); } -void ICOImageDecoder::decodeWithCheckForDataEnded(size_t index, bool onlySize) +void ICOImageDecoder::decode(size_t index, bool onlySize) { if (failed()) return; // If we couldn't decode the image but we've received all the data, decoding // has failed. - if ((!decodeDirectory() || (!onlySize && !decodeAtIndex(index))) - && m_allDataReceived) + if ((!decodeDirectory() || (!onlySize && !decodeAtIndex(index))) && isAllDataReceived()) setFailed(); } @@ -186,45 +161,47 @@ bool ICOImageDecoder::decodeDirectory() return false; // Read and process directory entries. - return (m_decodedOffset >= - (sizeOfDirectory + (m_dirEntries.size() * sizeOfDirEntry))) - || processDirectoryEntries(); + return (m_decodedOffset >= (sizeOfDirectory + (m_dirEntries.size() * sizeOfDirEntry))) || processDirectoryEntries(); } bool ICOImageDecoder::decodeAtIndex(size_t index) { ASSERT(index < m_dirEntries.size()); const IconDirectoryEntry& dirEntry = m_dirEntries[index]; - if (!m_bmpReaders[index] && !m_pngDecoders[index]) { - // Image type unknown. - const ImageType imageType = imageTypeAtIndex(index); - if (imageType == BMP) { + const ImageType imageType = imageTypeAtIndex(index); + if (imageType == Unknown) + return false; // Not enough data to determine image type yet. + + if (imageType == BMP) { + if (!m_bmpReaders[index]) { // We need to have already sized m_frameBufferCache before this, and // we must not resize it again later (see caution in frameCount()). ASSERT(m_frameBufferCache.size() == m_dirEntries.size()); - m_bmpReaders[index].set( - new BMPImageReader(this, dirEntry.m_imageOffset, 0, true)); + m_bmpReaders[index].set(new BMPImageReader(this, dirEntry.m_imageOffset, 0, true)); m_bmpReaders[index]->setData(m_data.get()); m_bmpReaders[index]->setBuffer(&m_frameBufferCache[index]); - } else if (imageType == PNG) { - m_pngDecoders[index].set(new PNGImageDecoder()); - setDataForPNGDecoderAtIndex(index); - } else { - // Not enough data to determine image type yet. - return false; } - } - - if (m_bmpReaders[index]) { m_frameSize = dirEntry.m_size; bool result = m_bmpReaders[index]->decodeBMP(false); m_frameSize = IntSize(); return result; } - // For PNGs, we're now done; further decoding will happen when calling - // frameBufferAtIndex() on the PNG decoder. - return true; + if (!m_pngDecoders[index]) { + m_pngDecoders[index].set(new PNGImageDecoder()); + setDataForPNGDecoderAtIndex(index); + } + // Fail if the size the PNGImageDecoder calculated does not match the size + // in the directory. + if (m_pngDecoders[index]->isSizeAvailable() && (m_pngDecoders[index]->size() != dirEntry.m_size)) { + setFailed(); + return false; + } + m_frameBufferCache[index] = *m_pngDecoders[index]->frameBufferAtIndex(0); + if (!m_pngDecoders[index]->failed()) + return true; + setFailed(); + return false; } bool ICOImageDecoder::processDirectory() @@ -259,18 +236,14 @@ bool ICOImageDecoder::processDirectoryEntries() { // Read directory entries. ASSERT(m_decodedOffset == sizeOfDirectory); - if ((m_decodedOffset > m_data->size()) - || ((m_data->size() - m_decodedOffset) < - (m_dirEntries.size() * sizeOfDirEntry))) + if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < (m_dirEntries.size() * sizeOfDirEntry))) return false; - for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); - i != m_dirEntries.end(); ++i) + for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); i != m_dirEntries.end(); ++i) *i = readDirectoryEntry(); // Updates m_decodedOffset. // Make sure the specified image offsets are past the end of the directory // entries. - for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); - i != m_dirEntries.end(); ++i) { + for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); i != m_dirEntries.end(); ++i) { if (i->m_imageOffset < m_decodedOffset) { setFailed(); return false; @@ -309,8 +282,7 @@ ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry() // this isn't quite what the bitmap info header says later, as we only use // this value to determine which icon entry is best. if (!entry.m_bitCount) { - int colorCount = - static_cast<uint8_t>(m_data->data()[m_decodedOffset + 2]); + int colorCount = static_cast<uint8_t>(m_data->data()[m_decodedOffset + 2]); if (!colorCount) colorCount = 256; // Vague in the spec, needed by real-world icons. for (--colorCount; colorCount; colorCount >>= 1) diff --git a/WebCore/platform/image-decoders/ico/ICOImageDecoder.h b/WebCore/platform/image-decoders/ico/ICOImageDecoder.h index 6117e06..c1f29c9 100644 --- a/WebCore/platform/image-decoders/ico/ICOImageDecoder.h +++ b/WebCore/platform/image-decoders/ico/ICOImageDecoder.h @@ -41,6 +41,7 @@ namespace WebCore { class ICOImageDecoder : public ImageDecoder { public: ICOImageDecoder(); + virtual ~ICOImageDecoder(); // ImageDecoder virtual String filenameExtension() const { return "ico"; } @@ -67,19 +68,16 @@ namespace WebCore { // Returns true if |a| is a preferable icon entry to |b|. // Larger sizes, or greater bitdepths at the same size, are preferable. - static bool compareEntries(const IconDirectoryEntry& a, - const IconDirectoryEntry& b); + static bool compareEntries(const IconDirectoryEntry& a, const IconDirectoryEntry& b); inline uint16_t readUint16(int offset) const { - return BMPImageReader::readUint16(m_data.get(), - m_decodedOffset + offset); + return BMPImageReader::readUint16(m_data.get(), m_decodedOffset + offset); } inline uint32_t readUint32(int offset) const { - return BMPImageReader::readUint32(m_data.get(), - m_decodedOffset + offset); + return BMPImageReader::readUint32(m_data.get(), m_decodedOffset + offset); } // If the desired PNGImageDecoder exists, gives it the appropriate data. @@ -88,12 +86,7 @@ namespace WebCore { // Decodes the entry at |index|. If |onlySize| is true, stops decoding // after calculating the image size. If decoding fails but there is no // more data coming, sets the "decode failure" flag. - // - // NOTE: If the desired entry is a PNG, this doesn't actually trigger - // decoding, it merely ensures the decoder is created and ready to - // decode. The caller will then call a function on the PNGImageDecoder - // that actually triggers decoding. - void decodeWithCheckForDataEnded(size_t index, bool onlySize); + void decode(size_t index, bool onlySize); // Decodes the directory and directory entries at the beginning of the // data, and initializes members. Returns true if all decoding @@ -120,9 +113,6 @@ namespace WebCore { // if the type could be determined. ImageType imageTypeAtIndex(size_t); - // True if we've seen all the data. - bool m_allDataReceived; - // An index into |m_data| representing how much we've already decoded. // Note that this only tracks data _this_ class decodes; once the // BMPImageReader takes over this will not be updated further. diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp index aaa9047..9ed20b6 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp @@ -59,18 +59,17 @@ extern "C" { namespace WebCore { struct decoder_error_mgr { - struct jpeg_error_mgr pub; /* "public" fields for IJG library*/ - jmp_buf setjmp_buffer; /* For handling catastropic errors */ + struct jpeg_error_mgr pub; // "public" fields for IJG library + jmp_buf setjmp_buffer; // For handling catastropic errors }; enum jstate { - JPEG_HEADER, /* Reading JFIF headers */ + JPEG_HEADER, // Reading JFIF headers JPEG_START_DECOMPRESS, - JPEG_DECOMPRESS_PROGRESSIVE, /* Output progressive pixels */ - JPEG_DECOMPRESS_SEQUENTIAL, /* Output sequential pixels */ + JPEG_DECOMPRESS_PROGRESSIVE, // Output progressive pixels + JPEG_DECOMPRESS_SEQUENTIAL, // Output sequential pixels JPEG_DONE, - JPEG_SINK_NON_JPEG_TRAILER, /* Some image files have a */ - /* non-JPEG trailer */ + JPEG_SINK_NON_JPEG_TRAILER, // Some image files have a non-JPEG trailer JPEG_ERROR }; @@ -80,14 +79,12 @@ void skip_input_data(j_decompress_ptr jd, long num_bytes); void term_source(j_decompress_ptr jd); void error_exit(j_common_ptr cinfo); -/* - * Implementation of a JPEG src object that understands our state machine - */ +// Implementation of a JPEG src object that understands our state machine struct decoder_source_mgr { - /* public fields; must be first in this struct! */ - struct jpeg_source_mgr pub; + // public fields; must be first in this struct! + struct jpeg_source_mgr pub; - JPEGImageReader *decoder; + JPEGImageReader* decoder; }; class JPEGImageReader @@ -102,11 +99,11 @@ public: { memset(&m_info, 0, sizeof(jpeg_decompress_struct)); - /* We set up the normal JPEG error routines, then override error_exit. */ + // We set up the normal JPEG error routines, then override error_exit. m_info.err = jpeg_std_error(&m_err.pub); m_err.pub.error_exit = error_exit; - /* Allocate and initialize JPEG decompression object */ + // Allocate and initialize JPEG decompression object. jpeg_create_decompress(&m_info); decoder_source_mgr* src = 0; @@ -120,7 +117,7 @@ public: m_info.src = (jpeg_source_mgr*)src; - /* Set up callback functions. */ + // Set up callback functions. src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; @@ -148,22 +145,20 @@ public: long bytesToSkip = std::min(num_bytes, (long)src->pub.bytes_in_buffer); src->pub.bytes_in_buffer -= (size_t)bytesToSkip; src->pub.next_input_byte += bytesToSkip; - - if (num_bytes > bytesToSkip) - m_bytesToSkip = (size_t)(num_bytes - bytesToSkip); - else - m_bytesToSkip = 0; + + m_bytesToSkip = std::max(num_bytes - bytesToSkip, static_cast<long>(0)); } - bool decode(const Vector<char>& data, bool sizeOnly) { - m_decodingSizeOnly = sizeOnly; - + bool decode(const Vector<char>& data, bool onlySize) + { + m_decodingSizeOnly = onlySize; + unsigned newByteCount = data.size() - m_bufferLength; unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer += newByteCount; m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset; - + // If we still have bytes to skip, try to skip those now. if (m_bytesToSkip) skipBytes(m_bytesToSkip); @@ -178,172 +173,150 @@ public: } switch (m_state) { - case JPEG_HEADER: - { - /* Read file parameters with jpeg_read_header() */ - if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) - return true; /* I/O suspension */ - - /* let libjpeg take care of gray->RGB and YCbCr->RGB conversions */ - switch (m_info.jpeg_color_space) { - case JCS_GRAYSCALE: - case JCS_RGB: - case JCS_YCbCr: - m_info.out_color_space = JCS_RGB; - break; - case JCS_CMYK: - case JCS_YCCK: - // jpeglib cannot convert these to rgb, but it can - // convert ycck to cmyk - m_info.out_color_space = JCS_CMYK; - break; - default: - m_state = JPEG_ERROR; - return false; - } - - /* - * Don't allocate a giant and superfluous memory buffer - * when the image is a sequential JPEG. - */ - m_info.buffered_image = jpeg_has_multiple_scans(&m_info); - - /* Used to set up image size so arrays can be allocated */ - jpeg_calc_output_dimensions(&m_info); - - /* - * Make a one-row-high sample array that will go away - * when done with image. Always make it big enough to - * hold an RGB row. Since this uses the IJG memory - * manager, it must be allocated before the call to - * jpeg_start_compress(). - */ - int row_stride = m_info.output_width * 4; // RGBA buffer + case JPEG_HEADER: + // Read file parameters with jpeg_read_header(). + if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) + return true; // I/O suspension. + + // Let libjpeg take care of gray->RGB and YCbCr->RGB conversions. + switch (m_info.jpeg_color_space) { + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + m_info.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + // jpeglib cannot convert these to rgb, but it can convert ycck + // to cmyk. + m_info.out_color_space = JCS_CMYK; + break; + default: + m_state = JPEG_ERROR; + return false; + } + // Don't allocate a giant and superfluous memory buffer when the + // image is a sequential JPEG. + m_info.buffered_image = jpeg_has_multiple_scans(&m_info); - m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, - JPOOL_IMAGE, - row_stride, 1); + // Used to set up image size so arrays can be allocated. + jpeg_calc_output_dimensions(&m_info); - m_state = JPEG_START_DECOMPRESS; + // Make a one-row-high sample array that will go away when done with + // image. Always make it big enough to hold an RGB row. Since this + // uses the IJG memory manager, it must be allocated before the call + // to jpeg_start_compress(). + m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, JPOOL_IMAGE, m_info.output_width * 4, 1); - // We can fill in the size now that the header is available. - if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) { - m_state = JPEG_ERROR; - return false; - } + m_state = JPEG_START_DECOMPRESS; - if (m_decodingSizeOnly) { - // We can stop here. - // Reduce our buffer length and available data. - m_bufferLength -= m_info.src->bytes_in_buffer; - m_info.src->bytes_in_buffer = 0; - return true; - } + // We can fill in the size now that the header is available. + if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) { + m_state = JPEG_ERROR; + return false; } - case JPEG_START_DECOMPRESS: - { - /* Set parameters for decompression */ - /* FIXME -- Should reset dct_method and dither mode - * for final pass of progressive JPEG - */ - m_info.dct_method = JDCT_ISLOW; - m_info.dither_mode = JDITHER_FS; - m_info.do_fancy_upsampling = true; - m_info.enable_2pass_quant = false; - m_info.do_block_smoothing = true; - - /* Start decompressor */ - if (!jpeg_start_decompress(&m_info)) - return true; /* I/O suspension */ - - /* If this is a progressive JPEG ... */ - m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; + if (m_decodingSizeOnly) { + // We can stop here. Reduce our buffer length and available + // data. + m_bufferLength -= m_info.src->bytes_in_buffer; + m_info.src->bytes_in_buffer = 0; + return true; } - - case JPEG_DECOMPRESS_SEQUENTIAL: - { - if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { - - if (!m_decoder->outputScanlines()) - return true; /* I/O suspension */ - - /* If we've completed image output ... */ - ASSERT(m_info.output_scanline == m_info.output_height); - m_state = JPEG_DONE; - } + // FALL THROUGH + + case JPEG_START_DECOMPRESS: + // Set parameters for decompression. + // FIXME -- Should reset dct_method and dither mode for final pass + // of progressive JPEG. + m_info.dct_method = JDCT_ISLOW; + m_info.dither_mode = JDITHER_FS; + m_info.do_fancy_upsampling = true; + m_info.enable_2pass_quant = false; + m_info.do_block_smoothing = true; + + // Start decompressor. + if (!jpeg_start_decompress(&m_info)) + return true; // I/O suspension. + + // If this is a progressive JPEG ... + m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; + // FALL THROUGH + + case JPEG_DECOMPRESS_SEQUENTIAL: + if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { + + if (!m_decoder->outputScanlines()) + return true; // I/O suspension. + + // If we've completed image output... + ASSERT(m_info.output_scanline == m_info.output_height); + m_state = JPEG_DONE; } + // FALL THROUGH + + case JPEG_DECOMPRESS_PROGRESSIVE: + if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { + int status; + do { + status = jpeg_consume_input(&m_info); + } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); + + for (;;) { + if (!m_info.output_scanline) { + int scan = m_info.input_scan_number; + + // If we haven't displayed anything yet + // (output_scan_number == 0) and we have enough data for + // a complete scan, force output of the last full scan. + if (!m_info.output_scan_number && (scan > 1) && (status != JPEG_REACHED_EOI)) + --scan; + + if (!jpeg_start_output(&m_info, scan)) + return true; // I/O suspension. + } + + if (m_info.output_scanline == 0xffffff) + m_info.output_scanline = 0; - case JPEG_DECOMPRESS_PROGRESSIVE: - { - if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { - int status; - do { - status = jpeg_consume_input(&m_info); - } while ((status != JPEG_SUSPENDED) && - (status != JPEG_REACHED_EOI)); - - for (;;) { - if (m_info.output_scanline == 0) { - int scan = m_info.input_scan_number; - - /* if we haven't displayed anything yet (output_scan_number==0) - and we have enough data for a complete scan, force output - of the last full scan */ - if ((m_info.output_scan_number == 0) && - (scan > 1) && - (status != JPEG_REACHED_EOI)) - scan--; - - if (!jpeg_start_output(&m_info, scan)) - return true; /* I/O suspension */ - } - - if (m_info.output_scanline == 0xffffff) - m_info.output_scanline = 0; - - if (!m_decoder->outputScanlines()) { - if (m_info.output_scanline == 0) - /* didn't manage to read any lines - flag so we don't call - jpeg_start_output() multiple times for the same scan */ - m_info.output_scanline = 0xffffff; - return true; /* I/O suspension */ - } - - if (m_info.output_scanline == m_info.output_height) { - if (!jpeg_finish_output(&m_info)) - return true; /* I/O suspension */ - - if (jpeg_input_complete(&m_info) && - (m_info.input_scan_number == m_info.output_scan_number)) - break; - - m_info.output_scanline = 0; - } + if (!m_decoder->outputScanlines()) { + if (!m_info.output_scanline) + // Didn't manage to read any lines - flag so we + // don't call jpeg_start_output() multiple times for + // the same scan. + m_info.output_scanline = 0xffffff; + return true; // I/O suspension. } - m_state = JPEG_DONE; - } - } + if (m_info.output_scanline == m_info.output_height) { + if (!jpeg_finish_output(&m_info)) + return true; // I/O suspension. - case JPEG_DONE: - { - /* Finish decompression */ - if (!jpeg_finish_decompress(&m_info)) - return true; /* I/O suspension */ + if (jpeg_input_complete(&m_info) && (m_info.input_scan_number == m_info.output_scan_number)) + break; - m_state = JPEG_SINK_NON_JPEG_TRAILER; + m_info.output_scanline = 0; + } + } - /* we're done */ - break; + m_state = JPEG_DONE; } - - case JPEG_SINK_NON_JPEG_TRAILER: - break; + // FALL THROUGH - case JPEG_ERROR: - break; + case JPEG_DONE: + // Finish decompression. + if (!jpeg_finish_decompress(&m_info)) + return true; // I/O suspension. + + m_state = JPEG_SINK_NON_JPEG_TRAILER; + break; + + case JPEG_SINK_NON_JPEG_TRAILER: + break; + + case JPEG_ERROR: + break; } return true; @@ -367,10 +340,10 @@ private: JSAMPARRAY m_samples; }; -/* Override the standard error method in the IJG JPEG decoder code. */ +// Override the standard error method in the IJG JPEG decoder code. void error_exit(j_common_ptr cinfo) { - /* Return control to the setjmp point. */ + // Return control to the setjmp point. decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err; longjmp(err->setjmp_buffer, -1); } @@ -388,12 +361,12 @@ void skip_input_data(j_decompress_ptr jd, long num_bytes) boolean fill_input_buffer(j_decompress_ptr jd) { // Our decode step always sets things up properly, so if this method is ever - // called, then we have hit the end of the buffer. A return value of FALSE indicates - // that we have no data to supply yet. + // called, then we have hit the end of the buffer. A return value of false + // indicates that we have no data to supply yet. return false; } -void term_source (j_decompress_ptr jd) +void term_source(j_decompress_ptr jd) { decoder_source_mgr *src = (decoder_source_mgr *)jd->src; src->decoder->decoder()->jpegComplete(); @@ -407,21 +380,17 @@ JPEGImageDecoder::~JPEGImageDecoder() { } -// Take the data and store it. void JPEGImageDecoder::setData(SharedBuffer* data, bool allDataReceived) { if (m_failed) return; - // Cache our new data. ImageDecoder::setData(data, allDataReceived); - // Create the JPEG reader. if (!m_reader && !m_failed) m_reader.set(new JPEGImageReader(this)); } -// Whether or not the size information has been decoded yet. bool JPEGImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) @@ -434,6 +403,7 @@ bool JPEGImageDecoder::setSize(unsigned width, unsigned height) { if (!ImageDecoder::setSize(width, height)) return false; + prepareScaleDataIfNecessary(); return true; } @@ -448,23 +418,10 @@ RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer& frame = m_frameBufferCache[0]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - // Decode this frame. - decode(); + decode(false); return &frame; } -// Feed data to the JPEG reader. -void JPEGImageDecoder::decode(bool sizeOnly) -{ - if (m_failed) - return; - - m_failed = !m_reader->decode(m_data->buffer(), sizeOnly); - - if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) - m_reader.clear(); -} - bool JPEGImageDecoder::outputScanlines() { if (m_frameBufferCache.isEmpty()) @@ -531,9 +488,20 @@ void JPEGImageDecoder::jpegComplete() if (m_frameBufferCache.isEmpty()) return; - // Hand back an appropriately sized buffer, even if the image ended up being empty. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - buffer.setStatus(RGBA32Buffer::FrameComplete); + // Hand back an appropriately sized buffer, even if the image ended up being + // empty. + m_frameBufferCache[0].setStatus(RGBA32Buffer::FrameComplete); +} + +void JPEGImageDecoder::decode(bool onlySize) +{ + if (m_failed) + return; + + m_failed = !m_reader->decode(m_data->buffer(), onlySize); + + if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) + m_reader.clear(); } } diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h index d8bfd70..2a95dbe 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h @@ -38,28 +38,24 @@ namespace WebCore { class JPEGImageDecoder : public ImageDecoder { public: JPEGImageDecoder(); - ~JPEGImageDecoder(); + virtual ~JPEGImageDecoder(); + // ImageDecoder virtual String filenameExtension() const { return "jpg"; } - - // Take the data and store it. virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. virtual bool isSizeAvailable(); - virtual bool setSize(unsigned width, unsigned height); - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - virtual bool supportsAlpha() const { return false; } - void decode(bool sizeOnly = false); - bool outputScanlines(); void jpegComplete(); private: + // Decodes the image. If |onlySize| is true, stops decoding after + // calculating the image size. + void decode(bool onlySize); + OwnPtr<JPEGImageReader> m_reader; }; diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index 35c8af0..36f818f 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -52,21 +52,41 @@ const double cInverseGamma = 0.45455; const unsigned long cMaxPNGSize = 1000000UL; // Called if the decoding of the image fails. -static void PNGAPI decodingFailed(png_structp png_ptr, png_const_charp error_msg); +static void PNGAPI decodingFailed(png_structp png, png_const_charp) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed(); + longjmp(png->jmpbuf, 1); +} -// Callbacks given to the read struct. The first is for warnings (we want to treat a particular warning -// as an error, which is why we have to register this callback. -static void PNGAPI decodingWarning(png_structp png_ptr, png_const_charp warning_msg); +// Callbacks given to the read struct. The first is for warnings (we want to +// treat a particular warning as an error, which is why we have to register this +// callback). +static void PNGAPI decodingWarning(png_structp png, png_const_charp warningMsg) +{ + // Mozilla did this, so we will too. + // Convert a tRNS warning to be an error (see + // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 ) + if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24)) + png_error(png, warningMsg); +} // Called when we have obtained the header information (including the size). -static void PNGAPI headerAvailable(png_structp png_ptr, png_infop info_ptr); +static void PNGAPI headerAvailable(png_structp png, png_infop) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); +} // Called when a row is ready. -static void PNGAPI rowAvailable(png_structp png_ptr, png_bytep new_row, - png_uint_32 row_num, int pass); +static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); +} // Called when we have completely finished decoding the image. -static void PNGAPI pngComplete(png_structp png_ptr, png_infop info_ptr); +static void PNGAPI pngComplete(png_structp png, png_infop) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); +} class PNGImageReader { @@ -75,7 +95,7 @@ public: : m_readOffset(0) , m_decodingSizeOnly(false) , m_interlaceBuffer(0) - , m_hasAlpha(0) + , m_hasAlpha(false) , m_hasFinishedDecoding(false) , m_currentBufferSize(0) { @@ -89,10 +109,12 @@ public: close(); } - void close() { + void close() + { if (m_png && m_info) - png_destroy_read_struct(&m_png, &m_info, 0); // Will zero the pointers. - delete []m_interlaceBuffer; + // This will zero the pointers. + png_destroy_read_struct(&m_png, &m_info, 0); + delete[] m_interlaceBuffer; m_interlaceBuffer = 0; m_readOffset = 0; m_hasFinishedDecoding = false; @@ -106,7 +128,7 @@ public: { m_decodingSizeOnly = sizeOnly; - // We need to do the setjmp here. Otherwise bad things will happen + // We need to do the setjmp here. Otherwise bad things will happen. if (setjmp(m_png->jmpbuf)) { close(); return; @@ -134,9 +156,7 @@ public: void setReadOffset(unsigned offset) { m_readOffset = offset; } void setHasAlpha(bool b) { m_hasAlpha = b; } - void createInterlaceBuffer(int size) { - m_interlaceBuffer = new png_byte[size]; - } + void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; } private: unsigned m_readOffset; @@ -157,21 +177,16 @@ PNGImageDecoder::~PNGImageDecoder() { } -// Take the data and store it. void PNGImageDecoder::setData(SharedBuffer* data, bool allDataReceived) { if (m_failed) return; - // Cache our new data. ImageDecoder::setData(data, allDataReceived); - // Create the PNG reader. if (!m_reader && !m_failed) m_reader.set(new PNGImageReader(this)); } - -// Whether or not the size information has been decoded yet. bool PNGImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) @@ -191,41 +206,10 @@ RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer& frame = m_frameBufferCache[0]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) // Decode this frame. - decode(); + decode(false); return &frame; } -// Feed data to the PNG reader. -void PNGImageDecoder::decode(bool sizeOnly) -{ - if (m_failed) - return; - - m_reader->decode(*m_data, sizeOnly); - - if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) - m_reader.clear(); -} - -void decodingFailed(png_structp png, png_const_charp errorMsg) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed(); - longjmp(png->jmpbuf, 1); -} - -void decodingWarning(png_structp png, png_const_charp warningMsg) -{ - // Mozilla did this, so we will too. - // Convert a tRNS warning to be an error (documented in bugzilla.mozilla.org bug #251381) - if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24)) - png_error(png, warningMsg); -} - -void headerAvailable(png_structp png, png_infop info) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); -} - void PNGImageDecoder::decodingFailed() { m_failed = true; @@ -256,14 +240,12 @@ void PNGImageDecoder::headerAvailable() } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; - png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, - &interlaceType, &compressionType, &filterType); + png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); // The options we set here match what Mozilla does. // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. - if (colorType == PNG_COLOR_TYPE_PALETTE || - (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) + if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) png_set_expand(png); png_bytep trns = 0; @@ -276,8 +258,7 @@ void PNGImageDecoder::headerAvailable() if (bitDepth == 16) png_set_strip_16(png); - if (colorType == PNG_COLOR_TYPE_GRAY || - colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); // Deal with gamma and keep it under our control. @@ -288,15 +269,14 @@ void PNGImageDecoder::headerAvailable() png_set_gAMA(png, info, gamma); } png_set_gamma(png, cDefaultGamma, gamma); - } - else + } else png_set_gamma(png, cDefaultGamma, cInverseGamma); // Tell libpng to send us rows for interlaced pngs. if (interlaceType == PNG_INTERLACE_ADAM7) png_set_interlace_handling(png); - // Update our info now + // Update our info now. png_read_update_info(png, info); channels = png_get_channels(png, info); ASSERT(channels == 3 || channels == 4); @@ -310,12 +290,6 @@ void PNGImageDecoder::headerAvailable() } } -void rowAvailable(png_structp png, png_bytep rowBuffer, - png_uint_32 rowIndex, int interlacePass) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); -} - void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) { if (m_frameBufferCache.isEmpty()) @@ -339,36 +313,36 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height()); } - if (rowBuffer == 0) + if (!rowBuffer) return; - /* libpng comments (pasted in here to explain what follows) - * - * this function is called for every row in the image. If the - * image is interlacing, and you turned on the interlace handler, - * this function will be called for every row in every pass. - * Some of these rows will not be changed from the previous pass. - * When the row is not changed, the new_row variable will be NULL. - * The rows and passes are called in order, so you don't really - * need the row_num and pass, but I'm supplying them because it - * may make your life easier. - * - * For the non-NULL rows of interlaced images, you must call - * png_progressive_combine_row() passing in the row and the - * old row. You can call this function for NULL rows (it will - * just return) and for non-interlaced images (it just does the - * memcpy for you) if it will make the code easier. Thus, you - * can just do this for all cases: - * - * png_progressive_combine_row(png_ptr, old_row, new_row); - * - * where old_row is what was displayed for previous rows. Note - * that the first pass (pass == 0 really) will completely cover - * the old row, so the rows do not have to be initialized. After - * the first pass (and only for interlaced images), you will have - * to pass the current row, and the function will combine the - * old row and the new row. - */ + // libpng comments (pasted in here to explain what follows) + /* + * this function is called for every row in the image. If the + * image is interlacing, and you turned on the interlace handler, + * this function will be called for every row in every pass. + * Some of these rows will not be changed from the previous pass. + * When the row is not changed, the new_row variable will be NULL. + * The rows and passes are called in order, so you don't really + * need the row_num and pass, but I'm supplying them because it + * may make your life easier. + * + * For the non-NULL rows of interlaced images, you must call + * png_progressive_combine_row() passing in the row and the + * old row. You can call this function for NULL rows (it will + * just return) and for non-interlaced images (it just does the + * memcpy for you) if it will make the code easier. Thus, you + * can just do this for all cases: + * + * png_progressive_combine_row(png_ptr, old_row, new_row); + * + * where old_row is what was displayed for previous rows. Note + * that the first pass (pass == 0 really) will completely cover + * the old row, so the rows do not have to be initialized. After + * the first pass (and only for interlaced images), you will have + * to pass the current row, and the function will combine the + * old row and the new row. + */ png_structp png = m_reader->pngPtr(); bool hasAlpha = m_reader->hasAlpha(); @@ -378,8 +352,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, if (interlaceBuffer) { row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(png, row, rowBuffer); - } - else + } else row = rowBuffer; // Copy the data into our buffer. @@ -388,7 +361,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, if (destY < 0) return; bool sawAlpha = buffer.hasAlpha(); - for (int x = 0; x < width; x++) { + for (int x = 0; x < width; ++x) { png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels; unsigned alpha = hasAlpha ? pixel[3] : 255; buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha); @@ -399,21 +372,23 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, } } -void pngComplete(png_structp png, png_infop info) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); -} - void PNGImageDecoder::pngComplete() { m_reader->setComplete(); - if (m_frameBufferCache.isEmpty()) + if (!m_frameBufferCache.isEmpty()) + m_frameBufferCache.first().setStatus(RGBA32Buffer::FrameComplete); +} + +void PNGImageDecoder::decode(bool onlySize) +{ + if (m_failed) return; - // Hand back an appropriately sized buffer, even if the image ended up being empty. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - buffer.setStatus(RGBA32Buffer::FrameComplete); + m_reader->decode(*m_data, onlySize); + + if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) + m_reader.clear(); } } // namespace WebCore diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.h b/WebCore/platform/image-decoders/png/PNGImageDecoder.h index 07a0b3a..ba0e19a 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.h +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.h @@ -37,20 +37,14 @@ namespace WebCore { class PNGImageDecoder : public ImageDecoder { public: PNGImageDecoder(); - ~PNGImageDecoder(); + virtual ~PNGImageDecoder(); + // ImageDecoder virtual String filenameExtension() const { return "png"; } - - // Take the data and store it. virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. virtual bool isSizeAvailable(); - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - void decode(bool sizeOnly = false); - // Callbacks from libpng void decodingFailed(); void headerAvailable(); @@ -58,6 +52,10 @@ namespace WebCore { void pngComplete(); private: + // Decodes the image. If |onlySize| is true, stops decoding after + // calculating the image size. + void decode(bool onlySize); + OwnPtr<PNGImageReader> m_reader; }; diff --git a/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp b/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp index d3218cd..b2e5e17 100644 --- a/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp +++ b/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp @@ -42,12 +42,17 @@ RGBA32Buffer::RGBA32Buffer() { } -// The image must not have format 8888 pre multiplied... -void RGBA32Buffer::setDecodedImage(const QImage& image) +RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) { - m_image = image; - m_size = image.size(); - m_hasAlpha = image.hasAlphaChannel(); + if (this == &other) + return *this; + + copyBitmapData(other); + setRect(other.rect()); + setStatus(other.status()); + setDuration(other.duration()); + setDisposalMethod(other.disposalMethod()); + return *this; } void RGBA32Buffer::clear() @@ -115,17 +120,12 @@ void RGBA32Buffer::setStatus(FrameStatus status) m_status = status; } -RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) +// The image must not have format 8888 pre multiplied... +void RGBA32Buffer::setDecodedImage(const QImage& image) { - if (this == &other) - return *this; - - copyBitmapData(other); - setRect(other.rect()); - setStatus(other.status()); - setDuration(other.duration()); - setDisposalMethod(other.disposalMethod()); - return *this; + m_image = image; + m_size = image.size(); + m_hasAlpha = image.hasAlphaChannel(); } int RGBA32Buffer::width() const diff --git a/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp b/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp index 543eca8..928524a 100644 --- a/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp +++ b/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp @@ -39,6 +39,22 @@ RGBA32Buffer::RGBA32Buffer() { } +RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) +{ + if (this == &other) + return *this; + + m_bitmap = other.m_bitmap; + // Keep the pixels locked since we will be writing directly into the + // bitmap throughout this object's lifetime. + m_bitmap.lockPixels(); + setRect(other.rect()); + setStatus(other.status()); + setDuration(other.duration()); + setDisposalMethod(other.disposalMethod()); + return *this; +} + void RGBA32Buffer::clear() { m_bitmap.reset(); @@ -105,22 +121,6 @@ void RGBA32Buffer::setStatus(FrameStatus status) m_bitmap.setDataComplete(); // Tell the bitmap it's done. } -RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) -{ - if (this == &other) - return *this; - - m_bitmap = other.m_bitmap; - // Keep the pixels locked since we will be writing directly into the - // bitmap throughout this object's lifetime. - m_bitmap.lockPixels(); - setRect(other.rect()); - setStatus(other.status()); - setDuration(other.duration()); - setDisposalMethod(other.disposalMethod()); - return *this; -} - int RGBA32Buffer::width() const { return m_bitmap.width(); diff --git a/WebCore/platform/mac/PasteboardMac.mm b/WebCore/platform/mac/PasteboardMac.mm index 690637a..086b272 100644 --- a/WebCore/platform/mac/PasteboardMac.mm +++ b/WebCore/platform/mac/PasteboardMac.mm @@ -300,9 +300,7 @@ void Pasteboard::writeImage(Node* node, const KURL& url, const String& title) ASSERT(node->renderer() && node->renderer()->isImage()); RenderImage* renderer = toRenderImage(node->renderer()); CachedImage* cachedImage = renderer->cachedImage(); - ASSERT(cachedImage); - - if (cachedImage->errorOccurred()) + if (!cachedImage || cachedImage->errorOccurred()) return; NSArray* types = writableTypesForImage(); diff --git a/WebCore/platform/network/brew/DNSBrew.cpp b/WebCore/platform/network/brew/DNSBrew.cpp new file mode 100644 index 0000000..ed1767a --- /dev/null +++ b/WebCore/platform/network/brew/DNSBrew.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Company 100, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE COMPUTER, INC. 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. + */ + +#include "config.h" +#include "DNS.h" + +#include "NotImplemented.h" + +namespace WebCore { + +void prefetchDNS(const String& hostname) +{ + // DNS prefetching is not implemented because the maximum number of UDP + // sockets is quite small in most BREW devices. + notImplemented(); +} + +} diff --git a/WebCore/platform/network/chromium/CookieJarChromium.cpp b/WebCore/platform/network/chromium/CookieJarChromium.cpp index 41cf331..e17816a 100644 --- a/WebCore/platform/network/chromium/CookieJarChromium.cpp +++ b/WebCore/platform/network/chromium/CookieJarChromium.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Google Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -39,46 +39,32 @@ namespace WebCore { void setCookies(Document* document, const KURL& url, const String& value) { - ChromiumBridge::setCookies(url, document->firstPartyForCookies(), value); + ChromiumBridge::setCookies(document, url, value); } String cookies(const Document* document, const KURL& url) { - return ChromiumBridge::cookies(url, document->firstPartyForCookies()); + return ChromiumBridge::cookies(document, url); } String cookieRequestHeaderFieldValue(const Document* document, const KURL& url) { - // FIXME: move in ChromiumBridge? - Vector<Cookie> cookies; - getRawCookies(document, url, cookies); - String cookieLine; - // FIXME: Set $Version=v; - for (size_t i = 0; i < cookies.size(); i++) { - Cookie cookie = cookies[i]; - if (i > 0) - cookieLine += "; "; - if (!cookie.name.isEmpty()) - cookieLine += cookie.name + "="; - cookieLine += cookie.value; - // FIXME: set $Path, $Domain, ... - } - return cookieLine; + return ChromiumBridge::cookieRequestHeaderFieldValue(document, url); } bool cookiesEnabled(const Document* document) { - return ChromiumBridge::cookiesEnabled(document->cookieURL(), document->firstPartyForCookies()); + return ChromiumBridge::cookiesEnabled(document); } bool getRawCookies(const Document* document, const KURL& url, Vector<Cookie>& rawCookies) { - return ChromiumBridge::rawCookies(url, document->firstPartyForCookies(), &rawCookies); + return ChromiumBridge::rawCookies(document, url, rawCookies); } -void deleteCookie(const Document*, const KURL& url, const String& cookieName) +void deleteCookie(const Document* document, const KURL& url, const String& cookieName) { - return ChromiumBridge::deleteCookie(url, cookieName); + return ChromiumBridge::deleteCookie(document, url, cookieName); } } // namespace WebCore diff --git a/WebCore/platform/network/chromium/ResourceRequest.cpp b/WebCore/platform/network/chromium/ResourceRequest.cpp index 5b27c1b..016bd34 100644 --- a/WebCore/platform/network/chromium/ResourceRequest.cpp +++ b/WebCore/platform/network/chromium/ResourceRequest.cpp @@ -31,7 +31,10 @@ namespace WebCore { // This is used by the loader to control the number of issued parallel load requests. unsigned initializeMaximumHTTPConnectionCountPerHost() { - return 6; + // The chromium network stack already handles limiting the number of + // parallel requests per host, so there's no need to do it here. Therefore, + // this is set to a high value that should never be hit in practice. + return 10000; } } // namespace WebCore diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp index ee8e7aa..4c59d34 100644 --- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -380,6 +380,7 @@ static gboolean parseDataUrl(gpointer callback_data) String charset = extractCharsetFromMediaType(mediaType); ResourceResponse response; + response.setURL(handle->request().url()); response.setMimeType(mimeType); if (isBase64) { @@ -387,7 +388,10 @@ static gboolean parseDataUrl(gpointer callback_data) response.setTextEncodingName(charset); client->didReceiveResponse(handle, response); - if (d->m_cancelled) + // The load may be cancelled, and the client may be destroyed + // by any of the client reporting calls, so we check, and bail + // out in either of those cases. + if (!handle->client() || d->m_cancelled) return false; // Use the GLib Base64, since WebCore's decoder isn't @@ -404,16 +408,16 @@ static gboolean parseDataUrl(gpointer callback_data) response.setTextEncodingName("UTF-16"); client->didReceiveResponse(handle, response); - if (d->m_cancelled) + if (!handle->client() || d->m_cancelled) return false; if (data.length() > 0) client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0); - - if (d->m_cancelled) - return false; } + if (!handle->client() || d->m_cancelled) + return false; + client->didFinishLoading(handle); return false; @@ -565,6 +569,11 @@ static bool startHttp(ResourceHandle* handle) // balanced by a deref() in finishedCallback, which should always run handle->ref(); + // Make sure we have an Accept header for subresources; some sites + // want this to serve some of their subresources + if (!soup_message_headers_get_one(d->m_msg->request_headers, "Accept")) + soup_message_headers_append(d->m_msg->request_headers, "Accept", "*/*"); + // Balanced in ResourceHandleInternal's destructor; we need to // keep our own ref, because after queueing the message, the // session owns the initial reference. diff --git a/WebCore/platform/qt/PasteboardQt.cpp b/WebCore/platform/qt/PasteboardQt.cpp index 44c9eec..70ec546 100644 --- a/WebCore/platform/qt/PasteboardQt.cpp +++ b/WebCore/platform/qt/PasteboardQt.cpp @@ -151,7 +151,8 @@ void Pasteboard::writeImage(Node* node, const KURL&, const String&) #ifndef QT_NO_CLIPBOARD CachedImage* cachedImage = toRenderImage(node->renderer())->cachedImage(); - ASSERT(cachedImage); + if (!cachedImage || cachedImage->errorOccurred()) + return; Image* image = cachedImage->image(); ASSERT(image); diff --git a/WebCore/platform/text/AtomicString.cpp b/WebCore/platform/text/AtomicString.cpp index 64c03cb..19821c0 100644 --- a/WebCore/platform/text/AtomicString.cpp +++ b/WebCore/platform/text/AtomicString.cpp @@ -248,7 +248,7 @@ PassRefPtr<StringImpl> AtomicString::add(const JSC::Identifier& identifier) return 0; UString::Rep* string = identifier.ustring().rep(); - unsigned length = string->size(); + unsigned length = string->length(); if (!length) return StringImpl::empty(); @@ -265,7 +265,7 @@ PassRefPtr<StringImpl> AtomicString::add(const JSC::UString& ustring) return 0; UString::Rep* string = ustring.rep(); - unsigned length = string->size(); + unsigned length = string->length(); if (!length) return StringImpl::empty(); @@ -282,7 +282,7 @@ AtomicStringImpl* AtomicString::find(const JSC::Identifier& identifier) return 0; UString::Rep* string = identifier.ustring().rep(); - unsigned length = string->size(); + unsigned length = string->length(); if (!length) return static_cast<AtomicStringImpl*>(StringImpl::empty()); diff --git a/WebCore/platform/text/StringImpl.cpp b/WebCore/platform/text/StringImpl.cpp index db6152d..3704c4e 100644 --- a/WebCore/platform/text/StringImpl.cpp +++ b/WebCore/platform/text/StringImpl.cpp @@ -52,36 +52,12 @@ static inline UChar* newUCharVector(unsigned n) return static_cast<UChar*>(fastMalloc(sizeof(UChar) * n)); } -static inline void deleteUCharVector(const UChar* p) -{ - fastFree(const_cast<UChar*>(p)); -} - -// Some of the factory methods create buffers using fastMalloc. -// We must ensure that all allocations of StringImpl are allocated using -// fastMalloc so that we don't have mis-matched frees. We accomplish -// this by overriding the new and delete operators. -void* StringImpl::operator new(size_t size, void* address) -{ - if (address) - return address; // Allocating using an internal buffer - return fastMalloc(size); -} - -void* StringImpl::operator new(size_t size) -{ - return fastMalloc(size); -} - -void StringImpl::operator delete(void* address) -{ - fastFree(address); -} - // This constructor is used only to create the empty string. StringImpl::StringImpl() : m_data(0) + , m_sharedBuffer(0) , m_length(0) + , m_refCountAndFlags(s_refCountIncrement | BufferInternal) , m_hash(0) { // Ensure that the hash is computed so that AtomicStringHash can call existingHash() @@ -90,35 +66,55 @@ StringImpl::StringImpl() hash(); } +inline StringImpl::StringImpl(unsigned length) + : m_data(reinterpret_cast<const UChar*>(this + 1)) + , m_sharedBuffer(0) + , m_length(length) + , m_refCountAndFlags(s_refCountIncrement | BufferInternal) + , m_hash(0) +{ + ASSERT(m_data); + ASSERT(m_length); +} + inline StringImpl::StringImpl(const UChar* characters, unsigned length) : m_data(characters) + , m_sharedBuffer(0) , m_length(length) + , m_refCountAndFlags(s_refCountIncrement | BufferOwned) , m_hash(0) { - ASSERT(characters); - ASSERT(length); - ASSERT(!bufferIsInternal()); + ASSERT(m_data); + ASSERT(m_length); } -inline StringImpl::StringImpl(unsigned length) - : m_data(reinterpret_cast<const UChar*>(this + 1)) +inline StringImpl::StringImpl(const UChar* characters, unsigned length, PassRefPtr<SharedUChar> sharedBuffer) + : m_data(characters) + , m_sharedBuffer(sharedBuffer.releaseRef()) , m_length(length) + , m_refCountAndFlags(s_refCountIncrement | BufferShared) , m_hash(0) { - ASSERT(length); - ASSERT(bufferIsInternal()); + ASSERT(m_data); + ASSERT(m_length); } StringImpl::~StringImpl() { if (inTable()) AtomicString::remove(this); - if (!bufferIsInternal()) { - SharedUChar* sharedBuffer = m_sharedBufferAndFlags.get(); - if (sharedBuffer) - sharedBuffer->deref(); - else - deleteUCharVector(m_data); + + BufferOwnership ownership = bufferOwnership(); + if (ownership != BufferInternal) { + if (ownership == BufferOwned) { + ASSERT(!m_sharedBuffer); + ASSERT(m_data); + fastFree(const_cast<UChar*>(m_data)); + } else { + ASSERT(ownership == BufferShared); + ASSERT(m_sharedBuffer); + m_sharedBuffer->deref(); + } } } @@ -976,13 +972,8 @@ PassRefPtr<StringImpl> StringImpl::create(const char* string) #if USE(JSC) PassRefPtr<StringImpl> StringImpl::create(const JSC::UString& str) { - SharedUChar* sharedBuffer = const_cast<JSC::UString*>(&str)->rep()->sharedBuffer(); - if (sharedBuffer) { - PassRefPtr<StringImpl> impl = adoptRef(new StringImpl(str.data(), str.size())); - sharedBuffer->ref(); - impl->m_sharedBufferAndFlags.set(sharedBuffer); - return impl; - } + if (SharedUChar* sharedBuffer = const_cast<JSC::UString*>(&str)->rep()->sharedBuffer()) + return adoptRef(new StringImpl(str.data(), str.size(), sharedBuffer)); return StringImpl::create(str.data(), str.size()); } @@ -1007,7 +998,7 @@ PassRefPtr<StringImpl> StringImpl::createWithTerminatingNullCharacter(const Stri data[length] = 0; terminatedString->m_length--; terminatedString->m_hash = string.m_hash; - terminatedString->m_sharedBufferAndFlags.setFlag(HasTerminatingNullCharacter); + terminatedString->m_refCountAndFlags |= s_refCountFlagHasTerminatingNullCharacter; return terminatedString.release(); } @@ -1021,12 +1012,8 @@ PassRefPtr<StringImpl> StringImpl::threadsafeCopy() const PassRefPtr<StringImpl> StringImpl::crossThreadString() { - SharedUChar* shared = sharedBuffer(); - if (shared) { - RefPtr<StringImpl> impl = adoptRef(new StringImpl(m_data, m_length)); - impl->m_sharedBufferAndFlags.set(shared->crossThreadCopy().releaseRef()); - return impl.release(); - } + if (SharedUChar* sharedBuffer = this->sharedBuffer()) + return adoptRef(new StringImpl(m_data, m_length, sharedBuffer->crossThreadCopy())); // If no shared buffer is available, create a copy. return threadsafeCopy(); @@ -1034,13 +1021,23 @@ PassRefPtr<StringImpl> StringImpl::crossThreadString() StringImpl::SharedUChar* StringImpl::sharedBuffer() { - if (m_length < minLengthToShare || bufferIsInternal()) + if (m_length < minLengthToShare) return 0; - if (!m_sharedBufferAndFlags.get()) - m_sharedBufferAndFlags.set(SharedUChar::create(new OwnFastMallocPtr<UChar>(const_cast<UChar*>(m_data))).releaseRef()); - return m_sharedBufferAndFlags.get(); -} + BufferOwnership ownership = bufferOwnership(); + if (ownership == BufferInternal) + return 0; + + if (ownership == BufferOwned) { + ASSERT(!m_sharedBuffer); + m_sharedBuffer = SharedUChar::create(new OwnFastMallocPtr<UChar>(const_cast<UChar*>(m_data))).releaseRef(); + m_refCountAndFlags = (m_refCountAndFlags & ~s_refCountMaskBufferOwnership) | BufferShared; + } + + ASSERT(bufferOwnership() == BufferShared); + ASSERT(m_sharedBuffer); + return m_sharedBuffer; +} } // namespace WebCore diff --git a/WebCore/platform/text/StringImpl.h b/WebCore/platform/text/StringImpl.h index 21f936d..65848bb 100644 --- a/WebCore/platform/text/StringImpl.h +++ b/WebCore/platform/text/StringImpl.h @@ -26,8 +26,8 @@ #include <limits.h> #include <wtf/ASCIICType.h> #include <wtf/CrossThreadRefCounted.h> +#include <wtf/Noncopyable.h> #include <wtf/OwnFastMallocPtr.h> -#include <wtf/PtrAndFlags.h> #include <wtf/RefCounted.h> #include <wtf/StringHashFunctions.h> #include <wtf/Vector.h> @@ -58,25 +58,33 @@ enum TextCaseSensitivity { TextCaseSensitive, TextCaseInsensitive }; typedef bool (*CharacterMatchFunctionPtr)(UChar); -class StringImpl : public RefCounted<StringImpl> { +class StringImpl : public Noncopyable { friend struct CStringTranslator; friend struct HashAndCharactersTranslator; friend struct UCharBufferTranslator; private: friend class ThreadGlobalData; - StringImpl(); - - // This constructor adopts the UChar* without copying the buffer. - StringImpl(const UChar*, unsigned length); - // This constructor assumes that 'this' was allocated with a UChar buffer of size 'length' at the end. + enum BufferOwnership { + BufferInternal, + BufferOwned, + BufferShared, + }; + + typedef CrossThreadRefCounted<OwnFastMallocPtr<UChar> > SharedUChar; + + // Used to create the empty string (""), automatically hashes. + StringImpl(); + // Create a StringImpl with internal storage (BufferInternal) StringImpl(unsigned length); + // Create a StringImpl adopting ownership of the provided buffer (BufferOwned) + StringImpl(const UChar*, unsigned length); + // Create a StringImpl using a shared buffer (BufferShared) + StringImpl(const UChar*, unsigned length, PassRefPtr<SharedUChar>); // For use only by AtomicString's XXXTranslator helpers. void setHash(unsigned hash) { ASSERT(!m_hash); m_hash = hash; } - typedef CrossThreadRefCounted<OwnFastMallocPtr<UChar> > SharedUChar; - public: ~StringImpl(); @@ -99,16 +107,20 @@ public: const UChar* characters() { return m_data; } unsigned length() { return m_length; } - bool hasTerminatingNullCharacter() const { return m_sharedBufferAndFlags.isFlagSet(HasTerminatingNullCharacter); } + bool hasTerminatingNullCharacter() const { return m_refCountAndFlags & s_refCountFlagHasTerminatingNullCharacter; } - bool inTable() const { return m_sharedBufferAndFlags.isFlagSet(InTable); } - void setInTable() { return m_sharedBufferAndFlags.setFlag(InTable); } + bool inTable() const { return m_refCountAndFlags & s_refCountFlagInTable; } + void setInTable() { m_refCountAndFlags |= s_refCountFlagInTable; } unsigned hash() { if (m_hash == 0) m_hash = computeHash(m_data, m_length); return m_hash; } unsigned existingHash() const { ASSERT(m_hash); return m_hash; } inline static unsigned computeHash(const UChar* data, unsigned length) { return WTF::stringHash(data, length); } inline static unsigned computeHash(const char* data) { return WTF::stringHash(data); } - + + StringImpl* ref() { m_refCountAndFlags += s_refCountIncrement; return this; } + ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & s_refCountMask)) delete this; } + ALWAYS_INLINE bool hasOneRef() const { return (m_refCountAndFlags & s_refCountMask) == s_refCountIncrement; } + // Returns a StringImpl suitable for use on another thread. PassRefPtr<StringImpl> crossThreadString(); // Makes a deep copy. Helpful only if you need to use a String on another thread @@ -178,31 +190,27 @@ public: operator NSString*(); #endif - void operator delete(void*); - private: - // Allocation from a custom buffer is only allowed internally to avoid - // mismatched allocators. Callers should use create(). - void* operator new(size_t size); - void* operator new(size_t size, void* address); + using Noncopyable::operator new; + void* operator new(size_t, void* inPlace) { ASSERT(inPlace); return inPlace; } static PassRefPtr<StringImpl> createStrippingNullCharactersSlowCase(const UChar*, unsigned length); // The StringImpl struct and its data may be allocated within a single heap block. // In this case, the m_data pointer is an "internal buffer", and does not need to be deallocated. - bool bufferIsInternal() { return m_data == reinterpret_cast<const UChar*>(this + 1); } + BufferOwnership bufferOwnership() const { return static_cast<BufferOwnership>(m_refCountAndFlags & s_refCountMaskBufferOwnership); } - enum StringImplFlags { - HasTerminatingNullCharacter, - InTable, - }; + static const unsigned s_refCountMask = 0xFFFFFFF0; + static const unsigned s_refCountIncrement = 0x10; + static const unsigned s_refCountFlagHasTerminatingNullCharacter = 0x8; + static const unsigned s_refCountFlagInTable = 0x4; + static const unsigned s_refCountMaskBufferOwnership = 0x3; const UChar* m_data; + SharedUChar* m_sharedBuffer; unsigned m_length; + unsigned m_refCountAndFlags; mutable unsigned m_hash; - PtrAndFlags<SharedUChar, StringImplFlags> m_sharedBufferAndFlags; - // There is a fictitious variable-length UChar array at the end, which is used - // as the internal buffer by the createUninitialized and create methods. }; bool equal(StringImpl*, StringImpl*); diff --git a/WebCore/platform/win/PasteboardWin.cpp b/WebCore/platform/win/PasteboardWin.cpp index d09769a..ffafd02 100644 --- a/WebCore/platform/win/PasteboardWin.cpp +++ b/WebCore/platform/win/PasteboardWin.cpp @@ -205,7 +205,8 @@ void Pasteboard::writeImage(Node* node, const KURL&, const String&) ASSERT(node && node->renderer() && node->renderer()->isImage()); RenderImage* renderer = toRenderImage(node->renderer()); CachedImage* cachedImage = renderer->cachedImage(); - ASSERT(cachedImage); + if (!cachedImage || cachedImage->errorOccurred()) + return; Image* image = cachedImage->image(); ASSERT(image); |