diff options
Diffstat (limited to 'WebCore/platform/graphics')
38 files changed, 912 insertions, 462 deletions
diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index b1d1ef9..5b62b2c 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -334,6 +334,11 @@ namespace WebCore { void addPath(const Path&); void clip(const Path&); + + // This clip function is used only by <canvas> code. It allows + // implementations to handle clipping on the canvas differently since + // the disipline is different. + void canvasClip(const Path&); void clipOut(const Path&); void scale(const FloatSize&); diff --git a/WebCore/platform/graphics/GraphicsContext3D.h b/WebCore/platform/graphics/GraphicsContext3D.h index 5223e05..07ec04d 100644 --- a/WebCore/platform/graphics/GraphicsContext3D.h +++ b/WebCore/platform/graphics/GraphicsContext3D.h @@ -29,6 +29,7 @@ #include "PlatformString.h" #include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> #if PLATFORM(MAC) #include <OpenGL/OpenGL.h> @@ -45,6 +46,7 @@ const Platform3DObject NullPlatform3DObject = 0; #endif namespace WebCore { + class CanvasActiveInfo; class CanvasArray; class CanvasBuffer; class CanvasUnsignedByteArray; @@ -61,17 +63,23 @@ namespace WebCore { class HTMLVideoElement; class ImageData; class WebKitCSSMatrix; - + + struct ActiveInfo { + String name; + unsigned type; + int size; + }; + // FIXME: ideally this would be used on all platforms. #if PLATFORM(CHROMIUM) class GraphicsContext3DInternal; #endif - class GraphicsContext3D : Noncopyable { + class GraphicsContext3D : public Noncopyable { public: enum ShaderType { FRAGMENT_SHADER, VERTEX_SHADER }; - GraphicsContext3D(); + static PassOwnPtr<GraphicsContext3D> create(); virtual ~GraphicsContext3D(); #if PLATFORM(MAC) @@ -85,7 +93,6 @@ namespace WebCore { Platform3DObject platformTexture() const { return NullPlatform3DObject; } #endif void checkError() const; - void makeContextCurrent(); // Helper to return the size in bytes of OpenGL data types @@ -141,6 +148,9 @@ namespace WebCore { void frontFace(unsigned long mode); void generateMipmap(unsigned long target); + bool getActiveAttrib(CanvasProgram* program, unsigned long index, ActiveInfo&); + bool getActiveUniform(CanvasProgram* program, unsigned long index, ActiveInfo&); + int getAttribLocation(CanvasProgram*, const String& name); bool getBoolean(unsigned long pname); @@ -204,8 +214,7 @@ namespace WebCore { void pixelStorei(unsigned long pname, long param); void polygonOffset(double factor, double units); - // TBD - //void readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type, void* pixels); + PassRefPtr<CanvasArray> readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type); void releaseShaderCompiler(); void renderbufferStorage(unsigned long target, unsigned long internalformat, unsigned long width, unsigned long height); @@ -311,6 +320,8 @@ namespace WebCore { void deleteTexture(unsigned); private: + GraphicsContext3D(); + int m_currentWidth, m_currentHeight; #if PLATFORM(MAC) diff --git a/WebCore/platform/graphics/GraphicsLayer.cpp b/WebCore/platform/graphics/GraphicsLayer.cpp index b375bd3..c8582bb 100644 --- a/WebCore/platform/graphics/GraphicsLayer.cpp +++ b/WebCore/platform/graphics/GraphicsLayer.cpp @@ -59,9 +59,7 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) : m_client(client) , m_anchorPoint(0.5f, 0.5f, 0) , m_opacity(1) -#ifndef NDEBUG , m_zPosition(0) -#endif , m_backgroundColorSet(false) , m_contentsOpaque(false) , m_preserves3D(false) @@ -74,9 +72,7 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) , m_contentsOrientation(CompositingCoordinatesTopDown) , m_parent(0) , m_maskLayer(0) -#ifndef NDEBUG , m_repaintCount(0) -#endif { } @@ -86,6 +82,16 @@ GraphicsLayer::~GraphicsLayer() removeFromParent(); } +bool GraphicsLayer::hasAncestor(GraphicsLayer* ancestor) const +{ + for (GraphicsLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr == ancestor) + return true; + } + + return false; +} + void GraphicsLayer::addChild(GraphicsLayer* childLayer) { ASSERT(childLayer != this); @@ -219,7 +225,6 @@ void GraphicsLayer::resumeAnimations() { } -#ifndef NDEBUG void GraphicsLayer::updateDebugIndicators() { if (GraphicsLayer::showDebugBorders()) { @@ -241,7 +246,6 @@ void GraphicsLayer::setZPosition(float position) { m_zPosition = position; } -#endif float GraphicsLayer::accumulatedOpacity() const { diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h index 2924073..85eace0 100644 --- a/WebCore/platform/graphics/GraphicsLayer.h +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -172,6 +172,9 @@ public: GraphicsLayer* parent() const { return m_parent; }; void setParent(GraphicsLayer* layer) { m_parent = layer; } // Internal use only. + // Returns true if the layer has the given layer as an ancestor (excluding self). + bool hasAncestor(GraphicsLayer*) const; + const Vector<GraphicsLayer*>& children() const { return m_children; } // Add child layers. If the child is already parented, it will be removed from its old parent. @@ -273,10 +276,8 @@ public: void dumpLayer(TextStream&, int indent = 0) const; -#ifndef NDEBUG int repaintCount() const { return m_repaintCount; } int incrementRepaintCount() { return ++m_repaintCount; } -#endif // Report whether the underlying compositing system uses a top-down // or a bottom-up coordinate system. @@ -291,7 +292,6 @@ public: virtual void setContentsOrientation(CompositingCoordinatesOrientation orientation) { m_contentsOrientation = orientation; } CompositingCoordinatesOrientation contentsOrientation() const { return m_contentsOrientation; } -#ifndef NDEBUG static bool showDebugBorders(); static bool showRepaintCounter(); @@ -302,7 +302,6 @@ public: // z-position is the z-equivalent of position(). It's only used for debugging purposes. virtual float zPosition() const { return m_zPosition; } virtual void setZPosition(float); -#endif virtual void distributeOpacity(float); virtual float accumulatedOpacity() const; @@ -339,9 +338,7 @@ protected: Color m_backgroundColor; float m_opacity; -#ifndef NDEBUG float m_zPosition; -#endif bool m_backgroundColorSet : 1; bool m_contentsOpaque : 1; @@ -362,9 +359,7 @@ protected: IntRect m_contentsRect; -#ifndef NDEBUG int m_repaintCount; -#endif }; diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index de8afb3..8741c5e 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -931,6 +931,11 @@ void GraphicsContext::clip(const Path& path) m_data->clip(path); } +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + void GraphicsContext::clipOut(const Path& path) { if (paintingDisabled()) diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index 1b843e4..1350bd3 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -849,6 +849,11 @@ void GraphicsContext::clip(const Path& path) m_data->clip(path); } +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + void GraphicsContext::clipOut(const Path& path) { if (paintingDisabled()) diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index dca0efb..a4526a8 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -57,6 +57,23 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +static bool isCanvasMultiLayered(SkCanvas* canvas) +{ + SkCanvas::LayerIter layerIterator(canvas, false); + layerIterator.next(); + return !layerIterator.done(); +} + +static bool adjustTextRenderMode(SkPaint* paint, bool isCanvasMultiLayered) +{ + // Our layers only have a single alpha channel. This means that subpixel + // rendered text cannot be compositied correctly when the layer is + // collapsed. Therefore, subpixel text is disabled when we are drawing + // onto a layer. + if (isCanvasMultiLayered) + paint->setLCDRenderText(false); +} + void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { @@ -84,12 +101,14 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, SkCanvas* canvas = gc->platformContext()->canvas(); int textMode = gc->platformContext()->getTextDrawingMode(); + bool haveMultipleLayers = isCanvasMultiLayered(canvas); // We draw text up to two times (once for fill, once for stroke). if (textMode & cTextFill) { SkPaint paint; gc->platformContext()->setupPaintForFilling(&paint); font->platformData().setupPaint(&paint); + adjustTextRenderMode(&paint, haveMultipleLayers); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setColor(gc->fillColor().rgb()); canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); @@ -102,6 +121,7 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); font->platformData().setupPaint(&paint); + adjustTextRenderMode(&paint, haveMultipleLayers); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setColor(gc->strokeColor().rgb()); @@ -472,15 +492,18 @@ void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, } TextRunWalker walker(run, point.x(), this); + bool haveMultipleLayers = isCanvasMultiLayered(canvas); while (walker.nextScriptRun()) { if (fill) { walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); + adjustTextRenderMode(&fillPaint, haveMultipleLayers); canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint); } if (stroke) { walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); + adjustTextRenderMode(&strokePaint, haveMultipleLayers); canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint); } } @@ -645,8 +668,6 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run, if (toX == -1 && !to) toX = rightEdge; - else if (!walker.rtl()) - toX += truncateFixedPointToInteger(toAdvance); ASSERT(fromX != -1 && toX != -1); diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/WebCore/platform/graphics/filters/FEColorMatrix.cpp index 1e2e552..a2ed9bd 100644 --- a/WebCore/platform/graphics/filters/FEColorMatrix.cpp +++ b/WebCore/platform/graphics/filters/FEColorMatrix.cpp @@ -30,6 +30,7 @@ #include "GraphicsContext.h" #include "ImageData.h" #include <math.h> +#include <wtf/MathExtras.h> namespace WebCore { @@ -92,8 +93,8 @@ inline void saturate(double& red, double& green, double& blue, const float& s) inline void huerotate(double& red, double& green, double& blue, const float& hue) { - double cosHue = cos(hue * M_PI / 180); - double sinHue = sin(hue * M_PI / 180); + double cosHue = cos(hue * piDouble / 180); + double sinHue = sin(hue * piDouble / 180); double r = red * (0.213 + cosHue * 0.787 - sinHue * 0.213) + green * (0.715 - cosHue * 0.715 - sinHue * 0.715) + blue * (0.072 - cosHue * 0.072 + sinHue * 0.928); diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp index 43e5edd..0d76d8d 100644 --- a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp @@ -92,11 +92,11 @@ void FEComponentTransfer::setAlphaFunction(const ComponentTransferFunction& func m_alphaFunc = func; } -void identity(unsigned char*, const ComponentTransferFunction&) +static void identity(unsigned char*, const ComponentTransferFunction&) { } -void table(unsigned char* values, const ComponentTransferFunction& transferFunction) +static void table(unsigned char* values, const ComponentTransferFunction& transferFunction) { const Vector<float>& tableValues = transferFunction.tableValues; unsigned n = tableValues.size(); @@ -113,7 +113,7 @@ void table(unsigned char* values, const ComponentTransferFunction& transferFunct } } -void discrete(unsigned char* values, const ComponentTransferFunction& transferFunction) +static void discrete(unsigned char* values, const ComponentTransferFunction& transferFunction) { const Vector<float>& tableValues = transferFunction.tableValues; unsigned n = tableValues.size(); @@ -128,7 +128,7 @@ void discrete(unsigned char* values, const ComponentTransferFunction& transferFu } } -void linear(unsigned char* values, const ComponentTransferFunction& transferFunction) +static void linear(unsigned char* values, const ComponentTransferFunction& transferFunction) { for (unsigned i = 0; i < 256; ++i) { double val = transferFunction.slope * i + 255 * transferFunction.intercept; @@ -137,7 +137,7 @@ void linear(unsigned char* values, const ComponentTransferFunction& transferFunc } } -void gamma(unsigned char* values, const ComponentTransferFunction& transferFunction) +static void gamma(unsigned char* values, const ComponentTransferFunction& transferFunction) { for (unsigned i = 0; i < 256; ++i) { double val = 255.0 * (transferFunction.amplitude * pow((i / 255.0), transferFunction.exponent) + transferFunction.offset); diff --git a/WebCore/platform/graphics/filters/FEGaussianBlur.cpp b/WebCore/platform/graphics/filters/FEGaussianBlur.cpp new file mode 100644 index 0000000..f480f10 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEGaussianBlur.cpp @@ -0,0 +1,140 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@webkit.org> + 2009 Dirk Schulze <krit@webkit.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(FILTERS) +#include "FEGaussianBlur.h" + +#include "CanvasPixelArray.h" +#include "Filter.h" +#include "GraphicsContext.h" +#include "ImageData.h" +#include <math.h> +#include <wtf/MathExtras.h> + +namespace WebCore { + +FEGaussianBlur::FEGaussianBlur(FilterEffect* in, const float& x, const float& y) + : FilterEffect() + , m_in(in) + , m_x(x) + , m_y(y) +{ +} + +PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(FilterEffect* in, const float& x, const float& y) +{ + return adoptRef(new FEGaussianBlur(in, x, y)); +} + +float FEGaussianBlur::stdDeviationX() const +{ + return m_x; +} + +void FEGaussianBlur::setStdDeviationX(float x) +{ + m_x = x; +} + +float FEGaussianBlur::stdDeviationY() const +{ + return m_y; +} + +void FEGaussianBlur::setStdDeviationY(float y) +{ + m_y = y; +} + +static void boxBlur(CanvasPixelArray*& srcPixelArray, CanvasPixelArray*& dstPixelArray, + unsigned dx, int stride, int strideLine, int effectWidth, int effectHeight, bool alphaImage) +{ + int dxLeft = dx / 2; + int dxRight = dx - dxLeft; + + for (int y = 0; y < effectHeight; ++y) { + int line = y * strideLine; + for (int channel = 3; channel >= 0; --channel) { + int sum = 0; + // Fill the kernel + int maxKernelSize = std::min(dxRight, effectWidth); + for (int i = 0; i < maxKernelSize; ++i) + sum += srcPixelArray->get(line + i * stride + channel); + + // Blurring + for (int x = 0; x < effectWidth; ++x) { + int pixelByteOffset = line + x * stride + channel; + dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx)); + if (x >= dxLeft) + sum -= srcPixelArray->get(pixelByteOffset - dxLeft * stride); + if (x + dxRight < effectWidth) + sum += srcPixelArray->get(pixelByteOffset + dxRight * stride); + } + if (alphaImage) // Source image is black, it just has different alpha values + break; + } + } +} + +void FEGaussianBlur::apply(Filter* filter) +{ + m_in->apply(filter); + if (!m_in->resultImage()) + return; + + if (!getEffectContext()) + return; + + setIsAlphaImage(m_in->isAlphaImage()); + + if (m_x == 0 || m_y == 0) + return; + + unsigned sdx = static_cast<unsigned>(floor(m_x * 3 * sqrt(2 * piDouble) / 4.f + 0.5f)); + unsigned sdy = static_cast<unsigned>(floor(m_y * 3 * sqrt(2 * piDouble) / 4.f + 0.5f)); + + IntRect effectDrawingRect = calculateDrawingIntRect(m_in->subRegion()); + RefPtr<ImageData> srcImageData(m_in->resultImage()->getPremultipliedImageData(effectDrawingRect)); + CanvasPixelArray* srcPixelArray(srcImageData->data()); + + IntRect imageRect(IntPoint(), resultImage()->size()); + RefPtr<ImageData> tmpImageData = ImageData::create(imageRect.width(), imageRect.height()); + CanvasPixelArray* tmpPixelArray(tmpImageData->data()); + + int stride = 4 * imageRect.width(); + for (int i = 0; i < 3; ++i) { + boxBlur(srcPixelArray, tmpPixelArray, sdx, 4, stride, imageRect.width(), imageRect.height(), isAlphaImage()); + boxBlur(tmpPixelArray, srcPixelArray, sdy, stride, 4, imageRect.height(), imageRect.width(), isAlphaImage()); + } + + resultImage()->putPremultipliedImageData(srcImageData.get(), imageRect, IntPoint()); +} + +void FEGaussianBlur::dump() +{ +} + +} // namespace WebCore + +#endif // ENABLE(FILTERS) diff --git a/WebCore/platform/graphics/filters/FEGaussianBlur.h b/WebCore/platform/graphics/filters/FEGaussianBlur.h new file mode 100644 index 0000000..ecdb9e3 --- /dev/null +++ b/WebCore/platform/graphics/filters/FEGaussianBlur.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@webkit.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef FEGaussianBlur_h +#define FEGaussianBlur_h + +#if ENABLE(FILTERS) +#include "FilterEffect.h" +#include "Filter.h" + +namespace WebCore { + + class FEGaussianBlur : public FilterEffect { + public: + static PassRefPtr<FEGaussianBlur> create(FilterEffect*, const float&, const float&); + + float stdDeviationX() const; + void setStdDeviationX(float); + + float stdDeviationY() const; + void setStdDeviationY(float); + + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return calculateUnionOfChildEffectSubregions(filter, m_in.get()); } + void apply(Filter*); + void dump(); + + private: + FEGaussianBlur(FilterEffect*, const float&, const float&); + + RefPtr<FilterEffect> m_in; + float m_x; + float m_y; + }; + +} // namespace WebCore + +#endif // ENABLE(FILTERS) + +#endif // FEGaussianBlur_h diff --git a/WebCore/platform/graphics/filters/FilterEffect.cpp b/WebCore/platform/graphics/filters/FilterEffect.cpp index 5818e50..68900b5 100644 --- a/WebCore/platform/graphics/filters/FilterEffect.cpp +++ b/WebCore/platform/graphics/filters/FilterEffect.cpp @@ -25,14 +25,11 @@ namespace WebCore { FilterEffect::FilterEffect() - : m_xBBoxMode(false) - , m_yBBoxMode(false) - , m_widthBBoxMode(false) - , m_heightBBoxMode(false) - , m_hasX(false) + : m_hasX(false) , m_hasY(false) , m_hasWidth(false) , m_hasHeight(false) + , m_alphaImage(false) { } diff --git a/WebCore/platform/graphics/filters/FilterEffect.h b/WebCore/platform/graphics/filters/FilterEffect.h index e2b8a0e..b30e513 100644 --- a/WebCore/platform/graphics/filters/FilterEffect.h +++ b/WebCore/platform/graphics/filters/FilterEffect.h @@ -38,18 +38,6 @@ namespace WebCore { public: virtual ~FilterEffect(); - bool xBoundingBoxMode() const { return m_xBBoxMode; } - void setXBoundingBoxMode(bool bboxMode) { m_xBBoxMode = bboxMode; } - - bool yBoundingBoxMode() const { return m_yBBoxMode; } - void setYBoundingBoxMode(bool bboxMode) { m_yBBoxMode = bboxMode; } - - bool widthBoundingBoxMode() const { return m_widthBBoxMode; } - void setWidthBoundingBoxMode(bool bboxMode) { m_widthBBoxMode = bboxMode; } - - bool heightBoundingBoxMode() const { return m_heightBBoxMode; } - void setHeightBoundingBoxMode(bool bboxMode) { m_heightBBoxMode = bboxMode; } - void setUnionOfChildEffectSubregions(const FloatRect& uniteRect) { m_unionOfChildEffectSubregions = uniteRect; } FloatRect unionOfChildEffectSubregions() const { return m_unionOfChildEffectSubregions; } @@ -79,6 +67,10 @@ namespace WebCore { FloatRect calculateDrawingRect(const FloatRect&); IntRect calculateDrawingIntRect(const FloatRect&); + // black image with different alpha values + bool isAlphaImage() { return m_alphaImage; } + void setIsAlphaImage(bool alphaImage) { m_alphaImage = alphaImage; } + virtual FloatRect uniteChildEffectSubregions(Filter* filter) { return filter->filterRegion(); } virtual FloatRect calculateEffectRect(Filter*); virtual void apply(Filter*) = 0; @@ -102,6 +94,8 @@ namespace WebCore { bool m_hasWidth : 1; bool m_hasHeight : 1; + bool m_alphaImage; + FloatRect m_subRegion; FloatRect m_unionOfChildEffectSubregions; diff --git a/WebCore/platform/graphics/filters/SourceAlpha.cpp b/WebCore/platform/graphics/filters/SourceAlpha.cpp index 57436be..1b6309b 100644 --- a/WebCore/platform/graphics/filters/SourceAlpha.cpp +++ b/WebCore/platform/graphics/filters/SourceAlpha.cpp @@ -59,6 +59,8 @@ void SourceAlpha::apply(Filter* filter) if (!filterContext) return; + setIsAlphaImage(true); + FloatRect imageRect(FloatPoint(), filter->sourceImage()->image()->size()); filterContext->save(); filterContext->clipToImageBuffer(imageRect, filter->sourceImage()); diff --git a/WebCore/platform/graphics/gtk/DataSourceGStreamer.cpp b/WebCore/platform/graphics/gtk/DataSourceGStreamer.cpp index a6c2dfb..567da74 100644 --- a/WebCore/platform/graphics/gtk/DataSourceGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/DataSourceGStreamer.cpp @@ -214,7 +214,7 @@ static gboolean webkit_data_src_uri_set_uri(GstURIHandler* handler, const gchar* GInputStream* stream = g_memory_input_stream_new_from_data(decoded_data, decoded_size, g_free); - g_object_set(src->kid, "stream", stream, 0); + g_object_set(src->kid, "stream", stream, NULL); g_object_unref(stream); if (src->uri) { diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index 65c64b4..8d1d261 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -25,9 +25,10 @@ #if ENABLE(VIDEO) #include "MediaPlayerPrivateGStreamer.h" -#include "DataSourceGStreamer.h" + #include "CString.h" +#include "DataSourceGStreamer.h" #include "GraphicsContext.h" #include "IntRect.h" #include "KURL.h" @@ -35,11 +36,10 @@ #include "MediaPlayer.h" #include "NotImplemented.h" #include "ScrollView.h" +#include "TimeRanges.h" #include "VideoSinkGStreamer.h" #include "Widget.h" -#include "TimeRanges.h" -#include <gst/base/gstbasesrc.h> #include <gst/gst.h> #include <gst/interfaces/mixer.h> #include <gst/interfaces/xoverlay.h> @@ -52,16 +52,20 @@ using namespace std; namespace WebCore { -gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data) +gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data) { - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { - GOwnPtr<GError> err; - GOwnPtr<gchar> debug; + GOwnPtr<GError> err; + GOwnPtr<gchar> debug; + MediaPlayer::NetworkState error; + MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); + gint percent = 0; + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message); - MediaPlayer::NetworkState error = MediaPlayer::Empty; + error = MediaPlayer::Empty; if (err->domain == GST_CORE_ERROR || err->domain == GST_LIBRARY_ERROR) error = MediaPlayer::DecodeError; else if (err->domain == GST_RESOURCE_ERROR) @@ -69,44 +73,32 @@ gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpoin else if (err->domain == GST_STREAM_ERROR) error = MediaPlayer::NetworkError; - MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); if (mp) mp->loadingFailed(error); - } - return true; -} - -gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data) -{ - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) { + break; + case GST_MESSAGE_EOS: LOG_VERBOSE(Media, "End of Stream"); - MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); mp->didEnd(); - } - return true; -} - -gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data) -{ - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_STATE_CHANGED) { - MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); + break; + case GST_MESSAGE_STATE_CHANGED: mp->updateStates(); - } - return true; -} - -gboolean mediaPlayerPrivateBufferingCallback(GstBus* bus, GstMessage* message, gpointer data) -{ - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_BUFFERING) { - gint percent = 0; + break; + case GST_MESSAGE_BUFFERING: gst_message_parse_buffering(message, &percent); LOG_VERBOSE(Media, "Buffering %d", percent); + break; + default: + LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s", + GST_MESSAGE_TYPE_NAME(message)); + break; } return true; } -static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, MediaPlayerPrivate* playerPrivate) +void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate) { + g_return_if_fail(GST_IS_BUFFER(buffer)); + gst_buffer_replace(&playerPrivate->m_buffer, buffer); playerPrivate->repaint(); } @@ -123,7 +115,8 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) static bool gstInitialized = false; -static void do_gst_init() { +static void do_gst_init() +{ // FIXME: We should pass the arguments from the command line if (!gstInitialized) { gst_init(0, 0); @@ -139,50 +132,33 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_playBin(0) , m_videoSink(0) , m_source(0) - , m_rate(1.0f) , m_endTime(numeric_limits<float>::infinity()) - , m_volume(0.5f) , m_networkState(MediaPlayer::Empty) , m_readyState(MediaPlayer::HaveNothing) , m_startedPlaying(false) , m_isStreaming(false) , m_size(IntSize()) - , m_visible(true) + , m_buffer(0) , m_paused(true) , m_seeking(false) , m_errorOccured(false) { do_gst_init(); - - // FIXME: The size shouldn't be fixed here, this is just a quick hack. - m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 640, 480); -} - -static gboolean idleUnref(gpointer data) -{ - g_object_unref(reinterpret_cast<GObject*>(data)); - return FALSE; } MediaPlayerPrivate::~MediaPlayerPrivate() { - if (m_surface) - cairo_surface_destroy(m_surface); + if (m_buffer) + gst_buffer_unref(m_buffer); + m_buffer = 0; if (m_playBin) { gst_element_set_state(m_playBin, GST_STATE_NULL); gst_object_unref(GST_OBJECT(m_playBin)); } - // FIXME: We should find a better way to handle the lifetime of this object; this is - // needed because the object is sometimes being destroyed inbetween a call to - // webkit_video_sink_render, and the idle it schedules. Adding a ref in - // webkit_video_sink_render that would be balanced by the idle is not an option, - // because in some cases the destruction of the sink may happen in time for the idle - // to be removed from the queue, so it may not run. It would also cause lots of ref - // counting churn (render/idle are called many times). This is an ugly race. if (m_videoSink) { - g_idle_add(idleUnref, m_videoSink); + g_object_unref(m_videoSink); m_videoSink = 0; } } @@ -226,24 +202,14 @@ float MediaPlayerPrivate::duration() const GstFormat timeFormat = GST_FORMAT_TIME; gint64 timeLength = 0; -#if !GST_CHECK_VERSION(0, 10, 23) - // We try to get the duration, but we do not trust the - // return value of the query function only; the problem we are - // trying to work-around here is that pipelines in stream mode may - // not be able to figure out the duration, but still return true! - // See https://bugs.webkit.org/show_bug.cgi?id=24639 which has been - // fixed in gst-plugins-base 0.10.23 - if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeLength <= 0) { -#else - if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength)) { -#endif + if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || timeLength == GST_CLOCK_TIME_NONE) { LOG_VERBOSE(Media, "Time duration query failed."); return numeric_limits<float>::infinity(); } LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); - return (float) (timeLength / 1000000000.0); + return (float) ((guint64) timeLength / 1000000000.0); // FIXME: handle 3.14.9.5 properly } @@ -288,7 +254,7 @@ void MediaPlayerPrivate::seek(float time) return; LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(sec)); - if (!gst_element_seek( m_playBin, m_rate, + if (!gst_element_seek(m_playBin, m_player->rate(), GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH), GST_SEEK_TYPE_SET, sec, @@ -340,14 +306,17 @@ IntSize MediaPlayerPrivate::naturalSize() const // 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")) { - gst_video_get_size(GST_PAD(pad), &width, &height); GstCaps* caps = GST_PAD_CAPS(pad); gfloat pixelAspectRatio; gint pixelAspectRatioNumerator, pixelAspectRatioDenominator; - if (!gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, - &pixelAspectRatioDenominator)) - pixelAspectRatioNumerator = pixelAspectRatioDenominator = 1; + if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps) || + !gst_video_format_parse_caps(caps, NULL, &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; @@ -376,21 +345,10 @@ bool MediaPlayerPrivate::hasAudio() const void MediaPlayerPrivate::setVolume(float volume) { - m_volume = volume; - LOG_VERBOSE(Media, "Volume to %f", volume); - if (!m_playBin) return; - g_object_set(G_OBJECT(m_playBin), "volume", m_volume, NULL); -} - -void MediaPlayerPrivate::setMuted(bool mute) -{ - if (!m_playBin) - return; - - g_object_set(G_OBJECT(m_playBin), "mute", mute, NULL); + g_object_set(G_OBJECT(m_playBin), "volume", static_cast<double>(volume), NULL); } void MediaPlayerPrivate::setRate(float rate) @@ -403,7 +361,6 @@ void MediaPlayerPrivate::setRate(float rate) if (m_isStreaming) return; - m_rate = rate; LOG_VERBOSE(Media, "Set Rate to %f", rate); seek(currentTime()); } @@ -495,7 +452,11 @@ unsigned MediaPlayerPrivate::totalBytes() const void MediaPlayerPrivate::cancelLoad() { - notImplemented(); + if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) + return; + + if (m_playBin) + gst_element_set_state(m_playBin, GST_STATE_NULL); } void MediaPlayerPrivate::updateStates() @@ -525,14 +486,15 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(state), gst_element_state_get_name(pending)); - if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::HaveEnoughData; - } else if (state == GST_STATE_PAUSED) + if (state == GST_STATE_READY) + m_readyState = MediaPlayer::HaveNothing; + else if (state == GST_STATE_PAUSED) m_readyState = MediaPlayer::HaveEnoughData; - if (state == GST_STATE_PLAYING) + if (state == GST_STATE_PLAYING) { + m_readyState = MediaPlayer::HaveEnoughData; m_paused = false; - else + } else m_paused = true; if (m_seeking) { @@ -563,9 +525,9 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(state), gst_element_state_get_name(pending)); - if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::HaveFutureData; - } else if (state == GST_STATE_PAUSED) + if (state == GST_STATE_READY) + m_readyState = MediaPlayer::HaveNothing; + else if (state == GST_STATE_PAUSED) m_readyState = MediaPlayer::HaveCurrentData; m_networkState = MediaPlayer::Loading; @@ -639,23 +601,11 @@ void MediaPlayerPrivate::loadingFailed(MediaPlayer::NetworkState error) void MediaPlayerPrivate::setSize(const IntSize& size) { - // Destroy and re-create the cairo surface only if the size - // changed. - if (size != m_size) { - if (m_surface) - cairo_surface_destroy(m_surface); - m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width(), - size.height()); - g_object_set(m_videoSink, "surface", m_surface, 0); - } - m_size = size; - } void MediaPlayerPrivate::setVisible(bool visible) { - m_visible = visible; } void MediaPlayerPrivate::repaint() @@ -668,20 +618,65 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) if (context->paintingDisabled()) return; - if (!m_visible) + if (!m_player->visible()) return; + if (!m_buffer) + return; + + int width = 0, height = 0; + int pixelAspectRatioNumerator = 0; + int pixelAspectRatioDenominator = 0; + double doublePixelAspectRatioNumerator = 0; + double doublePixelAspectRatioDenominator = 0; + double displayWidth; + double displayHeight; + double scale, gapHeight, gapWidth; + + GstCaps *caps = gst_buffer_get_caps(m_buffer); + + if (!gst_video_format_parse_caps(caps, NULL, &width, &height) || + !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, &pixelAspectRatioDenominator)) { + gst_caps_unref(caps); + return; + } + + displayWidth = width; + displayHeight = height; + doublePixelAspectRatioNumerator = pixelAspectRatioNumerator; + doublePixelAspectRatioDenominator = pixelAspectRatioDenominator; cairo_t* cr = context->platformContext(); + cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(m_buffer), + CAIRO_FORMAT_RGB24, + width, height, + 4 * width); cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + displayWidth *= doublePixelAspectRatioNumerator / doublePixelAspectRatioDenominator; + displayHeight *= doublePixelAspectRatioDenominator / doublePixelAspectRatioNumerator; + + scale = MIN (rect.width () / displayWidth, rect.height () / displayHeight); + displayWidth *= scale; + displayHeight *= scale; + + // Calculate gap between border an picture + gapWidth = (rect.width() - displayWidth) / 2.0; + gapHeight = (rect.height() - displayHeight) / 2.0; + // paint the rectangle on the context and draw the surface inside. - cairo_translate(cr, rect.x(), rect.y()); + cairo_translate(cr, rect.x() + gapWidth, rect.y() + gapHeight); cairo_rectangle(cr, 0, 0, rect.width(), rect.height()); - cairo_set_source_surface(cr, m_surface, 0, 0); + cairo_scale(cr, doublePixelAspectRatioNumerator / doublePixelAspectRatioDenominator, + doublePixelAspectRatioDenominator / doublePixelAspectRatioNumerator); + cairo_scale(cr, scale, scale); + cairo_set_source_surface(cr, src, 0, 0); cairo_fill(cr); cairo_restore(cr); + + cairo_surface_destroy(src); + gst_caps_unref(caps); } static HashSet<String> mimeTypeCache() @@ -805,25 +800,18 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin)); gst_bus_add_signal_watch(bus); - g_signal_connect(bus, "message::error", G_CALLBACK(mediaPlayerPrivateErrorCallback), this); - g_signal_connect(bus, "message::eos", G_CALLBACK(mediaPlayerPrivateEOSCallback), this); - g_signal_connect(bus, "message::state-changed", G_CALLBACK(mediaPlayerPrivateStateCallback), this); - g_signal_connect(bus, "message::buffering", G_CALLBACK(mediaPlayerPrivateBufferingCallback), this); + g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); gst_object_unref(bus); - g_object_set(G_OBJECT(m_playBin), "uri", url.utf8().data(), NULL); - - m_videoSink = webkit_video_sink_new(m_surface); + g_object_set(G_OBJECT(m_playBin), "uri", url.utf8().data(), + "volume", static_cast<double>(m_player->volume()), NULL); - // This ref is to protect the sink from being destroyed before we stop the idle it - // creates internally. See the comment in ~MediaPlayerPrivate. - g_object_ref(m_videoSink); + m_videoSink = webkit_video_sink_new(); + g_object_ref_sink(m_videoSink); g_object_set(m_playBin, "video-sink", m_videoSink, NULL); g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); - - setVolume(m_volume); } } diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h index d305759..54da420 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h @@ -30,8 +30,10 @@ #include <cairo.h> #include <glib.h> -typedef struct _GstElement GstElement; +typedef struct _WebKitVideoSink WebKitVideoSink; +typedef struct _GstBuffer GstBuffer; typedef struct _GstMessage GstMessage; +typedef struct _GstElement GstElement; typedef struct _GstBus GstBus; namespace WebCore { @@ -41,14 +43,11 @@ namespace WebCore { class IntRect; class String; - gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data); - gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); - gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); + gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data); class MediaPlayerPrivate : public MediaPlayerPrivateInterface { - friend gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data); - friend gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); - friend gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); + friend gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data); + friend void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate); public: static void registerMediaEngine(MediaEngineRegistrar); @@ -74,7 +73,6 @@ namespace WebCore { void setRate(float); void setVolume(float); - void setMuted(bool); int dataRate() const; @@ -126,17 +124,14 @@ namespace WebCore { GstElement* m_playBin; GstElement* m_videoSink; GstElement* m_source; - float m_rate; float m_endTime; bool m_isEndReached; - double m_volume; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; mutable bool m_isStreaming; IntSize m_size; - bool m_visible; - cairo_surface_t* m_surface; + GstBuffer* m_buffer; bool m_paused; bool m_seeking; diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp index fb86fe9..b5e1a8b 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp @@ -21,8 +21,9 @@ * SECTION:webkit-video-sink * @short_description: GStreamer video sink * - * #WebKitVideoSink is a GStreamer sink element that sends - * data to a #cairo_surface_t. + * #WebKitVideoSink is a GStreamer sink element that triggers + * repaints in the WebKit GStreamer media player for the + * current video buffer. */ #include "config.h" @@ -57,22 +58,26 @@ enum { }; enum { - PROP_0, - PROP_SURFACE + PROP_0 }; static guint webkit_video_sink_signals[LAST_SIGNAL] = { 0, }; struct _WebKitVideoSinkPrivate { - cairo_surface_t* surface; - GAsyncQueue* async_queue; - gboolean rgb_ordering; - int width; - int height; - int fps_n; - int fps_d; - int par_n; - int par_d; + GstBuffer* buffer; + guint timeout_id; + GMutex* buffer_mutex; + GCond* data_cond; + + // If this is TRUE all processing should finish ASAP + // This is necessary because there could be a race between + // unlock() and render(), where unlock() wins, signals the + // GCond, then render() tries to render a frame although + // everything else isn't running anymore. This will lead + // to deadlocks because render() holds the stream lock. + // + // Protected by the buffer mutex + gboolean unlocked; }; #define _do_init(bla) \ @@ -83,8 +88,8 @@ struct _WebKitVideoSinkPrivate { GST_BOILERPLATE_FULL(WebKitVideoSink, webkit_video_sink, - GstBaseSink, - GST_TYPE_BASE_SINK, + GstVideoSink, + GST_TYPE_VIDEO_SINK, _do_init); static void @@ -102,59 +107,37 @@ webkit_video_sink_init(WebKitVideoSink* sink, WebKitVideoSinkClass* klass) WebKitVideoSinkPrivate* priv; sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate); - priv->async_queue = g_async_queue_new(); + priv->data_cond = g_cond_new(); + priv->buffer_mutex = g_mutex_new(); } static gboolean -webkit_video_sink_idle_func(gpointer data) +webkit_video_sink_timeout_func(gpointer data) { WebKitVideoSink* sink = reinterpret_cast<WebKitVideoSink*>(data); WebKitVideoSinkPrivate* priv = sink->priv; GstBuffer* buffer; - GstCaps* caps; - GstVideoFormat format; - gint par_n, par_d; - gfloat par; - gint bwidth, bheight; - if (!priv->async_queue) - return FALSE; + g_mutex_lock(priv->buffer_mutex); + buffer = priv->buffer; + priv->buffer = 0; + priv->timeout_id = 0; - buffer = (GstBuffer*)g_async_queue_try_pop(priv->async_queue); - if (!buffer || G_UNLIKELY(!GST_IS_BUFFER(buffer))) + if (!buffer || priv->unlocked || G_UNLIKELY(!GST_IS_BUFFER(buffer))) { + g_cond_signal(priv->data_cond); + g_mutex_unlock(priv->buffer_mutex); return FALSE; + } - caps = GST_BUFFER_CAPS(buffer); - if (!gst_video_format_parse_caps(caps, &format, &bwidth, &bheight)) { - GST_ERROR_OBJECT(sink, "Unknown video format in buffer caps '%s'", - gst_caps_to_string(caps)); - return FALSE; + if (G_UNLIKELY(!GST_BUFFER_CAPS(buffer))) { + buffer = gst_buffer_make_metadata_writable(buffer); + gst_buffer_set_caps(buffer, GST_PAD_CAPS(GST_BASE_SINK_PAD(sink))); } - if (!gst_video_parse_caps_pixel_aspect_ratio(caps, &par_n, &par_d)) - par_n = par_d = 1; - - par = (gfloat) par_n / (gfloat) par_d; - - // TODO: consider priv->rgb_ordering? - cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(buffer), - CAIRO_FORMAT_RGB24, - bwidth, bheight, - 4 * bwidth); - - // TODO: We copy the data twice right now. This could be easily improved. - cairo_t* cr = cairo_create(priv->surface); - cairo_scale(cr, par, 1.0 / par); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(cr, src, 0, 0); - cairo_surface_destroy(src); - cairo_rectangle(cr, 0, 0, priv->width, priv->height); - cairo_fill(cr); - cairo_destroy(cr); + g_signal_emit(sink, webkit_video_sink_signals[REPAINT_REQUESTED], 0, buffer); gst_buffer_unref(buffer); - g_async_queue_unref(priv->async_queue); - - g_signal_emit(sink, webkit_video_sink_signals[REPAINT_REQUESTED], 0); + g_cond_signal(priv->data_cond); + g_mutex_unlock(priv->buffer_mutex); return FALSE; } @@ -165,53 +148,24 @@ webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(bsink); WebKitVideoSinkPrivate* priv = sink->priv; - g_async_queue_ref(priv->async_queue); - g_async_queue_push(priv->async_queue, gst_buffer_ref(buffer)); - g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, sink, 0); - - return GST_FLOW_OK; -} - -static gboolean -webkit_video_sink_set_caps(GstBaseSink* bsink, GstCaps* caps) -{ - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(bsink); - WebKitVideoSinkPrivate* priv = sink->priv; - GstStructure* structure; - gboolean ret; - gint width, height, fps_n, fps_d; - int red_mask; - - GstCaps* intersection = gst_caps_intersect(gst_static_pad_template_get_caps(&sinktemplate), caps); + g_mutex_lock(priv->buffer_mutex); - if (gst_caps_is_empty(intersection)) - return FALSE; - - gst_caps_unref(intersection); - - structure = gst_caps_get_structure(caps, 0); - - ret = gst_structure_get_int(structure, "width", &width); - ret &= gst_structure_get_int(structure, "height", &height); - - /* We dont yet use fps but handy to have */ - ret &= gst_structure_get_fraction(structure, "framerate", - &fps_n, &fps_d); - g_return_val_if_fail(ret, FALSE); - - priv->width = width; - priv->height = height; - priv->fps_n = fps_n; - priv->fps_d = fps_d; + if (priv->unlocked) { + g_mutex_unlock(priv->buffer_mutex); + return GST_FLOW_OK; + } - if (!gst_structure_get_fraction(structure, "pixel-aspect-ratio", - &priv->par_n, &priv->par_d)) - priv->par_n = priv->par_d = 1; + priv->buffer = gst_buffer_ref(buffer); - gst_structure_get_int(structure, "red_mask", &red_mask); - priv->rgb_ordering = (red_mask == static_cast<int>(0xff000000)); + // Use HIGH_IDLE+20 priority, like Gtk+ for redrawing operations. + priv->timeout_id = g_timeout_add_full(G_PRIORITY_HIGH_IDLE + 20, 0, + webkit_video_sink_timeout_func, + gst_object_ref(sink), + (GDestroyNotify)gst_object_unref); - return TRUE; + g_cond_wait(priv->data_cond, priv->buffer_mutex); + g_mutex_unlock(priv->buffer_mutex); + return GST_FLOW_OK; } static void @@ -220,56 +174,58 @@ webkit_video_sink_dispose(GObject* object) WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); WebKitVideoSinkPrivate* priv = sink->priv; - if (priv->surface) { - cairo_surface_destroy(priv->surface); - priv->surface = 0; + if (priv->data_cond) { + g_cond_free(priv->data_cond); + priv->data_cond = 0; } - if (priv->async_queue) { - g_async_queue_unref(priv->async_queue); - priv->async_queue = 0; + if (priv->buffer_mutex) { + g_mutex_free(priv->buffer_mutex); + priv->buffer_mutex = 0; } G_OBJECT_CLASS(parent_class)->dispose(object); } static void -webkit_video_sink_finalize(GObject* object) +unlock_buffer_mutex(WebKitVideoSinkPrivate* priv) { - G_OBJECT_CLASS(parent_class)->finalize(object); + g_mutex_lock(priv->buffer_mutex); + + if (priv->buffer) { + gst_buffer_unref(priv->buffer); + priv->buffer = 0; + } + + priv->unlocked = TRUE; + + g_cond_signal(priv->data_cond); + g_mutex_unlock(priv->buffer_mutex); } -static void -webkit_video_sink_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) +static gboolean +webkit_video_sink_unlock(GstBaseSink* object) { WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); - WebKitVideoSinkPrivate* priv = sink->priv; - switch (prop_id) { - case PROP_SURFACE: - if (priv->surface) - cairo_surface_destroy(priv->surface); - priv->surface = cairo_surface_reference((cairo_surface_t*)g_value_get_pointer(value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } + unlock_buffer_mutex(sink->priv); + + return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock, + (object), TRUE); } -static void -webkit_video_sink_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) +static gboolean +webkit_video_sink_unlock_stop(GstBaseSink* object) { WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); + WebKitVideoSinkPrivate* priv = sink->priv; - switch (prop_id) { - case PROP_SURFACE: - g_value_set_pointer(value, sink->priv->surface); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } + g_mutex_lock(priv->buffer_mutex); + priv->unlocked = FALSE; + g_mutex_unlock(priv->buffer_mutex); + + return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop, + (object), TRUE); } static gboolean @@ -277,20 +233,46 @@ webkit_video_sink_stop(GstBaseSink* base_sink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(base_sink)->priv; - g_async_queue_lock(priv->async_queue); - - /* Remove all remaining objects from the queue */ - while (GstBuffer* buffer = (GstBuffer*)g_async_queue_try_pop_unlocked(priv->async_queue)) - gst_buffer_unref(buffer); - - g_async_queue_unlock(priv->async_queue); + unlock_buffer_mutex(priv); + return TRUE; +} - g_idle_remove_by_data(base_sink); +static gboolean +webkit_video_sink_start(GstBaseSink* base_sink) +{ + WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(base_sink)->priv; + g_mutex_lock(priv->buffer_mutex); + priv->unlocked = FALSE; + g_mutex_unlock(priv->buffer_mutex); return TRUE; } static void +marshal_VOID__MINIOBJECT(GClosure * closure, GValue * return_value, + guint n_param_values, const GValue * param_values, + gpointer invocation_hint, gpointer marshal_data) +{ + typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1, gpointer data2); + marshalfunc_VOID__MINIOBJECT callback; + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + + g_return_if_fail(n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA(closure)) { + data1 = closure->data; + data2 = g_value_peek_pointer(param_values + 0); + } else { + data1 = g_value_peek_pointer(param_values + 0); + data2 = closure->data; + } + callback = (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data : cc->callback); + + callback(data1, gst_value_get_mini_object(param_values + 1), data2); +} + +static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) { GObjectClass* gobject_class = G_OBJECT_CLASS(klass); @@ -298,16 +280,14 @@ webkit_video_sink_class_init(WebKitVideoSinkClass* klass) g_type_class_add_private(klass, sizeof(WebKitVideoSinkPrivate)); - gobject_class->set_property = webkit_video_sink_set_property; - gobject_class->get_property = webkit_video_sink_get_property; - gobject_class->dispose = webkit_video_sink_dispose; - gobject_class->finalize = webkit_video_sink_finalize; + gstbase_sink_class->unlock = webkit_video_sink_unlock; + gstbase_sink_class->unlock_stop = webkit_video_sink_unlock_stop; gstbase_sink_class->render = webkit_video_sink_render; gstbase_sink_class->preroll = webkit_video_sink_render; gstbase_sink_class->stop = webkit_video_sink_stop; - gstbase_sink_class->set_caps = webkit_video_sink_set_caps; + gstbase_sink_class->start = webkit_video_sink_start; webkit_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested", G_TYPE_FROM_CLASS(klass), @@ -315,37 +295,20 @@ webkit_video_sink_class_init(WebKitVideoSinkClass* klass) 0, 0, 0, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - g_object_class_install_property( - gobject_class, PROP_SURFACE, - g_param_spec_pointer("surface", "surface", "Target cairo_surface_t*", - (GParamFlags)(G_PARAM_READWRITE))); + marshal_VOID__MINIOBJECT, + G_TYPE_NONE, 1, GST_TYPE_BUFFER); } /** * webkit_video_sink_new: - * @surface: a #cairo_surface_t * - * Creates a new GStreamer video sink which uses @surface as the target - * for sinking a video stream from GStreamer. + * Creates a new GStreamer video sink. * * Return value: a #GstElement for the newly created video sink */ GstElement* -webkit_video_sink_new(cairo_surface_t* surface) +webkit_video_sink_new(void) { - return (GstElement*)g_object_new(WEBKIT_TYPE_VIDEO_SINK, "surface", surface, 0); + return (GstElement*)g_object_new(WEBKIT_TYPE_VIDEO_SINK, 0); } -void -webkit_video_sink_set_surface(WebKitVideoSink* sink, cairo_surface_t* surface) -{ - WebKitVideoSinkPrivate* priv; - - sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate); - if (priv->surface) - cairo_surface_destroy(priv->surface); - priv->surface = cairo_surface_reference(surface); -} diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h index be2c94c..7ea7d91 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.h @@ -22,7 +22,7 @@ #include <cairo.h> #include <glib-object.h> -#include <gst/base/gstbasesink.h> +#include <gst/video/gstvideosink.h> G_BEGIN_DECLS @@ -54,13 +54,13 @@ typedef struct _WebKitVideoSinkPrivate WebKitVideoSinkPrivate; struct _WebKitVideoSink { /*< private >*/ - GstBaseSink parent; + GstVideoSink parent; WebKitVideoSinkPrivate *priv; }; struct _WebKitVideoSinkClass { /*< private >*/ - GstBaseSinkClass parent_class; + GstVideoSinkClass parent_class; /* Future padding */ void (* _webkit_reserved1)(void); @@ -72,9 +72,7 @@ struct _WebKitVideoSinkClass { }; GType webkit_video_sink_get_type(void) G_GNUC_CONST; -GstElement *webkit_video_sink_new(cairo_surface_t *surface); - -void webkit_video_sink_set_surface(WebKitVideoSink *sink, cairo_surface_t *surface); +GstElement *webkit_video_sink_new(void); G_END_DECLS diff --git a/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp b/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp index d785ef4..c23b8a9 100644 --- a/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp +++ b/WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp @@ -369,6 +369,11 @@ void GraphicsContext::clip(const Path& path) m_data->m_view->ConstrainClippingRegion(path.platformPath()); } +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + void GraphicsContext::clipOut(const Path& path) { if (paintingDisabled()) diff --git a/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp b/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp index 34941c0..adb7573 100644 --- a/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp +++ b/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp @@ -39,6 +39,8 @@ #include <unicode/unorm.h> +extern int charUnicodeToUTF8HACK(unsigned short, char*); + namespace WebCore { void SimpleFontData::platformInit() @@ -93,15 +95,15 @@ void SimpleFontData::determinePitch() float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - const char charArray[1] = { glyph }; - float escapements[1]; + if (!m_platformData.font()) + return 0; - if (m_platformData.font()) { - m_platformData.font()->GetEscapements(charArray, 1, escapements); - return escapements[0] * m_platformData.font()->Size(); - } + char charArray[4]; + float escapements[1]; - return 0; + charUnicodeToUTF8HACK(glyph, charArray); + m_platformData.font()->GetEscapements(charArray, 1, escapements); + return escapements[0] * m_platformData.font()->Size(); } } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/Canvas3DLayer.h b/WebCore/platform/graphics/mac/Canvas3DLayer.h index 6c65676..122ef39 100644 --- a/WebCore/platform/graphics/mac/Canvas3DLayer.h +++ b/WebCore/platform/graphics/mac/Canvas3DLayer.h @@ -41,7 +41,9 @@ namespace WebCore { GLuint m_texture; } --(id)initWithContext:(CGLContextObj)context texture:(GLuint)texture; +- (id)initWithContext:(CGLContextObj)context texture:(GLuint)texture; + +- (CGImageRef)copyImageSnapshotWithColorSpace:(CGColorSpaceRef)colorSpace; @end diff --git a/WebCore/platform/graphics/mac/Canvas3DLayer.mm b/WebCore/platform/graphics/mac/Canvas3DLayer.mm index 545c58b..94819d4 100644 --- a/WebCore/platform/graphics/mac/Canvas3DLayer.mm +++ b/WebCore/platform/graphics/mac/Canvas3DLayer.mm @@ -33,6 +33,8 @@ #import "GraphicsLayer.h" #import <QuartzCore/QuartzCore.h> #import <OpenGL/OpenGL.h> +#import <wtf/RetainPtr.h> +#include <wtf/FastMalloc.h> using namespace WebCore; @@ -48,19 +50,14 @@ using namespace WebCore; -(CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { - CGLPixelFormatAttribute attribs[] = - { - (CGLPixelFormatAttribute) kCGLPFAAccelerated, - (CGLPixelFormatAttribute) kCGLPFAColorSize, (CGLPixelFormatAttribute) 32, - (CGLPixelFormatAttribute) kCGLPFADisplayMask, (CGLPixelFormatAttribute) mask, - (CGLPixelFormatAttribute) 0 - }; - - CGLPixelFormatObj pixelFormatObj; - GLint numPixelFormats; - - CGLChoosePixelFormat(attribs, &pixelFormatObj, &numPixelFormats); - return pixelFormatObj; + // FIXME: The mask param tells you which display (on a multi-display system) + // is to be used. But since we are now getting the pixel format from the + // Canvas CGL context, we don't use it. This seems to do the right thing on + // one multi-display system. But there may be cases where this is not the case. + // If needed we will have to set the display mask in the Canvas CGLContext and + // make sure it matches. + UNUSED_PARAM(mask); + return CGLRetainPixelFormat(CGLGetPixelFormat(m_contextObj)); } -(CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat @@ -72,6 +69,10 @@ using namespace WebCore; -(void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp { + CGLSetCurrentContext(m_contextObj); + glFinish(); + CGLSetCurrentContext(glContext); + CGRect frame = [self frame]; // draw the FBO into the layer @@ -103,6 +104,42 @@ using namespace WebCore; [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp]; } +static void freeData(void *, const void *data, size_t /* size */) +{ + fastFree(const_cast<void *>(data)); +} + +-(CGImageRef)copyImageSnapshotWithColorSpace:(CGColorSpaceRef)colorSpace +{ + CGLSetCurrentContext(m_contextObj); + + RetainPtr<CGColorSpaceRef> imageColorSpace = colorSpace; + if (!imageColorSpace) + imageColorSpace.adoptCF(CGColorSpaceCreateDeviceRGB()); + + CGRect layerBounds = CGRectIntegral([self bounds]); + + size_t width = layerBounds.size.width; + size_t height = layerBounds.size.height; + + size_t rowBytes = (width * 4 + 15) & ~15; + size_t dataSize = rowBytes * height; + void* data = fastMalloc(dataSize); + if (!data) + return 0; + + glPixelStorei(GL_PACK_ROW_LENGTH, rowBytes / 4); + glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + + CGDataProviderRef provider = CGDataProviderCreateWithData(0, data, dataSize, freeData); + CGImageRef image = CGImageCreate(width, height, 8, 32, rowBytes, imageColorSpace.get(), + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, + provider, 0, true, + kCGRenderingIntentDefault); + CGDataProviderRelease(provider); + return image; +} + @end @implementation Canvas3DLayer(WebLayerAdditions) diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp index cd66445..47617d8 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp @@ -30,9 +30,10 @@ #include "GraphicsContext3D.h" #include "CachedImage.h" +#include "CanvasActiveInfo.h" +#include "CanvasArray.h" #include "CanvasBuffer.h" #include "CanvasFramebuffer.h" -#include "CanvasArray.h" #include "CanvasFloatArray.h" #include "CanvasIntArray.h" #include "CanvasObject.h" @@ -49,29 +50,91 @@ #include "WebKitCSSMatrix.h" #include <CoreGraphics/CGBitmapContext.h> +#include <OpenGL/CGLRenderers.h> namespace WebCore { -GraphicsContext3D::GraphicsContext3D() +static void setPixelFormat(Vector<CGLPixelFormatAttribute>& attribs, int colorBits, int depthBits, bool accelerated, bool supersample, bool closest) { - CGLPixelFormatAttribute attribs[] = - { - (CGLPixelFormatAttribute) kCGLPFAAccelerated, - (CGLPixelFormatAttribute) kCGLPFAColorSize, (CGLPixelFormatAttribute) 32, - (CGLPixelFormatAttribute) kCGLPFADepthSize, (CGLPixelFormatAttribute) 32, - (CGLPixelFormatAttribute) kCGLPFASupersample, - (CGLPixelFormatAttribute) 0 - }; + attribs.clear(); - CGLPixelFormatObj pixelFormatObj; - GLint numPixelFormats; + attribs.append(kCGLPFAColorSize); + attribs.append(static_cast<CGLPixelFormatAttribute>(colorBits)); + attribs.append(kCGLPFADepthSize); + attribs.append(static_cast<CGLPixelFormatAttribute>(depthBits)); - CGLChoosePixelFormat(attribs, &pixelFormatObj, &numPixelFormats); - - CGLCreateContext(pixelFormatObj, 0, &m_contextObj); + if (accelerated) + attribs.append(kCGLPFAAccelerated); + else { + attribs.append(kCGLPFARendererID); + attribs.append(static_cast<CGLPixelFormatAttribute>(kCGLRendererGenericFloatID)); + } + + if (supersample) + attribs.append(kCGLPFASupersample); + + if (closest) + attribs.append(kCGLPFAClosestPolicy); + + attribs.append(static_cast<CGLPixelFormatAttribute>(0)); +} + +PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create() +{ + OwnPtr<GraphicsContext3D> context(new GraphicsContext3D()); + return context->m_contextObj ? context.release() : 0; +} + +GraphicsContext3D::GraphicsContext3D() + : m_contextObj(0) + , m_texture(0) + , m_fbo(0) + , m_depthBuffer(0) +{ + Vector<CGLPixelFormatAttribute> attribs; + CGLPixelFormatObj pixelFormatObj = 0; + GLint numPixelFormats = 0; + + // We will try: + // + // 1) 32 bit RGBA/32 bit depth/accelerated/supersampled + // 2) 32 bit RGBA/32 bit depth/accelerated + // 3) 32 bit RGBA/16 bit depth/accelerated + // 4) closest to 32 bit RGBA/16 bit depth/software renderer + // + // If none of that works, we simply fail and set m_contextObj to 0. + + setPixelFormat(attribs, 32, 32, true, true, false); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + if (numPixelFormats == 0) { + setPixelFormat(attribs, 32, 32, true, false, false); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + + if (numPixelFormats == 0) { + setPixelFormat(attribs, 32, 16, true, false, false); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + + if (numPixelFormats == 0) { + setPixelFormat(attribs, 32, 16, false, false, true); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + + if (numPixelFormats == 0) { + // Could not find an acceptable renderer - fail + return; + } + } + } + } + CGLError err = CGLCreateContext(pixelFormatObj, 0, &m_contextObj); CGLDestroyPixelFormat(pixelFormatObj); + if (err != kCGLNoError || !m_contextObj) { + // Could not create the context - fail + m_contextObj = 0; + return; + } + // Set the current context to the one given to us. CGLSetCurrentContext(m_contextObj); @@ -102,12 +165,14 @@ GraphicsContext3D::GraphicsContext3D() GraphicsContext3D::~GraphicsContext3D() { - CGLSetCurrentContext(m_contextObj); - ::glDeleteRenderbuffersEXT(1, & m_depthBuffer); - ::glDeleteTextures(1, &m_texture); - ::glDeleteFramebuffersEXT(1, &m_fbo); - CGLSetCurrentContext(0); - CGLDestroyContext(m_contextObj); + if (m_contextObj) { + CGLSetCurrentContext(m_contextObj); + ::glDeleteRenderbuffersEXT(1, & m_depthBuffer); + ::glDeleteTextures(1, &m_texture); + ::glDeleteFramebuffersEXT(1, &m_fbo); + CGLSetCurrentContext(0); + CGLDestroyContext(m_contextObj); + } } void GraphicsContext3D::checkError() const @@ -135,7 +200,7 @@ void GraphicsContext3D::endPaint() void GraphicsContext3D::reshape(int width, int height) { - if (width == m_currentWidth && height == m_currentHeight) + if (width == m_currentWidth && height == m_currentHeight || !m_contextObj) return; m_currentWidth = width; @@ -167,6 +232,9 @@ void GraphicsContext3D::reshape(int width, int height) static inline void ensureContext(CGLContextObj context) { + if (!context) + return; + CGLContextObj currentContext = CGLGetCurrentContext(); if (currentContext != context) CGLSetCurrentContext(context); @@ -442,6 +510,46 @@ void GraphicsContext3D::generateMipmap(unsigned long target) ::glGenerateMipmapEXT(target); } +bool GraphicsContext3D::getActiveAttrib(CanvasProgram* program, unsigned long index, ActiveInfo& info) +{ + if (!program->object()) + return false; + ensureContext(m_contextObj); + GLint maxAttributeSize = 0; + ::glGetProgramiv(static_cast<GLuint>(program->object()), GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeSize); + GLchar name[maxAttributeSize]; // GL_ACTIVE_ATTRIBUTE_MAX_LENGTH includes null termination + GLsizei nameLength = 0; + GLint size = 0; + GLenum type = 0; + ::glGetActiveAttrib(static_cast<GLuint>(program->object()), index, maxAttributeSize, &nameLength, &size, &type, name); + if (!nameLength) + return false; + info.name = String(name, nameLength); + info.type = type; + info.size = size; + return true; +} + +bool GraphicsContext3D::getActiveUniform(CanvasProgram* program, unsigned long index, ActiveInfo& info) +{ + if (!program->object()) + return false; + ensureContext(m_contextObj); + GLint maxUniformSize = 0; + ::glGetProgramiv(static_cast<GLuint>(program->object()), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformSize); + GLchar name[maxUniformSize]; // GL_ACTIVE_UNIFORM_MAX_LENGTH includes null termination + GLsizei nameLength = 0; + GLint size = 0; + GLenum type = 0; + ::glGetActiveUniform(static_cast<GLuint>(program->object()), index, maxUniformSize, &nameLength, &size, &type, name); + if (!nameLength) + return false; + info.name = String(name, nameLength); + info.type = type; + info.size = size; + return true; +} + int GraphicsContext3D::getAttribLocation(CanvasProgram* program, const String& name) { if (!program) @@ -556,6 +664,22 @@ void GraphicsContext3D::polygonOffset(double factor, double units) ::glPolygonOffset(static_cast<float>(factor), static_cast<float>(units)); } +PassRefPtr<CanvasArray> GraphicsContext3D::readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type) +{ + ensureContext(m_contextObj); + + // FIXME: For now we only accept GL_UNSIGNED_BYTE/GL_RGBA. In reality OpenGL ES 2.0 accepts that pair and one other + // as specified by GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE. But for now we will + // not accept those. + // FIXME: Also, we should throw when an unacceptable value is passed + if (type != GL_UNSIGNED_BYTE || format != GL_RGBA) + return 0; + + RefPtr<CanvasUnsignedByteArray> array = CanvasUnsignedByteArray::create(width * height * 4); + ::glReadPixels(x, y, width, height, format, type, (GLvoid*) array->data()); + return array; +} + void GraphicsContext3D::releaseShaderCompiler() { // FIXME: This is not implemented on desktop OpenGL. We need to have ifdefs for the different GL variants diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h index d0e1108..8cf51b4 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -110,10 +110,8 @@ public: virtual PlatformLayer* platformLayer() const; -#ifndef NDEBUG virtual void setDebugBackgroundColor(const Color&); virtual void setDebugBorder(const Color&, float borderWidth); -#endif virtual void setGeometryOrientation(CompositingCoordinatesOrientation); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index e9960f1..b351956 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -274,7 +274,6 @@ static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& tim return 0; } -#ifndef NDEBUG static void setLayerBorderColor(PlatformLayer* layer, const Color& color) { CGColorRef borderColor = createCGColor(color); @@ -286,7 +285,6 @@ static void clearBorderColor(PlatformLayer* layer) { [layer setBorderColor:nil]; } -#endif static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) { @@ -317,7 +315,6 @@ GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoord return CompositingCoordinatesBottomUp; } -#ifndef NDEBUG bool GraphicsLayer::showDebugBorders() { static bool showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; @@ -329,7 +326,6 @@ bool GraphicsLayer::showRepaintCounter() static bool showRepaintCounter = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerRepaintCounter"]; return showRepaintCounter; } -#endif static NSDictionary* nullActionsDictionary() { @@ -373,9 +369,7 @@ GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) setContentsOrientation(defaultContentsOrientation()); #endif -#ifndef NDEBUG updateDebugIndicators(); -#endif m_animationDelegate.adoptNS([[WebAnimationDelegate alloc] init]); [m_animationDelegate.get() setLayer:this]; @@ -964,9 +958,7 @@ void GraphicsLayerCA::updateChildrenTransform() void GraphicsLayerCA::updateMasksToBounds() { [m_layer.get() setMasksToBounds:m_masksToBounds]; -#ifndef NDEBUG updateDebugIndicators(); -#endif } void GraphicsLayerCA::updateContentsOpaque() @@ -1051,9 +1043,7 @@ void GraphicsLayerCA::updateLayerDrawsContent() else [m_layer.get() setContents:nil]; -#ifndef NDEBUG updateDebugIndicators(); -#endif } void GraphicsLayerCA::updateLayerBackgroundColor() @@ -1245,6 +1235,21 @@ void GraphicsLayerCA::setAnimationOnLayer(CAPropertyAnimation* caAnim, AnimatedP [layer addAnimation:caAnim forKey:animationName]; } +// Workaround for <rdar://problem/7311367> +static void bug7311367Workaround(CALayer* transformLayer, const TransformationMatrix& transform) +{ + if (!transformLayer) + return; + + CATransform3D caTransform; + copyTransform(caTransform, transform); + caTransform.m41 += 1; + [transformLayer setTransform:caTransform]; + + caTransform.m41 -= 1; + [transformLayer setTransform:caTransform]; +} + bool GraphicsLayerCA::removeAnimationFromLayer(AnimatedPropertyID property, int index) { PlatformLayer* layer = animatedLayer(property); @@ -1255,10 +1260,11 @@ bool GraphicsLayerCA::removeAnimationFromLayer(AnimatedPropertyID property, int return false; [layer removeAnimationForKey:animationName]; + + bug7311367Workaround(m_transformLayer.get(), m_transform); return true; } - static void copyAnimationProperties(CAPropertyAnimation* from, CAPropertyAnimation* to) { [to setBeginTime:[from beginTime]]; @@ -1679,7 +1685,6 @@ PlatformLayer* GraphicsLayerCA::platformLayer() const return primaryLayer(); } -#ifndef NDEBUG void GraphicsLayerCA::setDebugBackgroundColor(const Color& color) { BEGIN_BLOCK_OBJC_EXCEPTIONS @@ -1706,7 +1711,6 @@ void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) END_BLOCK_OBJC_EXCEPTIONS } -#endif // NDEBUG bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const { @@ -1784,9 +1788,7 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) // need to tell new layer to draw itself setNeedsDisplay(); -#ifndef NDEBUG updateDebugIndicators(); -#endif } GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayerCA::defaultContentsOrientation() const @@ -1830,12 +1832,10 @@ void GraphicsLayerCA::setupContentsLayer(CALayer* contentsLayer) } else [contentsLayer setAnchorPoint:CGPointZero]; -#ifndef NDEBUG if (showDebugBorders()) { setLayerBorderColor(contentsLayer, Color(0, 0, 128, 180)); [contentsLayer setBorderWidth:1.0f]; } -#endif } void GraphicsLayerCA::setOpacityInternal(float accumulatedOpacity) diff --git a/WebCore/platform/graphics/mac/WebLayer.mm b/WebCore/platform/graphics/mac/WebLayer.mm index 2647466..56b28e6 100644 --- a/WebCore/platform/graphics/mac/WebLayer.mm +++ b/WebCore/platform/graphics/mac/WebLayer.mm @@ -80,7 +80,6 @@ using namespace WebCore; } #endif -#ifndef NDEBUG if (layerContents->showRepaintCounter()) { bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; @@ -107,7 +106,6 @@ using namespace WebCore; CGContextRestoreGState(context); } -#endif CGContextRestoreGState(context); } diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index fa7b070..57a481a 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -1053,6 +1053,11 @@ void GraphicsContext::clip(const Path& path) m_data->p()->setClipPath(*path.platformPath(), Qt::IntersectClip); } +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + void GraphicsContext::clipOut(const Path& path) { if (paintingDisabled()) diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index 3a27fe3..f8403b7 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -43,22 +43,13 @@ ImageDecoder* ImageDecoder::create(const SharedBuffer& data) if (data.size() < 4) return 0; - QByteArray bytes = QByteArray::fromRawData(data.data(), data.size()); - QBuffer buffer(&bytes); - if (!buffer.open(QBuffer::ReadOnly)) - return 0; - - QByteArray imageFormat = QImageReader::imageFormat(&buffer); - if (imageFormat.isEmpty()) - return 0; // Image format not supported - - return new ImageDecoderQt(imageFormat); + return new ImageDecoderQt; } -ImageDecoderQt::ImageDecoderQt(const QByteArray& imageFormat) - : m_buffer(0) +ImageDecoderQt::ImageDecoderQt() + : m_buffer(0) , m_reader(0) - , m_repetitionCount(-1) + , m_repetitionCount(cAnimationNone) { } @@ -73,13 +64,13 @@ void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) if (m_failed) return; - // Cache our own new data. - ImageDecoder::setData(data, allDataReceived); - // No progressive loading possible if (!allDataReceived) return; + // Cache our own new data. + ImageDecoder::setData(data, allDataReceived); + // We expect to be only called once with allDataReceived ASSERT(!m_buffer); ASSERT(!m_reader); @@ -89,15 +80,12 @@ void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) m_buffer = new QBuffer; m_buffer->setData(imageData); m_buffer->open(QBuffer::ReadOnly); - m_reader = new QImageReader(m_buffer); - - if (!m_reader->canRead()) - failRead(); + m_reader = new QImageReader(m_buffer, m_format); } bool ImageDecoderQt::isSizeAvailable() { - if (!m_failed && !ImageDecoder::isSizeAvailable() && m_reader) + if (!ImageDecoder::isSizeAvailable() && m_reader) internalDecodeSize(); return ImageDecoder::isSizeAvailable(); @@ -134,14 +122,16 @@ int ImageDecoderQt::repetitionCount() const String ImageDecoderQt::filenameExtension() const { - return m_format; + return String(m_format.constData(), m_format.length()); }; RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index) { - // this information might not have been set + // 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 (count == 0) { + if (!m_failed && count == 0) { internalDecodeSize(); count = frameCount(); } @@ -171,7 +161,12 @@ void ImageDecoderQt::internalDecodeSize() { ASSERT(m_reader); + // If we have a QSize() something failed QSize size = m_reader->size(); + if (size.isEmpty()) + return failRead(); + + m_format = m_reader->format(); setSize(size.width(), size.height()); } @@ -213,9 +208,15 @@ void ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) buffer->setDecodedImage(img); } -// We will parse everything and we have no idea how -// many images we have... We will have to find out the -// hard way. +// The QImageIOHandler is not able to tell us how many frames +// we have and we need to parse every image. We do this by +// increasing the m_frameBufferCache by one and try to parse +// the image. We stop when QImage::read fails and then need +// to resize the m_frameBufferCache to the final size and update +// the m_failed. In case we failed to decode the first image +// we want to keep m_failed set to true. + +// TODO: Do not increment the m_frameBufferCache.size() by one but more than one void ImageDecoderQt::forceLoadEverything() { int imageCount = 0; @@ -225,9 +226,12 @@ void ImageDecoderQt::forceLoadEverything() internalHandleCurrentImage(imageCount - 1); } while(!m_failed); - // reset the failed state and resize the vector... + // If we failed decoding the first image we actually + // have no images and need to keep m_failed set to + // true otherwise we want to reset it and forget about + // the last attempt to decode a image. m_frameBufferCache.resize(imageCount - 1); - m_failed = false; + m_failed = imageCount == 1; } void ImageDecoderQt::failRead() diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.h b/WebCore/platform/graphics/qt/ImageDecoderQt.h index 7b3b686..d11b938 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.h +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -40,7 +40,7 @@ namespace WebCore { class ImageDecoderQt : public ImageDecoder { public: - ImageDecoderQt(const QByteArray& imageFormat); + ImageDecoderQt(); ~ImageDecoderQt(); virtual void setData(SharedBuffer* data, bool allDataReceived); @@ -65,7 +65,7 @@ private: void failRead(); private: - String m_format; + QByteArray m_format; QBuffer* m_buffer; QImageReader* m_reader; mutable int m_repetitionCount; diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index c9f1349..889c41b 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -296,7 +296,7 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness r.inset(SkIntToScalar(thickness), SkIntToScalar(thickness)); path.addOval(r, SkPath::kCCW_Direction); } - platformContext()->canvas()->clipPath(path); + platformContext()->clipPathAntiAliased(path); } void GraphicsContext::addPath(const Path& path) @@ -356,6 +356,18 @@ void GraphicsContext::clip(const Path& path) if (!isPathSkiaSafe(getCTM(), p)) return; + platformContext()->clipPathAntiAliased(p); +} + +void GraphicsContext::canvasClip(const Path& path) +{ + if (paintingDisabled()) + return; + + const SkPath& p = *path.platformPath(); + if (!isPathSkiaSafe(getCTM(), p)) + return; + platformContext()->canvas()->clipPath(p); } @@ -407,7 +419,7 @@ void GraphicsContext::clipPath(WindRule clipRule) return; path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); - platformContext()->canvas()->clipPath(path); + platformContext()->clipPathAntiAliased(path); } void GraphicsContext::clipToImageBuffer(const FloatRect& rect, diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp index 5ac14b9..2cbb759 100644 --- a/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -123,26 +123,31 @@ void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlo SkScalar cx = WebCoreFloatToSkScalar(p.x()); SkScalar cy = WebCoreFloatToSkScalar(p.y()); SkScalar radius = WebCoreFloatToSkScalar(r); + SkScalar s360 = SkIntToScalar(360); SkRect oval; oval.set(cx - radius, cy - radius, cx + radius, cy + radius); float sweep = ea - sa; - // check for a circle - if (sweep >= 2 * piFloat || sweep <= -2 * piFloat) + SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat); + SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); + // Check for a circle. + if (sweepDegrees >= s360 || sweepDegrees <= -s360) { + // Move to the start position (0 sweep means we add a single point). + m_path->arcTo(oval, startDegrees, 0, false); + // Draw the circle. m_path->addOval(oval); - else { - SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat); - SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); - + // Force a moveTo the end position. + m_path->arcTo(oval, startDegrees + sweepDegrees, 0, true); + } else { // Counterclockwise arcs should be drawn with negative sweeps, while // clockwise arcs should be drawn with positive sweeps. Check to see // if the situation is reversed and correct it by adding or subtracting // a full circle if (anticlockwise && sweepDegrees > 0) { - sweepDegrees -= SkIntToScalar(360); + sweepDegrees -= s360; } else if (!anticlockwise && sweepDegrees < 0) { - sweepDegrees += SkIntToScalar(360); + sweepDegrees += s360; } m_path->arcTo(oval, startDegrees, sweepDegrees, false); diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 1fb62fc..a079da0 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -45,6 +45,7 @@ #include "SkDashPathEffect.h" #include <wtf/MathExtras.h> +#include <wtf/Vector.h> namespace WebCore { @@ -95,6 +96,10 @@ struct PlatformContextSkia::State { WebCore::FloatRect m_clip; #endif + // This is a list of clipping paths which are currently active, in the + // order in which they were pushed. + WTF::Vector<SkPath> m_antiAliasClipPaths; + private: // Not supported. void operator=(const State&); @@ -110,8 +115,8 @@ PlatformContextSkia::State::State() , m_fillShader(0) , m_strokeStyle(WebCore::SolidStroke) , m_strokeColor(WebCore::Color::black) - , m_strokeThickness(0) , m_strokeShader(0) + , m_strokeThickness(0) , m_dashRatio(3) , m_miterLimit(4) , m_lineCap(SkPaint::kDefault_Cap) @@ -130,8 +135,8 @@ PlatformContextSkia::State::State(const State& other) , m_fillShader(other.m_fillShader) , m_strokeStyle(other.m_strokeStyle) , m_strokeColor(other.m_strokeColor) - , m_strokeThickness(other.m_strokeThickness) , m_strokeShader(other.m_strokeShader) + , m_strokeThickness(other.m_strokeThickness) , m_dashRatio(other.m_dashRatio) , m_miterLimit(other.m_miterLimit) , m_lineCap(other.m_lineCap) @@ -249,6 +254,21 @@ void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rec } #endif +void PlatformContextSkia::clipPathAntiAliased(const SkPath& clipPath) +{ + // If we are currently tracking any anti-alias clip paths, then we already + // have a layer in place and don't need to add another. + bool haveLayerOutstanding = m_state->m_antiAliasClipPaths.size(); + + // See comments in applyAntiAliasedClipPaths about how this works. + m_state->m_antiAliasClipPaths.append(clipPath); + + if (!haveLayerOutstanding) { + SkRect bounds = clipPath.getBounds(); + canvas()->saveLayerAlpha(&bounds, 255, static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag)); + } +} + void PlatformContextSkia::restore() { #if defined(__linux__) || PLATFORM(WIN_OS) @@ -258,6 +278,9 @@ void PlatformContextSkia::restore() } #endif + if (!m_state->m_antiAliasClipPaths.isEmpty()) + applyAntiAliasedClipPaths(m_state->m_antiAliasClipPaths); + m_stateStack.removeLast(); m_state = &m_stateStack.last(); @@ -549,3 +572,40 @@ void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, con m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); } #endif + +void PlatformContextSkia::applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths) +{ + // Anti-aliased clipping: + // + // Skia's clipping is 1-bit only. Consider what would happen if it were 8-bit: + // We have a square canvas, filled with white and we declare a circular + // clipping path. Then we fill twice with a black rectangle. The fractional + // pixels would first get the correct color (white * alpha + black * (1 - + // alpha)), but the second fill would apply the alpha to the already + // modified color and the result would be too dark. + // + // This, anti-aliased clipping needs to be performed after the drawing has + // been done. In order to do this, we create a new layer of the canvas in + // clipPathAntiAliased and store the clipping path. All drawing is done to + // the layer's bitmap while it's in effect. When WebKit calls restore() to + // undo the clipping, this function is called. + // + // Here, we walk the list of clipping paths backwards and, for each, we + // clear outside of the clipping path. We only need a single extra layer + // for any number of clipping paths. + // + // When we call restore on the SkCanvas, the layer's bitmap is composed + // into the layer below and we end up with correct, anti-aliased clipping. + + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kClear_Mode); + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + + for (size_t i = paths.size() - 1; i < paths.size(); --i) { + paths[i].setFillType(SkPath::kInverseWinding_FillType); + m_canvas->drawPath(paths[i], paint); + } + + m_canvas->restore(); +} diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 0c87fc2..53590bf 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -92,6 +92,7 @@ public: void beginLayerClippedToImage(const WebCore::FloatRect&, const WebCore::ImageBuffer*); #endif + void clipPathAntiAliased(const SkPath&); // Sets up the common flags on a paint for antialiasing, effects, etc. // This is implicitly called by setupPaintFill and setupPaintStroke, but @@ -172,6 +173,7 @@ private: // m_canvas that are also in imageBuffer. void applyClipFromImage(const WebCore::FloatRect&, const SkBitmap&); #endif + void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths); // Defines drawing style. struct State; diff --git a/WebCore/platform/graphics/skia/SkiaUtils.cpp b/WebCore/platform/graphics/skia/SkiaUtils.cpp index bb15aa2..377ca06 100644 --- a/WebCore/platform/graphics/skia/SkiaUtils.cpp +++ b/WebCore/platform/graphics/skia/SkiaUtils.cpp @@ -200,8 +200,13 @@ bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath:: SkRect bounds = originalPath->getBounds(); - // We can immediately return false if the point is outside the bounding rect - if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) + // We can immediately return false if the point is outside the bounding + // rect. We don't use bounds.contains() here, since it would exclude + // points on the right and bottom edges of the bounding rect, and we want + // to include them. + SkScalar fX = SkFloatToScalar(point.x()); + SkScalar fY = SkFloatToScalar(point.y()); + if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) return false; originalPath->setFillType(ft); @@ -225,7 +230,7 @@ bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath:: int x = static_cast<int>(floorf(point.x() / scale)); int y = static_cast<int>(floorf(point.y() / scale)); - clip.setRect(x, y, x + 1, y + 1); + clip.setRect(x - 1, y - 1, x + 1, y + 1); bool contains = rgn.setPath(*path, clip); diff --git a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp index 26b22af..e845d85 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -34,11 +34,11 @@ #include "Font.h" #include "FontCache.h" #include "FontDescription.h" -#include "MathExtras.h" #include <cairo.h> #include <cairo-win32.h> #include <mlang.h> #include <tchar.h> +#include <wtf/MathExtras.h> namespace WebCore { diff --git a/WebCore/platform/graphics/wince/GraphicsContextWince.cpp b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp index c114c0e..f308840 100644 --- a/WebCore/platform/graphics/wince/GraphicsContextWince.cpp +++ b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp @@ -1219,6 +1219,11 @@ void GraphicsContext::clip(const Path& path) notImplemented(); } +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + void GraphicsContext::clipOut(const Path&) { notImplemented(); diff --git a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index 686fb07..39f14f4 100644 --- a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -344,6 +344,11 @@ void GraphicsContext::clip(const Path&) notImplemented(); } +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*) { notImplemented(); |