diff options
Diffstat (limited to 'WebCore/platform/graphics')
32 files changed, 905 insertions, 230 deletions
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; |