diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/qt | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/qt')
41 files changed, 10588 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/qt/ColorQt.cpp b/Source/WebCore/platform/graphics/qt/ColorQt.cpp new file mode 100644 index 0000000..151766a --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ColorQt.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Color.h" + +#include <QColor> + +namespace WebCore { + +Color::Color(const QColor& c) + : m_color(makeRGBA(c.red(), c.green(), c.blue(), c.alpha())) +{ + m_valid = c.isValid(); +} + +Color::operator QColor() const +{ + if (m_valid) + return QColor(red(), green(), blue(), alpha()); + else + return QColor(); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp b/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp new file mode 100644 index 0000000..834ca62 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2010 Sencha, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ContextShadow.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" +#include <QPainter> +#include <QTimerEvent> + +namespace WebCore { + +// ContextShadow needs a scratch image as the buffer for the blur filter. +// Instead of creating and destroying the buffer for every operation, +// we create a buffer which will be automatically purged via a timer. + +class ShadowBuffer: public QObject { +public: + ShadowBuffer(QObject* parent = 0); + + QImage* scratchImage(const QSize& size); + + void schedulePurge(); + +protected: + void timerEvent(QTimerEvent* event); + +private: + QImage image; + int timerId; +}; + +ShadowBuffer::ShadowBuffer(QObject* parent) + : QObject(parent) + , timerId(0) +{ +} + +QImage* ShadowBuffer::scratchImage(const QSize& size) +{ + int width = size.width(); + int height = size.height(); + + // We do not need to recreate the buffer if the buffer is reasonably + // larger than the requested size. However, if the requested size is + // much smaller than our buffer, reduce our buffer so that we will not + // keep too many allocated pixels for too long. + if (!image.isNull() && (image.width() > width) && (image.height() > height)) + if (((2 * width) > image.width()) && ((2 * height) > image.height())) { + image.fill(Qt::transparent); + return ℑ + } + + // Round to the nearest 32 pixels so we do not grow the buffer everytime + // there is larger request by 1 pixel. + width = (1 + (width >> 5)) << 5; + height = (1 + (height >> 5)) << 5; + + image = QImage(width, height, QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + return ℑ +} + +void ShadowBuffer::schedulePurge() +{ + static const double BufferPurgeDelay = 2; // seconds + killTimer(timerId); + timerId = startTimer(BufferPurgeDelay * 1000); +} + +void ShadowBuffer::timerEvent(QTimerEvent* event) +{ + if (event->timerId() == timerId) { + killTimer(timerId); + image = QImage(); + } + QObject::timerEvent(event); +} + +Q_GLOBAL_STATIC(ShadowBuffer, scratchShadowBuffer) + +PlatformContext ContextShadow::beginShadowLayer(GraphicsContext* context, const FloatRect& layerArea) +{ + // Set m_blurDistance. + adjustBlurDistance(context); + + PlatformContext p = context->platformContext(); + + QRect clipRect; + if (p->hasClipping()) +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) + clipRect = p->clipBoundingRect().toAlignedRect(); +#else + clipRect = p->clipRegion().boundingRect(); +#endif + else + clipRect = p->transform().inverted().mapRect(p->window()); + + // Set m_layerOrigin, m_layerContextTranslation, m_sourceRect. + IntRect clip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + IntRect layerRect = calculateLayerBoundingRect(context, layerArea, clip); + + // Don't paint if we are totally outside the clip region. + if (layerRect.isEmpty()) + return 0; + + ShadowBuffer* shadowBuffer = scratchShadowBuffer(); + QImage* shadowImage = shadowBuffer->scratchImage(layerRect.size()); + m_layerImage = QImage(*shadowImage); + + m_layerContext = new QPainter; + m_layerContext->begin(&m_layerImage); + m_layerContext->setFont(p->font()); + m_layerContext->translate(m_layerContextTranslation); + return m_layerContext; +} + +void ContextShadow::endShadowLayer(GraphicsContext* context) +{ + m_layerContext->end(); + delete m_layerContext; + m_layerContext = 0; + + if (m_type == BlurShadow) { + blurLayerImage(m_layerImage.bits(), IntSize(m_layerImage.width(), m_layerImage.height()), + m_layerImage.bytesPerLine()); + } + + if (m_type != NoShadow) { + // "Colorize" with the right shadow color. + QPainter p(&m_layerImage); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(m_layerImage.rect(), m_color.rgb()); + p.end(); + } + + context->platformContext()->drawImage(m_layerOrigin, m_layerImage, m_sourceRect); + + scratchShadowBuffer()->schedulePurge(); +} + +} diff --git a/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp b/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp new file mode 100644 index 0000000..cd28f0e --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp @@ -0,0 +1,61 @@ +/* + * 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 "Extensions3DQt.h" + +#include "GraphicsContext3D.h" + +namespace WebCore { + +Extensions3DQt::Extensions3DQt() +{ +} + +Extensions3DQt::~Extensions3DQt() +{ +} + +bool Extensions3DQt::supports(const String&) +{ + return false; +} + +void Extensions3DQt::ensureEnabled(const String& name) +{ + ASSERT(supports(name)); +} + +int Extensions3DQt::getGraphicsResetStatusARB() +{ + return GraphicsContext3D::NO_ERROR; +} + +} // namespace WebCore + +#endif // ENABLE(3D_CANVAS) diff --git a/Source/WebCore/platform/graphics/qt/Extensions3DQt.h b/Source/WebCore/platform/graphics/qt/Extensions3DQt.h new file mode 100644 index 0000000..ae4b375 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/Extensions3DQt.h @@ -0,0 +1,50 @@ +/* + * 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 AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Extensions3DQt_h +#define Extensions3DQt_h + +#include "Extensions3D.h" + +namespace WebCore { + +class Extensions3DQt : public Extensions3D { +public: + virtual ~Extensions3DQt(); + + // Extensions3D methods. + virtual bool supports(const String&); + virtual void ensureEnabled(const String&); + virtual int getGraphicsResetStatusARB(); + +private: + // This class only needs to be instantiated by GraphicsContext3D implementations. + friend class GraphicsContext3D; + Extensions3DQt(); +}; + +} // namespace WebCore + +#endif // Extensions3DQt_h diff --git a/Source/WebCore/platform/graphics/qt/FloatPointQt.cpp b/Source/WebCore/platform/graphics/qt/FloatPointQt.cpp new file mode 100644 index 0000000..82093d8 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FloatPointQt.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FloatPoint.h" + +#include <QPointF> + +namespace WebCore { + +FloatPoint::FloatPoint(const QPointF& p) + : m_x(p.x()) + , m_y(p.y()) +{ +} + +FloatPoint::operator QPointF() const +{ + return QPointF(m_x, m_y); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/FloatRectQt.cpp b/Source/WebCore/platform/graphics/qt/FloatRectQt.cpp new file mode 100644 index 0000000..063876b --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FloatRectQt.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FloatRect.h" + +#include <QRectF> + +namespace WebCore { + +FloatRect::FloatRect(const QRectF& r) + : m_location(r.topLeft()) + , m_size(r.width() + , r.height()) +{ +} + +FloatRect::operator QRectF() const +{ + return QRectF(x(), y(), width(), height()); +} + +FloatRect FloatRect::normalized() const +{ + FloatRect normalizedRect = *this; + + if (width() < 0) { + normalizedRect.setX(x() + width()); + normalizedRect.setWidth(-width()); + } + if (height() < 0) { + normalizedRect.setY(y() + height()); + normalizedRect.setHeight(-height()); + } + return normalizedRect; +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/FontCacheQt.cpp b/Source/WebCore/platform/graphics/qt/FontCacheQt.cpp new file mode 100644 index 0000000..c59c523 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FontCacheQt.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> + + 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 + along 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. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ +#include "config.h" +#include "FontCache.h" + +#include "FontDescription.h" +#include "FontPlatformData.h" +#include "Font.h" +#include "PlatformString.h" +#include <utility> +#include <wtf/ListHashSet.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.h> + +#include <QFont> + +using namespace WTF; + +namespace WebCore { + +void FontCache::platformInit() +{ +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font&, const UChar*, int) +{ + return 0; +} + +SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) +{ + const AtomicString fallbackFamily = QFont(fontDescription.family().family()).lastResortFamily(); + return getCachedFontData(new FontPlatformData(fontDescription, fallbackFamily)); +} + +void FontCache::getTraitsInFamily(const AtomicString&, Vector<unsigned>&) +{ +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) +{ + return new FontPlatformData(fontDescription, familyName); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h new file mode 100644 index 0000000..6c41d47 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ +#ifndef FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include "FontOrientation.h" +#include "FontRenderingMode.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { + ~FontCustomPlatformData(); + + // for use with QFontDatabase::addApplicationFont/removeApplicationFont + int m_handle; + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + + static bool supportsFormat(const String&); +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); + +} // namespace WebCore + +#endif // FontCustomPlatformData_h diff --git a/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp b/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp new file mode 100644 index 0000000..e2f009b --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ +#include "config.h" +#include "FontCustomPlatformData.h" + +#include "FontPlatformData.h" +#include "SharedBuffer.h" +#include <QFontDatabase> +#include <QStringList> + +namespace WebCore { + +FontCustomPlatformData::~FontCustomPlatformData() +{ + QFontDatabase::removeApplicationFont(m_handle); +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode) +{ + QFont font; + font.setFamily(QFontDatabase::applicationFontFamilies(m_handle)[0]); + font.setPixelSize(size); + if (bold) + font.setWeight(QFont::Bold); + font.setItalic(italic); + + return FontPlatformData(font); +} + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) +{ + ASSERT_ARG(buffer, buffer); + + int id = QFontDatabase::addApplicationFontFromData(QByteArray(buffer->data(), buffer->size())); + if (id == -1) + return 0; + + Q_ASSERT(QFontDatabase::applicationFontFamilies(id).size() > 0); + + FontCustomPlatformData *data = new FontCustomPlatformData; + data->m_handle = id; + return data; +} + +bool FontCustomPlatformData::supportsFormat(const String& format) +{ + return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype"); +} + +} diff --git a/Source/WebCore/platform/graphics/qt/FontPlatformData.h b/Source/WebCore/platform/graphics/qt/FontPlatformData.h new file mode 100644 index 0000000..1c57e29 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FontPlatformData.h @@ -0,0 +1,175 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + + 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 + along 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. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ +#ifndef FontPlatformData_h +#define FontPlatformData_h + +#include <wtf/Forward.h> +#include "FontDescription.h" +#include "FontOrientation.h" +#include <QFont> +#include <QHash> + +namespace WebCore { + +class FontPlatformDataPrivate : public Noncopyable { +public: + FontPlatformDataPrivate() + : refCount(1) + , size(font.pixelSize()) + , bold(font.bold()) + , oblique(false) + {} + FontPlatformDataPrivate(const float size, const bool bold, const bool oblique) + : refCount(1) + , size(size) + , bold(bold) + , oblique(oblique) + {} + FontPlatformDataPrivate(const QFont& font) + : refCount(1) + , font(font) + , size(font.pixelSize()) + , bold(font.bold()) + , oblique(false) + {} + unsigned refCount; + QFont font; + float size; + bool bold : 1; + bool oblique : 1; +}; + + + +class FontPlatformData : public FastAllocBase { +public: + FontPlatformData(float size, bool bold, bool oblique); + FontPlatformData(const FontPlatformData &); + FontPlatformData(const FontDescription&, const AtomicString& familyName, int wordSpacing = 0, int letterSpacing = 0); + FontPlatformData(const QFont& font) + : m_data(new FontPlatformDataPrivate(font)) + {} + FontPlatformData(WTF::HashTableDeletedValueType) + : m_data(reinterpret_cast<FontPlatformDataPrivate*>(-1)) + {} + + ~FontPlatformData(); + + FontPlatformData& operator=(const FontPlatformData&); + bool operator==(const FontPlatformData&) const; + + bool isHashTableDeletedValue() const + { + return m_data == reinterpret_cast<FontPlatformDataPrivate*>(-1); + } + + static inline QFont::Weight toQFontWeight(FontWeight fontWeight) + { + switch (fontWeight) { + case FontWeight100: + case FontWeight200: + return QFont::Light; // QFont::Light == Weight of 25 + case FontWeight600: + return QFont::DemiBold; // QFont::DemiBold == Weight of 63 + case FontWeight700: + case FontWeight800: + return QFont::Bold; // QFont::Bold == Weight of 75 + case FontWeight900: + return QFont::Black; // QFont::Black == Weight of 87 + case FontWeight300: + case FontWeight400: + case FontWeight500: + default: + return QFont::Normal; // QFont::Normal == Weight of 50 + } + } + + QFont font() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) + return m_data->font; + return QFont(); + } + float size() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) + return m_data->size; + return 0.0f; + } + QString family() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) + return m_data->font.family(); + return QString(); + } + bool bold() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) + return m_data->bold; + return false; + } + bool italic() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) + return m_data->font.italic(); + return false; + } + bool smallCaps() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) + return m_data->font.capitalization() == QFont::SmallCaps; + return false; + } + int pixelSize() const + { + Q_ASSERT(m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)); + if (m_data) { + // WebKit allows font size zero but QFont does not. + if (!m_data->size) + return m_data->size; + return m_data->font.pixelSize(); + } + return 0; + } + + FontOrientation orientation() const { return Horizontal; } // FIXME: Implement. + + unsigned hash() const; + +#ifndef NDEBUG + String description() const; +#endif +private: + FontPlatformDataPrivate* m_data; +}; + +} // namespace WebCore + +#endif // FontPlatformData_h diff --git a/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp new file mode 100644 index 0000000..4c9eb32 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -0,0 +1,132 @@ +/* + Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + + 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 + along 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" +#include "FontPlatformData.h" + +#include "PlatformString.h" + +namespace WebCore { + +static inline bool isEmptyValue(const float size, const bool bold, const bool oblique) +{ + // this is the empty value by definition of the trait FontDataCacheKeyTraits + return !bold && !oblique && size == 0.f; +} + +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) +{ + if (isEmptyValue(size, bold, oblique)) + m_data = 0; + else + m_data = new FontPlatformDataPrivate(size, bold, oblique); +} + +FontPlatformData::FontPlatformData(const FontPlatformData &other) : m_data(other.m_data) +{ + if (m_data && m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)) + ++m_data->refCount; +} + +FontPlatformData::FontPlatformData(const FontDescription& description, const AtomicString& familyName, int wordSpacing, int letterSpacing) + : m_data(new FontPlatformDataPrivate()) +{ + QFont& font = m_data->font; + int requestedSize = qRound(description.computedPixelSize()); + font.setFamily(familyName); + font.setPixelSize(qRound(requestedSize)); + font.setItalic(description.italic()); + font.setWeight(toQFontWeight(description.weight())); + font.setWordSpacing(wordSpacing); + font.setLetterSpacing(QFont::AbsoluteSpacing, letterSpacing); + const bool smallCaps = description.smallCaps(); + font.setCapitalization(smallCaps ? QFont::SmallCaps : QFont::MixedCase); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + font.setStyleStrategy(QFont::ForceIntegerMetrics); +#endif + + m_data->bold = font.bold(); + // WebKit allows font size zero but QFont does not. We will return + // m_data->size if a font size of zero is requested and pixelSize() + // otherwise. + m_data->size = (!requestedSize) ? requestedSize : font.pixelSize(); +} + +FontPlatformData::~FontPlatformData() +{ + if (!m_data || m_data == reinterpret_cast<FontPlatformDataPrivate*>(-1)) + return; + --m_data->refCount; + if (!m_data->refCount) + delete m_data; +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) +{ + if (m_data == other.m_data) + return *this; + if (m_data && m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)) { + --m_data->refCount; + if (!m_data->refCount) + delete m_data; + } + m_data = other.m_data; + if (m_data && m_data != reinterpret_cast<FontPlatformDataPrivate*>(-1)) + ++m_data->refCount; + return *this; +} + +bool FontPlatformData::operator==(const FontPlatformData& other) const +{ + if (m_data == other.m_data) + return true; + + if (!m_data || !other.m_data + || m_data == reinterpret_cast<FontPlatformDataPrivate*>(-1) || other.m_data == reinterpret_cast<FontPlatformDataPrivate*>(-1)) + return false; + + const bool equals = (m_data->size == other.m_data->size + && m_data->bold == other.m_data->bold + && m_data->oblique == other.m_data->oblique + && m_data->font == other.m_data->font); + return equals; +} + +unsigned FontPlatformData::hash() const +{ + if (!m_data) + return 0; + if (m_data == reinterpret_cast<FontPlatformDataPrivate*>(-1)) + return 1; + return qHash(m_data->font.toString()) + ^ qHash(*reinterpret_cast<quint32*>(&m_data->size)) + ^ qHash(m_data->bold) + ^ qHash(m_data->oblique); +} + +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + +} diff --git a/Source/WebCore/platform/graphics/qt/FontQt.cpp b/Source/WebCore/platform/graphics/qt/FontQt.cpp new file mode 100644 index 0000000..f1ced2b --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/FontQt.cpp @@ -0,0 +1,432 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008, 2010 Holger Hans Peter Freyther + Copyright (C) 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 + along 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" +#include "Font.h" + +#include "AffineTransform.h" +#include "ContextShadow.h" +#include "FontDescription.h" +#include "FontFallbackList.h" +#include "FontSelector.h" +#include "Gradient.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "Pattern.h" + +#include <QBrush> +#include <QFontInfo> +#include <QFontMetrics> +#include <QPainter> +#include <QPainterPath> +#include <QPen> +#include <QTextLayout> +#include <qalgorithms.h> +#include <qdebug.h> + +#include <limits.h> + +namespace WebCore { + +static const QString fromRawDataWithoutRef(const String& string, int start = 0, int len = -1) +{ + if (len < 0) + len = string.length() - start; + Q_ASSERT(start + len <= string.length()); + + // We don't detach. This assumes the WebCore string data will stay valid for the + // lifetime of the QString we pass back, since we don't ref the WebCore string. + return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters() + start), len); +} + +static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) +{ + int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; + if (style.padding()) + flags |= Qt::TextJustificationForced; + layout->setFlags(flags); + layout->beginLayout(); + QTextLine line = layout->createLine(); + line.setLineWidth(INT_MAX/256); + if (style.padding()) + line.setLineWidth(line.naturalTextWidth() + style.padding()); + layout->endLayout(); + return line; +} + +static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText) +{ + if (to < 0) + to = run.length(); + + QPainter *p = ctx->platformContext(); + + QPen textFillPen; + if (ctx->textDrawingMode() & TextModeFill) { + if (ctx->fillGradient()) { + QBrush brush(*ctx->fillGradient()->platformGradient()); + brush.setTransform(ctx->fillGradient()->gradientSpaceTransform()); + textFillPen = QPen(brush, 0); + } else if (ctx->fillPattern()) { + AffineTransform affine; + textFillPen = QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0); + } else + textFillPen = QPen(QColor(ctx->fillColor())); + } + + QPen textStrokePen; + if (ctx->textDrawingMode() & TextModeStroke) { + if (ctx->strokeGradient()) { + QBrush brush(*ctx->strokeGradient()->platformGradient()); + brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform()); + textStrokePen = QPen(brush, ctx->strokeThickness()); + } else if (ctx->strokePattern()) { + AffineTransform affine; + QBrush brush(ctx->strokePattern()->createPlatformPattern(affine)); + textStrokePen = QPen(brush, ctx->strokeThickness()); + } else + textStrokePen = QPen(QColor(ctx->strokeColor()), ctx->strokeThickness()); + } + + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + QPointF pt(point.x(), point.y()); + + if (from > 0 || to < run.length()) { + if (isComplexText) { + QTextLayout layout(string, font); + QTextLine line = setupLayout(&layout, run); + float x1 = line.cursorToX(from); + float x2 = line.cursorToX(to); + if (x2 < x1) + qSwap(x1, x2); + + QFontMetrics fm(font); + int ascent = fm.ascent(); + QRectF boundingRect(point.x() + x1, point.y() - ascent, x2 - x1, fm.height()); + QRectF clip = boundingRect; + + ContextShadow* ctxShadow = ctx->contextShadow(); + + if (ctxShadow->m_type != ContextShadow::NoShadow) { + qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0; + if (ctxShadow->offset().x() > 0) + dx2 = ctxShadow->offset().x(); + else + dx1 = -ctxShadow->offset().x(); + if (ctxShadow->offset().y() > 0) + dy2 = ctxShadow->offset().y(); + else + dy1 = -ctxShadow->offset().y(); + // expand the clip rect to include the text shadow as well + clip.adjust(dx1, dx2, dy1, dy2); + clip.adjust(-ctxShadow->m_blurDistance, -ctxShadow->m_blurDistance, ctxShadow->m_blurDistance, ctxShadow->m_blurDistance); + } + p->save(); + p->setClipRect(clip.toRect(), Qt::IntersectClip); + pt.setY(pt.y() - ascent); + + if (ctxShadow->m_type != ContextShadow::NoShadow) { + ContextShadow* ctxShadow = ctx->contextShadow(); + if (!ctxShadow->mustUseContextShadow(ctx)) { + p->save(); + p->setPen(ctxShadow->m_color); + p->translate(ctxShadow->offset()); + line.draw(p, pt); + p->restore(); + } else { + QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect); + if (shadowPainter) { + // Since it will be blurred anyway, we don't care about render hints. + shadowPainter->setFont(p->font()); + shadowPainter->setPen(ctxShadow->m_color); + line.draw(shadowPainter, pt); + ctxShadow->endShadowLayer(ctx); + } + } + } + p->setPen(textFillPen); + line.draw(p, pt); + p->restore(); + return; + } +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping); + pt.setX(pt.x() + skipWidth); + string = fromRawDataWithoutRef(sanitized, from, to - from); +#endif + } + + p->setFont(font); + + int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + // See QWebPagePrivate::QWebPagePrivate() where the default path is set to Complex for Qt 4.6 and earlier. + if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke)) + flags |= Qt::TextBypassShaping; +#endif + + QPainterPath textStrokePath; + if (ctx->textDrawingMode() & TextModeStroke) + textStrokePath.addText(pt, font, string); + + ContextShadow* ctxShadow = ctx->contextShadow(); + if (ctxShadow->m_type != ContextShadow::NoShadow) { + if (ctx->textDrawingMode() & TextModeFill) { + if (ctxShadow->m_type != ContextShadow::BlurShadow) { + p->save(); + p->setPen(ctxShadow->m_color); + p->translate(ctxShadow->offset()); + p->drawText(pt, string, flags, run.padding()); + p->restore(); + } else { + QFontMetrics fm(font); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height()); +#else + QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string), fm.height()); +#endif + QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect); + if (shadowPainter) { + // Since it will be blurred anyway, we don't care about render hints. + shadowPainter->setFont(p->font()); + shadowPainter->setPen(ctxShadow->m_color); + shadowPainter->drawText(pt, string, flags, run.padding()); + ctxShadow->endShadowLayer(ctx); + } + } + } else if (ctx->textDrawingMode() & TextModeStroke) { + if (ctxShadow->m_type != ContextShadow::BlurShadow) { + p->translate(ctxShadow->offset()); + p->strokePath(textStrokePath, QPen(ctxShadow->m_color)); + p->translate(-ctxShadow->offset()); + } else { + QFontMetrics fm(font); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height()); +#else + QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string), fm.height()); +#endif + QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect); + if (shadowPainter) { + // Since it will be blurred anyway, we don't care about render hints. + shadowPainter->setFont(p->font()); + shadowPainter->strokePath(textStrokePath, QPen(ctxShadow->m_color)); + ctxShadow->endShadowLayer(ctx); + } + } + } + } + + if (ctx->textDrawingMode() & TextModeStroke) + p->strokePath(textStrokePath, textStrokePen); + + if (ctx->textDrawingMode() & TextModeFill) { + QPen previousPen = p->pen(); + p->setPen(textFillPen); + p->drawText(pt, string, flags, run.padding()); + p->setPen(previousPen); + } +} + +void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false); +#else + Q_ASSERT(false); +#endif +} + +void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const +{ + drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true); +} + +int Font::emphasisMarkAscent(const AtomicString&) const +{ + notImplemented(); + return 0; +} + +int Font::emphasisMarkDescent(const AtomicString&) const +{ + notImplemented(); + return 0; +} + +int Font::emphasisMarkHeight(const AtomicString&) const +{ + notImplemented(); + return 0; +} + +void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const +{ + notImplemented(); +} + +void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const +{ + notImplemented(); +} + +float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +{ + if (!primaryFont()->platformData().size()) + return 0; + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + if (!run.length()) + return 0; + + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + + int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping); + + // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does) + if (treatAsSpace(run[0])) + w -= m_wordSpacing; + + return w + run.padding(); +#else + Q_ASSERT(false); + return 0; +#endif +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const +{ + if (!primaryFont()->platformData().size()) + return 0; + + if (!run.length()) + return 0; + + if (run.length() == 1 && treatAsSpace(run[0])) + return QFontMetrics(font()).width(space) + run.padding(); + + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + + int w = QFontMetrics(font()).width(string); + // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does) + if (treatAsSpace(run[0])) + w -= m_wordSpacing; + + return w + run.padding(); +} + +int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + + QFontMetrics fm(font()); + float delta = position; + int curPos = 0; + do { + float charWidth = fm.width(string[curPos]); + delta -= charWidth; + if (includePartialGlyphs) { + if (delta + charWidth / 2 <= 0) + break; + } else { + if (delta + charWidth <= 0) + break; + } + } while (++curPos < string.size()); + + return curPos; +#else + Q_ASSERT(false); + return 0; +#endif +} + +int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const +{ + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + + QTextLayout layout(string, font()); + QTextLine line = setupLayout(&layout, run); + return line.xToCursor(position); +} + +FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString wholeText = fromRawDataWithoutRef(sanitized); + QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from)); + + int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping); + int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping); + + return FloatRect(pt.x() + startX, pt.y(), width, h); +#else + Q_ASSERT(false); + return FloatRect(); +#endif +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const +{ + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + QString string = fromRawDataWithoutRef(sanitized); + + QTextLayout layout(string, font()); + QTextLine line = setupLayout(&layout, run); + + float x1 = line.cursorToX(from); + float x2 = line.cursorToX(to); + if (x2 < x1) + qSwap(x1, x2); + + return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h); +} + +bool Font::canReturnFallbackFontsForComplexText() +{ + return false; +} + +bool Font::primaryFontHasGlyphForCharacter(UChar32) const +{ + notImplemented(); + return true; +} + +QFont Font::font() const +{ + QFont f = primaryFont()->getQtFont(); + if (m_letterSpacing != 0) + f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing); + if (m_wordSpacing != 0) + f.setWordSpacing(m_wordSpacing); + return f; +} + +} + diff --git a/Source/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp b/Source/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp new file mode 100644 index 0000000..2121206 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/GlyphPageTreeNodeQt.cpp @@ -0,0 +1,36 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther + + 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 + along 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. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ +#include "config.h" +#include "GlyphPageTreeNode.h" + +namespace WebCore { + +void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData*) +{ +} + +void GlyphPageTreeNode::pruneTreeFontData(const WebCore::SimpleFontData*) +{ +} + +} diff --git a/Source/WebCore/platform/graphics/qt/GradientQt.cpp b/Source/WebCore/platform/graphics/qt/GradientQt.cpp new file mode 100644 index 0000000..72bb009 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/GradientQt.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Gradient.h" + +#include "CSSParser.h" +#include "GraphicsContext.h" + +#include <QGradient> +#include <QPainter> + +namespace WebCore { + +void Gradient::platformDestroy() +{ + delete m_gradient; + m_gradient = 0; +} + +QGradient* Gradient::platformGradient() +{ + if (m_gradient) + return m_gradient; + + bool reversed = m_r0 > m_r1; + + qreal innerRadius = reversed ? m_r1 : m_r0; + qreal outerRadius = reversed ? m_r0 : m_r1; + QPointF center = reversed ? m_p0 : m_p1; + QPointF focalPoint = reversed ? m_p1 : m_p0; + + if (m_radial) + m_gradient = new QRadialGradient(center, outerRadius, focalPoint); + else + m_gradient = new QLinearGradient(m_p0.x(), m_p0.y(), m_p1.x(), m_p1.y()); + + m_gradient->setInterpolationMode(QGradient::ComponentInterpolation); + + sortStopsIfNecessary(); + + QColor stopColor; + Vector<ColorStop>::iterator stopIterator = m_stops.begin(); + qreal lastStop(0.0); + const qreal lastStopDiff = 0.0000001; + while (stopIterator != m_stops.end()) { + stopColor.setRgbF(stopIterator->red, stopIterator->green, stopIterator->blue, stopIterator->alpha); + if (qFuzzyCompare(lastStop, qreal(stopIterator->stop))) + lastStop = stopIterator->stop + lastStopDiff; + else + lastStop = stopIterator->stop; + + if (m_radial && !qFuzzyCompare(1 + outerRadius, qreal(1))) { + lastStop = lastStop * (1.0f - innerRadius / outerRadius); + if (!reversed) + lastStop += innerRadius / outerRadius; + } + + qreal stopPosition = qMin(lastStop, qreal(1.0f)); + + if (m_radial && reversed) + stopPosition = 1 - stopPosition; + + m_gradient->setColorAt(stopPosition, stopColor); + // Keep the lastStop as orginal value, since the following stopColor depend it + lastStop = stopIterator->stop; + ++stopIterator; + } + + if (m_stops.isEmpty()) { + // The behavior of QGradient with no stops is defined differently from HTML5 spec, + // where the latter requires the gradient to be transparent black. + m_gradient->setColorAt(0.0, QColor(0, 0, 0, 0)); + } + + switch (m_spreadMethod) { + case SpreadMethodPad: + m_gradient->setSpread(QGradient::PadSpread); + break; + case SpreadMethodReflect: + m_gradient->setSpread(QGradient::ReflectSpread); + break; + case SpreadMethodRepeat: + m_gradient->setSpread(QGradient::RepeatSpread); + break; + } + + return m_gradient; +} + +void Gradient::fill(GraphicsContext* context, const FloatRect& rect) +{ + context->platformContext()->fillRect(rect, *platformGradient()); +} + +} //namespace diff --git a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp new file mode 100644 index 0000000..295212c --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp @@ -0,0 +1,1674 @@ +/* + Copyright (C) 2010 Tieto Corporation. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "config.h" + +#include "GraphicsContext3D.h" + +#include "WebGLObject.h" +#include "CanvasRenderingContext.h" +#include "Extensions3DQt.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include "HostWindow.h" +#include "ImageBuffer.h" +#include "NotImplemented.h" +#include "QWebPageClient.h" +#include <QAbstractScrollArea> +#include <QGLContext> +#include <wtf/UnusedParam.h> +#include <wtf/text/CString.h> + +#if ENABLE(3D_CANVAS) + +namespace WebCore { + +#if !defined(GLchar) +typedef char GLchar; +#endif + +#if !defined(APIENTRY) +#define APIENTRY +#endif + +#ifdef QT_OPENGL_ES_2 +typedef GLsizeiptr GLsizeiptrType; +typedef GLintptr GLintptrType; +#else +typedef ptrdiff_t GLsizeiptrType; +typedef ptrdiff_t GLintptrType; +#endif + +typedef void (APIENTRY* glActiveTextureType) (GLenum); +typedef void (APIENTRY* glAttachShaderType) (GLuint, GLuint); +typedef void (APIENTRY* glBindAttribLocationType) (GLuint, GLuint, const char*); +typedef void (APIENTRY* glBindBufferType) (GLenum, GLuint); +typedef void (APIENTRY* glBindFramebufferType) (GLenum, GLuint); +typedef void (APIENTRY* glBindRenderbufferType) (GLenum, GLuint); +typedef void (APIENTRY* glBlendColorType) (GLclampf, GLclampf, GLclampf, GLclampf); +typedef void (APIENTRY* glBlendEquationType) (GLenum); +typedef void (APIENTRY* glBlendEquationSeparateType)(GLenum, GLenum); +typedef void (APIENTRY* glBlendFuncSeparateType)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRY* glBufferDataType) (GLenum, GLsizeiptrType, const GLvoid*, GLenum); +typedef void (APIENTRY* glBufferSubDataType) (GLenum, GLintptrType, GLsizeiptrType, const GLvoid*); +typedef GLenum (APIENTRY* glCheckFramebufferStatusType) (GLenum); +typedef void (APIENTRY* glCompileShaderType) (GLuint); +typedef GLuint (APIENTRY* glCreateProgramType) (); +typedef GLuint (APIENTRY* glCreateShaderType) (GLenum); +typedef void (APIENTRY* glDeleteBuffersType) (GLsizei, const GLuint*); +typedef void (APIENTRY* glDeleteFramebuffersType) (GLsizei n, const GLuint*); +typedef void (APIENTRY* glDeleteProgramType) (GLuint); +typedef void (APIENTRY* glDeleteRenderbuffersType) (GLsizei n, const GLuint*); +typedef void (APIENTRY* glDeleteShaderType) (GLuint); +typedef void (APIENTRY* glDetachShaderType) (GLuint, GLuint); +typedef void (APIENTRY* glDisableVertexAttribArrayType) (GLuint); +typedef void (APIENTRY* glEnableVertexAttribArrayType) (GLuint); +typedef void (APIENTRY* glFramebufferRenderbufferType) (GLenum, GLenum, GLenum, GLuint); +typedef void (APIENTRY* glFramebufferTexture2DType) (GLenum, GLenum, GLenum, GLuint, GLint); +typedef void (APIENTRY* glGenBuffersType) (GLsizei, GLuint*); +typedef void (APIENTRY* glGenerateMipmapType) (GLenum target); +typedef void (APIENTRY* glGenFramebuffersType) (GLsizei, GLuint*); +typedef void (APIENTRY* glGenRenderbuffersType) (GLsizei, GLuint*); +typedef void (APIENTRY* glGetActiveAttribType) (GLuint, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLchar*); +typedef void (APIENTRY* glGetActiveUniformType) (GLuint, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLchar*); +typedef void (APIENTRY* glGetAttachedShadersType) (GLuint, GLsizei, GLsizei*, GLuint*); +typedef GLint (APIENTRY* glGetAttribLocationType) (GLuint, const char*); +typedef void (APIENTRY* glGetBufferParameterivType) (GLenum, GLenum, GLint*); +typedef void (APIENTRY* glGetFramebufferAttachmentParameterivType) (GLenum, GLenum, GLenum, GLint* params); +typedef void (APIENTRY* glGetProgramInfoLogType) (GLuint, GLsizei, GLsizei*, char*); +typedef void (APIENTRY* glGetProgramivType) (GLuint, GLenum, GLint*); +typedef void (APIENTRY* glGetRenderbufferParameterivType) (GLenum, GLenum, GLint*); +typedef void (APIENTRY* glGetShaderInfoLogType) (GLuint, GLsizei, GLsizei*, char*); +typedef void (APIENTRY* glGetShaderivType) (GLuint, GLenum, GLint*); +typedef void (APIENTRY* glGetShaderSourceType) (GLuint, GLsizei, GLsizei*, char*); +typedef GLint (APIENTRY* glGetUniformLocationType) (GLuint, const char*); +typedef void (APIENTRY* glGetUniformfvType) (GLuint, GLint, GLfloat*); +typedef void (APIENTRY* glGetUniformivType) (GLuint, GLint, GLint*); +typedef void (APIENTRY* glGetVertexAttribfvType) (GLuint, GLenum, GLfloat*); +typedef void (APIENTRY* glGetVertexAttribivType) (GLuint, GLenum, GLint*); +typedef void (APIENTRY* glGetVertexAttribPointervType) (GLuint, GLenum, GLvoid**); +typedef GLboolean (APIENTRY* glIsBufferType) (GLuint); +typedef GLboolean (APIENTRY* glIsFramebufferType) (GLuint); +typedef GLboolean (APIENTRY* glIsProgramType) (GLuint); +typedef GLboolean (APIENTRY* glIsRenderbufferType) (GLuint); +typedef GLboolean (APIENTRY* glIsShaderType) (GLuint); +typedef void (APIENTRY* glLinkProgramType) (GLuint); +typedef void (APIENTRY* glRenderbufferStorageType) (GLenum, GLenum, GLsizei, GLsizei); +typedef void (APIENTRY* glSampleCoverageType) (GLclampf, GLboolean); +typedef void (APIENTRY* glShaderSourceType) (GLuint, GLsizei, const char**, const GLint*); +typedef void (APIENTRY* glStencilFuncSeparateType) (GLenum, GLenum, GLint, GLuint); +typedef void (APIENTRY* glStencilMaskSeparateType) (GLenum, GLuint); +typedef void (APIENTRY* glStencilOpSeparateType) (GLenum, GLenum, GLenum, GLenum); +typedef void (APIENTRY* glUniform1fType) (GLint, GLfloat); +typedef void (APIENTRY* glUniform1fvType) (GLint, GLsizei, const GLfloat*); +typedef void (APIENTRY* glUniform1iType) (GLint, GLint); +typedef void (APIENTRY* glUniform1ivType) (GLint, GLsizei, const GLint*); +typedef void (APIENTRY* glUniform2fType) (GLint, GLfloat, GLfloat); +typedef void (APIENTRY* glUniform2fvType) (GLint, GLsizei, const GLfloat*); +typedef void (APIENTRY* glUniform2iType) (GLint, GLint, GLint); +typedef void (APIENTRY* glUniform2ivType) (GLint, GLsizei, const GLint*); +typedef void (APIENTRY* glUniform3fType) (GLint, GLfloat, GLfloat, GLfloat); +typedef void (APIENTRY* glUniform3fvType) (GLint, GLsizei, const GLfloat*); +typedef void (APIENTRY* glUniform3iType) (GLint, GLint, GLint, GLint); +typedef void (APIENTRY* glUniform3ivType) (GLint, GLsizei, const GLint*); +typedef void (APIENTRY* glUniform4fType) (GLint, GLfloat, GLfloat, GLfloat, GLfloat); +typedef void (APIENTRY* glUniform4fvType) (GLint, GLsizei, const GLfloat*); +typedef void (APIENTRY* glUniform4iType) (GLint, GLint, GLint, GLint, GLint); +typedef void (APIENTRY* glUniform4ivType) (GLint, GLsizei, const GLint*); +typedef void (APIENTRY* glUniformMatrix2fvType) (GLint, GLsizei, GLboolean, const GLfloat*); +typedef void (APIENTRY* glUniformMatrix3fvType) (GLint, GLsizei, GLboolean, const GLfloat*); +typedef void (APIENTRY* glUniformMatrix4fvType) (GLint, GLsizei, GLboolean, const GLfloat*); +typedef void (APIENTRY* glUseProgramType) (GLuint); +typedef void (APIENTRY* glValidateProgramType) (GLuint); +typedef void (APIENTRY* glVertexAttrib1fType) (GLuint, const GLfloat); +typedef void (APIENTRY* glVertexAttrib1fvType) (GLuint, const GLfloat*); +typedef void (APIENTRY* glVertexAttrib2fType) (GLuint, const GLfloat, const GLfloat); +typedef void (APIENTRY* glVertexAttrib2fvType) (GLuint, const GLfloat*); +typedef void (APIENTRY* glVertexAttrib3fType) (GLuint, const GLfloat, const GLfloat, const GLfloat); +typedef void (APIENTRY* glVertexAttrib3fvType) (GLuint, const GLfloat*); +typedef void (APIENTRY* glVertexAttrib4fType) (GLuint, const GLfloat, const GLfloat, const GLfloat, const GLfloat); +typedef void (APIENTRY* glVertexAttrib4fvType) (GLuint, const GLfloat*); +typedef void (APIENTRY* glVertexAttribPointerType) (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*); + +class GraphicsContext3DInternal { +public: + GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow); + ~GraphicsContext3DInternal(); + + bool isContextValid() { return m_contextValid; } + QGLWidget* getOwnerGLWidget(QWebPageClient* webPageClient); + + glActiveTextureType activeTexture; + glAttachShaderType attachShader; + glBindAttribLocationType bindAttribLocation; + glBindBufferType bindBuffer; + glBindFramebufferType bindFramebuffer; + glBindRenderbufferType bindRenderbuffer; + glBlendColorType blendColor; + glBlendEquationType blendEquation; + glBlendEquationSeparateType blendEquationSeparate; + glBlendFuncSeparateType blendFuncSeparate; + glBufferDataType bufferData; + glBufferSubDataType bufferSubData; + glCheckFramebufferStatusType checkFramebufferStatus; + glCompileShaderType compileShader; + glCreateProgramType createProgram; + glCreateShaderType createShader; + glDeleteBuffersType deleteBuffers; + glDeleteFramebuffersType deleteFramebuffers; + glDeleteProgramType deleteProgram; + glDeleteRenderbuffersType deleteRenderbuffers; + glDeleteShaderType deleteShader; + glDetachShaderType detachShader; + glDisableVertexAttribArrayType disableVertexAttribArray; + glEnableVertexAttribArrayType enableVertexAttribArray; + glFramebufferRenderbufferType framebufferRenderbuffer; + glFramebufferTexture2DType framebufferTexture2D; + glGenBuffersType genBuffers; + glGenerateMipmapType generateMipmap; + glGenFramebuffersType genFramebuffers; + glGenRenderbuffersType genRenderbuffers; + glGetActiveAttribType getActiveAttrib; + glGetActiveUniformType getActiveUniform; + glGetAttachedShadersType getAttachedShaders; + glGetAttribLocationType getAttribLocation; + glGetBufferParameterivType getBufferParameteriv; + glGetFramebufferAttachmentParameterivType getFramebufferAttachmentParameteriv; + glGetProgramInfoLogType getProgramInfoLog; + glGetProgramivType getProgramiv; + glGetRenderbufferParameterivType getRenderbufferParameteriv; + glGetShaderInfoLogType getShaderInfoLog; + glGetShaderivType getShaderiv; + glGetShaderSourceType getShaderSource; + glGetUniformfvType getUniformfv; + glGetUniformivType getUniformiv; + glGetUniformLocationType getUniformLocation; + glGetVertexAttribfvType getVertexAttribfv; + glGetVertexAttribivType getVertexAttribiv; + glGetVertexAttribPointervType getVertexAttribPointerv; + glIsBufferType isBuffer; + glIsFramebufferType isFramebuffer; + glIsProgramType isProgram; + glIsRenderbufferType isRenderbuffer; + glIsShaderType isShader; + glLinkProgramType linkProgram; + glRenderbufferStorageType renderbufferStorage; + glSampleCoverageType sampleCoverage; + glShaderSourceType shaderSource; + glStencilFuncSeparateType stencilFuncSeparate; + glStencilMaskSeparateType stencilMaskSeparate; + glStencilOpSeparateType stencilOpSeparate; + glUniform1fType uniform1f; + glUniform1fvType uniform1fv; + glUniform1iType uniform1i; + glUniform1ivType uniform1iv; + glUniform2fType uniform2f; + glUniform2fvType uniform2fv; + glUniform2iType uniform2i; + glUniform2ivType uniform2iv; + glUniform3fType uniform3f; + glUniform3fvType uniform3fv; + glUniform3iType uniform3i; + glUniform3ivType uniform3iv; + glUniform4fType uniform4f; + glUniform4fvType uniform4fv; + glUniform4iType uniform4i; + glUniform4ivType uniform4iv; + glUniformMatrix2fvType uniformMatrix2fv; + glUniformMatrix3fvType uniformMatrix3fv; + glUniformMatrix4fvType uniformMatrix4fv; + glUseProgramType useProgram; + glValidateProgramType validateProgram; + glVertexAttrib1fType vertexAttrib1f; + glVertexAttrib1fvType vertexAttrib1fv; + glVertexAttrib2fType vertexAttrib2f; + glVertexAttrib2fvType vertexAttrib2fv; + glVertexAttrib3fType vertexAttrib3f; + glVertexAttrib3fvType vertexAttrib3fv; + glVertexAttrib4fType vertexAttrib4f; + glVertexAttrib4fvType vertexAttrib4fv; + glVertexAttribPointerType vertexAttribPointer; + + GraphicsContext3D::Attributes m_attrs; + HostWindow* m_hostWindow; + QGLWidget* m_glWidget; + GLuint m_texture; + GLuint m_mainFbo; + GLuint m_currentFbo; + GLuint m_depthBuffer; + QImage m_pixels; + ListHashSet<unsigned long> m_syntheticErrors; + + OwnPtr<Extensions3DQt> m_extensions; + +private: + + void* getProcAddress(const String& proc); + bool m_contextValid; +}; + +#if defined (QT_OPENGL_ES_2) +#define GET_PROC_ADDRESS(Proc) Proc +#else +#define GET_PROC_ADDRESS(Proc) reinterpret_cast<Proc##Type>(getProcAddress(#Proc)); +#endif + +bool GraphicsContext3D::isGLES2Compliant() const +{ +#if defined (QT_OPENGL_ES_2) + return true; +#else + return false; +#endif +} + +// Even with underlying GLES2 driver, the below flags should still be set to +// false if extentions exist (and they almost always do). +bool GraphicsContext3D::isGLES2NPOTStrict() const +{ + return false; +} + +bool GraphicsContext3D::isErrorGeneratedOnOutOfBoundsAccesses() const +{ + return false; +} + +int GraphicsContext3D::getGraphicsResetStatusARB() +{ + return NO_ERROR; +} + + +GraphicsContext3DInternal::GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow) + : m_attrs(attrs) + , m_hostWindow(hostWindow) + , m_glWidget(0) + , m_texture(0) + , m_mainFbo(0) + , m_currentFbo(0) + , m_depthBuffer(0) + , m_contextValid(true) +{ + QWebPageClient* webPageClient = hostWindow->platformPageClient(); + QGLWidget* ownerGLWidget = getOwnerGLWidget(webPageClient); + + if (ownerGLWidget) + m_glWidget = new QGLWidget(0, ownerGLWidget); + else { + QGLFormat format; + format.setDepth(true); + format.setSampleBuffers(true); + format.setStencil(false); + + m_glWidget = new QGLWidget(format); + } + + if (!m_glWidget->isValid()) { + LOG_ERROR("GraphicsContext3D: QGLWidget does not have a valid context"); + m_contextValid = false; + return; + } + + QGLFormat format = m_glWidget->format(); + + m_attrs.alpha = format.alpha(); + m_attrs.depth = format.depth(); + m_attrs.stencil = format.stencil(); + m_attrs.antialias = false; + m_attrs.premultipliedAlpha = true; + + m_glWidget->makeCurrent(); + + activeTexture = GET_PROC_ADDRESS(glActiveTexture); + attachShader = GET_PROC_ADDRESS(glAttachShader); + bindAttribLocation = GET_PROC_ADDRESS(glBindAttribLocation); + bindBuffer = GET_PROC_ADDRESS(glBindBuffer); + bindFramebuffer = GET_PROC_ADDRESS(glBindFramebuffer); + bindRenderbuffer = GET_PROC_ADDRESS(glBindRenderbuffer); + blendColor = GET_PROC_ADDRESS(glBlendColor); + blendEquation = GET_PROC_ADDRESS(glBlendEquation); + blendEquationSeparate = GET_PROC_ADDRESS(glBlendEquationSeparate); + blendFuncSeparate = GET_PROC_ADDRESS(glBlendFuncSeparate); + bufferData = GET_PROC_ADDRESS(glBufferData); + bufferSubData = GET_PROC_ADDRESS(glBufferSubData); + checkFramebufferStatus = GET_PROC_ADDRESS(glCheckFramebufferStatus); + compileShader = GET_PROC_ADDRESS(glCompileShader); + createProgram = GET_PROC_ADDRESS(glCreateProgram); + createShader = GET_PROC_ADDRESS(glCreateShader); + deleteBuffers = GET_PROC_ADDRESS(glDeleteBuffers); + deleteFramebuffers = GET_PROC_ADDRESS(glDeleteFramebuffers); + deleteProgram = GET_PROC_ADDRESS(glDeleteProgram); + deleteRenderbuffers = GET_PROC_ADDRESS(glDeleteRenderbuffers); + deleteShader = GET_PROC_ADDRESS(glDeleteShader); + detachShader = GET_PROC_ADDRESS(glDetachShader); + disableVertexAttribArray = GET_PROC_ADDRESS(glDisableVertexAttribArray); + enableVertexAttribArray = GET_PROC_ADDRESS(glEnableVertexAttribArray); + framebufferRenderbuffer = GET_PROC_ADDRESS(glFramebufferRenderbuffer); + framebufferTexture2D = GET_PROC_ADDRESS(glFramebufferTexture2D); + genBuffers = GET_PROC_ADDRESS(glGenBuffers); + generateMipmap = GET_PROC_ADDRESS(glGenerateMipmap); + genFramebuffers = GET_PROC_ADDRESS(glGenFramebuffers); + genRenderbuffers = GET_PROC_ADDRESS(glGenRenderbuffers); + getActiveAttrib = GET_PROC_ADDRESS(glGetActiveAttrib); + getActiveUniform = GET_PROC_ADDRESS(glGetActiveUniform); + getAttachedShaders = GET_PROC_ADDRESS(glGetAttachedShaders); + getAttribLocation = GET_PROC_ADDRESS(glGetAttribLocation); + getBufferParameteriv = GET_PROC_ADDRESS(glGetBufferParameteriv); + getFramebufferAttachmentParameteriv = GET_PROC_ADDRESS(glGetFramebufferAttachmentParameteriv); + getProgramInfoLog = GET_PROC_ADDRESS(glGetProgramInfoLog); + getProgramiv = GET_PROC_ADDRESS(glGetProgramiv); + getRenderbufferParameteriv = GET_PROC_ADDRESS(glGetRenderbufferParameteriv); + getShaderInfoLog = GET_PROC_ADDRESS(glGetShaderInfoLog); + getShaderiv = GET_PROC_ADDRESS(glGetShaderiv); + getShaderSource = GET_PROC_ADDRESS(glGetShaderSource); + getUniformfv = GET_PROC_ADDRESS(glGetUniformfv); + getUniformiv = GET_PROC_ADDRESS(glGetUniformiv); + getUniformLocation = GET_PROC_ADDRESS(glGetUniformLocation); + getVertexAttribfv = GET_PROC_ADDRESS(glGetVertexAttribfv); + getVertexAttribiv = GET_PROC_ADDRESS(glGetVertexAttribiv); + getVertexAttribPointerv = GET_PROC_ADDRESS(glGetVertexAttribPointerv); + isBuffer = GET_PROC_ADDRESS(glIsBuffer); + isFramebuffer = GET_PROC_ADDRESS(glIsFramebuffer); + isProgram = GET_PROC_ADDRESS(glIsProgram); + isRenderbuffer = GET_PROC_ADDRESS(glIsRenderbuffer); + isShader = GET_PROC_ADDRESS(glIsShader); + linkProgram = GET_PROC_ADDRESS(glLinkProgram); + renderbufferStorage = GET_PROC_ADDRESS(glRenderbufferStorage); + sampleCoverage = GET_PROC_ADDRESS(glSampleCoverage); + shaderSource = GET_PROC_ADDRESS(glShaderSource); + stencilFuncSeparate = GET_PROC_ADDRESS(glStencilFuncSeparate); + stencilMaskSeparate = GET_PROC_ADDRESS(glStencilMaskSeparate); + stencilOpSeparate = GET_PROC_ADDRESS(glStencilOpSeparate); + uniform1f = GET_PROC_ADDRESS(glUniform1f); + uniform1fv = GET_PROC_ADDRESS(glUniform1fv); + uniform1i = GET_PROC_ADDRESS(glUniform1i); + uniform1iv = GET_PROC_ADDRESS(glUniform1iv); + uniform2f = GET_PROC_ADDRESS(glUniform2f); + uniform2fv = GET_PROC_ADDRESS(glUniform2fv); + uniform2i = GET_PROC_ADDRESS(glUniform2i); + uniform2iv = GET_PROC_ADDRESS(glUniform2iv); + uniform3f = GET_PROC_ADDRESS(glUniform3f); + uniform3fv = GET_PROC_ADDRESS(glUniform3fv); + uniform3i = GET_PROC_ADDRESS(glUniform3i); + uniform3iv = GET_PROC_ADDRESS(glUniform3iv); + uniform4f = GET_PROC_ADDRESS(glUniform4f); + uniform4fv = GET_PROC_ADDRESS(glUniform4fv); + uniform4i = GET_PROC_ADDRESS(glUniform4i); + uniform4iv = GET_PROC_ADDRESS(glUniform4iv); + uniformMatrix2fv = GET_PROC_ADDRESS(glUniformMatrix2fv); + uniformMatrix3fv = GET_PROC_ADDRESS(glUniformMatrix3fv); + uniformMatrix4fv = GET_PROC_ADDRESS(glUniformMatrix4fv); + useProgram = GET_PROC_ADDRESS(glUseProgram); + validateProgram = GET_PROC_ADDRESS(glValidateProgram); + vertexAttrib1f = GET_PROC_ADDRESS(glVertexAttrib1f); + vertexAttrib1fv = GET_PROC_ADDRESS(glVertexAttrib1fv); + vertexAttrib2f = GET_PROC_ADDRESS(glVertexAttrib2f); + vertexAttrib2fv = GET_PROC_ADDRESS(glVertexAttrib2fv); + vertexAttrib3f = GET_PROC_ADDRESS(glVertexAttrib3f); + vertexAttrib3fv = GET_PROC_ADDRESS(glVertexAttrib3fv); + vertexAttrib4f = GET_PROC_ADDRESS(glVertexAttrib4f); + vertexAttrib4fv = GET_PROC_ADDRESS(glVertexAttrib4fv); + vertexAttribPointer = GET_PROC_ADDRESS(glVertexAttribPointer); + + if (!m_contextValid) { + LOG_ERROR("GraphicsContext3D: All needed OpenGL extensions are not available"); + m_contextValid = false; + return; + } + + glGenTextures(1, &m_texture); + glBindTexture(GraphicsContext3D::TEXTURE_2D, m_texture); + glTexParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); + glTexParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + glTexParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + glTexParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + glTexImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, 1, 1, 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, 0); + glBindTexture(GraphicsContext3D::TEXTURE_2D, 0); + + genFramebuffers(/* count */ 1, &m_mainFbo); + m_currentFbo = m_mainFbo; + + bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_mainFbo); + + genRenderbuffers(/* count */ 1, &m_depthBuffer); + bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer); +#if defined(QT_OPENGL_ES_2) + renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, /* width */ 1, /* height */ 1); +#else + renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT, /* width */ 1, /* height */ 1); +#endif + + bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); + + framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_texture, 0); + framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer); + glClearColor(/* red */ 0, /* green */ 0, /* blue */ 0, /* alpha */ 0); + + if (checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { + LOG_ERROR("GraphicsContext3D: Wasn't able to create the main framebuffer"); + m_contextValid = false; + } +} + +GraphicsContext3DInternal::~GraphicsContext3DInternal() +{ + delete m_glWidget; + m_glWidget = 0; +} + +QGLWidget* GraphicsContext3DInternal::getOwnerGLWidget(QWebPageClient* webPageClient) +{ + QAbstractScrollArea* scrollArea = qobject_cast<QAbstractScrollArea*>(webPageClient->ownerWidget()); + + if (scrollArea) + return qobject_cast<QGLWidget*>(scrollArea->viewport()); + + return 0; +} + +void* GraphicsContext3DInternal::getProcAddress(const String& proc) +{ + String ext[3] = { "", "ARB", "EXT" }; + + for (int i = 0; i < 3; i++) { + String nameWithExt = proc + ext[i]; + + void* addr = m_glWidget->context()->getProcAddress(nameWithExt.utf8().data()); + if (addr) + return addr; + } + + LOG_ERROR("GraphicsContext3D: Did not find GL function %s", proc.utf8().data()); + m_contextValid = false; + return 0; +} + +PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) +{ + // This implementation doesn't currently support rendering directly to the HostWindow. + if (renderStyle == RenderDirectlyToHostWindow) + return 0; + OwnPtr<GraphicsContext3D> context(new GraphicsContext3D(attrs, hostWindow, false)); + return context->m_internal ? context.release() : 0; +} + +GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, bool) + : m_internal(new GraphicsContext3DInternal(attrs, hostWindow)) +{ + if (!m_internal->isContextValid()) + m_internal = 0; +} + +GraphicsContext3D::~GraphicsContext3D() +{ +} + +PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D() +{ + return m_internal->m_glWidget; +} + +Platform3DObject GraphicsContext3D::platformTexture() const +{ + return m_internal->m_texture; +} + +void GraphicsContext3D::makeContextCurrent() +{ + m_internal->m_glWidget->makeCurrent(); +} + +void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* context) +{ + m_internal->m_glWidget->makeCurrent(); + HTMLCanvasElement* canvas = context->canvas(); + ImageBuffer* imageBuffer = canvas->buffer(); + QPainter* painter = imageBuffer->context()->platformContext(); + paint(painter, QRect(QPoint(0, 0), QSize(m_currentWidth, m_currentHeight))); +} + +void GraphicsContext3D::paint(QPainter* painter, const QRect& rect) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + QWebPageClient* webPageClient = m_internal->m_hostWindow->platformPageClient(); + QGLWidget* ownerGLWidget = m_internal->getOwnerGLWidget(webPageClient); + if (ownerGLWidget) { + ownerGLWidget->drawTexture(rect, m_internal->m_texture); + return; + } +#endif + m_internal->m_glWidget->makeCurrent(); + m_internal->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_internal->m_mainFbo); + glReadPixels(/* x */ 0, /* y */ 0, m_currentWidth, m_currentHeight, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, m_internal->m_pixels.bits()); + painter->drawImage(/* x */ 0, /* y */ 0, m_internal->m_pixels.rgbSwapped().transformed(QMatrix().rotate(180))); + m_internal->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_internal->m_currentFbo); +} + +void GraphicsContext3D::reshape(int width, int height) +{ + if (((width == m_currentWidth) && (height == m_currentHeight)) || (!m_internal)) + return; + + m_currentWidth = width; + m_currentHeight = height; + + m_internal->m_pixels = QImage(m_currentWidth, m_currentHeight, QImage::Format_ARGB32); + + m_internal->m_glWidget->makeCurrent(); + + glBindTexture(GraphicsContext3D::TEXTURE_2D, m_internal->m_texture); + glTexImage2D(GraphicsContext3D::TEXTURE_2D, /* level */ 0, GraphicsContext3D::RGBA, width, height, /* border */ 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, /* data */ 0); + glBindTexture(GraphicsContext3D::TEXTURE_2D, 0); + + m_internal->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_internal->m_mainFbo); + m_internal->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_internal->m_depthBuffer); +#if defined(QT_OPENGL_ES_2) + renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, width, height); +#else + renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT, width, height); +#endif + m_internal->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); + + m_internal->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_internal->m_texture, 0); + m_internal->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_internal->m_depthBuffer); + + GLenum status = m_internal->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER); + if (status != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { + LOG_ERROR("GraphicsContext3D: Wasn't able to reshape the main framebuffer"); + notImplemented(); + } + + glClear(GraphicsContext3D::COLOR_BUFFER_BIT); + glFlush(); +} + +IntSize GraphicsContext3D::getInternalFramebufferSize() +{ + return IntSize(m_currentWidth, m_currentHeight); +} + +void GraphicsContext3D::activeTexture(GC3Denum texture) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->activeTexture(texture); +} + +void GraphicsContext3D::attachShader(Platform3DObject program, Platform3DObject shader) +{ + ASSERT(program); + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->attachShader(program, shader); +} + +void GraphicsContext3D::getAttachedShaders(Platform3DObject program, GC3Dsizei maxCount, GC3Dsizei* count, Platform3DObject* shaders) +{ + if (!program) { + synthesizeGLError(INVALID_VALUE); + return; + } + + m_internal->m_glWidget->makeCurrent(); + getAttachedShaders(program, maxCount, count, shaders); +} + +void GraphicsContext3D::bindAttribLocation(Platform3DObject program, GC3Duint index, const String& name) +{ + ASSERT(program); + m_internal->m_glWidget->makeCurrent(); + m_internal->bindAttribLocation(program, index, name.utf8().data()); +} + +void GraphicsContext3D::bindBuffer(GC3Denum target, Platform3DObject buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bindBuffer(target, buffer); +} + +void GraphicsContext3D::bindFramebuffer(GC3Denum target, Platform3DObject buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->m_currentFbo = buffer ? buffer : m_internal->m_mainFbo; + m_internal->bindFramebuffer(target, m_internal->m_currentFbo); +} + +void GraphicsContext3D::bindRenderbuffer(GC3Denum target, Platform3DObject renderbuffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bindRenderbuffer(target, renderbuffer); +} + +void GraphicsContext3D::bindTexture(GC3Denum target, Platform3DObject texture) +{ + m_internal->m_glWidget->makeCurrent(); + glBindTexture(target, texture); +} + +void GraphicsContext3D::blendColor(GC3Dclampf red, GC3Dclampf green, GC3Dclampf blue, GC3Dclampf alpha) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendColor(red, green, blue, alpha); +} + +void GraphicsContext3D::blendEquation(GC3Denum mode) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendEquation(mode); +} + +void GraphicsContext3D::blendEquationSeparate(GC3Denum modeRGB, GC3Denum modeAlpha) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendEquationSeparate(modeRGB, modeAlpha); +} + +void GraphicsContext3D::blendFunc(GC3Denum sfactor, GC3Denum dfactor) +{ + m_internal->m_glWidget->makeCurrent(); + glBlendFunc(sfactor, dfactor); +} + +void GraphicsContext3D::blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, GC3Denum srcAlpha, GC3Denum dstAlpha) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); +} + +void GraphicsContext3D::bufferData(GC3Denum target, GC3Dsizeiptr size, GC3Denum usage) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bufferData(target, size, /* data */ 0, usage); +} + +void GraphicsContext3D::bufferData(GC3Denum target, GC3Dsizeiptr size, const void* data, GC3Denum usage) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bufferData(target, size, data, usage); +} + +void GraphicsContext3D::bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr size, const void* data) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bufferSubData(target, offset, size, data); +} + +GC3Denum GraphicsContext3D::checkFramebufferStatus(GC3Denum target) +{ + m_internal->m_glWidget->makeCurrent(); + return m_internal->checkFramebufferStatus(target); +} + +void GraphicsContext3D::clearColor(GC3Dclampf r, GC3Dclampf g, GC3Dclampf b, GC3Dclampf a) +{ + m_internal->m_glWidget->makeCurrent(); + glClearColor(r, g, b, a); +} + +void GraphicsContext3D::clear(GC3Dbitfield mask) +{ + m_internal->m_glWidget->makeCurrent(); + glClear(mask); +} + +void GraphicsContext3D::clearDepth(GC3Dclampf depth) +{ + m_internal->m_glWidget->makeCurrent(); +#if defined(QT_OPENGL_ES_2) + glClearDepthf(depth); +#else + glClearDepth(depth); +#endif +} + +void GraphicsContext3D::clearStencil(GC3Dint s) +{ + m_internal->m_glWidget->makeCurrent(); + glClearStencil(s); +} + +void GraphicsContext3D::colorMask(GC3Dboolean red, GC3Dboolean green, GC3Dboolean blue, GC3Dboolean alpha) +{ + m_internal->m_glWidget->makeCurrent(); + glColorMask(red, green, blue, alpha); +} + +void GraphicsContext3D::compileShader(Platform3DObject shader) +{ + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->compileShader(shader); +} + +void GraphicsContext3D::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border) +{ + m_internal->m_glWidget->makeCurrent(); + glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); +} + +void GraphicsContext3D::copyTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) +{ + m_internal->m_glWidget->makeCurrent(); + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); +} + +void GraphicsContext3D::cullFace(GC3Denum mode) +{ + m_internal->m_glWidget->makeCurrent(); + glCullFace(mode); +} + +void GraphicsContext3D::depthFunc(GC3Denum func) +{ + m_internal->m_glWidget->makeCurrent(); + glDepthFunc(func); +} + +void GraphicsContext3D::depthMask(GC3Dboolean flag) +{ + m_internal->m_glWidget->makeCurrent(); + glDepthMask(flag); +} + +void GraphicsContext3D::depthRange(GC3Dclampf zNear, GC3Dclampf zFar) +{ + m_internal->m_glWidget->makeCurrent(); +#if defined(QT_OPENGL_ES_2) + glDepthRangef(zNear, zFar); +#else + glDepthRange(zNear, zFar); +#endif +} + +void GraphicsContext3D::detachShader(Platform3DObject program, Platform3DObject shader) +{ + ASSERT(program); + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->detachShader(program, shader); +} + +void GraphicsContext3D::disable(GC3Denum cap) +{ + m_internal->m_glWidget->makeCurrent(); + glDisable(cap); +} + +void GraphicsContext3D::disableVertexAttribArray(GC3Duint index) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->disableVertexAttribArray(index); +} + +void GraphicsContext3D::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count) +{ + m_internal->m_glWidget->makeCurrent(); + glDrawArrays(mode, first, count); +} + +void GraphicsContext3D::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset) +{ + m_internal->m_glWidget->makeCurrent(); + glDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset))); +} + +void GraphicsContext3D::enable(GC3Denum cap) +{ + m_internal->m_glWidget->makeCurrent(); + glEnable(cap); +} + +void GraphicsContext3D::enableVertexAttribArray(GC3Duint index) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->enableVertexAttribArray(index); +} + +void GraphicsContext3D::finish() +{ + m_internal->m_glWidget->makeCurrent(); + glFinish(); +} + +void GraphicsContext3D::flush() +{ + m_internal->m_glWidget->makeCurrent(); + glFlush(); +} + +void GraphicsContext3D::framebufferRenderbuffer(GC3Denum target, GC3Denum attachment, GC3Denum renderbuffertarget, Platform3DObject buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->framebufferRenderbuffer(target, attachment, renderbuffertarget, buffer); +} + +void GraphicsContext3D::framebufferTexture2D(GC3Denum target, GC3Denum attachment, GC3Denum textarget, Platform3DObject texture, GC3Dint level) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->framebufferTexture2D(target, attachment, textarget, texture, level); +} + +void GraphicsContext3D::frontFace(GC3Denum mode) +{ + m_internal->m_glWidget->makeCurrent(); + glFrontFace(mode); +} + +void GraphicsContext3D::generateMipmap(GC3Denum target) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->generateMipmap(target); +} + +bool GraphicsContext3D::getActiveAttrib(Platform3DObject program, GC3Duint index, ActiveInfo& info) +{ + if (!program) { + synthesizeGLError(INVALID_VALUE); + return false; + } + + m_internal->m_glWidget->makeCurrent(); + + GLint maxLength = 0; + m_internal->getProgramiv(program, GraphicsContext3D::ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength); + + GLchar* name = (GLchar*) fastMalloc(maxLength); + GLsizei nameLength = 0; + GLint size = 0; + GLenum type = 0; + + m_internal->getActiveAttrib(program, index, maxLength, &nameLength, &size, &type, name); + + if (!nameLength) { + fastFree(name); + return false; + } + + info.name = String(name, nameLength); + info.type = type; + info.size = size; + + fastFree(name); + return true; +} + +bool GraphicsContext3D::getActiveUniform(Platform3DObject program, GC3Duint index, ActiveInfo& info) +{ + if (!program) { + synthesizeGLError(INVALID_VALUE); + return false; + } + + m_internal->m_glWidget->makeCurrent(); + + GLint maxLength = 0; + m_internal->getProgramiv(static_cast<GLuint>(program), GraphicsContext3D::ACTIVE_UNIFORM_MAX_LENGTH, &maxLength); + + GLchar* name = (GLchar*) fastMalloc(maxLength); + GLsizei nameLength = 0; + GLint size = 0; + GLenum type = 0; + + m_internal->getActiveUniform(static_cast<GLuint>(program), index, maxLength, &nameLength, &size, &type, name); + + if (!nameLength) { + fastFree(name); + return false; + } + + info.name = String(name, nameLength); + info.type = type; + info.size = size; + + fastFree(name); + return true; +} + +int GraphicsContext3D::getAttribLocation(Platform3DObject program, const String& name) +{ + if (!program) + return -1; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->getAttribLocation(program, name.utf8().data()); +} + +GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes() +{ + return m_internal->m_attrs; +} + +GC3Denum GraphicsContext3D::getError() +{ + if (m_internal->m_syntheticErrors.size() > 0) { + ListHashSet<GC3Denum>::iterator iter = m_internal->m_syntheticErrors.begin(); + GC3Denum err = *iter; + m_internal->m_syntheticErrors.remove(iter); + return err; + } + + m_internal->m_glWidget->makeCurrent(); + return glGetError(); +} + +String GraphicsContext3D::getString(GC3Denum name) +{ + m_internal->m_glWidget->makeCurrent(); + return String((const char*) glGetString(name)); +} + +void GraphicsContext3D::hint(GC3Denum target, GC3Denum mode) +{ + m_internal->m_glWidget->makeCurrent(); + glHint(target, mode); +} + +GC3Dboolean GraphicsContext3D::isBuffer(Platform3DObject buffer) +{ + if (!buffer) + return GL_FALSE; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isBuffer(buffer); +} + +GC3Dboolean GraphicsContext3D::isEnabled(GC3Denum cap) +{ + m_internal->m_glWidget->makeCurrent(); + return glIsEnabled(cap); +} + +GC3Dboolean GraphicsContext3D::isFramebuffer(Platform3DObject framebuffer) +{ + if (!framebuffer) + return GL_FALSE; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isFramebuffer(framebuffer); +} + +GC3Dboolean GraphicsContext3D::isProgram(Platform3DObject program) +{ + if (!program) + return GL_FALSE; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isProgram(program); +} + +GC3Dboolean GraphicsContext3D::isRenderbuffer(Platform3DObject renderbuffer) +{ + if (!renderbuffer) + return GL_FALSE; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isRenderbuffer(renderbuffer); +} + +GC3Dboolean GraphicsContext3D::isShader(Platform3DObject shader) +{ + if (!shader) + return GL_FALSE; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isShader(shader); +} + +GC3Dboolean GraphicsContext3D::isTexture(Platform3DObject texture) +{ + if (!texture) + return GL_FALSE; + + m_internal->m_glWidget->makeCurrent(); + return glIsTexture(texture); +} + +void GraphicsContext3D::lineWidth(double width) +{ + m_internal->m_glWidget->makeCurrent(); + glLineWidth(static_cast<float>(width)); +} + +void GraphicsContext3D::linkProgram(Platform3DObject program) +{ + ASSERT(program); + m_internal->m_glWidget->makeCurrent(); + m_internal->linkProgram(program); +} + +void GraphicsContext3D::pixelStorei(GC3Denum paramName, GC3Dint param) +{ + m_internal->m_glWidget->makeCurrent(); + glPixelStorei(paramName, param); +} + +void GraphicsContext3D::polygonOffset(GC3Dfloat factor, GC3Dfloat units) +{ + m_internal->m_glWidget->makeCurrent(); + glPolygonOffset(factor, units); +} + +void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, void* data) +{ + m_internal->m_glWidget->makeCurrent(); + + if (type != GraphicsContext3D::UNSIGNED_BYTE || format != GraphicsContext3D::RGBA) + return; + + glReadPixels(x, y, width, height, format, type, data); +} + +void GraphicsContext3D::releaseShaderCompiler() +{ + m_internal->m_glWidget->makeCurrent(); + notImplemented(); +} + +void GraphicsContext3D::renderbufferStorage(GC3Denum target, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->renderbufferStorage(target, internalformat, width, height); +} + +void GraphicsContext3D::sampleCoverage(GC3Dclampf value, GC3Dboolean invert) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->sampleCoverage(value, invert); +} + +void GraphicsContext3D::scissor(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) +{ + m_internal->m_glWidget->makeCurrent(); + glScissor(x, y, width, height); +} + +void GraphicsContext3D::shaderSource(Platform3DObject shader, const String& source) +{ + ASSERT(shader); + + m_internal->m_glWidget->makeCurrent(); + + String prefixedSource; + +#if defined (QT_OPENGL_ES_2) + prefixedSource.append("precision mediump float;\n"); +#endif + + prefixedSource.append(source); + + CString sourceCS = prefixedSource.utf8(); + const char* data = sourceCS.data(); + int length = prefixedSource.length(); + m_internal->shaderSource((GLuint) shader, /* count */ 1, &data, &length); +} + +void GraphicsContext3D::stencilFunc(GC3Denum func, GC3Dint ref, GC3Duint mask) +{ + m_internal->m_glWidget->makeCurrent(); + glStencilFunc(func, ref, mask); +} + +void GraphicsContext3D::stencilFuncSeparate(GC3Denum face, GC3Denum func, GC3Dint ref, GC3Duint mask) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->stencilFuncSeparate(face, func, ref, mask); +} + +void GraphicsContext3D::stencilMask(GC3Duint mask) +{ + m_internal->m_glWidget->makeCurrent(); + glStencilMask(mask); +} + +void GraphicsContext3D::stencilMaskSeparate(GC3Denum face, GC3Duint mask) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->stencilMaskSeparate(face, mask); +} + +void GraphicsContext3D::stencilOp(GC3Denum fail, GC3Denum zfail, GC3Denum zpass) +{ + m_internal->m_glWidget->makeCurrent(); + glStencilOp(fail, zfail, zpass); +} + +void GraphicsContext3D::stencilOpSeparate(GC3Denum face, GC3Denum fail, GC3Denum zfail, GC3Denum zpass) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->stencilOpSeparate(face, fail, zfail, zpass); +} + +void GraphicsContext3D::texParameterf(GC3Denum target, GC3Denum paramName, GC3Dfloat value) +{ + m_internal->m_glWidget->makeCurrent(); + glTexParameterf(target, paramName, value); +} + +void GraphicsContext3D::texParameteri(GC3Denum target, GC3Denum paramName, GC3Dint value) +{ + m_internal->m_glWidget->makeCurrent(); + glTexParameteri(target, paramName, value); +} + +void GraphicsContext3D::uniform1f(GC3Dint location, GC3Dfloat v0) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1f(location, v0); +} + +void GraphicsContext3D::uniform1fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1fv(location, size, array); +} + +void GraphicsContext3D::uniform2f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2f(location, v0, v1); +} + +void GraphicsContext3D::uniform2fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2fv(location, size, array); +} + +void GraphicsContext3D::uniform3f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3f(location, v0, v1, v2); +} + +void GraphicsContext3D::uniform3fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3fv(location, size, array); +} + +void GraphicsContext3D::uniform4f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4f(location, v0, v1, v2, v3); +} + +void GraphicsContext3D::uniform4fv(GC3Dint location, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4fv(location, size, array); +} + +void GraphicsContext3D::uniform1i(GC3Dint location, GC3Dint v0) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1i(location, v0); +} + +void GraphicsContext3D::uniform1iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1iv(location, size, array); +} + +void GraphicsContext3D::uniform2i(GC3Dint location, GC3Dint v0, GC3Dint v1) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2i(location, v0, v1); +} + +void GraphicsContext3D::uniform2iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2iv(location, size, array); +} + +void GraphicsContext3D::uniform3i(GC3Dint location, GC3Dint v0, GC3Dint v1, GC3Dint v2) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3i(location, v0, v1, v2); +} + +void GraphicsContext3D::uniform3iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3iv(location, size, array); +} + +void GraphicsContext3D::uniform4i(GC3Dint location, GC3Dint v0, GC3Dint v1, GC3Dint v2, GC3Dint v3) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4i(location, v0, v1, v2, v3); +} + +void GraphicsContext3D::uniform4iv(GC3Dint location, GC3Dint* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4iv(location, size, array); +} + +void GraphicsContext3D::uniformMatrix2fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniformMatrix2fv(location, size, transpose, array); +} + +void GraphicsContext3D::uniformMatrix3fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniformMatrix3fv(location, size, transpose, array); +} + +void GraphicsContext3D::uniformMatrix4fv(GC3Dint location, GC3Dboolean transpose, GC3Dfloat* array, GC3Dsizei size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniformMatrix4fv(location, size, transpose, array); +} + +void GraphicsContext3D::useProgram(Platform3DObject program) +{ + ASSERT(program); + + m_internal->m_glWidget->makeCurrent(); + m_internal->useProgram(program); +} + +void GraphicsContext3D::validateProgram(Platform3DObject program) +{ + ASSERT(program); + + m_internal->m_glWidget->makeCurrent(); + m_internal->validateProgram(program); +} + +void GraphicsContext3D::vertexAttrib1f(GC3Duint index, GC3Dfloat v0) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib1f(index, v0); +} + +void GraphicsContext3D::vertexAttrib1fv(GC3Duint index, GC3Dfloat* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib1fv(index, array); +} + +void GraphicsContext3D::vertexAttrib2f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib2f(index, v0, v1); +} + +void GraphicsContext3D::vertexAttrib2fv(GC3Duint index, GC3Dfloat* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib2fv(index, array); +} + +void GraphicsContext3D::vertexAttrib3f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib3f(index, v0, v1, v2); +} + +void GraphicsContext3D::vertexAttrib3fv(GC3Duint index, GC3Dfloat* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib3fv(index, array); +} + +void GraphicsContext3D::vertexAttrib4f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib4f(index, v0, v1, v2, v3); +} + +void GraphicsContext3D::vertexAttrib4fv(GC3Duint index, GC3Dfloat* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib4fv(index, array); +} + +void GraphicsContext3D::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, GC3Dintptr offset) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset))); +} + +void GraphicsContext3D::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) +{ + m_internal->m_glWidget->makeCurrent(); + glViewport(x, y, width, height); +} + +void GraphicsContext3D::getBooleanv(GC3Denum paramName, GC3Dboolean* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetBooleanv(paramName, value); +} + +void GraphicsContext3D::getBufferParameteriv(GC3Denum target, GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getBufferParameteriv(target, paramName, value); +} + +void GraphicsContext3D::getFloatv(GC3Denum paramName, GC3Dfloat* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetFloatv(paramName, value); +} + +void GraphicsContext3D::getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getFramebufferAttachmentParameteriv(target, attachment, paramName, value); +} + +void GraphicsContext3D::getIntegerv(GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetIntegerv(paramName, value); +} + +void GraphicsContext3D::getProgramiv(Platform3DObject program, GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getProgramiv(program, paramName, value); +} + +String GraphicsContext3D::getProgramInfoLog(Platform3DObject program) +{ + m_internal->m_glWidget->makeCurrent(); + + GLint length = 0; + m_internal->getProgramiv(program, GraphicsContext3D::INFO_LOG_LENGTH, &length); + + GLsizei size = 0; + + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + + m_internal->getProgramInfoLog(program, length, &size, info); + + String result(info); + fastFree(info); + + return result; +} + +void GraphicsContext3D::getRenderbufferParameteriv(GC3Denum target, GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getRenderbufferParameteriv(target, paramName, value); +} + +void GraphicsContext3D::getShaderiv(Platform3DObject shader, GC3Denum paramName, GC3Dint* value) +{ + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->getShaderiv(shader, paramName, value); +} + +String GraphicsContext3D::getShaderInfoLog(Platform3DObject shader) +{ + m_internal->m_glWidget->makeCurrent(); + + GLint length = 0; + m_internal->getShaderiv(shader, GraphicsContext3D::INFO_LOG_LENGTH, &length); + + GLsizei size = 0; + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + + m_internal->getShaderInfoLog(shader, length, &size, info); + + String result(info); + fastFree(info); + + return result; +} + +String GraphicsContext3D::getShaderSource(Platform3DObject shader) +{ + m_internal->m_glWidget->makeCurrent(); + + GLint length = 0; + m_internal->getShaderiv(shader, GraphicsContext3D::SHADER_SOURCE_LENGTH, &length); + + GLsizei size = 0; + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + + m_internal->getShaderSource(shader, length, &size, info); + + String result(info); + fastFree(info); + + return result; +} + +void GraphicsContext3D::getTexParameterfv(GC3Denum target, GC3Denum paramName, GC3Dfloat* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetTexParameterfv(target, paramName, value); +} + +void GraphicsContext3D::getTexParameteriv(GC3Denum target, GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetTexParameteriv(target, paramName, value); +} + +void GraphicsContext3D::getUniformfv(Platform3DObject program, GC3Dint location, GC3Dfloat* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getUniformfv(program, location, value); +} + +void GraphicsContext3D::getUniformiv(Platform3DObject program, GC3Dint location, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getUniformiv(program, location, value); +} + +long GraphicsContext3D::getUniformLocation(Platform3DObject program, const String& name) +{ + ASSERT(program); + + m_internal->m_glWidget->makeCurrent(); + return m_internal->getUniformLocation(program, name.utf8().data()); +} + +void GraphicsContext3D::getVertexAttribfv(GC3Duint index, GC3Denum paramName, GC3Dfloat* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getVertexAttribfv(index, paramName, value); +} + +void GraphicsContext3D::getVertexAttribiv(GC3Duint index, GC3Denum paramName, GC3Dint* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getVertexAttribiv(index, paramName, value); +} + +GC3Dsizeiptr GraphicsContext3D::getVertexAttribOffset(GC3Duint index, GC3Denum paramName) +{ + m_internal->m_glWidget->makeCurrent(); + + GLvoid* pointer = 0; + m_internal->getVertexAttribPointerv(index, paramName, &pointer); + return static_cast<GC3Dsizeiptr>(reinterpret_cast<intptr_t>(pointer)); +} + +bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels) +{ + m_internal->m_glWidget->makeCurrent(); + glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + return true; +} + +void GraphicsContext3D::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoff, GC3Dint yoff, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, const void* pixels) +{ + m_internal->m_glWidget->makeCurrent(); + glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels); +} + +Platform3DObject GraphicsContext3D::createBuffer() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle = 0; + m_internal->genBuffers(/* count */ 1, &handle); + return handle; +} + +Platform3DObject GraphicsContext3D::createFramebuffer() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle = 0; + m_internal->genFramebuffers(/* count */ 1, &handle); + return handle; +} + +Platform3DObject GraphicsContext3D::createProgram() +{ + m_internal->m_glWidget->makeCurrent(); + return m_internal->createProgram(); +} + +Platform3DObject GraphicsContext3D::createRenderbuffer() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle = 0; + m_internal->genRenderbuffers(/* count */ 1, &handle); + return handle; +} + +Platform3DObject GraphicsContext3D::createShader(GC3Denum type) +{ + m_internal->m_glWidget->makeCurrent(); + return m_internal->createShader(type); +} + +Platform3DObject GraphicsContext3D::createTexture() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle = 0; + glGenTextures(1, &handle); + return handle; +} + +void GraphicsContext3D::deleteBuffer(Platform3DObject buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteBuffers(1, &buffer); +} + +void GraphicsContext3D::deleteFramebuffer(Platform3DObject framebuffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteFramebuffers(1, &framebuffer); +} + +void GraphicsContext3D::deleteProgram(Platform3DObject program) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteProgram(program); +} + +void GraphicsContext3D::deleteRenderbuffer(Platform3DObject renderbuffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteRenderbuffers(1, &renderbuffer); +} + +void GraphicsContext3D::deleteShader(Platform3DObject shader) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteShader(shader); +} + +void GraphicsContext3D::deleteTexture(Platform3DObject texture) +{ + m_internal->m_glWidget->makeCurrent(); + glDeleteTextures(1, &texture); +} + +unsigned int GraphicsContext3D::sizeInBytes(GC3Denum type) +{ + switch (type) { + case GraphicsContext3D::BYTE: + return sizeof(GLbyte); + case GraphicsContext3D::UNSIGNED_BYTE: + return sizeof(GLubyte); + case GraphicsContext3D::SHORT: + return sizeof(GLshort); + case GraphicsContext3D::UNSIGNED_SHORT: + return sizeof(GLushort); + case GraphicsContext3D::INT: + return sizeof(GLint); + case GraphicsContext3D::UNSIGNED_INT: + return sizeof(GLuint); + case GraphicsContext3D::FLOAT: + return sizeof(GLfloat); + default: + return 0; + } +} + +void GraphicsContext3D::synthesizeGLError(GC3Denum error) +{ + m_internal->m_syntheticErrors.add(error); +} + +Extensions3D* GraphicsContext3D::getExtensions() +{ + if (!m_internal->m_extensions) + m_internal->m_extensions = adoptPtr(new Extensions3DQt); + return m_internal->m_extensions; +} + +bool GraphicsContext3D::getImageData(Image* image, + GC3Denum format, + GC3Denum type, + bool premultiplyAlpha, + bool ignoreGammaAndColorProfile, + Vector<uint8_t>& outputVector) +{ + UNUSED_PARAM(ignoreGammaAndColorProfile); + if (!image) + return false; + QPixmap* nativePixmap = image->nativeImageForCurrentFrame(); + if (!nativePixmap) + return false; + + AlphaOp neededAlphaOp = kAlphaDoNothing; + if (!premultiplyAlpha) + // FIXME: must fetch the image data before the premultiplication step + neededAlphaOp = kAlphaDoUnmultiply; + QImage nativeImage = nativePixmap->toImage().convertToFormat(QImage::Format_ARGB32); + outputVector.resize(nativeImage.byteCount()); + return packPixels(nativeImage.rgbSwapped().bits(), kSourceFormatRGBA8, image->width(), image->height(), 0, + format, type, neededAlphaOp, outputVector.data()); +} + +} + +#endif // ENABLE(3D_CANVAS) diff --git a/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp new file mode 100644 index 0000000..e237fc0 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -0,0 +1,1370 @@ +/* + * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org> + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> + * Copyright (C) 2010 Sencha, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GraphicsContext.h" + +#ifdef Q_WS_WIN +#include <windows.h> +#endif + +#include "AffineTransform.h" +#include "Color.h" +#include "ContextShadow.h" +#include "FloatConversion.h" +#include "Font.h" +#include "ImageBuffer.h" +#include "NotImplemented.h" +#include "Path.h" +#include "Pattern.h" +#include "TransparencyLayer.h" + +#include <QBrush> +#include <QGradient> +#include <QPaintDevice> +#include <QPaintEngine> +#include <QPainter> +#include <QPainterPath> +#include <QPixmap> +#include <QPolygonF> +#include <QStack> +#include <QVector> + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +namespace WebCore { + +QPainter::CompositionMode GraphicsContext::toQtCompositionMode(CompositeOperator op) +{ + switch (op) { + case CompositeClear: + return QPainter::CompositionMode_Clear; + case CompositeCopy: + return QPainter::CompositionMode_Source; + case CompositeSourceOver: + return QPainter::CompositionMode_SourceOver; + case CompositeSourceIn: + return QPainter::CompositionMode_SourceIn; + case CompositeSourceOut: + return QPainter::CompositionMode_SourceOut; + case CompositeSourceAtop: + return QPainter::CompositionMode_SourceAtop; + case CompositeDestinationOver: + return QPainter::CompositionMode_DestinationOver; + case CompositeDestinationIn: + return QPainter::CompositionMode_DestinationIn; + case CompositeDestinationOut: + return QPainter::CompositionMode_DestinationOut; + case CompositeDestinationAtop: + return QPainter::CompositionMode_DestinationAtop; + case CompositeXOR: + return QPainter::CompositionMode_Xor; + case CompositePlusDarker: + // there is no exact match, but this is the closest + return QPainter::CompositionMode_Darken; + case CompositeHighlight: + return QPainter::CompositionMode_SourceOver; + case CompositePlusLighter: + return QPainter::CompositionMode_Plus; + default: + ASSERT_NOT_REACHED(); + } + + return QPainter::CompositionMode_SourceOver; +} + +static inline Qt::PenCapStyle toQtLineCap(LineCap lc) +{ + switch (lc) { + case ButtCap: + return Qt::FlatCap; + case RoundCap: + return Qt::RoundCap; + case SquareCap: + return Qt::SquareCap; + default: + ASSERT_NOT_REACHED(); + } + + return Qt::FlatCap; +} + +static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj) +{ + switch (lj) { + case MiterJoin: + return Qt::SvgMiterJoin; + case RoundJoin: + return Qt::RoundJoin; + case BevelJoin: + return Qt::BevelJoin; + default: + ASSERT_NOT_REACHED(); + } + + return Qt::SvgMiterJoin; +} + +static Qt::PenStyle toQPenStyle(StrokeStyle style) +{ + switch (style) { + case NoStroke: + return Qt::NoPen; + break; + case SolidStroke: + return Qt::SolidLine; + break; + case DottedStroke: + return Qt::DotLine; + break; + case DashedStroke: + return Qt::DashLine; + break; + default: + ASSERT_NOT_REACHED(); + } + return Qt::NoPen; +} + +static inline Qt::FillRule toQtFillRule(WindRule rule) +{ + switch (rule) { + case RULE_EVENODD: + return Qt::OddEvenFill; + case RULE_NONZERO: + return Qt::WindingFill; + default: + ASSERT_NOT_REACHED(); + } + return Qt::OddEvenFill; +} + +class GraphicsContextPlatformPrivate : public Noncopyable { +public: + GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor); + ~GraphicsContextPlatformPrivate(); + + inline QPainter* p() const + { + if (layers.isEmpty()) + return painter; + return &layers.top()->painter; + } + + bool antiAliasingForRectsAndLines; + + QStack<TransparencyLayer*> layers; + // Counting real layers. Required by inTransparencyLayer() calls + // For example, layers with valid alphaMask are not real layers + int layerCount; + + // reuse this brush for solid color (to prevent expensive QBrush construction) + QBrush solidColor; + + InterpolationQuality imageInterpolationQuality; + bool initialSmoothPixmapTransformHint; + + ContextShadow shadow; + QStack<ContextShadow> shadowStack; + + QRectF clipBoundingRect() const + { +#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) + return p()->clipBoundingRect(); +#else + return p()->clipRegion().boundingRect(); +#endif + } + + void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; } + +private: + QPainter* painter; + bool platformContextIsOwned; +}; + +GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor) + : antiAliasingForRectsAndLines(false) + , layerCount(0) + , solidColor(initialSolidColor) + , imageInterpolationQuality(InterpolationDefault) + , initialSmoothPixmapTransformHint(false) + , painter(p) + , platformContextIsOwned(false) +{ + if (!painter) + return; + + // Use the default the QPainter was constructed with. + antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing); + + // Used for default image interpolation quality. + initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform); + + painter->setRenderHint(QPainter::Antialiasing, true); +} + +GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate() +{ + if (!platformContextIsOwned) + return; + + QPaintDevice* device = painter->device(); + painter->end(); + delete painter; + delete device; +} + +void GraphicsContext::platformInit(PlatformGraphicsContext* painter) +{ + m_data = new GraphicsContextPlatformPrivate(painter, fillColor()); + + setPaintingDisabled(!painter); + + if (!painter) + return; + + // solidColor is initialized with the fillColor(). + painter->setBrush(m_data->solidColor); + + QPen pen(painter->pen()); + pen.setColor(strokeColor()); + pen.setJoinStyle(toQtLineJoin(MiterJoin)); + painter->setPen(pen); +} + +void GraphicsContext::platformDestroy() +{ + while (!m_data->layers.isEmpty()) + endTransparencyLayer(); + + delete m_data; +} + +PlatformGraphicsContext* GraphicsContext::platformContext() const +{ + return m_data->p(); +} + +AffineTransform GraphicsContext::getCTM() const +{ + const QTransform& matrix = platformContext()->combinedTransform(); + return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(), + matrix.m22(), matrix.dx(), matrix.dy()); +} + +void GraphicsContext::savePlatformState() +{ + if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull()) + ++m_data->layers.top()->saveCounter; + m_data->p()->save(); + m_data->shadowStack.push(m_data->shadow); +} + +void GraphicsContext::restorePlatformState() +{ + if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull()) + if (!--m_data->layers.top()->saveCounter) + endTransparencyLayer(); + + m_data->p()->restore(); + + if (m_data->shadowStack.isEmpty()) + m_data->shadow = ContextShadow(); + else + m_data->shadow = m_data->shadowStack.pop(); +} + +// Draws a filled rectangle with a stroked border. +// This is only used to draw borders (real fill is done via fillRect), and +// thus it must not cast any shadow. +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); + + p->drawRect(rect); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); +} + +// This is only used to draw borders. +// Must not cast any shadow. +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (paintingDisabled()) + return; + + StrokeStyle style = strokeStyle(); + Color color = strokeColor(); + if (style == NoStroke) + return; + + float width = strokeThickness(); + + FloatPoint p1 = point1; + FloatPoint p2 = point2; + bool isVerticalLine = (p1.x() == p2.x()); + + QPainter* p = m_data->p(); + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); + adjustLineToPixelBoundaries(p1, p2, width, style); + + int patWidth = 0; + switch (style) { + case NoStroke: + case SolidStroke: + break; + case DottedStroke: + patWidth = static_cast<int>(width); + break; + case DashedStroke: + patWidth = 3 * static_cast<int>(width); + break; + } + + if (patWidth) { + p->save(); + + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed line. + if (isVerticalLine) { + p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color)); + p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color)); + } else { + p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color)); + p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color)); + } + + // Example: 80 pixels with a width of 30 pixels. + // Remainder is 20. The maximum pixels of line we could paint + // will be 50 pixels. + int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width; + int remainder = distance % patWidth; + int coverage = distance - remainder; + int numSegments = coverage / patWidth; + + float patternOffset = 0.0f; + // Special case 1px dotted borders for speed. + if (patWidth == 1) + patternOffset = 1.0f; + else { + bool evenNumberOfSegments = !(numSegments % 2); + if (remainder) + evenNumberOfSegments = !evenNumberOfSegments; + if (evenNumberOfSegments) { + if (remainder) { + patternOffset += patWidth - remainder; + patternOffset += remainder / 2; + } else + patternOffset = patWidth / 2; + } else { + if (remainder) + patternOffset = (patWidth - remainder) / 2; + } + } + + QVector<qreal> dashes; + dashes << qreal(patWidth) / width << qreal(patWidth) / width; + + QPen pen = p->pen(); + pen.setWidthF(width); + pen.setCapStyle(Qt::FlatCap); + pen.setDashPattern(dashes); + pen.setDashOffset(patternOffset / width); + p->setPen(pen); + } + + p->drawLine(p1, p2); + + if (patWidth) + p->restore(); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); +} + +// This method is only used to draw the little circles used in lists. +void GraphicsContext::drawEllipse(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->p()->drawEllipse(rect); +} + +void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) +{ + if (paintingDisabled()) + return; + + if (npoints <= 1) + return; + + QPolygonF polygon(npoints); + + for (size_t i = 0; i < npoints; i++) + polygon[i] = points[i]; + + QPainter* p = m_data->p(); + + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, shouldAntialias); + + p->drawConvexPolygon(polygon); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); +} + +void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + QPainterPath path(points[0]); + for (size_t i = 1; i < numPoints; ++i) + path.lineTo(points[i]); + path.setFillRule(Qt::WindingFill); + + QPainter* p = m_data->p(); + + bool painterWasAntialiased = p->testRenderHint(QPainter::Antialiasing); + + if (painterWasAntialiased != antialiased) + p->setRenderHint(QPainter::Antialiasing, antialiased); + + p->setClipPath(path, Qt::IntersectClip); + + if (painterWasAntialiased != antialiased) + p->setRenderHint(QPainter::Antialiasing, painterWasAntialiased); +} + +void GraphicsContext::fillPath(const Path& path) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPainterPath platformPath = path.platformPath(); + platformPath.setFillRule(toQtFillRule(fillRule())); + + if (hasShadow()) { + ContextShadow* shadow = contextShadow(); + if (shadow->mustUseContextShadow(this) || m_state.fillPattern || m_state.fillGradient) + { + QPainter* shadowPainter = shadow->beginShadowLayer(this, platformPath.controlPointRect()); + if (shadowPainter) { + shadowPainter->setCompositionMode(QPainter::CompositionMode_Source); + shadowPainter->fillPath(platformPath, QColor(m_data->shadow.m_color)); + shadow->endShadowLayer(this); + } + } else { + QPointF offset = shadow->offset(); + p->translate(offset); + p->fillPath(platformPath, QColor(shadow->m_color)); + p->translate(-offset); + } + } + if (m_state.fillPattern) { + AffineTransform affine; + p->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern(affine))); + } else if (m_state.fillGradient) { + QBrush brush(*m_state.fillGradient->platformGradient()); + brush.setTransform(m_state.fillGradient->gradientSpaceTransform()); + p->fillPath(platformPath, brush); + } else + p->fillPath(platformPath, p->brush()); +} + +void GraphicsContext::strokePath(const Path& path) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPen pen(p->pen()); + QPainterPath platformPath = path.platformPath(); + platformPath.setFillRule(toQtFillRule(fillRule())); + + if (hasShadow()) { + ContextShadow* shadow = contextShadow(); + if (shadow->mustUseContextShadow(this) || m_state.strokePattern || m_state.strokeGradient) + { + FloatRect boundingRect = platformPath.controlPointRect(); + boundingRect.inflate(pen.miterLimit() + pen.widthF()); + QPainter* shadowPainter = shadow->beginShadowLayer(this, boundingRect); + if (shadowPainter) { + shadowPainter->setOpacity(static_cast<qreal>(m_data->shadow.m_color.alpha()) / 255); + shadowPainter->strokePath(platformPath, pen); + shadow->endShadowLayer(this); + } + } else { + QPen shadowPen(pen); + shadowPen.setColor(m_data->shadow.m_color); + QPointF offset = shadow->offset(); + p->translate(offset); + p->strokePath(platformPath, shadowPen); + p->translate(-offset); + } + } + + if (m_state.strokePattern) { + AffineTransform affine; + pen.setBrush(QBrush(m_state.strokePattern->createPlatformPattern(affine))); + p->setPen(pen); + p->strokePath(platformPath, pen); + } else if (m_state.strokeGradient) { + QBrush brush(*m_state.strokeGradient->platformGradient()); + brush.setTransform(m_state.strokeGradient->gradientSpaceTransform()); + pen.setBrush(brush); + p->setPen(pen); + p->strokePath(platformPath, pen); + } else + p->strokePath(platformPath, pen); +} + +static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY) +{ + // Patterns must be painted so that the top left of the first image is anchored at + // the origin of the coordinate space + if (image) { + int w = image->width(); + int h = image->height(); + int startX, startY; + QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height())); + + // startX, startY is the coordinate of the first image we need to put on the left-top of the rect + if (repeatX && repeatY) { + // repeat + // startX, startY is at the left top side of the left-top of the rect + startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); + startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); + } else { + if (!repeatX && !repeatY) { + // no-repeat + // only draw the image once at orgin once, check if need to draw + QRect imageRect(0, 0, w, h); + if (imageRect.intersects(r)) { + startX = 0; + startY = 0; + } else + return; + } else if (repeatX && !repeatY) { + // repeat-x + // startY is fixed, but startX change based on the left-top of the rect + QRect imageRect(r.x(), 0, r.width(), h); + if (imageRect.intersects(r)) { + startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); + startY = 0; + } else + return; + } else { + // repeat-y + // startX is fixed, but startY change based on the left-top of the rect + QRect imageRect(0, r.y(), w, r.height()); + if (imageRect.intersects(r)) { + startX = 0; + startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); + } else + return; + } + } + + int x = startX; + int y = startY; + do { + // repeat Y + do { + // repeat X + QRect imageRect(x, y, w, h); + QRect intersectRect = imageRect.intersected(r); + QPoint destStart(intersectRect.x(), intersectRect.y()); + QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height()); + + p->drawPixmap(destStart, *image, sourceRect); + x += w; + } while (repeatX && x < r.x() + r.width()); + x = startX; + y += h; + } while (repeatY && y < r.y() + r.height()); + } +} + +void GraphicsContext::fillRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QRectF normalizedRect = rect.normalized(); + ContextShadow* shadow = contextShadow(); + + if (m_state.fillPattern) { + AffineTransform affine; + QBrush brush(m_state.fillPattern->createPlatformPattern(affine)); + QPixmap* image = m_state.fillPattern->tileImage()->nativeImageForCurrentFrame(); + QPainter* shadowPainter = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0; + if (shadowPainter) { + drawRepeatPattern(shadowPainter, image, normalizedRect, m_state.fillPattern->repeatX(), m_state.fillPattern->repeatY()); + shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn); + shadowPainter->fillRect(normalizedRect, shadow->m_color); + shadow->endShadowLayer(this); + } + drawRepeatPattern(p, image, normalizedRect, m_state.fillPattern->repeatX(), m_state.fillPattern->repeatY()); + } else if (m_state.fillGradient) { + QBrush brush(*m_state.fillGradient->platformGradient()); + brush.setTransform(m_state.fillGradient->gradientSpaceTransform()); + QPainter* shadowPainter = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0; + if (shadowPainter) { + shadowPainter->fillRect(normalizedRect, brush); + shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn); + shadowPainter->fillRect(normalizedRect, shadow->m_color); + shadow->endShadowLayer(this); + } + p->fillRect(normalizedRect, brush); + } else { + if (hasShadow()) { + if (shadow->mustUseContextShadow(this)) { + QPainter* shadowPainter = shadow->beginShadowLayer(this, normalizedRect); + if (shadowPainter) { + shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255); + shadowPainter->fillRect(normalizedRect, p->brush()); + shadow->endShadowLayer(this); + } + } else { + // Solid rectangle fill with no blur shadow or transformations applied can be done + // faster without using the shadow layer at all. + QColor shadowColor = shadow->m_color; + shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF()); + p->fillRect(normalizedRect.translated(shadow->offset()), shadowColor); + } + } + + p->fillRect(normalizedRect, p->brush()); + } +} + + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled() || !color.isValid()) + return; + + m_data->solidColor.setColor(color); + QPainter* p = m_data->p(); + QRectF normalizedRect = rect.normalized(); + + if (hasShadow()) { + ContextShadow* shadow = contextShadow(); + if (shadow->mustUseContextShadow(this)) { + QPainter* shadowPainter = shadow->beginShadowLayer(this, normalizedRect); + if (shadowPainter) { + shadowPainter->setCompositionMode(QPainter::CompositionMode_Source); + shadowPainter->fillRect(normalizedRect, shadow->m_color); + shadow->endShadowLayer(this); + } + } else + p->fillRect(normalizedRect.translated(shadow->offset()), shadow->m_color); + } + + p->fillRect(normalizedRect, m_data->solidColor); +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled() || !color.isValid()) + return; + + Path path; + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + QPainter* p = m_data->p(); + if (hasShadow()) { + ContextShadow* shadow = contextShadow(); + if (shadow->mustUseContextShadow(this)) { + QPainter* shadowPainter = shadow->beginShadowLayer(this, rect); + if (shadowPainter) { + shadowPainter->setCompositionMode(QPainter::CompositionMode_Source); + shadowPainter->fillPath(path.platformPath(), QColor(m_data->shadow.m_color)); + shadow->endShadowLayer(this); + } + } else { + p->translate(m_data->shadow.offset()); + p->fillPath(path.platformPath(), QColor(m_data->shadow.m_color)); + p->translate(-m_data->shadow.offset()); + } + } + p->fillPath(path.platformPath(), QColor(color)); +} + +bool GraphicsContext::inTransparencyLayer() const +{ + return m_data->layerCount; +} + +ContextShadow* GraphicsContext::contextShadow() +{ + return &m_data->shadow; +} + +void GraphicsContext::clip(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + m_data->p()->setClipRect(rect, Qt::IntersectClip); +} + +void GraphicsContext::clipPath(const Path& path, WindRule clipRule) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPainterPath platformPath = path.platformPath(); + platformPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); + p->setClipPath(platformPath, Qt::IntersectClip); +} + +void drawFocusRingForPath(QPainter* p, const QPainterPath& path, int width, const Color& color, bool antiAliasing) +{ + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, antiAliasing); + + const QPen oldPen = p->pen(); + const QBrush oldBrush = p->brush(); + + QPen nPen = p->pen(); + nPen.setColor(color); + nPen.setWidth(width); + p->setBrush(Qt::NoBrush); + nPen.setStyle(Qt::SolidLine); + + p->strokePath(path, nPen); + p->setBrush(oldBrush); + p->setPen(oldPen); + + p->setRenderHint(QPainter::Antialiasing, antiAlias); +} + +void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) +{ + // FIXME: Use 'offset' for something? http://webkit.org/b/49909 + + if (paintingDisabled() || !color.isValid()) + return; + + drawFocusRingForPath(m_data->p(), path.platformPath(), width, color, m_data->antiAliasingForRectsAndLines); +} + +/** + * Focus ring handling for form controls is not handled here. Qt style in + * RenderTheme handles drawing focus on widgets which + * need it. It is still handled here for links. + */ +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) +{ + if (paintingDisabled() || !color.isValid()) + return; + + unsigned rectCount = rects.size(); + + if (!rects.size()) + return; + + int radius = (width - 1) / 2; + QPainterPath path; + for (unsigned i = 0; i < rectCount; ++i) { + QRect rect = QRect((rects[i])).adjusted(-offset - radius, -offset - radius, offset + radius, offset + radius); + // This is not the most efficient way to add a rect to a path, but if we don't create the tmpPath, + // we will end up with ugly lines in between rows of text on anchors with multiple lines. + QPainterPath tmpPath; + tmpPath.addRoundedRect(rect, radius, radius); + path = path.united(tmpPath); + } + + drawFocusRingForPath(m_data->p(), path, width, color, m_data->antiAliasingForRectsAndLines); +} + +void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool) +{ + if (paintingDisabled()) + return; + + IntPoint startPoint = origin; + IntPoint endPoint = origin + IntSize(width, 0); + + // If paintengine type is X11 to avoid artifacts + // like bug https://bugs.webkit.org/show_bug.cgi?id=42248 +#if defined(Q_WS_X11) + QPainter* p = m_data->p(); + if (p->paintEngine()->type() == QPaintEngine::X11) { + // If stroke thickness is odd we need decrease Y coordinate by 1 pixel, + // because inside method adjustLineToPixelBoundaries(...), which + // called from drawLine(...), Y coordinate will be increased by 0.5f + // and then inside Qt painting engine will be rounded to next greater + // integer value. + float strokeWidth = strokeThickness(); + if (static_cast<int>(strokeWidth) % 2) { + startPoint.setY(startPoint.y() - 1); + endPoint.setY(endPoint.y() - 1); + } + } +#endif // defined(Q_WS_X11) + + drawLine(startPoint, endPoint); +} + +void GraphicsContext::drawLineForTextChecking(const IntPoint&, int, TextCheckingLineStyle) +{ + if (paintingDisabled()) + return; + + notImplemented(); +} + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) +{ + // 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; + + 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 FloatSize& size, float blur, const Color& color, ColorSpace) +{ + // Qt doesn't support shadows natively, they are drawn manually in the draw* + // functions + + if (m_state.shadowsIgnoreTransforms) { + // Meaning that this graphics context is associated with a CanvasRenderingContext + // We flip the height since CG and HTML5 Canvas have opposite Y axis + m_state.shadowOffset = FloatSize(size.width(), -size.height()); + m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height())); + } else + m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height())); + + m_data->shadow.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms); +} + +void GraphicsContext::clearPlatformShadow() +{ + m_data->shadow.clear(); +} + +void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask) +{ + QPainter* p = m_data->p(); + m_data->layers.push(new TransparencyLayer(p, p->transform().mapRect(rect), 1.0, alphaMask)); +} + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + if (paintingDisabled()) + return; + + int x, y, w, h; + x = y = 0; + QPainter* p = m_data->p(); + const QPaintDevice* device = p->device(); + w = device->width(); + h = device->height(); + + QRectF clip = m_data->clipBoundingRect(); + QRectF deviceClip = p->transform().mapRect(clip); + x = int(qBound(qreal(0), deviceClip.x(), (qreal)w)); + y = int(qBound(qreal(0), deviceClip.y(), (qreal)h)); + w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2); + h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2); + + QPixmap emptyAlphaMask; + m_data->layers.push(new TransparencyLayer(p, QRect(x, y, w, h), opacity, emptyAlphaMask)); + ++m_data->layerCount; +} + +void GraphicsContext::endTransparencyLayer() +{ + if (paintingDisabled()) + return; + + TransparencyLayer* layer = m_data->layers.pop(); + if (!layer->alphaMask.isNull()) { + layer->painter.resetTransform(); + layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + layer->painter.drawPixmap(QPoint(), layer->alphaMask); + } else + --m_data->layerCount; // see the comment for layerCount + layer->painter.end(); + + QPainter* p = m_data->p(); + p->save(); + p->resetTransform(); + p->setOpacity(layer->opacity); + p->drawPixmap(layer->offset, layer->pixmap); + p->restore(); + + delete layer; +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPainter::CompositionMode currentCompositionMode = p->compositionMode(); + if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + p->setCompositionMode(QPainter::CompositionMode_Source); + p->fillRect(rect, Qt::transparent); + if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + p->setCompositionMode(currentCompositionMode); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (paintingDisabled()) + return; + + Path path; + path.addRect(rect); + + float previousStrokeThickness = strokeThickness(); + + if (lineWidth != previousStrokeThickness) + setStrokeThickness(lineWidth); + + strokePath(path); + + if (lineWidth != previousStrokeThickness) + setStrokeThickness(previousStrokeThickness); +} + +void GraphicsContext::setLineCap(LineCap lc) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPen nPen = p->pen(); + nPen.setCapStyle(toQtLineCap(lc)); + p->setPen(nPen); +} + +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + QPainter* p = m_data->p(); + QPen pen = p->pen(); + unsigned dashLength = dashes.size(); + if (dashLength) { + QVector<qreal> pattern; + unsigned count = dashLength; + if (dashLength % 2) + count *= 2; + + float penWidth = narrowPrecisionToFloat(double(pen.widthF())); + for (unsigned i = 0; i < count; i++) + pattern.append(dashes[i % dashLength] / penWidth); + + pen.setDashPattern(pattern); + pen.setDashOffset(dashOffset / penWidth); + } else + pen.setStyle(Qt::SolidLine); + p->setPen(pen); +} + +void GraphicsContext::setLineJoin(LineJoin lj) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPen nPen = p->pen(); + nPen.setJoinStyle(toQtLineJoin(lj)); + p->setPen(nPen); +} + +void GraphicsContext::setMiterLimit(float limit) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPen nPen = p->pen(); + nPen.setMiterLimit(limit); + p->setPen(nPen); +} + +void GraphicsContext::setAlpha(float opacity) +{ + if (paintingDisabled()) + return; + QPainter* p = m_data->p(); + p->setOpacity(opacity); +} + +void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) +{ + if (paintingDisabled()) + return; + + QPainter* painter = m_data->p(); + + if (!painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + return; + + painter->setCompositionMode(toQtCompositionMode(op)); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + + QPainterPath clipPath = path.platformPath(); + clipPath.setFillRule(Qt::WindingFill); + m_data->p()->setClipPath(clipPath, Qt::IntersectClip); +} + +void GraphicsContext::canvasClip(const Path& path) +{ + clip(path); +} + +void GraphicsContext::clipOut(const Path& path) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPainterPath clippedOut = path.platformPath(); + QPainterPath newClip; + newClip.setFillRule(Qt::OddEvenFill); + if (p->hasClipping()) { + newClip.addRect(m_data->clipBoundingRect()); + newClip.addPath(clippedOut); + p->setClipPath(newClip, Qt::IntersectClip); + } else { + QRect windowRect = p->transform().inverted().mapRect(p->window()); + newClip.addRect(windowRect); + newClip.addPath(clippedOut.intersected(newClip)); + p->setClipPath(newClip); + } +} + +void GraphicsContext::translate(float x, float y) +{ + if (paintingDisabled()) + return; + + m_data->p()->translate(x, y); +} + +void GraphicsContext::rotate(float radians) +{ + if (paintingDisabled()) + return; + + m_data->p()->rotate(180 / M_PI*radians); +} + +void GraphicsContext::scale(const FloatSize& s) +{ + if (paintingDisabled()) + return; + + m_data->p()->scale(s.width(), s.height()); +} + +void GraphicsContext::clipOut(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + QPainter* p = m_data->p(); + QPainterPath newClip; + newClip.setFillRule(Qt::OddEvenFill); + if (p->hasClipping()) { + newClip.addRect(m_data->clipBoundingRect()); + newClip.addRect(QRect(rect)); + p->setClipPath(newClip, Qt::IntersectClip); + } else { + QRect clipOutRect(rect); + QRect window = p->transform().inverted().mapRect(p->window()); + clipOutRect &= window; + newClip.addRect(window); + newClip.addRect(clipOutRect); + p->setClipPath(newClip); + } +} + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, + int thickness) +{ + if (paintingDisabled()) + return; + + clip(rect); + QPainterPath path; + + // Add outer ellipse + path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height())); + + // Add inner ellipse. + path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness, + rect.width() - (thickness * 2), rect.height() - (thickness * 2))); + + path.setFillRule(Qt::OddEvenFill); + + QPainter* p = m_data->p(); + + const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::Antialiasing, true); + p->setClipPath(path, Qt::IntersectClip); + p->setRenderHint(QPainter::Antialiasing, antiAlias); +} + +void GraphicsContext::concatCTM(const AffineTransform& transform) +{ + if (paintingDisabled()) + return; + + m_data->p()->setWorldTransform(transform, true); +} + +void GraphicsContext::setURLForRect(const KURL&, const IntRect&) +{ + notImplemented(); +} + +void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled() || !color.isValid()) + return; + + QPainter* p = m_data->p(); + QPen newPen(p->pen()); + m_data->solidColor.setColor(color); + newPen.setBrush(m_data->solidColor); + p->setPen(newPen); +} + +void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle) +{ + if (paintingDisabled()) + return; + QPainter* p = m_data->p(); + QPen newPen(p->pen()); + newPen.setStyle(toQPenStyle(strokeStyle)); + p->setPen(newPen); +} + +void GraphicsContext::setPlatformStrokeThickness(float thickness) +{ + if (paintingDisabled()) + return; + QPainter* p = m_data->p(); + QPen newPen(p->pen()); + newPen.setWidthF(thickness); + p->setPen(newPen); +} + +void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled() || !color.isValid()) + return; + + m_data->solidColor.setColor(color); + m_data->p()->setBrush(m_data->solidColor); +} + +void GraphicsContext::setPlatformShouldAntialias(bool enable) +{ + if (paintingDisabled()) + return; + m_data->p()->setRenderHint(QPainter::Antialiasing, enable); +} + +#ifdef Q_WS_WIN + +HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true + Q_ASSERT(mayCreateBitmap); + + if (dstRect.isEmpty()) + return 0; + + // Create a bitmap DC in which to draw. + BITMAPINFO bitmapInfo; + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = dstRect.width(); + bitmapInfo.bmiHeader.biHeight = dstRect.height(); + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = 0; + bitmapInfo.bmiHeader.biXPelsPerMeter = 0; + bitmapInfo.bmiHeader.biYPelsPerMeter = 0; + bitmapInfo.bmiHeader.biClrUsed = 0; + bitmapInfo.bmiHeader.biClrImportant = 0; + + void* pixels = 0; + HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); + if (!bitmap) + return 0; + + HDC displayDC = ::GetDC(0); + HDC bitmapDC = ::CreateCompatibleDC(displayDC); + ::ReleaseDC(0, displayDC); + + ::SelectObject(bitmapDC, bitmap); + + // Fill our buffer with clear if we're going to alpha blend. + if (supportAlphaBlend) { + BITMAP bmpInfo; + GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); + int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; + memset(bmpInfo.bmBits, 0, bufferSize); + } + +#if !OS(WINCE) + // Make sure we can do world transforms. + SetGraphicsMode(bitmapDC, GM_ADVANCED); + + // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = -dstRect.x(); + xform.eDy = -dstRect.y(); + ::SetWorldTransform(bitmapDC, &xform); +#endif + + return bitmapDC; +} + +void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) +{ + // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true + Q_ASSERT(mayCreateBitmap); + + if (hdc) { + + if (!dstRect.isEmpty()) { + + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + BITMAP info; + GetObject(bitmap, sizeof(info), &info); + ASSERT(info.bmBitsPixel == 32); + + QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha); + m_data->p()->drawPixmap(dstRect, pixmap); + + ::DeleteObject(bitmap); + } + + ::DeleteDC(hdc); + } +} +#endif + +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality) +{ + m_data->imageInterpolationQuality = quality; + + switch (quality) { + case InterpolationNone: + case InterpolationLow: + // use nearest-neigbor + m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false); + break; + + case InterpolationMedium: + case InterpolationHigh: + // use the filter + m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true); + break; + + case InterpolationDefault: + default: + m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, m_data->initialSmoothPixmapTransformHint); + break; + }; +} + +InterpolationQuality GraphicsContext::imageInterpolationQuality() const +{ + return m_data->imageInterpolationQuality; +} + +void GraphicsContext::takeOwnershipOfPlatformContext() +{ + m_data->takeOwnershipOfPlatformContext(); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp new file mode 100644 index 0000000..f31844a --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -0,0 +1,1779 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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" +#include "GraphicsLayerQt.h" + +#include "CurrentTime.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "Image.h" +#include "RefCounted.h" +#include "TranslateTransformOperation.h" +#include "UnitBezier.h" +#include <QtCore/qabstractanimation.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qset.h> +#include <QtCore/qtimer.h> +#include <QtGui/qcolor.h> +#include <QtGui/qgraphicseffect.h> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qgraphicsview.h> +#include <QtGui/qgraphicswidget.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qpixmapcache.h> +#include <QtGui/qstyleoption.h> + +#if ENABLE(TILED_BACKING_STORE) +#include "TiledBackingStore.h" +#include "TiledBackingStoreClient.h" + +// The minimum width/height for tiling. We use the same value as the Windows implementation. +#define GRAPHICS_LAYER_TILING_THRESHOLD 2000 +#endif + + +#define QT_DEBUG_RECACHE 0 +#define QT_DEBUG_CACHEDUMP 0 + +#define QT_DEBUG_FPS 0 + +namespace WebCore { + +#ifndef QT_NO_GRAPHICSEFFECT +class MaskEffectQt : public QGraphicsEffect { +public: + MaskEffectQt(QObject* parent, QGraphicsItem* maskLayer) + : QGraphicsEffect(parent) + , m_maskLayer(maskLayer) + { + } + + void draw(QPainter* painter) + { + // This is a modified clone of QGraphicsOpacityEffect. + // It's more efficient to do it this way because: + // (a) We don't need the QBrush abstraction - we always end up using QGraphicsItem::paint + // from the mask layer. + // (b) QGraphicsOpacityEffect detaches the pixmap, which is inefficient on OpenGL. + const QSize maskSize = sourceBoundingRect().toAlignedRect().size(); + if (!maskSize.isValid() || maskSize.isEmpty()) { + drawSource(painter); + return; + } + QPixmap maskPixmap(maskSize); + + // We need to do this so the pixmap would have hasAlpha(). + maskPixmap.fill(Qt::transparent); + QPainter maskPainter(&maskPixmap); + QStyleOptionGraphicsItem option; + option.exposedRect = option.rect = maskPixmap.rect(); + maskPainter.setRenderHints(painter->renderHints(), true); + m_maskLayer->paint(&maskPainter, &option, 0); + maskPainter.end(); + + QPoint offset; + QPixmap srcPixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, QGraphicsEffect::NoPad); + + // We have to use another intermediate pixmap, to make sure the mask applies only to this item + // and doesn't modify pixels already painted into this paint-device. + QPixmap pixmap(srcPixmap.size()); + pixmap.fill(Qt::transparent); + + if (pixmap.isNull()) + return; + + QPainter pixmapPainter(&pixmap); + + pixmapPainter.setRenderHints(painter->renderHints()); + pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); + + // We use drawPixmap rather than detaching, because it's more efficient on OpenGL. + pixmapPainter.drawPixmap(0, 0, srcPixmap); + pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + pixmapPainter.drawPixmap(0, 0, maskPixmap); + + pixmapPainter.end(); + painter->drawPixmap(offset, pixmap); + } + + QGraphicsItem* m_maskLayer; +}; +#endif // QT_NO_GRAPHICSEFFECT + +class GraphicsLayerQtImpl : public QGraphicsObject +#if ENABLE(TILED_BACKING_STORE) +, public virtual TiledBackingStoreClient +#endif +{ + Q_OBJECT + +public: + // This set of flags help us defer which properties of the layer have been + // modified by the compositor, so we can know what to look for in the next flush. + enum ChangeMask { + NoChanges = 0, + + ParentChange = (1L << 0), + ChildrenChange = (1L << 1), + MaskLayerChange = (1L << 2), + PositionChange = (1L << 3), + + AnchorPointChange = (1L << 4), + SizeChange = (1L << 5), + TransformChange = (1L << 6), + ContentChange = (1L << 7), + + ContentsOrientationChange = (1L << 8), + OpacityChange = (1L << 9), + ContentsRectChange = (1L << 10), + + Preserves3DChange = (1L << 11), + MasksToBoundsChange = (1L << 12), + DrawsContentChange = (1L << 13), + ContentsOpaqueChange = (1L << 14), + + BackfaceVisibilityChange = (1L << 15), + ChildrenTransformChange = (1L << 16), + DisplayChange = (1L << 17), + BackgroundColorChange = (1L << 18), + + DistributesOpacityChange = (1L << 19) + }; + + // The compositor lets us special-case images and colors, so we try to do so. + enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType, MediaContentType, Canvas3DContentType}; + + const GraphicsLayerQtImpl* rootLayer() const; + + GraphicsLayerQtImpl(GraphicsLayerQt* newLayer); + virtual ~GraphicsLayerQtImpl(); + + // reimps from QGraphicsItem + virtual QPainterPath opaqueArea() const; + virtual QRectF boundingRect() const; + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + + // We manage transforms ourselves because transform-origin acts differently in webkit and in Qt, + // and we need it as a fallback in case we encounter an un-invertible matrix. + void setBaseTransform(const TransformationMatrix&); + void updateTransform(); + + // let the compositor-API tell us which properties were changed + void notifyChange(ChangeMask); + + // Actual rendering of the web-content into a QPixmap: + // We prefer to use our own caching because it gives us a higher level of granularity than + // QGraphicsItem cache modes - Sometimes we need to cache the contents even though the item + // needs to be updated, e.g. when the background-color is changed. + // TODO: investigate if QGraphicsItem caching can be improved to support that out of the box. + QPixmap recache(const QRegion&); + + // Called when the compositor is ready for us to show the changes on screen. + // 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, bool forceTransformUpdate = false); + +#if ENABLE(TILED_BACKING_STORE) + // reimplementations from TiledBackingStoreClient + virtual void tiledBackingStorePaintBegin(); + virtual void tiledBackingStorePaint(GraphicsContext*, const IntRect&); + virtual void tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea); + virtual IntRect tiledBackingStoreContentsRect(); + virtual IntRect tiledBackingStoreVisibleRect(); + virtual Color tiledBackingStoreBackgroundColor() const; +#endif + +public slots: + // We need to notify the client (ie. the layer compositor) when the animation actually starts. + void notifyAnimationStarted(); + + // We notify WebCore of a layer changed asynchronously; otherwise we end up calling flushChanges too often. + void notifySyncRequired(); + +signals: + // Optimization: Avoid using QTimer::singleShot(). + void notifyAnimationStartedAsync(); + +public: + GraphicsLayerQt* m_layer; + + TransformationMatrix m_baseTransform; + TransformationMatrix m_transformRelativeToRootLayer; + bool m_transformAnimationRunning; + bool m_opacityAnimationRunning; + bool m_blockNotifySyncRequired; +#ifndef QT_NO_GRAPHICSEFFECT + QWeakPointer<MaskEffectQt> m_maskEffect; +#endif + + struct ContentData { + QPixmap pixmap; + QRegion regionToUpdate; + bool updateAll; + + QColor contentsBackgroundColor; + QColor backgroundColor; + + QWeakPointer<QGraphicsObject> mediaLayer; + StaticContentType contentType; + + float opacity; + + ContentData() + : updateAll(false) + , contentType(HTMLContentType) + , opacity(1.f) + { + } + + }; + + ContentData m_pendingContent; + ContentData m_currentContent; + + int m_changeMask; + +#if ENABLE(TILED_BACKING_STORE) + TiledBackingStore* m_tiledBackingStore; +#endif + + QSizeF m_size; + struct { + QPixmapCache::Key key; + QSizeF size; + } m_backingStore; +#ifndef QT_NO_ANIMATION + QList<QWeakPointer<QAbstractAnimation> > m_animations; +#endif + QTimer m_suspendTimer; + + struct State { + GraphicsLayer* maskLayer; + FloatPoint pos; + FloatPoint3D anchorPoint; + FloatSize size; + TransformationMatrix transform; + TransformationMatrix childrenTransform; + Color backgroundColor; + Color currentColor; + GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation; + float opacity; + QRect contentsRect; + + bool preserves3D: 1; + bool masksToBounds: 1; + bool drawsContent: 1; + bool contentsOpaque: 1; + bool backfaceVisibility: 1; + bool distributeOpacity: 1; + bool align: 2; + + State() + : maskLayer(0) + , opacity(1.f) + , preserves3D(false) + , masksToBounds(false) + , drawsContent(false) + , contentsOpaque(false) + , backfaceVisibility(false) + , distributeOpacity(false) + { + } + } m_state; + +#if ENABLE(3D_CANVAS) + const GraphicsContext3D* m_gc3D; +#endif + +#ifndef QT_NO_ANIMATION + friend class AnimationQtBase; +#endif +}; + +inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsItem* item) +{ + ASSERT(item); + return qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject()); +} + +inline GraphicsLayerQtImpl* toGraphicsLayerQtImpl(QGraphicsObject* item) +{ + return qobject_cast<GraphicsLayerQtImpl*>(item); +} + +GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) + : QGraphicsObject(0) + , m_layer(newLayer) + , m_transformAnimationRunning(false) + , m_opacityAnimationRunning(false) + , m_blockNotifySyncRequired(false) + , m_changeMask(NoChanges) +#if ENABLE(TILED_BACKING_STORE) + , m_tiledBackingStore(0) +#endif +#if ENABLE(3D_CANVAS) + , m_gc3D(0) +#endif +{ + // We use graphics-view for compositing-only, not for interactivity. + setAcceptedMouseButtons(Qt::NoButton); + + // 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); + + connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection); +} + +GraphicsLayerQtImpl::~GraphicsLayerQtImpl() +{ + // The compositor manages lifecycle of item, so we do not want the graphicsview system to delete + // our items automatically. + const QList<QGraphicsItem*> children = childItems(); + QList<QGraphicsItem*>::const_iterator cit; + for (cit = children.constBegin(); cit != children.constEnd(); ++cit) { + if (QGraphicsItem* item = *cit) { + if (scene()) + scene()->removeItem(item); + item->setParentItem(0); + } + } +#if ENABLE(TILED_BACKING_STORE) + delete m_tiledBackingStore; +#endif +#ifndef QT_NO_ANIMATION + // We do, however, own the animations. + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_animations.begin(); it != m_animations.end(); ++it) + if (QAbstractAnimation* anim = it->data()) + delete anim; +#endif +} + +const GraphicsLayerQtImpl* GraphicsLayerQtImpl::rootLayer() const +{ + if (const GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject())) + return parent->rootLayer(); + return this; +} + +QPixmap GraphicsLayerQtImpl::recache(const QRegion& regionToUpdate) +{ + if (!m_layer->drawsContent() || m_size.isEmpty() || !m_size.isValid()) + return QPixmap(); + +#if ENABLE(TILED_BACKING_STORE) + const bool requiresTiling = (m_state.drawsContent && m_currentContent.contentType == HTMLContentType) && (m_size.width() > GRAPHICS_LAYER_TILING_THRESHOLD || m_size.height() > GRAPHICS_LAYER_TILING_THRESHOLD); + if (requiresTiling && !m_tiledBackingStore) { + m_tiledBackingStore = new TiledBackingStore(this); + m_tiledBackingStore->setTileCreationDelay(0); + setFlag(ItemUsesExtendedStyleOption, true); + } else if (!requiresTiling && m_tiledBackingStore) { + delete m_tiledBackingStore; + m_tiledBackingStore = 0; + setFlag(ItemUsesExtendedStyleOption, false); + } + + if (m_tiledBackingStore) { + m_tiledBackingStore->adjustVisibleRect(); + const QVector<QRect> rects = regionToUpdate.rects(); + for (int i = 0; i < rects.size(); ++i) + m_tiledBackingStore->invalidate(rects[i]); + return QPixmap(); + } +#endif + + QPixmap pixmap; + QRegion region = regionToUpdate; + if (QPixmapCache::find(m_backingStore.key, &pixmap)) { + if (region.isEmpty()) + return pixmap; + QPixmapCache::remove(m_backingStore.key); // Remove the reference to the pixmap in the cache to avoid a detach. + } + + { + bool erased = false; + + // If the pixmap is not in the cache or the view has grown since last cached. + if (pixmap.isNull() || m_size != m_backingStore.size) { +#if QT_DEBUG_RECACHE + if (pixmap.isNull()) + qDebug() << "CacheMiss" << this << m_size; +#endif + bool fill = true; + QRegion newRegion; + QPixmap oldPixmap = pixmap; + + // If the pixmap is two small to hold the view contents we enlarge, otherwise just use the old (large) pixmap. + if (pixmap.width() < m_size.width() || pixmap.height() < m_size.height()) { +#if QT_DEBUG_RECACHE + qDebug() << "CacheGrow" << this << m_size; +#endif + pixmap = QPixmap(m_size.toSize()); + pixmap.fill(Qt::transparent); + newRegion = QRegion(0, 0, m_size.width(), m_size.height()); + } + +#if 1 + // Blit the contents of oldPixmap back into the cached pixmap as we are just adding new pixels. + if (!oldPixmap.isNull()) { + const QRegion cleanRegion = (QRegion(0, 0, m_size.width(), m_size.height()) + & QRegion(0, 0, m_backingStore.size.width(), m_backingStore.size.height())) - regionToUpdate; + if (!cleanRegion.isEmpty()) { +#if QT_DEBUG_RECACHE + qDebug() << "CacheBlit" << this << cleanRegion; +#endif + const QRect cleanBounds(cleanRegion.boundingRect()); + QPainter painter(&pixmap); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawPixmap(cleanBounds.topLeft(), oldPixmap, cleanBounds); + newRegion -= cleanRegion; + fill = false; // We cannot just fill the pixmap. + } + oldPixmap = QPixmap(); + } +#endif + region += newRegion; + if (fill && !region.isEmpty()) { // Clear the entire pixmap with the background. +#if QT_DEBUG_RECACHE + qDebug() << "CacheErase" << this << m_size << background; +#endif + erased = true; + pixmap.fill(Qt::transparent); + } + } + region &= QRegion(0, 0, m_size.width(), m_size.height()); + + // If we have something to draw its time to erase it and render the contents. + if (!region.isEmpty()) { +#if QT_DEBUG_CACHEDUMP + static int recacheCount = 0; + ++recacheCount; + qDebug() << "**** CACHEDUMP" << recacheCount << this << m_layer << region << m_size; + pixmap.save(QString().sprintf("/tmp/%05d_A.png", recacheCount), "PNG"); +#endif + + QPainter painter(&pixmap); + GraphicsContext gc(&painter); + + painter.setClipRegion(region); + + if (!erased) { // Erase the area in cache that we're drawing into. + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(region.boundingRect(), Qt::transparent); + +#if QT_DEBUG_CACHEDUMP + qDebug() << "**** CACHEDUMP" << recacheCount << this << m_layer << region << m_size; + pixmap.save(QString().sprintf("/tmp/%05d_B.png", recacheCount), "PNG"); +#endif + } + + // Render the actual contents into the cache. + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + m_layer->paintGraphicsLayerContents(gc, region.boundingRect()); + painter.end(); + +#if QT_DEBUG_CACHEDUMP + qDebug() << "**** CACHEDUMP" << recacheCount << this << m_layer << region << m_size; + pixmap.save(QString().sprintf("/tmp/%05d_C.png", recacheCount), "PNG"); +#endif + } + m_backingStore.size = m_size; // Store the used size of the pixmap. + } + + // Finally insert into the cache and allow a reference there. + m_backingStore.key = QPixmapCache::insert(pixmap); + return pixmap; +} + +void GraphicsLayerQtImpl::updateTransform() +{ + if (!m_transformAnimationRunning) + m_baseTransform = m_layer->transform(); + + TransformationMatrix localTransform; + + GraphicsLayerQtImpl* parent = toGraphicsLayerQtImpl(parentObject()); + + // WebCore has relative-to-size originPoint, where as the QGraphicsView has a pixel originPoint. + // Thus, we need to convert here as we have to manage this outselves due to the fact that the + // transformOrigin of the graphicsview is imcompatible. + const qreal originX = m_state.anchorPoint.x() * m_size.width(); + const qreal originY = m_state.anchorPoint.y() * m_size.height(); + + // We ignore QGraphicsItem::pos completely, and use transforms only, due to the fact that we + // have to maintain that ourselves for 3D. + localTransform + .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z()) + .multLeft(m_baseTransform) + .translate3d(-originX, -originY, -m_state.anchorPoint.z()); + + // This is the actual 3D transform of this item, with the ancestors' transform baked in. + m_transformRelativeToRootLayer = TransformationMatrix(parent ? parent->m_transformRelativeToRootLayer : TransformationMatrix()) + .multLeft(localTransform); + + // Now we have enough information to determine if the layer is facing backwards. + if (!m_state.backfaceVisibility && m_transformRelativeToRootLayer.inverse().m33() < 0) { + setVisible(false); + // No point in making extra calculations for invisible elements. + return; + } + + // The item is front-facing or backface-visibility is on. + setVisible(true); + + // Flatten to 2D-space of this item if it doesn't preserve 3D. + if (!m_state.preserves3D) { + m_transformRelativeToRootLayer.setM13(0); + m_transformRelativeToRootLayer.setM23(0); + m_transformRelativeToRootLayer.setM31(0); + m_transformRelativeToRootLayer.setM32(0); + m_transformRelativeToRootLayer.setM33(1); + m_transformRelativeToRootLayer.setM34(0); + m_transformRelativeToRootLayer.setM43(0); + } + + // Apply perspective for the use of this item's children. Perspective is always applied from the item's + // center. + if (!m_state.childrenTransform.isIdentity()) { + m_transformRelativeToRootLayer + .translate(m_size.width() / 2, m_size.height() /2) + .multLeft(m_state.childrenTransform) + .translate(-m_size.width() / 2, -m_size.height() /2); + } + + bool inverseOk = true; + // Use QTransform::inverse to extrapolate the relative transform of this item, based on the parent's + // transform relative to the root layer and the desired transform for this item relative to the root layer. + const QTransform parentTransform = parent ? parent->itemTransform(rootLayer()) : QTransform(); + const QTransform transform2D = QTransform(m_transformRelativeToRootLayer) * parentTransform.inverted(&inverseOk); + + // In rare cases the transformation cannot be inversed - in that case we don't apply the transformation at + // all, otherwise we'd flicker. FIXME: This should be amended when Qt moves to a real 3D scene-graph. + if (!inverseOk) + return; + + setTransform(transform2D); + + const QList<QGraphicsItem*> children = childItems(); + QList<QGraphicsItem*>::const_iterator it; + for (it = children.constBegin(); it != children.constEnd(); ++it) + if (GraphicsLayerQtImpl* layer= toGraphicsLayerQtImpl(*it)) + layer->updateTransform(); +} + +void GraphicsLayerQtImpl::setBaseTransform(const TransformationMatrix& baseTransform) +{ + m_baseTransform = baseTransform; + updateTransform(); +} + +QPainterPath GraphicsLayerQtImpl::opaqueArea() const +{ + QPainterPath painterPath; + + // We try out best to return the opaque area, maybe it will help graphics-view render less items. + if (m_currentContent.backgroundColor.isValid() && m_currentContent.backgroundColor.alpha() == 0xff) + painterPath.addRect(boundingRect()); + else { + if (m_state.contentsOpaque + || (m_currentContent.contentType == ColorContentType && m_currentContent.contentsBackgroundColor.alpha() == 0xff) + || (m_currentContent.contentType == MediaContentType) + || (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlpha())) { + painterPath.addRect(m_state.contentsRect); + } + } + return painterPath; +} + +QRectF GraphicsLayerQtImpl::boundingRect() const +{ + return QRectF(QPointF(0, 0), QSizeF(m_size)); +} + +void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ +#if ENABLE(TILED_BACKING_STORE) + // FIXME: There's currently no Qt API to know if a new region of an item is exposed outside of the paint event. + // Suggested for Qt: http://bugreports.qt.nokia.com/browse/QTBUG-14877. + if (m_tiledBackingStore) + m_tiledBackingStore->adjustVisibleRect(); +#endif + + if (m_currentContent.backgroundColor.isValid()) + painter->fillRect(option->exposedRect, QColor(m_currentContent.backgroundColor)); + + switch (m_currentContent.contentType) { + case HTMLContentType: + if (m_state.drawsContent) { + QPixmap backingStore; + // We might need to recache, in case we try to paint and the cache was purged (e.g. if it was full). + if (!QPixmapCache::find(m_backingStore.key, &backingStore) || backingStore.size() != m_size.toSize()) + backingStore = recache(QRegion(m_state.contentsRect)); + const QRectF bounds(0, 0, m_backingStore.size.width(), m_backingStore.size.height()); + painter->drawPixmap(0, 0, backingStore); + } + break; + case PixmapContentType: + painter->drawPixmap(m_state.contentsRect, m_currentContent.pixmap); + break; + case ColorContentType: + painter->fillRect(m_state.contentsRect, m_currentContent.contentsBackgroundColor); + break; + case MediaContentType: + // we don't need to paint anything: we have a QGraphicsItem from the media element + break; +#if ENABLE(3D_CANVAS) + case Canvas3DContentType: + m_gc3D->paint(painter, option->rect); + break; +#endif + } +} + +void GraphicsLayerQtImpl::notifySyncRequired() +{ + m_blockNotifySyncRequired = false; + + if (m_layer->client()) + m_layer->client()->notifySyncRequired(m_layer); +} + +void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) +{ + m_changeMask |= changeMask; + + if (m_blockNotifySyncRequired) + return; + + static QMetaMethod syncMethod = staticMetaObject.method(staticMetaObject.indexOfMethod("notifySyncRequired()")); + syncMethod.invoke(this, Qt::QueuedConnection); + + m_blockNotifySyncRequired = true; +} + +void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform) +{ + // This is the bulk of the work. understanding what the compositor is trying to achieve, what + // graphicsview can do, and trying to find a sane common-ground. + if (!m_layer || m_changeMask == NoChanges) + goto afterLayerChanges; + + if (m_changeMask & ParentChange) { + // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't + // try to snatch that ownership. + if (!m_layer->parent() && !parentItem()) + setParentItem(0); + else if (m_layer && m_layer->parent() && m_layer->parent()->platformLayer() != parentItem()) + setParentItem(m_layer->parent()->platformLayer()); + } + + if (m_changeMask & ChildrenChange) { + // We basically do an XOR operation on the list of current children and the list of wanted + // children, and remove/add. + QSet<QGraphicsItem*> newChildren; + const Vector<GraphicsLayer*> newChildrenVector = (m_layer->children()); + newChildren.reserve(newChildrenVector.size()); + + for (size_t i = 0; i < newChildrenVector.size(); ++i) + newChildren.insert(newChildrenVector[i]->platformLayer()); + + const QSet<QGraphicsItem*> currentChildren = childItems().toSet(); + const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren; + const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren; + + QSet<QGraphicsItem*>::const_iterator it; + for (it = childrenToAdd.constBegin(); it != childrenToAdd.constEnd(); ++it) { + if (QGraphicsItem* w = *it) + w->setParentItem(this); + } + + QSet<QGraphicsItem*>::const_iterator rit; + for (rit = childrenToRemove.constBegin(); rit != childrenToRemove.constEnd(); ++rit) { + if (GraphicsLayerQtImpl* w = toGraphicsLayerQtImpl(*rit)) + w->setParentItem(0); + } + + // Children are ordered by z-value, let graphicsview know. + for (size_t i = 0; i < newChildrenVector.size(); ++i) { + if (newChildrenVector[i]->platformLayer()) + newChildrenVector[i]->platformLayer()->setZValue(i); + } + } + + if (m_changeMask & MaskLayerChange) { + // We can't paint here, because we don't know if the mask layer itself is ready... we'll have + // to wait till this layer tries to paint. + setFlag(ItemClipsChildrenToShape, m_layer->maskLayer() || m_layer->masksToBounds()); +#ifndef QT_NO_GRAPHICSEFFECT + setGraphicsEffect(0); + if (m_layer->maskLayer()) { + if (GraphicsLayerQtImpl* mask = toGraphicsLayerQtImpl(m_layer->maskLayer()->platformLayer())) { + mask->m_maskEffect = new MaskEffectQt(this, mask); + setGraphicsEffect(mask->m_maskEffect.data()); + } + } +#endif + } + + if (m_changeMask & SizeChange) { + if (m_layer->size() != m_state.size) { + prepareGeometryChange(); + m_size = QSizeF(m_layer->size().width(), m_layer->size().height()); + } + } + + // 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()) + if (scene()) + scene()->update(); + + if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | BackfaceVisibilityChange | PositionChange | ParentChange)) { + // 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)) { + switch (m_pendingContent.contentType) { + case PixmapContentType: + update(); + setFlag(ItemHasNoContents, false); + break; + + case MediaContentType: + setFlag(ItemHasNoContents, true); + m_pendingContent.mediaLayer.data()->setParentItem(this); + break; + + case ColorContentType: + if (m_pendingContent.contentType != m_currentContent.contentType + || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor) + update(); + m_state.drawsContent = false; + setFlag(ItemHasNoContents, false); + + // Only use ItemUsesExtendedStyleOption for HTML content as colors don't gain much from that. + setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false); + break; + + case HTMLContentType: + if (m_pendingContent.contentType != m_currentContent.contentType) + update(); + if (!m_state.drawsContent && m_layer->drawsContent()) + update(); + + setFlag(ItemHasNoContents, !m_layer->drawsContent()); + break; + +#if ENABLE(3D_CANVAS) + case Canvas3DContentType: + if (m_pendingContent.contentType != m_currentContent.contentType) + update(); + + setCacheMode(NoCache); + setFlag(ItemHasNoContents, false); + break; +#endif + } + } + + if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity() && !m_opacityAnimationRunning) + setOpacity(m_layer->opacity()); + + if (m_changeMask & ContentsRectChange) { + const QRect rect(m_layer->contentsRect()); + if (m_state.contentsRect != rect) { + m_state.contentsRect = rect; + if (m_pendingContent.mediaLayer) { + QGraphicsWidget* widget = qobject_cast<QGraphicsWidget*>(m_pendingContent.mediaLayer.data()); + if (widget) + widget->setGeometry(rect); + } + update(); + } + } + + if ((m_changeMask & MasksToBoundsChange) && m_state.masksToBounds != m_layer->masksToBounds()) { + setFlag(QGraphicsItem::ItemClipsToShape, m_layer->masksToBounds()); + setFlag(QGraphicsItem::ItemClipsChildrenToShape, m_layer->masksToBounds()); + } + + if ((m_changeMask & ContentsOpaqueChange) && m_state.contentsOpaque != m_layer->contentsOpaque()) + prepareGeometryChange(); + +#ifndef QT_NO_GRAPHICSEFFECT + if (m_maskEffect) + m_maskEffect.data()->update(); + else +#endif + if (m_changeMask & DisplayChange) { +#ifndef QT_GRAPHICS_LAYER_NO_RECACHE_ON_DISPLAY_CHANGE + // Recache now: all the content is ready and we don't want to wait until the paint event. + // We only need to do this for HTML content, there's no point in caching directly composited + // content like images or solid rectangles. + if (m_pendingContent.contentType == HTMLContentType) + recache(m_pendingContent.regionToUpdate); +#endif + update(m_pendingContent.regionToUpdate.boundingRect()); + m_pendingContent.regionToUpdate = QRegion(); + } + + if ((m_changeMask & BackgroundColorChange) + && (m_pendingContent.backgroundColor != m_currentContent.backgroundColor)) + update(); + + m_state.maskLayer = m_layer->maskLayer(); + m_state.pos = m_layer->position(); + m_state.anchorPoint = m_layer->anchorPoint(); + m_state.size = m_layer->size(); + m_state.transform = m_layer->transform(); + m_state.contentsOrientation =m_layer->contentsOrientation(); + m_state.opacity = m_layer->opacity(); + m_state.contentsRect = m_layer->contentsRect(); + m_state.preserves3D = m_layer->preserves3D(); + m_state.masksToBounds = m_layer->masksToBounds(); + 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.mediaLayer = m_pendingContent.mediaLayer; + m_currentContent.backgroundColor = m_pendingContent.backgroundColor; + m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor; + m_pendingContent.regionToUpdate = QRegion(); + m_changeMask = NoChanges; + +afterLayerChanges: + if (forceUpdateTransform) + updateTransform(); + + if (!recursive) + return; + + QList<QGraphicsItem*> children = childItems(); + if (m_state.maskLayer) + children.append(m_state.maskLayer->platformLayer()); + + QList<QGraphicsItem*>::const_iterator it; + for (it = children.constBegin(); it != children.constEnd(); ++it) { + if (QGraphicsItem* item = *it) { + if (GraphicsLayerQtImpl* layer = toGraphicsLayerQtImpl(item)) + layer->flushChanges(true, forceUpdateTransform); + } + } +} + +#if ENABLE(TILED_BACKING_STORE) +/* \reimp (TiledBackingStoreClient.h) +*/ +void GraphicsLayerQtImpl::tiledBackingStorePaintBegin() +{ +} + +/* \reimp (TiledBackingStoreClient.h) +*/ +void GraphicsLayerQtImpl::tiledBackingStorePaint(GraphicsContext* gc, const IntRect& rect) +{ + m_layer->paintGraphicsLayerContents(*gc, rect); +} + +/* \reimp (TiledBackingStoreClient.h) +*/ +void GraphicsLayerQtImpl::tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea) +{ + for (int i = 0; i < paintedArea.size(); ++i) + update(QRectF(paintedArea[i])); +} + +/* \reimp (TiledBackingStoreClient.h) +*/ +IntRect GraphicsLayerQtImpl::tiledBackingStoreContentsRect() +{ + return m_layer->contentsRect(); +} + +/* \reimp (TiledBackingStoreClient.h) +*/ +Color GraphicsLayerQtImpl::tiledBackingStoreBackgroundColor() const +{ + if (m_currentContent.contentType == PixmapContentType && !m_currentContent.pixmap.hasAlphaChannel()) + return Color(0, 0, 0); + // We return a transparent color so that the tiles initialize with alpha. + return Color(0, 0, 0, 0); +} + +IntRect GraphicsLayerQtImpl::tiledBackingStoreVisibleRect() +{ + const QGraphicsView* view = scene()->views().isEmpty() ? 0 : scene()->views().first(); + if (!view) + return mapFromScene(scene()->sceneRect()).boundingRect().toAlignedRect(); + + // All we get is the viewport's visible region. We have to map it to the scene and then to item coordinates. + return mapFromScene(view->mapToScene(view->viewport()->visibleRegion().boundingRect()).boundingRect()).boundingRect().toAlignedRect(); +} +#endif + +void GraphicsLayerQtImpl::notifyAnimationStarted() +{ + // WebCore notifies javascript when the animation starts. Here we're letting it know. + m_layer->client()->notifyAnimationStarted(m_layer, /* DOM time */ WTF::currentTime()); +} + +GraphicsLayerQt::GraphicsLayerQt(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_impl(PassOwnPtr<GraphicsLayerQtImpl>(new GraphicsLayerQtImpl(this))) +{ +} + +GraphicsLayerQt::~GraphicsLayerQt() +{ +} + +// This is the hook for WebCore compositor to know that Qt implements compositing with GraphicsLayerQt. +PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerQt(client); +} + +/* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display. +*/ +void GraphicsLayerQt::setNeedsDisplay() +{ + m_impl->m_pendingContent.regionToUpdate = QRegion(QRect(QPoint(0, 0), QSize(size().width(), size().height()))); + m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect) +{ + m_impl->m_pendingContent.regionToUpdate |= QRectF(rect).toAlignedRect(); + m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); +} + +void GraphicsLayerQt::setContentsNeedsDisplay() +{ + switch (m_impl->m_pendingContent.contentType) { + case GraphicsLayerQtImpl::MediaContentType: + if (!m_impl->m_pendingContent.mediaLayer) + return; + m_impl->m_pendingContent.mediaLayer.data()->update(); + break; + default: + setNeedsDisplay(); + break; + } +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setName(const String& name) +{ + m_impl->setObjectName(name); + GraphicsLayer::setName(name); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setParent(GraphicsLayer* layer) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange); + GraphicsLayer::setParent(layer); +} + +/* \reimp (GraphicsLayer.h) +*/ +bool GraphicsLayerQt::setChildren(const Vector<GraphicsLayer*>& children) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); + return GraphicsLayer::setChildren(children); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::addChild(GraphicsLayer* layer) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); + GraphicsLayer::addChild(layer); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::addChildAtIndex(GraphicsLayer* layer, int index) +{ + GraphicsLayer::addChildAtIndex(layer, index); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(layer, sibling); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + + GraphicsLayer::addChildBelow(layer, sibling); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +bool GraphicsLayerQt::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenChange); + return true; + } + + return false; +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::removeFromParent() +{ + if (parent()) + m_impl->notifyChange(GraphicsLayerQtImpl::ParentChange); + GraphicsLayer::removeFromParent(); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setMaskLayer(GraphicsLayer* value) +{ + if (value == maskLayer()) + return; + GraphicsLayer::setMaskLayer(value); + m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setPosition(const FloatPoint& value) +{ + if (value == position()) + return; + GraphicsLayer::setPosition(value); + m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& value) +{ + if (value == anchorPoint()) + return; + GraphicsLayer::setAnchorPoint(value); + m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setSize(const FloatSize& value) +{ + if (value == size()) + return; + GraphicsLayer::setSize(value); + m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setTransform(const TransformationMatrix& value) +{ + if (value == transform()) + return; + GraphicsLayer::setTransform(value); + m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& value) +{ + if (value == childrenTransform()) + return; + GraphicsLayer::setChildrenTransform(value); + m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setPreserves3D(bool value) +{ + if (value == preserves3D()) + return; + GraphicsLayer::setPreserves3D(value); + m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setMasksToBounds(bool value) +{ + if (value == masksToBounds()) + return; + GraphicsLayer::setMasksToBounds(value); + m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setDrawsContent(bool value) +{ + if (value == drawsContent()) + return; + m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange); + GraphicsLayer::setDrawsContent(value); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setBackgroundColor(const Color& value) +{ + if (value == m_impl->m_pendingContent.backgroundColor) + return; + m_impl->m_pendingContent.backgroundColor = value; + GraphicsLayer::setBackgroundColor(value); + m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::clearBackgroundColor() +{ + if (!m_impl->m_pendingContent.backgroundColor.isValid()) + return; + m_impl->m_pendingContent.backgroundColor = QColor(); + GraphicsLayer::clearBackgroundColor(); + m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setContentsOpaque(bool value) +{ + if (value == contentsOpaque()) + return; + m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange); + GraphicsLayer::setContentsOpaque(value); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setBackfaceVisibility(bool value) +{ + if (value == backfaceVisibility()) + return; + GraphicsLayer::setBackfaceVisibility(value); + m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setOpacity(float value) +{ + if (value == opacity()) + return; + GraphicsLayer::setOpacity(value); + m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setContentsRect(const IntRect& value) +{ + if (value == contentsRect()) + return; + GraphicsLayer::setContentsRect(value); + m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setContentsToImage(Image* image) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType; + GraphicsLayer::setContentsToImage(image); + if (image) { + QPixmap* pxm = image->nativeImageForCurrentFrame(); + if (pxm) { + m_impl->m_pendingContent.pixmap = *pxm; + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::PixmapContentType; + return; + } + } + m_impl->m_pendingContent.pixmap = QPixmap(); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setContentsBackgroundColor(const Color& color) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::ColorContentType; + m_impl->m_pendingContent.contentsBackgroundColor = QColor(color); + GraphicsLayer::setContentsBackgroundColor(color); +} + +#if ENABLE(3D_CANVAS) +void GraphicsLayerQt::setContentsToGraphicsContext3D(const GraphicsContext3D* ctx) +{ + if (ctx == m_impl->m_gc3D) + return; + + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::Canvas3DContentType; + m_impl->m_gc3D = ctx; + m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); +} + +void GraphicsLayerQt::setGraphicsContext3DNeedsDisplay() +{ + setNeedsDisplay(); +} +#endif + +void GraphicsLayerQt::setContentsToMedia(PlatformLayer* media) +{ + if (media) { + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::MediaContentType; + m_impl->m_pendingContent.mediaLayer = media->toGraphicsObject(); + } else + m_impl->m_pendingContent.contentType = GraphicsLayerQtImpl::HTMLContentType; + + m_impl->notifyChange(GraphicsLayerQtImpl::ContentChange); + GraphicsLayer::setContentsToMedia(media); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::setContentsOrientation(CompositingCoordinatesOrientation orientation) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOrientationChange); + GraphicsLayer::setContentsOrientation(orientation); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::distributeOpacity(float o) +{ + m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); + m_impl->m_state.distributeOpacity = true; +} + +/* \reimp (GraphicsLayer.h) +*/ +float GraphicsLayerQt::accumulatedOpacity() const +{ + return m_impl->effectiveOpacity(); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::syncCompositingState() +{ + m_impl->flushChanges(); + GraphicsLayer::syncCompositingState(); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerQt::syncCompositingStateForThisLayerOnly() +{ + // We can't call flushChanges recursively here + m_impl->flushChanges(false); + GraphicsLayer::syncCompositingStateForThisLayerOnly(); +} + +/* \reimp (GraphicsLayer.h) +*/ +PlatformLayer* GraphicsLayerQt::platformLayer() const +{ + return m_impl.get(); +} + +// Now we start dealing with WebCore animations translated to Qt animations + +template <typename T> +struct KeyframeValueQt { + const TimingFunction* timingFunction; + T value; +}; + +/* Copied from AnimationBase.cpp +*/ +static inline double solveEpsilon(double duration) +{ + return 1.0 / (200.0 * duration); +} + +static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, qreal p2y, double t, double duration) +{ + UnitBezier bezier(p1x, p1y, p2x, p2y); + return bezier.solve(t, solveEpsilon(duration)); +} + +static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t) +{ + if (stepAtStart) + return qMin(1.0, (floor(numSteps * t) + 1) / numSteps); + return floor(numSteps * t) / numSteps; +} + +static inline qreal applyTimingFunction(const TimingFunction* timingFunction, qreal progress, double duration) +{ + // We want the timing function to be as close as possible to what the web-developer intended, so + // we're using the same function used by WebCore when compositing is disabled. Using easing-curves + // would probably work for some of the cases, but wouldn't really buy us anything as we'd have to + // convert the bezier function back to an easing curve. + + if (timingFunction->isCubicBezierTimingFunction()) { + const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction); + return solveCubicBezierFunction(ctf->x1(), + ctf->y1(), + ctf->x2(), + ctf->y2(), + double(progress), double(duration) / 1000); + } else if (timingFunction->isStepsTimingFunction()) { + const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction); + return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress)); + } else + return progress; +} + +// Helper functions to safely get a value out of WebCore's AnimationValue*. + +static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, TransformOperations& transformOperations) +{ + transformOperations = TransformOperations(); + if (!animationValue) + return; + + if (const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value()) + transformOperations = *ops; +} + +static void webkitAnimationToQtAnimationValue(const AnimationValue* animationValue, qreal& realValue) +{ + realValue = animationValue ? static_cast<const FloatAnimationValue*>(animationValue)->value() : 0; +} + +#ifndef QT_NO_ANIMATION +// We put a bit of the functionality in a base class to allow casting and to save some code size. + +class AnimationQtBase : public QAbstractAnimation { +public: + AnimationQtBase(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + : QAbstractAnimation(0) + , m_layer(layer) + , m_boxSize(boxSize) + , m_duration(anim->duration() * 1000) + , m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate) + , m_webkitPropertyID(values.property()) + , m_webkitAnimation(anim) + , m_keyframesName(name) + , m_fillsForwards(false) + { + } + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { + QAbstractAnimation::updateState(newState, oldState); + + // For some reason we have do this asynchronously - or the animation won't work. + if (newState == Running && oldState == Stopped && m_layer.data()) + m_layer.data()->notifyAnimationStartedAsync(); + } + + virtual int duration() const { return m_duration; } + + QWeakPointer<GraphicsLayerQtImpl> m_layer; + IntSize m_boxSize; + int m_duration; + bool m_isAlternate; + AnimatedPropertyID m_webkitPropertyID; + + // We might need this in case the same animation is added again (i.e. resumed by WebCore). + const Animation* m_webkitAnimation; + QString m_keyframesName; + bool m_fillsForwards; +}; + +// We'd rather have a templatized QAbstractAnimation than QPropertyAnimation / QVariantAnimation; +// Since we know the types that we're dealing with, the QObject/QProperty/QVariant abstraction +// buys us very little in this case, for too much overhead. +template <typename T> +class AnimationQt : public AnimationQtBase { + +public: + AnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + : AnimationQtBase(layer, values, boxSize, anim, name) + { + // Copying those WebCore structures is not trivial, we have to do it like this. + for (size_t i = 0; i < values.size(); ++i) { + const AnimationValue* animationValue = values.at(i); + KeyframeValueQt<T> keyframeValue; + if (animationValue->timingFunction()) + keyframeValue.timingFunction = animationValue->timingFunction(); + else + keyframeValue.timingFunction = anim->timingFunction().get(); + webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value); + m_keyframeValues[animationValue->keyTime()] = keyframeValue; + } + } + +protected: + + // This is the part that differs between animated properties. + virtual void applyFrame(const T& fromValue, const T& toValue, qreal progress) = 0; + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { +#if QT_DEBUG_FPS + if (newState == Running && oldState == Stopped) { + qDebug("Animation Started!"); + m_fps.frames = 0; + m_fps.duration.start(); + } else if (newState == Stopped && oldState == Running) { + const int duration = m_fps.duration.elapsed(); + qDebug("Animation Ended! %dms [%f FPS]", duration, + (1000 / (((float)duration) / m_fps.frames))); + } +#endif + AnimationQtBase::updateState(newState, oldState); + } + + virtual void updateCurrentTime(int currentTime) + { + if (!m_layer) + return; + + qreal progress = qreal(currentLoopTime()) / duration(); + + if (m_isAlternate && currentLoop()%2) + progress = 1-progress; + + if (m_keyframeValues.isEmpty()) + return; + + // Find the current from-to keyframes in our little map. + typename QMap<qreal, KeyframeValueQt<T> >::iterator it = m_keyframeValues.find(progress); + + // We didn't find an exact match, we try the closest match (lower bound). + if (it == m_keyframeValues.end()) + it = m_keyframeValues.lowerBound(progress)-1; + + // We didn't find any match; use the first keyframe. + if (it == m_keyframeValues.end()) + it = m_keyframeValues.begin(); + + typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it + 1; + if (it2 == m_keyframeValues.end()) + it2 = it; + const KeyframeValueQt<T>& fromKeyframe = it.value(); + const KeyframeValueQt<T>& toKeyframe = it2.value(); + + const TimingFunction* timingFunc = fromKeyframe.timingFunction; + const T& fromValue = fromKeyframe.value; + const T& toValue = toKeyframe.value; + + // Now we have a source keyframe, origin keyframe and a timing function. + // We can now process the progress and apply the frame. + progress = (!progress || progress == 1 || it.key() == it2.key()) ? + progress : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration()); + applyFrame(fromValue, toValue, progress); +#if QT_DEBUG_FPS + ++m_fps.frames; +#endif + } + + QMap<qreal, KeyframeValueQt<T> > m_keyframeValues; +#if QT_DEBUG_FPS + struct { + QTime duration; + int frames; + } m_fps; +#endif +}; + +class TransformAnimationQt : public AnimationQt<TransformOperations> { +public: + TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) + : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name) + { + } + + ~TransformAnimationQt() + { + if (m_fillsForwards) + setCurrentTime(1); + } + + // The idea is that we let WebCore manage the transform operations and Qt just manage the + // animation heartbeat and the bottom-line QTransform. We gain performance, not by using + // Transform instead of TransformationMatrix, but by proper caching of items that are + // expensive for WebCore to render. We want the rest to be as close to WebCore's idea as possible. + virtual void applyFrame(const TransformOperations& sourceOperations, const TransformOperations& targetOperations, qreal progress) + { + TransformationMatrix transformMatrix; + + bool validTransformLists = true; + const int sourceOperationCount = sourceOperations.size(); + if (sourceOperationCount) { + if (targetOperations.size() != sourceOperationCount) + validTransformLists = false; + else { + for (size_t j = 0; j < sourceOperationCount && validTransformLists; ++j) { + if (!sourceOperations.operations()[j]->isSameType(*targetOperations.operations()[j])) + validTransformLists = false; + } + } + } + + if (validTransformLists) { + for (size_t i = 0; i < targetOperations.size(); ++i) + targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize); + } else { + targetOperations.apply(m_boxSize, transformMatrix); + transformMatrix.blend(m_sourceMatrix, progress); + } + + m_layer.data()->m_layer->setTransform(transformMatrix); + // We force the actual opacity change, otherwise it would be ignored because of the animation. + m_layer.data()->setBaseTransform(transformMatrix); + } + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { + AnimationQt<TransformOperations>::updateState(newState, oldState); + if (!m_layer) + return; + + m_layer.data()->flushChanges(true); + + // To increase FPS, we use a less accurate caching mechanism while animation is going on + // this is a UX choice that should probably be customizable. + if (newState == QAbstractAnimation::Running) { + m_sourceMatrix = m_layer.data()->m_layer->transform(); + m_layer.data()->m_transformAnimationRunning = true; + } else if (newState == QAbstractAnimation::Stopped) { + // We update the transform back to the default. This already takes fill-modes into account. + m_layer.data()->m_transformAnimationRunning = false; + if (m_layer && m_layer.data()->m_layer) + m_layer.data()->setBaseTransform(m_layer.data()->m_layer->transform()); + } + } + + TransformationMatrix m_sourceMatrix; +}; + +class OpacityAnimationQt : public AnimationQt<qreal> { +public: + OpacityAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString& name) + : AnimationQt<qreal>(layer, values, boxSize, anim, name) + { + } + + ~OpacityAnimationQt() + { + if (m_fillsForwards) + setCurrentTime(1); + } + + virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress) + { + qreal opacity = qBound(qreal(0), fromValue + (toValue - fromValue) * progress, qreal(1)); + + // FIXME: This is a hack, due to a probable QGraphicsScene bug. + // Without this the opacity change doesn't always have immediate effect. + if (m_layer.data()->scene() && !m_layer.data()->opacity() && opacity) + m_layer.data()->scene()->update(); + + m_layer.data()->m_layer->setOpacity(opacity); + // We force the actual opacity change, otherwise it would be ignored because of the animation. + m_layer.data()->setOpacity(opacity); + } + + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) + { + AnimationQt<qreal>::updateState(newState, oldState); + + if (m_layer) + m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running); + + // If stopped, we update the opacity back to the default. This already takes fill-modes into account. + if (newState == Stopped) + if (m_layer && m_layer.data()->m_layer) + m_layer.data()->setOpacity(m_layer.data()->m_layer->opacity()); + + } +}; + +bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset) +{ + if (!anim->duration() || !anim->iterationCount()) + return false; + + AnimationQtBase* newAnim = 0; + + // Fixed: we might already have the Qt animation object associated with this WebCore::Animation object. + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (*it) { + AnimationQtBase* curAnimation = static_cast<AnimationQtBase*>(it->data()); + if (curAnimation && curAnimation->m_webkitAnimation == anim) + newAnim = curAnimation; + } + } + + if (!newAnim) { + switch (values.property()) { + case AnimatedPropertyOpacity: + newAnim = new OpacityAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName); + break; + case AnimatedPropertyWebkitTransform: + newAnim = new TransformAnimationQt(m_impl.get(), values, boxSize, anim, keyframesName); + break; + default: + return false; + } + + // We make sure WebCore::Animation and QAnimation are on the same terms. + newAnim->setLoopCount(anim->iterationCount()); + newAnim->m_fillsForwards = anim->fillsForwards(); + m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim)); + QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume())); + } + + // Flush now to avoid flicker. + m_impl->flushChanges(false); + + // Qhen fill-mode is backwards/both, we set the value to 0 before the delay takes place. + if (anim->fillsBackwards()) + newAnim->setCurrentTime(0); + + newAnim->start(); + + // We synchronize the animation's clock to WebCore's timeOffset. + newAnim->setCurrentTime(timeOffset * 1000); + + // We don't need to manage the animation object's lifecycle: + // WebCore would call removeAnimations when it's time to delete. + + return true; +} + +void GraphicsLayerQt::removeAnimationsForProperty(AnimatedPropertyID id) +{ + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (!(*it)) + continue; + + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); + if (anim && anim->m_webkitPropertyID == id) { + // We need to stop the animation right away, or it might flicker before it's deleted. + anim->stop(); + anim->deleteLater(); + it = m_impl->m_animations.erase(it); + --it; + } + } +} + +void GraphicsLayerQt::removeAnimationsForKeyframes(const String& name) +{ + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (!(*it)) + continue; + + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); + if (anim && anim->m_keyframesName == QString(name)) { + // We need to stop the animation right away, or it might flicker before it's deleted. + anim->stop(); + anim->deleteLater(); + it = m_impl->m_animations.erase(it); + --it; + } + } +} + +void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset) +{ + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (!(*it)) + continue; + + AnimationQtBase* anim = static_cast<AnimationQtBase*>(it->data()); + if (anim && anim->m_keyframesName == QString(name)) { + // we synchronize the animation's clock to WebCore's timeOffset + anim->setCurrentTime(timeOffset * 1000); + anim->pause(); + } + } +} + +void GraphicsLayerQt::suspendAnimations(double time) +{ + if (m_impl->m_suspendTimer.isActive()) { + m_impl->m_suspendTimer.stop(); + m_impl->m_suspendTimer.start(time * 1000); + } else { + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (QAbstractAnimation* anim = it->data()) + anim->pause(); + } + } +} + +void GraphicsLayerQt::resumeAnimations() +{ + if (m_impl->m_suspendTimer.isActive()) { + m_impl->m_suspendTimer.stop(); + QList<QWeakPointer<QAbstractAnimation> >::iterator it; + for (it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) { + if (QAbstractAnimation* anim = it->data()) + anim->resume(); + } + } +} + +#endif // QT_NO_ANIMATION +} + +#include <GraphicsLayerQt.moc> diff --git a/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h new file mode 100644 index 0000000..b1692d2 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h @@ -0,0 +1,95 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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 GraphicsLayerQt_h +#define GraphicsLayerQt_h + +#if ENABLE(3D_CANVAS) +#include "GraphicsContext3D.h" +#endif +#include "GraphicsLayer.h" +#include "GraphicsLayerClient.h" + +namespace WebCore { + +class GraphicsLayerQtImpl; + +class GraphicsLayerQt : public GraphicsLayer { + friend class GraphicsLayerQtImpl; + +public: + GraphicsLayerQt(GraphicsLayerClient*); + virtual ~GraphicsLayerQt(); + + // reimps from GraphicsLayer.h + virtual PlatformLayer* platformLayer() const; + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + virtual void setParent(GraphicsLayer* layer); + virtual void setName(const String& name); + virtual bool setChildren(const Vector<GraphicsLayer*>&); + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + virtual void removeFromParent(); + virtual void setMaskLayer(GraphicsLayer* layer); + virtual void setPosition(const FloatPoint& p); + virtual void setAnchorPoint(const FloatPoint3D& p); + virtual void setSize(const FloatSize& size); + virtual void setTransform(const TransformationMatrix& t); + virtual void setChildrenTransform(const TransformationMatrix& t); + virtual void setPreserves3D(bool b); + virtual void setMasksToBounds(bool b); + virtual void setDrawsContent(bool b); + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + virtual void setContentsOpaque(bool b); + virtual void setBackfaceVisibility(bool b); + virtual void setOpacity(float opacity); + virtual void setContentsRect(const IntRect& r); +#ifndef QT_NO_ANIMATION + virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double timeOffset); + virtual void removeAnimationsForProperty(AnimatedPropertyID); + virtual void removeAnimationsForKeyframes(const String& keyframesName); + virtual void pauseAnimation(const String& keyframesName, double timeOffset); + virtual void suspendAnimations(double time); + virtual void resumeAnimations(); +#endif // QT_NO_ANIMATION + virtual void setContentsToImage(Image*); + virtual void setContentsNeedsDisplay(); + virtual void setContentsToMedia(PlatformLayer*); + virtual void setContentsBackgroundColor(const Color&); +#if ENABLE(3D_CANVAS) + virtual void setContentsToGraphicsContext3D(const GraphicsContext3D*); + virtual void setGraphicsContext3DNeedsDisplay(); +#endif + virtual void setContentsOrientation(CompositingCoordinatesOrientation orientation); + virtual void distributeOpacity(float); + virtual float accumulatedOpacity() const; + virtual void syncCompositingState(); + virtual void syncCompositingStateForThisLayerOnly(); + +private: + OwnPtr<GraphicsLayerQtImpl> m_impl; +}; + +} +#endif // GraphicsLayerQt_h diff --git a/Source/WebCore/platform/graphics/qt/IconQt.cpp b/Source/WebCore/platform/graphics/qt/IconQt.cpp new file mode 100644 index 0000000..eb09eda --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/IconQt.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.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 + * along 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" +#include "Icon.h" + +#include "GraphicsContext.h" +#include "PlatformString.h" +#include "IntRect.h" + +#include <qpainter.h> +#include <qpixmap.h> +#include <qrect.h> +#include <qglobal.h> + +namespace WebCore { + +Icon::Icon() +{ +} + +Icon::~Icon() +{ +} + +// FIXME: Move the code to ChromeClient::iconForFiles(). +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + if (filenames.isEmpty()) + return 0; + + if (filenames.size() == 1) { + RefPtr<Icon> i = adoptRef(new Icon); + i->m_icon = QIcon(filenames[0]); + return i.release(); + } + + //FIXME: Implement this + return 0; +} + +void Icon::paint(GraphicsContext* ctx, const IntRect& rect) +{ + QPixmap px = m_icon.pixmap(rect.size()); + QPainter *p = static_cast<QPainter*>(ctx->platformContext()); + if (p && !px.isNull()) + p->drawPixmap(rect.x(), rect.y(), px); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/ImageBufferData.h b/Source/WebCore/platform/graphics/qt/ImageBufferData.h new file mode 100644 index 0000000..aa32253 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ImageBufferData.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ImageBufferData_h +#define ImageBufferData_h + +#include "Image.h" +#include <wtf/RefPtr.h> + +#include <QPainter> +#include <QPixmap> + +#include "OwnPtr.h" + +namespace WebCore { + +class IntSize; + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + QPixmap m_pixmap; + OwnPtr<QPainter> m_painter; + RefPtr<Image> m_image; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp new file mode 100644 index 0000000..f56603d --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageBuffer.h" + +#include "GraphicsContext.h" +#include "ImageData.h" +#include "MIMETypeRegistry.h" +#include "StillImageQt.h" +#include "TransparencyLayer.h" +#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> + +#include <QBuffer> +#include <QColor> +#include <QImage> +#include <QImageWriter> +#include <QPainter> +#include <QPixmap> +#include <math.h> + +namespace WebCore { + +ImageBufferData::ImageBufferData(const IntSize& size) + : m_pixmap(size) + , m_painter(0) +{ + if (m_pixmap.isNull()) + return; + + m_pixmap.fill(QColor(Qt::transparent)); + + QPainter* painter = new QPainter; + m_painter.set(painter); + + if (!painter->begin(&m_pixmap)) + return; + + // Since ImageBuffer is used mainly for Canvas, explicitly initialize + // its painter's pen and brush with the corresponding canvas defaults + // NOTE: keep in sync with CanvasRenderingContext2D::State + QPen pen = painter->pen(); + pen.setColor(Qt::black); + pen.setWidth(1); + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::SvgMiterJoin); + pen.setMiterLimit(10); + painter->setPen(pen); + QBrush brush = painter->brush(); + brush.setColor(Qt::black); + painter->setBrush(brush); + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + + m_image = StillImage::createForRendering(&m_pixmap); +} + +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success) + : m_data(size) + , m_size(size) +{ + success = m_data.m_painter && m_data.m_painter->isActive(); + if (!success) + return; + + m_context.set(new GraphicsContext(m_data.m_painter.get())); +} + +ImageBuffer::~ImageBuffer() +{ +} + +GraphicsContext* ImageBuffer::context() const +{ + ASSERT(m_data.m_painter->isActive()); + + return m_context.get(); +} + +bool ImageBuffer::drawsUsingCopy() const +{ + return false; +} + +PassRefPtr<Image> ImageBuffer::copyImage() const +{ + return StillImage::create(m_data.m_pixmap); +} + +void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, + CompositeOperator op, bool useLowQualityScale) +{ + if (destContext == context()) { + // We're drawing into our own buffer. In order for this to work, we need to copy the source buffer first. + RefPtr<Image> copy = copyImage(); + destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, useLowQualityScale); + } else + destContext->drawImage(m_data.m_image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); +} + +void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) +{ + if (destContext == context()) { + // We're drawing into our own buffer. In order for this to work, we need to copy the source buffer first. + RefPtr<Image> copy = copyImage(); + copy->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect); + } else + m_data.m_image->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect); +} + +void ImageBuffer::clip(GraphicsContext* context, const FloatRect& floatRect) const +{ + QPixmap* nativeImage = m_data.m_image->nativeImageForCurrentFrame(); + if (!nativeImage) + return; + + IntRect rect = enclosingIntRect(floatRect); + QPixmap alphaMask = *nativeImage; + if (alphaMask.width() != rect.width() || alphaMask.height() != rect.height()) + alphaMask = alphaMask.scaled(rect.width(), rect.height()); + + context->pushTransparencyLayerInternal(rect, 1.0, alphaMask); +} + +void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + bool isPainting = m_data.m_painter->isActive(); + if (isPainting) + m_data.m_painter->end(); + + QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + ASSERT(!image.isNull()); + + uchar* bits = image.bits(); + const int bytesPerLine = image.bytesPerLine(); + + for (int y = 0; y < m_size.height(); ++y) { + quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine); + for (int x = 0; x < m_size.width(); ++x) { + QRgb& pixel = scanLine[x]; + pixel = qRgba(lookUpTable[qRed(pixel)], + lookUpTable[qGreen(pixel)], + lookUpTable[qBlue(pixel)], + qAlpha(pixel)); + } + } + + m_data.m_pixmap = QPixmap::fromImage(image); + + if (isPainting) + m_data.m_painter->begin(&m_data.m_pixmap); +} + +template <Multiply multiplied> +PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size) +{ + RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); + unsigned char* data = result->data(); + + if (rect.x() < 0 || rect.y() < 0 || rect.right() > size.width() || rect.bottom() > size.height()) + memset(data, 0, result->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.right(); + if (endx > size.width()) + endx = size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.bottom(); + if (endy > size.height()) + endy = size.height(); + int numRows = endy - originy; + + // NOTE: For unmultiplied data, we undo the premultiplication below. + QImage image = imageData.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); + + ASSERT(!image.isNull()); + + const int bytesPerLine = image.bytesPerLine(); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + const uchar* bits = image.constBits(); +#else + const uchar* bits = image.bits(); +#endif + + quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() * 4 + destx * 4]); + + if (multiplied == Unmultiplied) { + for (int y = 0; y < numRows; ++y) { + const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine); + for (int x = 0; x < numColumns; x++) { + QRgb pixel = scanLine[x + originx]; + int alpha = qAlpha(pixel); + // Un-premultiply and convert RGB to BGR. + if (alpha == 255) + destRows[x] = (0xFF000000 + | (qBlue(pixel) << 16) + | (qGreen(pixel) << 8) + | (qRed(pixel))); + else if (alpha > 0) + destRows[x] = ((alpha << 24) + | (((255 * qBlue(pixel)) / alpha)) << 16) + | (((255 * qGreen(pixel)) / alpha) << 8) + | ((255 * qRed(pixel)) / alpha); + else + destRows[x] = 0; + } + destRows += rect.width(); + } + } else { + for (int y = 0; y < numRows; ++y) { + const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine); + for (int x = 0; x < numColumns; x++) { + QRgb pixel = scanLine[x + originx]; + // Convert RGB to BGR. + destRows[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00); + + } + destRows += rect.width(); + } + } + + return result.release(); +} + +PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const +{ + return getImageData<Unmultiplied>(rect, m_data, m_size); +} + +PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const +{ + return getImageData<Premultiplied>(rect, m_data, m_size); +} + +static inline unsigned int premultiplyABGRtoARGB(unsigned int x) +{ + unsigned int a = x >> 24; + if (a == 255) + return (x << 16) | ((x >> 16) & 0xff) | (x & 0xff00ff00); + unsigned int t = (x & 0xff00ff) * a; + t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; + t = ((t << 16) | (t >> 16)) & 0xff00ff; + + x = ((x >> 8) & 0xff) * a; + x = (x + ((x >> 8) & 0xff) + 0x80); + x &= 0xff00; + x |= t | (a << 24); + return x; +} + +template <Multiply multiplied> +void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size) +{ + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * sourceSize.width(); + + // NOTE: For unmultiplied input data, we do the premultiplication below. + QImage image(numColumns, numRows, QImage::Format_ARGB32_Premultiplied); + uchar* bits = image.bits(); + const int bytesPerLine = image.bytesPerLine(); + + const quint32* srcScanLine = reinterpret_cast_ptr<const quint32*>(source->data() + originy * srcBytesPerRow + originx * 4); + + if (multiplied == Unmultiplied) { + for (int y = 0; y < numRows; ++y) { + quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine); + for (int x = 0; x < numColumns; x++) { + // Premultiply and convert BGR to RGB. + quint32 pixel = srcScanLine[x]; + destScanLine[x] = premultiplyABGRtoARGB(pixel); + } + srcScanLine += sourceSize.width(); + } + } else { + for (int y = 0; y < numRows; ++y) { + quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine); + for (int x = 0; x < numColumns; x++) { + // Convert BGR to RGB. + quint32 pixel = srcScanLine[x]; + destScanLine[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00); + } + srcScanLine += sourceSize.width(); + } + } + + bool isPainting = data.m_painter->isActive(); + if (!isPainting) + data.m_painter->begin(&data.m_pixmap); + else { + data.m_painter->save(); + + // putImageData() should be unaffected by painter state + data.m_painter->resetTransform(); + data.m_painter->setOpacity(1.0); + data.m_painter->setClipping(false); + } + + data.m_painter->setCompositionMode(QPainter::CompositionMode_Source); + data.m_painter->drawImage(destx, desty, image); + + if (!isPainting) + data.m_painter->end(); + else + data.m_painter->restore(); +} + +void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) +{ + putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size); +} + +void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) +{ + putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size); +} + +// We get a mimeType here but QImageWriter does not support mimetypes but +// only formats (png, gif, jpeg..., xpm). So assume we get image/ as image +// mimetypes and then remove the image/ to get the Qt format. +String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const +{ + ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); + + if (!mimeType.startsWith("image/")) + return "data:,"; + + // prepare our target + QByteArray data; + QBuffer buffer(&data); + buffer.open(QBuffer::WriteOnly); + + if (quality && *quality >= 0.0 && *quality <= 1.0) { + if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data(), *quality * 100 + 0.5)) { + buffer.close(); + return "data:,"; + } + } else { + if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data(), 100)) { + buffer.close(); + return "data:,"; + } + } + + buffer.close(); + + return makeString("data:", mimeType, ";base64,", data.toBase64().data()); +} + +} diff --git a/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp new file mode 100644 index 0000000..2bbb9ce --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2006 Friedemann Kleint <fkleint@trolltech.com> + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageDecoderQt.h" + +#include <QtCore/QByteArray> +#include <QtCore/QBuffer> + +#include <QtGui/QImageReader> +#include <qdebug.h> + +namespace WebCore { + +ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +{ + // We need at least 4 bytes to figure out what kind of image we're dealing with. + if (data.size() < 4) + return 0; + + return new ImageDecoderQt(alphaOption, gammaAndColorProfileOption); +} + +ImageDecoderQt::ImageDecoderQt(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) + : ImageDecoder(alphaOption, gammaAndColorProfileOption) + , m_repetitionCount(cAnimationNone) +{ +} + +ImageDecoderQt::~ImageDecoderQt() +{ +} + +void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) +{ + if (failed()) + return; + + // 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); + + // Attempt to load the data + QByteArray imageData = QByteArray::fromRawData(m_data->data(), m_data->size()); + m_buffer.set(new QBuffer); + m_buffer->setData(imageData); + m_buffer->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + m_reader.set(new QImageReader(m_buffer.get(), m_format)); + + // This will force the JPEG decoder to use JDCT_IFAST + m_reader->setQuality(49); + + // QImageReader only allows retrieving the format before reading the image + m_format = m_reader->format(); +} + +bool ImageDecoderQt::isSizeAvailable() +{ + if (!ImageDecoder::isSizeAvailable() && m_reader) + internalDecodeSize(); + + return ImageDecoder::isSizeAvailable(); +} + +size_t ImageDecoderQt::frameCount() +{ + if (m_frameBufferCache.isEmpty() && m_reader) { + if (m_reader->supportsAnimation()) { + int imageCount = m_reader->imageCount(); + + // Fixup for Qt decoders... imageCount() is wrong + // and jumpToNextImage does not work either... so + // we will have to parse everything... + if (!imageCount) + forceLoadEverything(); + else { + m_frameBufferCache.resize(imageCount); + for (size_t i = 0; i < m_frameBufferCache.size(); ++i) + m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha); + } + } else { + m_frameBufferCache.resize(1); + m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); + } + } + + return m_frameBufferCache.size(); +} + +int ImageDecoderQt::repetitionCount() const +{ + if (m_reader && m_reader->supportsAnimation()) + m_repetitionCount = m_reader->loopCount(); + return m_repetitionCount; +} + +String ImageDecoderQt::filenameExtension() const +{ + return String(m_format.constData(), m_format.length()); +}; + +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. + size_t count = m_frameBufferCache.size(); + if (!failed() && !count) { + internalDecodeSize(); + count = frameCount(); + } + + if (index >= count) + return 0; + + RGBA32Buffer& frame = m_frameBufferCache[index]; + if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) + internalReadImage(index); + return &frame; +} + +void ImageDecoderQt::clearFrameBufferCache(size_t /*index*/) +{ +} + +void ImageDecoderQt::internalDecodeSize() +{ + ASSERT(m_reader); + + // If we have a QSize() something failed + QSize size = m_reader->size(); + if (size.isEmpty()) { + setFailed(); + return clearPointers(); + } + + setSize(size.width(), size.height()); +} + +void ImageDecoderQt::internalReadImage(size_t frameIndex) +{ + ASSERT(m_reader); + + if (m_reader->supportsAnimation()) + m_reader->jumpToImage(frameIndex); + else if (frameIndex) { + setFailed(); + return clearPointers(); + } + + if (!internalHandleCurrentImage(frameIndex)) + setFailed(); + + // Attempt to return some memory + for (int i = 0; i < m_frameBufferCache.size(); ++i) { + if (m_frameBufferCache[i].status() != RGBA32Buffer::FrameComplete) + return; + } + + clearPointers(); +} + +bool ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) +{ + QPixmap pixmap; + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + pixmap = QPixmap::fromImageReader(m_reader.get()); +#else + QImage img; + if (m_reader->read(&img)) + pixmap = QPixmap::fromImage(img); +#endif + + if (pixmap.isNull()) { + frameCount(); + repetitionCount(); + clearPointers(); + return false; + } + + // now into the RGBA32Buffer - even if the image is not + RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; + buffer->setRect(m_reader->currentImageRect()); + buffer->setStatus(RGBA32Buffer::FrameComplete); + buffer->setDuration(m_reader->nextImageDelay()); + buffer->setPixmap(pixmap); + return true; +} + +// 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 failed bit. If we failed to decode the first image +// then we truly failed to decode, otherwise we're OK. + +// TODO: Do not increment the m_frameBufferCache.size() by one but more than one +void ImageDecoderQt::forceLoadEverything() +{ + int imageCount = 0; + + do { + m_frameBufferCache.resize(++imageCount); + } while (internalHandleCurrentImage(imageCount - 1)); + + // If we failed decoding the first image we actually + // have no images and need to set the failed bit. + // Otherwise, we want to forget about + // the last attempt to decode a image. + m_frameBufferCache.resize(imageCount - 1); + for (size_t i = 0; i < m_frameBufferCache.size(); ++i) + m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha); + if (imageCount == 1) + setFailed(); +} + +void ImageDecoderQt::clearPointers() +{ + m_reader.clear(); + m_buffer.clear(); +} +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/ImageDecoderQt.h b/Source/WebCore/platform/graphics/qt/ImageDecoderQt.h new file mode 100644 index 0000000..23fb79a --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2006 Friedemann Kleint <fkleint@trolltech.com> + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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. + */ + +#ifndef ImageDecoderQt_h +#define ImageDecoderQt_h + +#include "ImageDecoder.h" +#include <QtGui/QImageReader> +#include <QtGui/QPixmap> +#include <QtCore/QList> +#include <QtCore/QHash> +#include <QtCore/QBuffer> +#include <wtf/OwnPtr.h> + +namespace WebCore { + + +class ImageDecoderQt : public ImageDecoder +{ +public: + ImageDecoderQt(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + ~ImageDecoderQt(); + + virtual void setData(SharedBuffer* data, bool allDataReceived); + virtual bool isSizeAvailable(); + virtual size_t frameCount(); + virtual int repetitionCount() const; + virtual RGBA32Buffer* frameBufferAtIndex(size_t index); + + virtual String filenameExtension() const; + + virtual void clearFrameBufferCache(size_t clearBeforeFrame); + +private: + ImageDecoderQt(const ImageDecoderQt&); + ImageDecoderQt &operator=(const ImageDecoderQt&); + +private: + void internalDecodeSize(); + void internalReadImage(size_t); + bool internalHandleCurrentImage(size_t); + void forceLoadEverything(); + void clearPointers(); + +private: + QByteArray m_format; + OwnPtr<QBuffer> m_buffer; + OwnPtr<QImageReader> m_reader; + mutable int m_repetitionCount; +}; + + + +} + +#endif + diff --git a/Source/WebCore/platform/graphics/qt/ImageQt.cpp b/Source/WebCore/platform/graphics/qt/ImageQt.cpp new file mode 100644 index 0000000..49afd29 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ImageQt.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * Copyright (C) 2010 Sencha, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Image.h" + +#include "AffineTransform.h" +#include "BitmapImage.h" +#include "ContextShadow.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "ImageObserver.h" +#include "PlatformString.h" +#include "StillImageQt.h" +#include "qwebsettings.h" + +#include <QPixmap> +#include <QPainter> +#include <QImage> +#include <QImageReader> +#include <QTransform> + +#include <QDebug> + +#include <math.h> + +// This function loads resources into WebKit +static QPixmap loadResourcePixmap(const char *name) +{ + QPixmap pixmap; + if (qstrcmp(name, "missingImage") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::MissingImageGraphic); + else if (qstrcmp(name, "nullPlugin") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::MissingPluginGraphic); + else if (qstrcmp(name, "urlIcon") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::DefaultFrameIconGraphic); + else if (qstrcmp(name, "textAreaResizeCorner") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::TextAreaSizeGripCornerGraphic); + else if (qstrcmp(name, "deleteButton") == 0) + pixmap = QWebSettings::webGraphic(QWebSettings::DeleteButtonGraphic); + else if (!qstrcmp(name, "inputSpeech")) + pixmap = QWebSettings::webGraphic(QWebSettings::InputSpeechButtonGraphic); + + return pixmap; +} + +namespace WebCore { + +bool FrameData::clear(bool clearMetadata) +{ + if (clearMetadata) + m_haveMetadata = false; + + if (m_frame) { + delete m_frame; + m_frame = 0; + return true; + } + return false; +} + + +// ================================================ +// Image Class +// ================================================ + +PassRefPtr<Image> Image::loadPlatformResource(const char* name) +{ + return StillImage::create(loadResourcePixmap(name)); +} + +void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, + const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect) +{ + QPixmap* framePixmap = nativeImageForCurrentFrame(); + if (!framePixmap) // If it's too early we won't have an image yet. + return; + + // Qt interprets 0 width/height as full width/height so just short circuit. + QRectF dr = QRectF(destRect).normalized(); + QRect tr = QRectF(tileRect).toRect().normalized(); + if (!dr.width() || !dr.height() || !tr.width() || !tr.height()) + return; + + QPixmap pixmap = *framePixmap; + if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) + pixmap = pixmap.copy(tr); + + CompositeOperator previousOperator = ctxt->compositeOperation(); + + ctxt->setCompositeOperation(op); + QPainter* p = ctxt->platformContext(); + if (!pixmap.hasAlpha() && p->compositionMode() == QPainter::CompositionMode_SourceOver) + p->setCompositionMode(QPainter::CompositionMode_Source); + + /* Translate the coordinates as phase is not in world matrix coordinate space but the tile rect origin is. */ + QTransform transform(patternTransform); + transform *= QTransform().translate(phase.x(), phase.y()); + transform.translate(tr.x(), tr.y()); + + QBrush b(pixmap); + b.setTransform(transform); + p->fillRect(dr, b); + + ctxt->setCompositeOperation(previousOperator); + + if (imageObserver()) + imageObserver()->didDraw(this); +} + +BitmapImage::BitmapImage(QPixmap* pixmap, ImageObserver* observer) + : Image(observer) + , m_currentFrame(0) + , m_frames(0) + , m_frameTimer(0) + , m_repetitionCount(cAnimationNone) + , m_repetitionCountStatus(Unknown) + , m_repetitionsComplete(0) + , m_isSolidColor(false) + , m_checkedForSolidColor(false) + , m_animationFinished(true) + , m_allDataReceived(true) + , m_haveSize(true) + , m_sizeAvailable(true) + , m_decodedSize(0) + , m_haveFrameCount(true) + , m_frameCount(1) +{ + initPlatformData(); + + int width = pixmap->width(); + int height = pixmap->height(); + m_decodedSize = width * height * 4; + m_size = IntSize(width, height); + + m_frames.grow(1); + m_frames[0].m_frame = pixmap; + m_frames[0].m_hasAlpha = pixmap->hasAlpha(); + m_frames[0].m_haveMetadata = true; + checkForSolidColor(); +} + +void BitmapImage::initPlatformData() +{ +} + +void BitmapImage::invalidatePlatformData() +{ +} + +// Drawing Routines +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, + const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op) +{ + QRectF normalizedDst = dst.normalized(); + QRectF normalizedSrc = src.normalized(); + + startAnimation(); + + if (normalizedSrc.isEmpty() || normalizedDst.isEmpty()) + return; + + QPixmap* image = nativeImageForCurrentFrame(); + if (!image) + return; + + if (mayFillWithSolidColor()) { + fillWithSolidColor(ctxt, normalizedDst, solidColor(), styleColorSpace, op); + return; + } + + QPainter* painter(ctxt->platformContext()); + + QPainter::CompositionMode compositionMode = GraphicsContext::toQtCompositionMode(op); + + if (!image->hasAlpha() && painter->compositionMode() == QPainter::CompositionMode_SourceOver) + compositionMode = QPainter::CompositionMode_Source; + + QPainter::CompositionMode lastCompositionMode = painter->compositionMode(); + painter->setCompositionMode(compositionMode); + + ContextShadow* shadow = ctxt->contextShadow(); + if (shadow->m_type != ContextShadow::NoShadow) { + QPainter* shadowPainter = shadow->beginShadowLayer(ctxt, normalizedDst); + if (shadowPainter) { + shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255); + shadowPainter->drawPixmap(normalizedDst, *image, normalizedSrc); + shadow->endShadowLayer(ctxt); + } + } + + // Test using example site at + // http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html + painter->drawPixmap(normalizedDst, *image, normalizedSrc); + + painter->setCompositionMode(lastCompositionMode); + + if (imageObserver()) + imageObserver()->didDraw(this); +} + +void BitmapImage::checkForSolidColor() +{ + m_isSolidColor = false; + m_checkedForSolidColor = true; + + if (frameCount() > 1) + return; + + QPixmap* framePixmap = frameAtIndex(0); + if (!framePixmap || framePixmap->width() != 1 || framePixmap->height() != 1) + return; + + m_isSolidColor = true; + m_solidColor = QColor::fromRgba(framePixmap->toImage().pixel(0, 0)); +} + +#if OS(WINDOWS) +PassRefPtr<BitmapImage> BitmapImage::create(HBITMAP hBitmap) +{ + return BitmapImage::create(new QPixmap(QPixmap::fromWinHBITMAP(hBitmap))); +} +#endif + +} + + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/IntPointQt.cpp b/Source/WebCore/platform/graphics/qt/IntPointQt.cpp new file mode 100644 index 0000000..f9d336a --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/IntPointQt.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IntPoint.h" + +#include <QPoint> + +namespace WebCore { + +IntPoint::IntPoint(const QPoint& p) + : m_x(p.x()) + , m_y(p.y()) +{ +} + +IntPoint::operator QPoint() const +{ + return QPoint(m_x, m_y); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/IntRectQt.cpp b/Source/WebCore/platform/graphics/qt/IntRectQt.cpp new file mode 100644 index 0000000..ccc153e --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/IntRectQt.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IntRect.h" + +#include <QRect> + +namespace WebCore { + +IntRect::IntRect(const QRect& r) + : m_location(r.topLeft()) + , m_size(r.width(), r.height()) +{ +} + +IntRect::operator QRect() const +{ + return QRect(x(), y(), width(), height()); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/IntSizeQt.cpp b/Source/WebCore/platform/graphics/qt/IntSizeQt.cpp new file mode 100644 index 0000000..4f2bf35 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/IntSizeQt.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include <QSize> + +#include "IntSize.h" + +namespace WebCore { + +IntSize::IntSize(const QSize& r) + : m_width(r.width()) + , m_height(r.height()) +{ +} + +IntSize::operator QSize() const +{ + return QSize(width(), height()); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp new file mode 100644 index 0000000..b881036 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -0,0 +1,547 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Apple Inc. All rights reserved. + + 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 + along 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" +#include "MediaPlayerPrivatePhonon.h" + +#include <limits> + +#include "FrameView.h" +#include "GraphicsContext.h" +#include "Logging.h" +#include "MIMETypeRegistry.h" +#include "NotImplemented.h" +#include "TimeRanges.h" +#include "Widget.h" +#include <wtf/HashSet.h> +#include <wtf/text/CString.h> + +#include <QDebug> +#include <QEvent> +#include <QMetaEnum> +#include <QPainter> +#include <QWidget> +#include <QUrl> + +#include <phonon/audiooutput.h> +#include <phonon/backendcapabilities.h> +#include <phonon/path.h> +#include <phonon/mediaobject.h> +#include <phonon/videowidget.h> + +using namespace Phonon; + +#define LOG_MEDIAOBJECT() (LOG(Media, "%s", debugMediaObject(this, *m_mediaObject).constData())) + +#if !LOG_DISABLED +static QByteArray debugMediaObject(WebCore::MediaPlayerPrivatePhonon* mediaPlayer, const MediaObject& mediaObject) +{ + QByteArray byteArray; + QTextStream stream(&byteArray); + + const QMetaObject* metaObj = mediaPlayer->metaObject(); + QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState")); + + stream << "debugMediaObject -> Phonon::MediaObject("; + stream << "State: " << phononStates.valueToKey(mediaObject.state()); + stream << " | Current time: " << mediaObject.currentTime(); + stream << " | Remaining time: " << mediaObject.remainingTime(); + stream << " | Total time: " << mediaObject.totalTime(); + stream << " | Meta-data: "; + QMultiMap<QString, QString> map = mediaObject.metaData(); + for (QMap<QString, QString>::const_iterator it = map.constBegin(); + it != map.constEnd(); ++it) { + stream << "(" << it.key() << ", " << it.value() << ")"; + } + stream << " | Has video: " << mediaObject.hasVideo(); + stream << " | Is seekable: " << mediaObject.isSeekable(); + stream << ")"; + + stream.flush(); + + return byteArray; +} +#endif + +using namespace WTF; + +namespace WebCore { + +MediaPlayerPrivatePhonon::MediaPlayerPrivatePhonon(MediaPlayer* player) + : m_player(player) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::HaveNothing) + , m_mediaObject(new MediaObject()) + , m_videoWidget(new VideoWidget(0)) + , m_audioOutput(new AudioOutput()) + , m_isVisible(false) +{ + // Hint to Phonon to disable overlay painting + m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen); + m_videoWidget->setAttribute(Qt::WA_QuitOnClose, false); + + createPath(m_mediaObject, m_videoWidget); + createPath(m_mediaObject, m_audioOutput); + + // Make sure we get updates for each frame + m_videoWidget->installEventFilter(this); + foreach (QWidget* widget, qFindChildren<QWidget*>(m_videoWidget)) + widget->installEventFilter(this); + + connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), + this, SLOT(stateChanged(Phonon::State,Phonon::State))); + connect(m_mediaObject, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged())); + connect(m_mediaObject, SIGNAL(seekableChanged(bool)), this, SLOT(seekableChanged(bool))); + connect(m_mediaObject, SIGNAL(hasVideoChanged(bool)), this, SLOT(hasVideoChanged(bool))); + connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(bufferStatus(int))); + connect(m_mediaObject, SIGNAL(finished()), this, SLOT(finished())); + connect(m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)), + this, SLOT(currentSourceChanged(Phonon::MediaSource))); + connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish())); + connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); +} + +MediaPlayerPrivateInterface* MediaPlayerPrivatePhonon::create(MediaPlayer* player) +{ + return new MediaPlayerPrivatePhonon(player); +} + +void MediaPlayerPrivatePhonon::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + + +MediaPlayerPrivatePhonon::~MediaPlayerPrivatePhonon() +{ + LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget"); + m_videoWidget->close(); + delete m_videoWidget; + m_videoWidget = 0; + + LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting audiooutput"); + delete m_audioOutput; + m_audioOutput = 0; + + LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting mediaobject"); + delete m_mediaObject; + m_mediaObject = 0; +} + +HashSet<String>& MediaPlayerPrivatePhonon::supportedTypesCache() +{ + static HashSet<String> supportedTypes; + if (!supportedTypes.isEmpty()) + return supportedTypes; + + // FIXME: we should rebuild the MIME type cache every time the backend is changed, + // however, this would have no effect on MIMETypeRegistry anyway, because it + // pulls this data only once. + + QStringList types = Phonon::BackendCapabilities::availableMimeTypes(); + foreach (const QString& type, types) { + QString first = type.split(QLatin1Char('/')).at(0); + + // We're only interested in types which are not supported by WebCore itself. + if (first != QLatin1String("video") + && first != QLatin1String("audio") + && first != QLatin1String("application")) + continue; + if (MIMETypeRegistry::isSupportedNonImageMIMEType(type)) + continue; + + supportedTypes.add(String(type)); + } + + // These formats are supported by GStreamer, but not correctly advertised. + if (supportedTypes.contains(String("video/x-h264")) + || supportedTypes.contains(String("audio/x-m4a"))) { + supportedTypes.add(String("video/mp4")); + supportedTypes.add(String("audio/aac")); + } + + if (supportedTypes.contains(String("video/x-theora"))) + supportedTypes.add(String("video/ogg")); + + if (supportedTypes.contains(String("audio/x-vorbis"))) + supportedTypes.add(String("audio/ogg")); + + if (supportedTypes.contains(String("audio/x-wav"))) + supportedTypes.add(String("audio/wav")); + + return supportedTypes; +} + +void MediaPlayerPrivatePhonon::getSupportedTypes(HashSet<String>& types) +{ + types = supportedTypesCache(); +} + +MediaPlayer::SupportsType MediaPlayerPrivatePhonon::supportsType(const String& type, const String& codecs) +{ + if (type.isEmpty()) + return MediaPlayer::IsNotSupported; + + if (supportedTypesCache().contains(type)) + return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; + return MediaPlayer::IsNotSupported; +} + +bool MediaPlayerPrivatePhonon::hasVideo() const +{ + bool hasVideo = m_mediaObject->hasVideo(); + LOG(Media, "MediaPlayerPrivatePhonon::hasVideo() -> %s", hasVideo ? "true" : "false"); + return hasVideo; +} + +bool MediaPlayerPrivatePhonon::hasAudio() const +{ + // FIXME: Phonon::MediaObject does not have such a hasAudio() function + bool hasAudio = true; + LOG(Media, "MediaPlayerPrivatePhonon::hasAudio() -> %s", hasAudio ? "true" : "false"); + return hasAudio; +} + +void MediaPlayerPrivatePhonon::load(const String& url) +{ + LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data()); + + // We are now loading + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + } + + // And we don't have any data yet + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; + m_player->readyStateChanged(); + } + + m_mediaObject->setCurrentSource(QUrl(url)); + m_audioOutput->setVolume(m_player->volume()); + setVisible(m_player->visible()); +} + +void MediaPlayerPrivatePhonon::cancelLoad() +{ + notImplemented(); +} + + +void MediaPlayerPrivatePhonon::play() +{ + LOG(Media, "MediaPlayerPrivatePhonon::play()"); + m_mediaObject->play(); +} + +void MediaPlayerPrivatePhonon::pause() +{ + LOG(Media, "MediaPlayerPrivatePhonon::pause()"); + m_mediaObject->pause(); +} + + +bool MediaPlayerPrivatePhonon::paused() const +{ + bool paused = m_mediaObject->state() == Phonon::PausedState; + LOG(Media, "MediaPlayerPrivatePhonon::paused() --> %s", paused ? "true" : "false"); + return paused; +} + +void MediaPlayerPrivatePhonon::seek(float position) +{ + LOG(Media, "MediaPlayerPrivatePhonon::seek(%f)", position); + + if (!m_mediaObject->isSeekable()) + return; + + if (position > duration()) + position = duration(); + + m_mediaObject->seek(position * 1000.0f); +} + +bool MediaPlayerPrivatePhonon::seeking() const +{ + return false; +} + +float MediaPlayerPrivatePhonon::duration() const +{ + if (m_readyState < MediaPlayer::HaveMetadata) + return 0.0f; + + float duration = m_mediaObject->totalTime() / 1000.0f; + + if (duration == 0.0f) // We are streaming + duration = std::numeric_limits<float>::infinity(); + + LOG(Media, "MediaPlayerPrivatePhonon::duration() --> %f", duration); + return duration; +} + +float MediaPlayerPrivatePhonon::currentTime() const +{ + float currentTime = m_mediaObject->currentTime() / 1000.0f; + + LOG(Media, "MediaPlayerPrivatePhonon::currentTime() --> %f", currentTime); + return currentTime; +} + +PassRefPtr<TimeRanges> MediaPlayerPrivatePhonon::buffered() const +{ + notImplemented(); + return TimeRanges::create(); +} + +float MediaPlayerPrivatePhonon::maxTimeSeekable() const +{ + notImplemented(); + return 0.0f; +} + +unsigned MediaPlayerPrivatePhonon::bytesLoaded() const +{ + notImplemented(); + return 0; +} + +unsigned MediaPlayerPrivatePhonon::totalBytes() const +{ + //notImplemented(); + return 0; +} + +void MediaPlayerPrivatePhonon::setRate(float) +{ + notImplemented(); +} + +void MediaPlayerPrivatePhonon::setVolume(float volume) +{ + LOG(Media, "MediaPlayerPrivatePhonon::setVolume()"); + m_audioOutput->setVolume(volume); +} + +void MediaPlayerPrivatePhonon::setMuted(bool muted) +{ + LOG(Media, "MediaPlayerPrivatePhonon::setMuted()"); + m_audioOutput->setMuted(muted); +} + +MediaPlayer::NetworkState MediaPlayerPrivatePhonon::networkState() const +{ + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState")); + LOG(Media, "MediaPlayerPrivatePhonon::networkState() --> %s", networkStates.valueToKey(m_networkState)); + return m_networkState; +} + +MediaPlayer::ReadyState MediaPlayerPrivatePhonon::readyState() const +{ + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState")); + LOG(Media, "MediaPlayerPrivatePhonon::readyState() --> %s", readyStates.valueToKey(m_readyState)); + return m_readyState; +} + +void MediaPlayerPrivatePhonon::updateStates() +{ + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + + Phonon::State phononState = m_mediaObject->state(); + + if (phononState == Phonon::StoppedState) { + if (m_readyState < MediaPlayer::HaveMetadata) { + m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle? + m_readyState = MediaPlayer::HaveMetadata; + m_mediaObject->pause(); + } + } else if (phononState == Phonon::PausedState) { + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; + } else if (phononState == Phonon::ErrorState) { + if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) { + // FIXME: is it possile to differentiate between different types of errors + m_networkState = MediaPlayer::NetworkError; + m_readyState = MediaPlayer::HaveNothing; + cancelLoad(); + } else + m_mediaObject->pause(); + } + + if (seeking()) + m_readyState = MediaPlayer::HaveNothing; + + if (m_networkState != oldNetworkState) { + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState")); + LOG(Media, "Network state changed from '%s' to '%s'", + networkStates.valueToKey(oldNetworkState), + networkStates.valueToKey(m_networkState)); + m_player->networkStateChanged(); + } + + if (m_readyState != oldReadyState) { + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState")); + LOG(Media, "Ready state changed from '%s' to '%s'", + readyStates.valueToKey(oldReadyState), + readyStates.valueToKey(m_readyState)); + m_player->readyStateChanged(); + } +} + +void MediaPlayerPrivatePhonon::setVisible(bool visible) +{ + m_isVisible = visible; + LOG(Media, "MediaPlayerPrivatePhonon::setVisible(%s)", visible ? "true" : "false"); + + m_videoWidget->setVisible(m_isVisible); +} + +void MediaPlayerPrivatePhonon::setSize(const IntSize& newSize) +{ + if (!m_videoWidget) + return; + + LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)", + newSize.width(), newSize.height()); + + QRect currentRect = m_videoWidget->rect(); + + if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height()) + m_videoWidget->resize(newSize.width(), newSize.height()); +} + +IntSize MediaPlayerPrivatePhonon::naturalSize() const +{ + if (!hasVideo()) { + LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", + 0, 0); + return IntSize(); + } + + if (m_readyState < MediaPlayer::HaveMetadata) { + LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", + 0, 0); + return IntSize(); + } + + QSize videoSize = m_videoWidget->sizeHint(); + IntSize naturalSize(videoSize.width(), videoSize.height()); + LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", + naturalSize.width(), naturalSize.height()); + return naturalSize; +} + +bool MediaPlayerPrivatePhonon::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::UpdateRequest) + m_player->repaint(); + + return QObject::eventFilter(obj, event); +} + +void MediaPlayerPrivatePhonon::paint(GraphicsContext* graphicsContect, const IntRect& rect) +{ + if (graphicsContect->paintingDisabled()) + return; + + if (!m_isVisible) + return; + + QPainter* painter = graphicsContect->platformContext(); + + painter->fillRect(rect, Qt::black); + + m_videoWidget->render(painter, QPoint(rect.x(), rect.y()), + QRegion(0, 0, rect.width(), rect.height())); +} + +// ====================== Phonon::MediaObject signals ====================== + +void MediaPlayerPrivatePhonon::stateChanged(Phonon::State newState, Phonon::State oldState) +{ + const QMetaObject* metaObj = this->metaObject(); + QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState")); + LOG(Media, "MediaPlayerPrivatePhonon::stateChanged(newState=%s, oldState=%s)", + phononStates.valueToKey(newState), phononStates.valueToKey(oldState)); + + updateStates(); +} + +void MediaPlayerPrivatePhonon::metaDataChanged() +{ + LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()"); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivatePhonon::seekableChanged(bool) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivatePhonon::hasVideoChanged(bool hasVideo) +{ + LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false"); +} + +void MediaPlayerPrivatePhonon::bufferStatus(int) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivatePhonon::finished() +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivatePhonon::currentSourceChanged(const Phonon::MediaSource&) +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivatePhonon::aboutToFinish() +{ + notImplemented(); + LOG_MEDIAOBJECT(); +} + +void MediaPlayerPrivatePhonon::totalTimeChanged(qint64 totalTime) +{ +#if OS(WINDOWS) + LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%I64d)", totalTime); +#else + LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%lld)", totalTime); +#endif + LOG_MEDIAOBJECT(); +} + +} // namespace WebCore + +#include "moc_MediaPlayerPrivatePhonon.cpp" diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h new file mode 100644 index 0000000..d793675 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -0,0 +1,153 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Apple Inc. All rights reserved. + + 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 + along 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 MediaPlayerPrivatePhonon_h +#define MediaPlayerPrivatePhonon_h + +#include "MediaPlayerPrivate.h" + +#include <QObject> +#include <phononnamespace.h> + +QT_BEGIN_NAMESPACE +class QWidget; +class QUrl; + +namespace Phonon { + class MediaObject; + class VideoWidget; + class AudioOutput; + class MediaSource; +} +QT_END_NAMESPACE + +namespace WebCore { + + class MediaPlayerPrivatePhonon : public QObject, public MediaPlayerPrivateInterface { + + Q_OBJECT + + public: + static void registerMediaEngine(MediaEngineRegistrar); + ~MediaPlayerPrivatePhonon(); + + // These enums are used for debugging + Q_ENUMS(ReadyState NetworkState PhononState) + + enum ReadyState { + HaveNothing, + HaveMetadata, + HaveCurrentData, + HaveFutureData, + HaveEnoughData + }; + + enum NetworkState { + Empty, + Idle, + Loading, + Loaded, + FormatError, + NetworkError, + DecodeError + }; + + enum PhononState { + LoadingState, + StoppedState, + PlayingState, + BufferingState, + PausedState, + ErrorState + }; + + IntSize naturalSize() const; + bool hasVideo() const; + bool hasAudio() const; + + void load(const String &url); + void cancelLoad(); + + void play(); + void pause(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float); + + void setRate(float); + void setVolume(float); + void setMuted(bool); + + MediaPlayer::NetworkState networkState() const; + MediaPlayer::ReadyState readyState() const; + + PassRefPtr<TimeRanges> buffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setSize(const IntSize&); + + void paint(GraphicsContext*, const IntRect&); + + protected: + bool eventFilter(QObject*, QEvent*); + + private slots: + void stateChanged(Phonon::State, Phonon::State); + void metaDataChanged(); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + void finished(); + void currentSourceChanged(const Phonon::MediaSource&); + void aboutToFinish(); + void totalTimeChanged(qint64); + + private: + MediaPlayerPrivatePhonon(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static HashSet<String>& supportedTypesCache(); + static bool isAvailable() { return true; } + + void updateStates(); + + MediaPlayer* m_player; + + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + + Phonon::MediaObject* m_mediaObject; + Phonon::VideoWidget* m_videoWidget; + Phonon::AudioOutput* m_audioOutput; + + bool m_isVisible; + }; +} + +#endif // MediaPlayerPrivatePhonon_h diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp new file mode 100644 index 0000000..dd4b6e6 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp @@ -0,0 +1,674 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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" +#include "MediaPlayerPrivateQt.h" + +#include "FrameLoaderClientQt.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLMediaElement.h" +#include "HTMLVideoElement.h" +#include "QtNAMThreadSafeProxy.h" +#include "NetworkingContext.h" +#include "NotImplemented.h" +#include "RenderVideo.h" +#include "TimeRanges.h" +#include "Widget.h" +#include "qwebframe.h" +#include "qwebpage.h" + +#include <QGraphicsScene> +#include <QGraphicsVideoItem> +#include <QMediaPlayerControl> +#include <QMediaService> +#include <QNetworkAccessManager> +#include <QNetworkCookieJar> +#include <QNetworkRequest> +#include <QPainter> +#include <QPoint> +#include <QRect> +#include <QStyleOptionGraphicsItem> +#include <QTime> +#include <QTimer> +#include <QUrl> +#include <limits> +#include <wtf/HashSet.h> +#include <wtf/text/CString.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "texmap/TextureMapperPlatformLayer.h" +#endif + +using namespace WTF; + +namespace WebCore { + +MediaPlayerPrivateInterface* MediaPlayerPrivateQt::create(MediaPlayer* player) +{ + return new MediaPlayerPrivateQt(player); +} + +void MediaPlayerPrivateQt::registerMediaEngine(MediaEngineRegistrar registrar) +{ + registrar(create, getSupportedTypes, supportsType); +} + +void MediaPlayerPrivateQt::getSupportedTypes(HashSet<String> &supported) +{ + QStringList types = QMediaPlayer::supportedMimeTypes(); + + for (int i = 0; i < types.size(); i++) { + QString mime = types.at(i); + if (mime.startsWith("audio/") || mime.startsWith("video/")) + supported.add(mime); + } +} + +MediaPlayer::SupportsType MediaPlayerPrivateQt::supportsType(const String& mime, const String& codec) +{ + if (!mime.startsWith("audio/") && !mime.startsWith("video/")) + return MediaPlayer::IsNotSupported; + + if (QMediaPlayer::hasSupport(mime, QStringList(codec)) >= QtMultimediaKit::ProbablySupported) + return MediaPlayer::IsSupported; + + return MediaPlayer::MayBeSupported; +} + +MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player) + : m_webCorePlayer(player) + , m_mediaPlayer(new QMediaPlayer) + , m_mediaPlayerControl(0) + , m_videoItem(new QGraphicsVideoItem) + , m_videoScene(new QGraphicsScene) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::HaveNothing) + , m_currentSize(0, 0) + , m_naturalSize(RenderVideo::defaultSize()) + , m_isVisible(false) + , m_isSeeking(false) + , m_composited(false) + , m_queuedSeek(-1) + , m_preload(MediaPlayer::Auto) +{ + m_mediaPlayer->bind(m_videoItem); + m_videoScene->addItem(m_videoItem); + + // Signal Handlers + connect(m_mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SLOT(stateChanged(QMediaPlayer::State))); + connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), + this, SLOT(handleError(QMediaPlayer::Error))); + connect(m_mediaPlayer, SIGNAL(bufferStatusChanged(int)), + this, SLOT(bufferStatusChanged(int))); + connect(m_mediaPlayer, SIGNAL(durationChanged(qint64)), + this, SLOT(durationChanged(qint64))); + connect(m_mediaPlayer, SIGNAL(positionChanged(qint64)), + this, SLOT(positionChanged(qint64))); + connect(m_mediaPlayer, SIGNAL(volumeChanged(int)), + this, SLOT(volumeChanged(int))); + connect(m_mediaPlayer, SIGNAL(mutedChanged(bool)), + this, SLOT(mutedChanged(bool))); + connect(m_videoScene, SIGNAL(changed(QList<QRectF>)), + this, SLOT(repaint())); + connect(m_videoItem, SIGNAL(nativeSizeChanged(QSizeF)), + this, SLOT(nativeSizeChanged(QSizeF))); + + // Grab the player control + if (QMediaService* service = m_mediaPlayer->service()) { + m_mediaPlayerControl = qobject_cast<QMediaPlayerControl *>( + service->requestControl(QMediaPlayerControl_iid)); + } +} + +MediaPlayerPrivateQt::~MediaPlayerPrivateQt() +{ + m_mediaPlayer->disconnect(this); + m_mediaPlayer->stop(); + m_mediaPlayer->setMedia(QMediaContent()); + + delete m_mediaPlayer; + delete m_videoScene; +} + +bool MediaPlayerPrivateQt::hasVideo() const +{ + return m_mediaPlayer->isVideoAvailable(); +} + +bool MediaPlayerPrivateQt::hasAudio() const +{ + return true; +} + +void MediaPlayerPrivateQt::load(const String& url) +{ + m_mediaUrl = url; + + // QtMultimedia does not have an API to throttle loading + // so we handle this ourselves by delaying the load + if (m_preload == MediaPlayer::None) { + m_delayingLoad = true; + return; + } + + commitLoad(url); +} + +void MediaPlayerPrivateQt::commitLoad(const String& url) +{ + // We are now loading + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_webCorePlayer->networkStateChanged(); + } + + // And we don't have any data yet + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; + m_webCorePlayer->readyStateChanged(); + } + + const QUrl rUrl = QUrl(QString(url)); + const QString scheme = rUrl.scheme().toLower(); + + // Grab the client media element + HTMLMediaElement* element = static_cast<HTMLMediaElement*>(m_webCorePlayer->mediaPlayerClient()); + + // Construct the media content with a network request if the resource is http[s] + if (scheme == "http" || scheme == "https") { + QNetworkRequest request = QNetworkRequest(rUrl); + + // Grab the current document + Document* document = element->document(); + if (!document) + document = element->ownerDocument(); + + // Grab the frame and network manager + Frame* frame = document ? document->frame() : 0; + QNetworkAccessManager* manager = frame ? frame->loader()->networkingContext()->networkAccessManager() : 0; + FrameLoaderClientQt* frameLoader = frame ? static_cast<FrameLoaderClientQt*>(frame->loader()->client()) : 0; + + if (document && manager) { + // Set the cookies + QtNAMThreadSafeProxy managerProxy(manager); + QList<QNetworkCookie> cookies = managerProxy.cookiesForUrl(rUrl); + + // Don't set the header if there are no cookies. + // This prevents a warning from being emitted. + if (!cookies.isEmpty()) + request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies)); + + // Set the refferer, but not when requesting insecure content from a secure page + QUrl documentUrl = QUrl(QString(document->documentURI())); + if (documentUrl.scheme().toLower() == "http" || scheme == "https") + request.setRawHeader("Referer", documentUrl.toEncoded()); + + // Set the user agent + request.setRawHeader("User-Agent", frameLoader->userAgent(rUrl).utf8().data()); + } + + m_mediaPlayer->setMedia(QMediaContent(request)); + } else { + // Otherwise, just use the URL + m_mediaPlayer->setMedia(QMediaContent(rUrl)); + } + + // Set the current volume and mute status + // We get these from the element, rather than the player, in case we have + // transitioned from a media engine which doesn't support muting, to a media + // engine which does. + m_mediaPlayer->setMuted(element->muted()); + m_mediaPlayer->setVolume(static_cast<int>(element->volume() * 100.0)); + + // Setting a media source will start loading the media, but we need + // to pre-roll as well to get video size-hints and buffer-status + if (element->paused()) + m_mediaPlayer->pause(); + else + m_mediaPlayer->play(); +} + +void MediaPlayerPrivateQt::resumeLoad() +{ + m_delayingLoad = false; + + if (!m_mediaUrl.isNull()) + commitLoad(m_mediaUrl); +} + +void MediaPlayerPrivateQt::cancelLoad() +{ + m_mediaPlayer->setMedia(QMediaContent()); + updateStates(); +} + +void MediaPlayerPrivateQt::prepareToPlay() +{ + if (m_mediaPlayer->media().isNull() || m_delayingLoad) + resumeLoad(); +} + +void MediaPlayerPrivateQt::play() +{ + if (m_mediaPlayer->state() != QMediaPlayer::PlayingState) + m_mediaPlayer->play(); +} + +void MediaPlayerPrivateQt::pause() +{ + if (m_mediaPlayer->state() == QMediaPlayer::PlayingState) + m_mediaPlayer->pause(); +} + +bool MediaPlayerPrivateQt::paused() const +{ + return (m_mediaPlayer->state() != QMediaPlayer::PlayingState); +} + +void MediaPlayerPrivateQt::seek(float position) +{ + if (!m_mediaPlayer->isSeekable()) + return; + + if (m_mediaPlayerControl && !m_mediaPlayerControl->availablePlaybackRanges().contains(position * 1000)) + return; + + if (m_isSeeking) + return; + + if (position > duration()) + position = duration(); + + // Seeking is most reliable when we're paused. + // Webkit will try to pause before seeking, but due to the asynchronous nature + // of the backend, the player may not actually be paused yet. + // In this case, we should queue the seek and wait until pausing has completed + // before attempting to seek. + if (m_mediaPlayer->state() == QMediaPlayer::PlayingState) { + m_mediaPlayer->pause(); + m_isSeeking = true; + m_queuedSeek = static_cast<qint64>(position * 1000); + + // Set a timeout, so that in the event that we don't get a state changed + // signal, we still attempt the seek. + QTimer::singleShot(1000, this, SLOT(queuedSeekTimeout())); + } else { + m_isSeeking = true; + m_mediaPlayer->setPosition(static_cast<qint64>(position * 1000)); + + // Set a timeout, in case we don't get a position changed signal + QTimer::singleShot(10000, this, SLOT(seekTimeout())); + } +} + +bool MediaPlayerPrivateQt::seeking() const +{ + return m_isSeeking; +} + +float MediaPlayerPrivateQt::duration() const +{ + if (m_readyState < MediaPlayer::HaveMetadata) + return 0.0f; + + float duration = m_mediaPlayer->duration() / 1000.0f; + + // We are streaming + if (duration <= 0.0f) + duration = std::numeric_limits<float>::infinity(); + + return duration; +} + +float MediaPlayerPrivateQt::currentTime() const +{ + return m_mediaPlayer->position() / 1000.0f; +} + +PassRefPtr<TimeRanges> MediaPlayerPrivateQt::buffered() const +{ + RefPtr<TimeRanges> buffered = TimeRanges::create(); + + if (!m_mediaPlayerControl) + return buffered; + + QMediaTimeRange playbackRanges = m_mediaPlayerControl->availablePlaybackRanges(); + + foreach (const QMediaTimeInterval interval, playbackRanges.intervals()) { + float rangeMin = static_cast<float>(interval.start()) / 1000.0f; + float rangeMax = static_cast<float>(interval.end()) / 1000.0f; + buffered->add(rangeMin, rangeMax); + } + + return buffered.release(); +} + +float MediaPlayerPrivateQt::maxTimeSeekable() const +{ + if (!m_mediaPlayerControl) + return 0; + + return static_cast<float>(m_mediaPlayerControl->availablePlaybackRanges().latestTime()) / 1000.0f; +} + +unsigned MediaPlayerPrivateQt::bytesLoaded() const +{ + QLatin1String bytesLoadedKey("bytes-loaded"); + if (m_mediaPlayer->availableExtendedMetaData().contains(bytesLoadedKey)) + return m_mediaPlayer->extendedMetaData(bytesLoadedKey).toInt(); + + return m_mediaPlayer->bufferStatus(); +} + +unsigned MediaPlayerPrivateQt::totalBytes() const +{ + if (m_mediaPlayer->availableMetaData().contains(QtMultimediaKit::Size)) + return m_mediaPlayer->metaData(QtMultimediaKit::Size).toInt(); + + return 100; +} + +void MediaPlayerPrivateQt::setPreload(MediaPlayer::Preload preload) +{ + m_preload = preload; + if (m_delayingLoad && m_preload != MediaPlayer::None) + resumeLoad(); +} + +void MediaPlayerPrivateQt::setRate(float rate) +{ + m_mediaPlayer->setPlaybackRate(rate); +} + +void MediaPlayerPrivateQt::setVolume(float volume) +{ + m_mediaPlayer->setVolume(static_cast<int>(volume * 100.0)); +} + +bool MediaPlayerPrivateQt::supportsMuting() const +{ + return true; +} + +void MediaPlayerPrivateQt::setMuted(bool muted) +{ + m_mediaPlayer->setMuted(muted); +} + +MediaPlayer::NetworkState MediaPlayerPrivateQt::networkState() const +{ + return m_networkState; +} + +MediaPlayer::ReadyState MediaPlayerPrivateQt::readyState() const +{ + return m_readyState; +} + +void MediaPlayerPrivateQt::setVisible(bool visible) +{ + m_isVisible = visible; +} + +void MediaPlayerPrivateQt::mediaStatusChanged(QMediaPlayer::MediaStatus) +{ + updateStates(); +} + +void MediaPlayerPrivateQt::handleError(QMediaPlayer::Error) +{ + updateStates(); +} + +void MediaPlayerPrivateQt::stateChanged(QMediaPlayer::State state) +{ + if (state != QMediaPlayer::PlayingState && m_isSeeking && m_queuedSeek >= 0) { + m_mediaPlayer->setPosition(m_queuedSeek); + m_queuedSeek = -1; + } +} + +void MediaPlayerPrivateQt::nativeSizeChanged(const QSizeF& size) +{ + LOG(Media, "MediaPlayerPrivateQt::naturalSizeChanged(%dx%d)", + size.toSize().width(), size.toSize().height()); + + if (!size.isValid()) + return; + + m_naturalSize = size.toSize(); + m_webCorePlayer->sizeChanged(); +} + +void MediaPlayerPrivateQt::queuedSeekTimeout() +{ + // If we haven't heard anything, assume the player is now paused + // and we can attempt the seek + if (m_isSeeking && m_queuedSeek >= 0) { + m_mediaPlayer->setPosition(m_queuedSeek); + m_queuedSeek = -1; + + // Set a timeout, in case we don't get a position changed signal + QTimer::singleShot(10000, this, SLOT(seekTimeout())); + } +} + +void MediaPlayerPrivateQt::seekTimeout() +{ + // If we haven't heard anything, assume the seek succeeded + if (m_isSeeking) { + m_webCorePlayer->timeChanged(); + m_isSeeking = false; + } +} + +void MediaPlayerPrivateQt::positionChanged(qint64) +{ + // Only propagate this event if we are seeking + if (m_isSeeking && m_queuedSeek == -1) { + m_webCorePlayer->timeChanged(); + m_isSeeking = false; + } +} + +void MediaPlayerPrivateQt::bufferStatusChanged(int) +{ + notImplemented(); +} + +void MediaPlayerPrivateQt::durationChanged(qint64) +{ + m_webCorePlayer->durationChanged(); +} + +void MediaPlayerPrivateQt::volumeChanged(int volume) +{ + m_webCorePlayer->volumeChanged(static_cast<float>(volume) / 100.0); +} + +void MediaPlayerPrivateQt::mutedChanged(bool muted) +{ + m_webCorePlayer->muteChanged(muted); +} + +void MediaPlayerPrivateQt::updateStates() +{ + // Store the old states so that we can detect a change and raise change events + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + + QMediaPlayer::MediaStatus currentStatus = m_mediaPlayer->mediaStatus(); + QMediaPlayer::Error currentError = m_mediaPlayer->error(); + + if (currentError != QMediaPlayer::NoError) { + m_readyState = MediaPlayer::HaveNothing; + if (currentError == QMediaPlayer::FormatError) + m_networkState = MediaPlayer::FormatError; + else + m_networkState = MediaPlayer::NetworkError; + } else if (currentStatus == QMediaPlayer::UnknownMediaStatus + || currentStatus == QMediaPlayer::NoMedia) { + m_networkState = MediaPlayer::Idle; + m_readyState = MediaPlayer::HaveNothing; + } else if (currentStatus == QMediaPlayer::LoadingMedia) { + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveNothing; + } else if (currentStatus == QMediaPlayer::LoadedMedia) { + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveMetadata; + } else if (currentStatus == QMediaPlayer::BufferingMedia) { + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveFutureData; + } else if (currentStatus == QMediaPlayer::StalledMedia) { + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveCurrentData; + } else if (currentStatus == QMediaPlayer::BufferedMedia + || currentStatus == QMediaPlayer::EndOfMedia) { + m_networkState = MediaPlayer::Idle; + m_readyState = MediaPlayer::HaveEnoughData; + } else if (currentStatus == QMediaPlayer::InvalidMedia) { + m_networkState = MediaPlayer::NetworkError; + m_readyState = MediaPlayer::HaveNothing; + } + + // Log the state changes and raise the state change events + // NB: The readyStateChanged event must come before the networkStateChanged event. + // Breaking this invariant will cause the resource selection algorithm for multiple + // sources to fail. + if (m_readyState != oldReadyState) + m_webCorePlayer->readyStateChanged(); + + if (m_networkState != oldNetworkState) + m_webCorePlayer->networkStateChanged(); +} + +void MediaPlayerPrivateQt::setSize(const IntSize& size) +{ + LOG(Media, "MediaPlayerPrivateQt::setSize(%dx%d)", + size.width(), size.height()); + + if (size == m_currentSize) + return; + + m_currentSize = size; + m_videoItem->setSize(QSizeF(QSize(size))); +} + +IntSize MediaPlayerPrivateQt::naturalSize() const +{ + if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) { + LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> 0x0 (!hasVideo || !haveMetaData)"); + return IntSize(); + } + + LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> %dx%d (m_naturalSize)", + m_naturalSize.width(), m_naturalSize.height()); + + return m_naturalSize; +} + +void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect) +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_composited) + return; +#endif + if (context->paintingDisabled()) + return; + + if (!m_isVisible) + return; + + QPainter* painter = context->platformContext(); + m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect()); +} + +void MediaPlayerPrivateQt::repaint() +{ + m_webCorePlayer->repaint(); +} + +#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) + +class TextureMapperVideoLayerQt : public virtual TextureMapperVideoLayer { +public: + TextureMapperVideoLayerQt(QGraphicsVideoItem* videoItem) + : m_videoItem(videoItem) + { + } + + virtual void setPlatformLayerClient(TextureMapperLayerClient* client) + { + m_client = client; + } + + virtual void paint(GraphicsContext* context) + { + if (!m_videoItem) + return; + + QStyleOptionGraphicsItem opt; + opt.exposedRect = m_videoItem.data()->sceneBoundingRect(); + opt.rect = opt.exposedRect.toRect(); + m_videoItem.data()->paint(context->platformContext(), &opt); + } + + virtual IntSize size() const + { + return m_videoItem ? IntSize(m_videoItem.data()->size().width(), m_videoItem.data()->size().height()) : IntSize(); + } + + QWeakPointer<QGraphicsVideoItem> m_videoItem; + TextureMapperLayerClient* m_client; +}; + + +void MediaPlayerPrivateQt::acceleratedRenderingStateChanged() +{ + MediaPlayerClient* client = m_webCorePlayer->mediaPlayerClient(); + bool composited = client->mediaPlayerRenderingCanBeAccelerated(m_webCorePlayer); + if (composited == m_composited) + return; + + m_composited = composited; + if (composited) + m_platformLayer = new TextureMapperVideoLayerQt(m_videoItem); +} + +PlatformLayer* MediaPlayerPrivateQt::platformLayer() const +{ + return m_composited ? m_platformLayer.get() : 0; +} +#endif + +PlatformMedia MediaPlayerPrivateQt::platformMedia() const +{ + PlatformMedia pm; + pm.type = PlatformMedia::QtMediaPlayerType; + pm.media.qtMediaPlayer = const_cast<MediaPlayerPrivateQt*>(this); + return pm; +} + +} // namespace WebCore + +#include "moc_MediaPlayerPrivateQt.cpp" diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h new file mode 100644 index 0000000..93c9d1c --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h @@ -0,0 +1,156 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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 MediaPlayerPrivateQt_h +#define MediaPlayerPrivateQt_h + +#include "MediaPlayerPrivate.h" + +#include <QMediaPlayer> +#include <QObject> + +QT_BEGIN_NAMESPACE +class QMediaPlayerControl; +class QGraphicsVideoItem; +class QGraphicsScene; +QT_END_NAMESPACE + +namespace WebCore { + +class TextureMapperVideoLayer; + +class MediaPlayerPrivateQt : public QObject, public MediaPlayerPrivateInterface { + + Q_OBJECT + +public: + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + ~MediaPlayerPrivateQt(); + + static void registerMediaEngine(MediaEngineRegistrar); + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String&, const String&); + static bool isAvailable() { return true; } + + bool hasVideo() const; + bool hasAudio() const; + + void load(const String &url); + void commitLoad(const String& url); + void resumeLoad(); + void cancelLoad(); + + void play(); + void pause(); + void prepareToPlay(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float); + + void setRate(float); + void setVolume(float); + + bool supportsMuting() const; + void setMuted(bool); + + void setPreload(MediaPlayer::Preload); + + MediaPlayer::NetworkState networkState() const; + MediaPlayer::ReadyState readyState() const; + + PassRefPtr<TimeRanges> buffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + unsigned totalBytes() const; + + void setVisible(bool); + + IntSize naturalSize() const; + void setSize(const IntSize&); + + void paint(GraphicsContext*, const IntRect&); + + bool supportsFullscreen() const { return false; } + +#if USE(ACCELERATED_COMPOSITING) +#if USE(TEXTURE_MAPPER) + // whether accelerated rendering is supported by the media engine for the current media. + virtual bool supportsAcceleratedRendering() const { return true; } + // called when the rendering system flips the into or out of accelerated rendering mode. + virtual void acceleratedRenderingStateChanged(); + // returns an object that can be directly composited via GraphicsLayerQt (essentially a QGraphicsItem*) + virtual PlatformLayer* platformLayer() const; +#else + virtual bool supportsAcceleratedRendering() const { return false; } + virtual void acceleratedRenderingStateChanged() { } + virtual PlatformLayer* platformLayer() const { return 0; } +#endif +#endif + + virtual PlatformMedia platformMedia() const; +private slots: + void mediaStatusChanged(QMediaPlayer::MediaStatus); + void handleError(QMediaPlayer::Error); + void stateChanged(QMediaPlayer::State); + void nativeSizeChanged(const QSizeF&); + void queuedSeekTimeout(); + void seekTimeout(); + void positionChanged(qint64); + void durationChanged(qint64); + void bufferStatusChanged(int); + void volumeChanged(int); + void mutedChanged(bool); + void repaint(); + +private: + void updateStates(); + +private: + MediaPlayerPrivateQt(MediaPlayer*); + + MediaPlayer* m_webCorePlayer; + QMediaPlayer* m_mediaPlayer; + QMediaPlayerControl* m_mediaPlayerControl; + QGraphicsVideoItem* m_videoItem; + QGraphicsScene* m_videoScene; +#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) + OwnPtr<TextureMapperVideoLayer> m_platformLayer; +#endif + + mutable MediaPlayer::NetworkState m_networkState; + mutable MediaPlayer::ReadyState m_readyState; + + IntSize m_currentSize; + IntSize m_naturalSize; + bool m_isVisible; + bool m_isSeeking; + bool m_composited; + qint64 m_queuedSeek; + MediaPlayer::Preload m_preload; + bool m_delayingLoad; + String m_mediaUrl; + +}; +} + +#endif // MediaPlayerPrivateQt_h diff --git a/Source/WebCore/platform/graphics/qt/PathQt.cpp b/Source/WebCore/platform/graphics/qt/PathQt.cpp new file mode 100644 index 0000000..571b405 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/PathQt.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * 2006 Rob Buis <buis@kde.org> + * 2009, 2010 Dirk Schulze <krit@webkit.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Path.h" + +#include "AffineTransform.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "PlatformString.h" +#include "StrokeStyleApplier.h" +#include <QPainterPath> +#include <QTransform> +#include <QString> +#include <wtf/OwnPtr.h> + +#define _USE_MATH_DEFINES +#include <math.h> + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +namespace WebCore { + +Path::Path() +{ +} + +Path::~Path() +{ +} + +Path::Path(const Path& other) + : m_path(other.m_path) +{ +} + +Path& Path::operator=(const Path& other) +{ + m_path = other.m_path; + return *this; +} + +static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c) +{ + // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx) + return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x())); +} + +static inline bool withinRange(qreal p, qreal a, qreal b) +{ + return (p >= a && p <= b) || (p >= b && p <= a); +} + +// Check whether a point is on the border +static bool isPointOnPathBorder(const QPolygonF& border, const QPointF& p) +{ + // null border doesn't contain points + if (border.isEmpty()) + return false; + + QPointF p1 = border.at(0); + QPointF p2; + + for (int i = 1; i < border.size(); ++i) { + p2 = border.at(i); + if (areCollinear(p, p1, p2) + // Once we know that the points are collinear we + // only need to check one of the coordinates + && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ? + withinRange(p.x(), p1.x(), p2.x()) : + withinRange(p.y(), p1.y(), p2.y()))) { + return true; + } + p1 = p2; + } + return false; +} + +bool Path::contains(const FloatPoint& point, WindRule rule) const +{ + Qt::FillRule savedRule = m_path.fillRule(); + const_cast<QPainterPath*>(&m_path)->setFillRule(rule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); + + bool contains = m_path.contains(point); + + if (!contains) { + // check whether the point is on the border + contains = isPointOnPathBorder(m_path.toFillPolygon(), point); + } + + const_cast<QPainterPath*>(&m_path)->setFillRule(savedRule); + return contains; +} + +static GraphicsContext* scratchContext() +{ + static QImage image(1, 1, QImage::Format_ARGB32_Premultiplied); + static QPainter painter(&image); + static GraphicsContext* context = new GraphicsContext(&painter); + return context; +} + +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ + ASSERT(applier); + + QPainterPathStroker stroke; + GraphicsContext* context = scratchContext(); + applier->strokeStyle(context); + + QPen pen = context->platformContext()->pen(); + stroke.setWidth(pen.widthF()); + stroke.setCapStyle(pen.capStyle()); + stroke.setJoinStyle(pen.joinStyle()); + stroke.setMiterLimit(pen.miterLimit()); + stroke.setDashPattern(pen.dashPattern()); + stroke.setDashOffset(pen.dashOffset()); + + return stroke.createStroke(m_path).contains(point); +} + +void Path::translate(const FloatSize& size) +{ + QTransform matrix; + matrix.translate(size.width(), size.height()); + m_path = m_path * matrix; +} + +FloatRect Path::boundingRect() const +{ + return m_path.boundingRect(); +} + +FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) +{ + GraphicsContext* context = scratchContext(); + QPainterPathStroker stroke; + if (applier) { + applier->strokeStyle(context); + + QPen pen = context->platformContext()->pen(); + stroke.setWidth(pen.widthF()); + stroke.setCapStyle(pen.capStyle()); + stroke.setJoinStyle(pen.joinStyle()); + stroke.setMiterLimit(pen.miterLimit()); + stroke.setDashPattern(pen.dashPattern()); + stroke.setDashOffset(pen.dashOffset()); + } + return stroke.createStroke(m_path).boundingRect(); +} + +void Path::moveTo(const FloatPoint& point) +{ + m_path.moveTo(point); +} + +void Path::addLineTo(const FloatPoint& p) +{ + m_path.lineTo(p); +} + +void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p) +{ + m_path.quadTo(cp, p); +} + +void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p) +{ + m_path.cubicTo(cp1, cp2, p); +} + +void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) +{ + FloatPoint p0(m_path.currentPosition()); + + FloatPoint p1p0((p0.x() - p1.x()), (p0.y() - p1.y())); + FloatPoint p1p2((p2.x() - p1.x()), (p2.y() - p1.y())); + float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); + + double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); + + // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8) + // We could have used areCollinear() here, but since we're reusing + // the variables computed above later on we keep this logic. + if (qFuzzyCompare(qAbs(cos_phi), 1.0)) { + m_path.lineTo(p1); + return; + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); + + FloatPoint orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; + + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + + FloatPoint p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * piDouble - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + FloatPoint orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * piDouble - ea; + if ((sa > ea) && ((sa - ea) < piDouble)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > piDouble)) + anticlockwise = true; + + m_path.lineTo(t_p1p0); + + addArc(p, radius, sa, ea, anticlockwise); +} + +void Path::closeSubpath() +{ + m_path.closeSubpath(); +} + +#define DEGREES(t) ((t) * 180.0 / M_PI) +void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool anticlockwise) +{ + qreal xc = p.x(); + qreal yc = p.y(); + qreal radius = r; + + + //### HACK + // In Qt we don't switch the coordinate system for degrees + // and still use the 0,0 as bottom left for degrees so we need + // to switch + sar = -sar; + ear = -ear; + anticlockwise = !anticlockwise; + //end hack + + float sa = DEGREES(sar); + float ea = DEGREES(ear); + + double span = 0; + + double xs = xc - radius; + double ys = yc - radius; + double width = radius*2; + double height = radius*2; + + if ((!anticlockwise && (ea - sa >= 360)) || (anticlockwise && (sa - ea >= 360))) { + // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the + // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole + // circumference of this circle. + span = 360; + + if (anticlockwise) + span = -span; + } else { + if (!anticlockwise && (ea < sa)) + span += 360; + else if (anticlockwise && (sa < ea)) + span -= 360; + + // this is also due to switched coordinate system + // we would end up with a 0 span instead of 360 + if (!(qFuzzyCompare(span + (ea - sa) + 1, 1.0) + && qFuzzyCompare(qAbs(span), 360.0))) { + // mod 360 + span += (ea - sa) - (static_cast<int>((ea - sa) / 360)) * 360; + } + } + + // If the path is empty, move to where the arc will start to avoid painting a line from (0,0) + // NOTE: QPainterPath::isEmpty() won't work here since it ignores a lone MoveToElement + if (!m_path.elementCount()) + m_path.arcMoveTo(xs, ys, width, height, sa); + else if (!radius) { + m_path.lineTo(xc, yc); + return; + } + + m_path.arcTo(xs, ys, width, height, sa, span); + +} + +void Path::addRect(const FloatRect& r) +{ + m_path.addRect(r.x(), r.y(), r.width(), r.height()); +} + +void Path::addEllipse(const FloatRect& r) +{ + m_path.addEllipse(r.x(), r.y(), r.width(), r.height()); +} + +void Path::clear() +{ + if (!m_path.elementCount()) + return; + m_path = QPainterPath(); +} + +bool Path::isEmpty() const +{ + // Don't use QPainterPath::isEmpty(), as that also returns true if there's only + // one initial MoveTo element in the path. + return !m_path.elementCount(); +} + +bool Path::hasCurrentPoint() const +{ + return !isEmpty(); +} + +FloatPoint Path::currentPoint() const +{ + return m_path.currentPosition(); +} + +void Path::apply(void* info, PathApplierFunction function) const +{ + PathElement pelement; + FloatPoint points[3]; + pelement.points = points; + for (int i = 0; i < m_path.elementCount(); ++i) { + const QPainterPath::Element& cur = m_path.elementAt(i); + + switch (cur.type) { + case QPainterPath::MoveToElement: + pelement.type = PathElementMoveToPoint; + pelement.points[0] = QPointF(cur); + function(info, &pelement); + break; + case QPainterPath::LineToElement: + pelement.type = PathElementAddLineToPoint; + pelement.points[0] = QPointF(cur); + function(info, &pelement); + break; + case QPainterPath::CurveToElement: + { + const QPainterPath::Element& c1 = m_path.elementAt(i + 1); + const QPainterPath::Element& c2 = m_path.elementAt(i + 2); + + Q_ASSERT(c1.type == QPainterPath::CurveToDataElement); + Q_ASSERT(c2.type == QPainterPath::CurveToDataElement); + + pelement.type = PathElementAddCurveToPoint; + pelement.points[0] = QPointF(cur); + pelement.points[1] = QPointF(c1); + pelement.points[2] = QPointF(c2); + function(info, &pelement); + + i += 2; + break; + } + case QPainterPath::CurveToDataElement: + Q_ASSERT(false); + } + } +} + +void Path::transform(const AffineTransform& transform) +{ + QTransform qTransform(transform); +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + // Workaround for http://bugreports.qt.nokia.com/browse/QTBUG-11264 + // QTransform.map doesn't handle the MoveTo element because of the isEmpty issue + if (m_path.isEmpty() && m_path.elementCount()) { + QPointF point = qTransform.map(m_path.currentPosition()); + moveTo(point); + } else +#endif + m_path = qTransform.map(m_path); +} + +float Path::length() +{ + return m_path.length(); +} + +FloatPoint Path::pointAtLength(float length, bool& ok) +{ + ok = (length >= 0 && length <= m_path.length()); + + qreal percent = m_path.percentAtLength(length); + QPointF point = m_path.pointAtPercent(percent); + + return point; +} + +float Path::normalAngleAtLength(float length, bool& ok) +{ + ok = (length >= 0 && length <= m_path.length()); + + qreal percent = m_path.percentAtLength(length); + qreal angle = m_path.angleAtPercent(percent); + + return angle; +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/PatternQt.cpp b/Source/WebCore/platform/graphics/qt/PatternQt.cpp new file mode 100644 index 0000000..af7b128 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/PatternQt.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Pattern.h" + +#include "AffineTransform.h" +#include "GraphicsContext.h" + +namespace WebCore { + +QBrush Pattern::createPlatformPattern(const AffineTransform&) const +{ + QPixmap* pixmap = tileImage()->nativeImageForCurrentFrame(); + if (!pixmap) + return QBrush(); + + // Qt merges patter space and user space itself + QBrush brush(*pixmap); + brush.setTransform(m_patternSpaceTransformation); + + return brush; +} + +} diff --git a/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp b/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp new file mode 100644 index 0000000..47ddf02 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2008 Holger Hans Peter Freyther + + 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 + along 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. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ + +#include "config.h" +#include "SimpleFontData.h" + +#include <QFontMetrics> + +namespace WebCore { + +void SimpleFontData::determinePitch() +{ + m_treatAsFixedPitch = m_platformData.font().fixedPitch(); +} + +bool SimpleFontData::containsCharacters(const UChar*, int) const +{ + return true; +} + +void SimpleFontData::platformInit() +{ + if (!m_platformData.size()) { + m_ascent = 0; + m_descent = 0; + m_lineGap = 0; + m_lineSpacing = 0; + m_avgCharWidth = 0; + m_maxCharWidth = 0; + m_xHeight = 0; + m_unitsPerEm = 0; + return; + } + + QFontMetrics fm(m_platformData.font()); + + m_ascent = fm.ascent(); + m_descent = fm.descent(); + m_lineSpacing = fm.lineSpacing(); + m_xHeight = fm.xHeight(); + m_spaceWidth = fm.width(QLatin1Char(' ')); + m_lineGap = fm.leading(); +} + +void SimpleFontData::platformGlyphInit() +{ + if (!m_platformData.size()) + return; + m_spaceGlyph = 0; + m_adjustedSpaceWidth = m_spaceWidth; + determinePitch(); + m_missingGlyphData.fontData = this; + m_missingGlyphData.glyph = 0; +} + +void SimpleFontData::platformCharWidthInit() +{ + if (!m_platformData.size()) + return; + QFontMetrics fm(m_platformData.font()); + m_avgCharWidth = fm.averageCharWidth(); + m_maxCharWidth = fm.maxWidth(); +} + +void SimpleFontData::platformDestroy() +{ +} + +} diff --git a/Source/WebCore/platform/graphics/qt/StillImageQt.cpp b/Source/WebCore/platform/graphics/qt/StillImageQt.cpp new file mode 100644 index 0000000..3b08995 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/StillImageQt.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StillImageQt.h" + +#include "ContextShadow.h" +#include "GraphicsContext.h" +#include "IntSize.h" + +#include <QPainter> + +namespace WebCore { + +StillImage::StillImage(const QPixmap& pixmap) + : m_pixmap(new QPixmap(pixmap)) + , m_ownsPixmap(true) +{} + +StillImage::StillImage(const QPixmap* pixmap) + : m_pixmap(pixmap) + , m_ownsPixmap(false) +{} + +StillImage::~StillImage() +{ + if (m_ownsPixmap) + delete m_pixmap; +} + +IntSize StillImage::size() const +{ + return IntSize(m_pixmap->width(), m_pixmap->height()); +} + +NativeImagePtr StillImage::nativeImageForCurrentFrame() +{ + return const_cast<NativeImagePtr>(m_pixmap); +} + +void StillImage::draw(GraphicsContext* ctxt, const FloatRect& dst, + const FloatRect& src, ColorSpace, CompositeOperator op) +{ + if (m_pixmap->isNull()) + return; + + FloatRect normalizedSrc = src.normalized(); + FloatRect normalizedDst = dst.normalized(); + + CompositeOperator previousOperator = ctxt->compositeOperation(); + ctxt->setCompositeOperation(op); + + ContextShadow* shadow = ctxt->contextShadow(); + if (shadow->m_type != ContextShadow::NoShadow) { + QPainter* shadowPainter = shadow->beginShadowLayer(ctxt, normalizedDst); + if (shadowPainter) { + shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255); + shadowPainter->drawPixmap(normalizedDst, *m_pixmap, normalizedSrc); + shadow->endShadowLayer(ctxt); + } + } + + ctxt->platformContext()->drawPixmap(normalizedDst, *m_pixmap, normalizedSrc); + ctxt->setCompositeOperation(previousOperator); +} + +} diff --git a/Source/WebCore/platform/graphics/qt/StillImageQt.h b/Source/WebCore/platform/graphics/qt/StillImageQt.h new file mode 100644 index 0000000..58071d9 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/StillImageQt.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * 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. + */ + +#ifndef StillImageQt_h +#define StillImageQt_h + +#include "Image.h" + +namespace WebCore { + + class StillImage : public Image { + public: + static PassRefPtr<StillImage> create(const QPixmap& pixmap) + { + return adoptRef(new StillImage(pixmap)); + } + + static PassRefPtr<StillImage> createForRendering(const QPixmap* pixmap) + { + return adoptRef(new StillImage(pixmap)); + } + + // FIXME: StillImages are underreporting decoded sizes and will be unable + // to prune because these functions are not implemented yet. + virtual void destroyDecodedData(bool destroyAll = true) { Q_UNUSED(destroyAll); } + virtual unsigned decodedSize() const { return 0; } + + virtual IntSize size() const; + virtual NativeImagePtr nativeImageForCurrentFrame(); + virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator); + + private: + StillImage(const QPixmap& pixmap); + StillImage(const QPixmap* pixmap); + ~StillImage(); + + const QPixmap* m_pixmap; + bool m_ownsPixmap; + }; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/qt/TextureMapperQt.cpp b/Source/WebCore/platform/graphics/qt/TextureMapperQt.cpp new file mode 100644 index 0000000..6fdd7df --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/TextureMapperQt.cpp @@ -0,0 +1,214 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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" +#include "TextureMapperQt.h" + +#include <QtCore/qdebug.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/qpixmap.h> + +#ifdef QT_OPENGL_LIB +# include "opengl/TextureMapperGL.h" +#endif + +namespace WebCore { + +void BitmapTextureQt::destroy() +{ + if (m_pixmap.paintingActive()) + qFatal("Destroying an active pixmap"); + m_pixmap = QPixmap(); +} + +void BitmapTextureQt::reset(const IntSize& size, bool isOpaque) +{ + BitmapTexture::reset(size, isOpaque); + + if (size.width() > m_pixmap.size().width() || size.height() > m_pixmap.size().height() || m_pixmap.isNull()) + m_pixmap = QPixmap(size.width(), size.height()); + if (!isOpaque) + m_pixmap.fill(Qt::transparent); +} + +PlatformGraphicsContext* BitmapTextureQt::beginPaint(const IntRect& dirtyRect) +{ + m_painter.begin(&m_pixmap); + TextureMapperQt::initialize(&m_painter); + m_painter.setCompositionMode(QPainter::CompositionMode_Clear); + m_painter.fillRect(QRect(dirtyRect), Qt::transparent); + m_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + return &m_painter; +} + +void BitmapTextureQt::endPaint() +{ + m_painter.end(); +} + +bool BitmapTextureQt::save(const String& path) +{ + return m_pixmap.save(path, "PNG"); +} + +void BitmapTextureQt::setContentsToImage(Image* image) +{ + if (!image) + return; + const QPixmap* pixmap = image->nativeImageForCurrentFrame(); + if (!pixmap) + return; + BitmapTexture::reset(pixmap->size(), !pixmap->hasAlphaChannel()); + m_pixmap = *pixmap; +} + +void BitmapTextureQt::pack() +{ + if (m_pixmap.isNull()) + return; + + m_image = m_pixmap.toImage(); + m_pixmap = QPixmap(); + m_isPacked = true; +} + +void BitmapTextureQt::unpack() +{ + m_isPacked = false; + if (m_image.isNull()) + return; + + m_pixmap = QPixmap::fromImage(m_image); + m_image = QImage(); +} + +void TextureMapperQt::setClip(const IntRect& rect) +{ + QPainter* painter = m_currentSurface ? &m_currentSurface->m_painter : m_painter; + painter->setClipRect(rect); +} + +TextureMapperQt::TextureMapperQt() + : m_currentSurface(0) +{ +} + +void TextureMapperQt::setGraphicsContext(GraphicsContext* context) +{ + m_painter = context->platformContext(); + initialize(m_painter); +} + +void TextureMapperQt::bindSurface(BitmapTexture* surface) +{ + if (m_currentSurface == surface) + return; + if (m_currentSurface) + m_currentSurface->m_painter.end(); + if (!surface) { + m_currentSurface = 0; + return; + } + BitmapTextureQt* surfaceQt = static_cast<BitmapTextureQt*>(surface); + if (!surfaceQt->m_painter.isActive()) + surfaceQt->m_painter.begin(&surfaceQt->m_pixmap); + m_currentSurface = surfaceQt; +} + + +void TextureMapperQt::drawTexture(const BitmapTexture& texture, const IntRect& targetRect, const TransformationMatrix& matrix, float opacity, const BitmapTexture* maskTexture) +{ + const BitmapTextureQt& textureQt = static_cast<const BitmapTextureQt&>(texture); + QPainter* painter = m_painter; + QPixmap pixmap = textureQt.m_pixmap; + if (m_currentSurface) + painter = &m_currentSurface->m_painter; + + if (maskTexture && maskTexture->isValid()) { + const BitmapTextureQt* mask = static_cast<const BitmapTextureQt*>(maskTexture); + QPixmap intermediatePixmap(pixmap.size()); + intermediatePixmap.fill(Qt::transparent); + QPainter maskPainter(&intermediatePixmap); + maskPainter.setCompositionMode(QPainter::CompositionMode_Source); + maskPainter.drawPixmap(0, 0, pixmap); + maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + maskPainter.drawPixmap(QRect(0, 0, pixmap.width(), pixmap.height()), mask->m_pixmap, mask->sourceRect()); + maskPainter.end(); + pixmap = intermediatePixmap; + } + + const qreal prevOpacity = painter->opacity(); + const QTransform prevTransform = painter->transform(); + painter->setOpacity(opacity); + painter->setTransform(matrix, true); + painter->drawPixmap(targetRect, pixmap, textureQt.sourceRect()); + painter->setTransform(prevTransform); + painter->setOpacity(prevOpacity); +} + +PassOwnPtr<TextureMapper> TextureMapper::create(GraphicsContext* context) +{ +#ifdef QT_OPENGL_LIB + if (context && context->platformContext()->paintEngine()->type() == QPaintEngine::OpenGL2) + return new TextureMapperGL; +#endif + return new TextureMapperQt; +} + +PassRefPtr<BitmapTexture> TextureMapperQt::createTexture() +{ + return adoptRef(new BitmapTextureQt()); +} + +BitmapTextureQt::BitmapTextureQt() + : m_isPacked(false) +{ + +} + +#ifdef QT_OPENGL_LIB +class RGBA32PremultimpliedBufferQt : public RGBA32PremultimpliedBuffer { +public: + virtual PlatformGraphicsContext* beginPaint(const IntRect& rect, bool opaque) + { + // m_image is only using during paint, it's safe to override it. + m_image = QImage(rect.size().width(), rect.size().height(), QImage::Format_ARGB32_Premultiplied); + if (!opaque) + m_image.fill(0); + m_painter.begin(&m_image); + TextureMapperQt::initialize(&m_painter); + m_painter.translate(-rect.x(), -rect.y()); + return &m_painter; + } + + virtual void endPaint() { m_painter.end(); } + virtual const void* data() const { return m_image.constBits(); } + +private: + QPainter m_painter; + QImage m_image; +}; + +PassRefPtr<RGBA32PremultimpliedBuffer> RGBA32PremultimpliedBuffer::create() +{ + return adoptRef(new RGBA32PremultimpliedBufferQt()); +} + +#endif +}; diff --git a/Source/WebCore/platform/graphics/qt/TextureMapperQt.h b/Source/WebCore/platform/graphics/qt/TextureMapperQt.h new file mode 100644 index 0000000..e17b968 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/TextureMapperQt.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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 "texmap/TextureMapper.h" + +#ifndef TextureMapperQt_h +#define TextureMapperQt_h + +namespace WebCore { + +class BitmapTextureQt : public BitmapTexture { + friend class TextureMapperQt; +public: + BitmapTextureQt(); + ~BitmapTextureQt() { destroy(); } + virtual void destroy(); + virtual IntSize size() const { return IntSize(m_pixmap.width(), m_pixmap.height()); } + virtual void reset(const IntSize&, bool opaque); + virtual PlatformGraphicsContext* beginPaint(const IntRect& dirtyRect); + virtual void endPaint(); + virtual void setContentsToImage(Image*); + virtual bool save(const String& path); + virtual bool isValid() const { return !m_pixmap.isNull() || !m_image.isNull(); } + IntRect sourceRect() const { return IntRect(0, 0, contentSize().width(), contentSize().height()); } + virtual void pack(); + virtual void unpack(); + virtual bool isPacked() const { return m_isPacked; } + +private: + QPainter m_painter; + QPixmap m_pixmap; + QImage m_image; + bool m_isPacked; +}; + +class TextureMapperQt : public TextureMapper { +public: + TextureMapperQt(); + + virtual void drawTexture(const BitmapTexture& texture, const IntRect& targetRect, const TransformationMatrix& matrix, float opacity, const BitmapTexture* maskTexture); + virtual void bindSurface(BitmapTexture* surface); + virtual void setClip(const IntRect&); + virtual void setGraphicsContext(GraphicsContext*); + virtual bool allowSurfaceForRoot() const { return false; } + virtual PassRefPtr<BitmapTexture> createTexture(); + + static void initialize(QPainter* painter) + { + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, false); + } + + static PassOwnPtr<TextureMapper> create() { return new TextureMapperQt; } + +private: + QPainter* m_painter; + RefPtr<BitmapTextureQt> m_currentSurface; +}; + +} +#endif diff --git a/Source/WebCore/platform/graphics/qt/TileQt.cpp b/Source/WebCore/platform/graphics/qt/TileQt.cpp new file mode 100644 index 0000000..096ce14 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/TileQt.cpp @@ -0,0 +1,179 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 + along 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" +#include "Tile.h" + +#if ENABLE(TILED_BACKING_STORE) + +#include "GraphicsContext.h" +#include "TiledBackingStore.h" +#include "TiledBackingStoreClient.h" +#include <QApplication> +#include <QObject> +#include <QPainter> +#include <QRegion> + +namespace WebCore { + +static const unsigned checkerSize = 16; +static const unsigned checkerColor1 = 0xff555555; +static const unsigned checkerColor2 = 0xffaaaaaa; + +static QPixmap& checkeredPixmap() +{ + static QPixmap* pixmap; + if (!pixmap) { + pixmap = new QPixmap(checkerSize, checkerSize); + QPainter painter(pixmap); + QColor color1(checkerColor1); + QColor color2(checkerColor2); + for (unsigned y = 0; y < checkerSize; y += checkerSize / 2) { + bool alternate = y % checkerSize; + for (unsigned x = 0; x < checkerSize; x += checkerSize / 2) { + painter.fillRect(x, y, checkerSize / 2, checkerSize / 2, alternate ? color1 : color2); + alternate = !alternate; + } + } + } + return *pixmap; +} + +Tile::Tile(TiledBackingStore* backingStore, const Coordinate& tileCoordinate) + : m_backingStore(backingStore) + , m_coordinate(tileCoordinate) + , m_rect(m_backingStore->tileRectForCoordinate(tileCoordinate)) + , m_buffer(0) + , m_backBuffer(0) + , m_dirtyRegion(new QRegion(m_rect)) +{ +} + +Tile::~Tile() +{ + delete m_buffer; + delete m_backBuffer; + delete m_dirtyRegion; +} + +bool Tile::isDirty() const +{ + return !m_dirtyRegion->isEmpty(); +} + +bool Tile::isReadyToPaint() const +{ + return m_buffer; +} + +void Tile::invalidate(const IntRect& dirtyRect) +{ + IntRect tileDirtyRect = intersection(dirtyRect, m_rect); + if (tileDirtyRect.isEmpty()) + return; + + *m_dirtyRegion += tileDirtyRect; +} + +void Tile::updateBackBuffer() +{ + if (m_buffer && !isDirty()) + return; + + if (!m_backBuffer) { + if (!m_buffer) { + m_backBuffer = new QPixmap(m_backingStore->m_tileSize.width(), m_backingStore->m_tileSize.height()); + m_backBuffer->fill(m_backingStore->m_client->tiledBackingStoreBackgroundColor()); + } else { + // Currently all buffers are updated synchronously at the same time so there is no real need + // to have separate back and front buffers. Just use the existing buffer. + m_backBuffer = m_buffer; + m_buffer = 0; + } + } + + QVector<QRect> dirtyRects = m_dirtyRegion->rects(); + *m_dirtyRegion = QRegion(); + + QPainter painter(m_backBuffer); + GraphicsContext context(&painter); + context.translate(-m_rect.x(), -m_rect.y()); + + int size = dirtyRects.size(); + for (int n = 0; n < size; ++n) { + context.save(); + IntRect rect = dirtyRects[n]; + context.clip(FloatRect(rect)); + context.scale(FloatSize(m_backingStore->m_contentsScale, m_backingStore->m_contentsScale)); + m_backingStore->m_client->tiledBackingStorePaint(&context, m_backingStore->mapToContents(rect)); + context.restore(); + } +} + +void Tile::swapBackBufferToFront() +{ + if (!m_backBuffer) + return; + delete m_buffer; + m_buffer = m_backBuffer; + m_backBuffer = 0; +} + +void Tile::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_buffer) + return; + + IntRect target = intersection(rect, m_rect); + IntRect source((target.x() - m_rect.x()), + (target.y() - m_rect.y()), + target.width(), + target.height()); + + context->platformContext()->drawPixmap(target, *m_buffer, source); +} + +void Tile::paintCheckerPattern(GraphicsContext* context, const FloatRect& target) +{ + QPainter* painter = context->platformContext(); + QTransform worldTransform = painter->worldTransform(); + qreal scaleX = worldTransform.m11(); + qreal scaleY = worldTransform.m22(); + + QRect targetViewRect = QRectF(target.x() * scaleX, + target.y() * scaleY, + target.width() * scaleX, + target.height() * scaleY).toAlignedRect(); + + QTransform adjustedTransform(1., worldTransform.m12(), worldTransform.m13(), + worldTransform.m21(), 1., worldTransform.m23(), + worldTransform.m31(), worldTransform.m32(), worldTransform.m33()); + painter->setWorldTransform(adjustedTransform); + + painter->drawTiledPixmap(targetViewRect, + checkeredPixmap(), + QPoint(targetViewRect.left() % checkerSize, + targetViewRect.top() % checkerSize)); + + painter->setWorldTransform(worldTransform); +} + +} + +#endif diff --git a/Source/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp b/Source/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp new file mode 100644 index 0000000..a5bc3c3 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AffineTransform.h" +#include "TransformationMatrix.h" + +#include "IntRect.h" +#include "FloatRect.h" + +namespace WebCore { + +TransformationMatrix::operator QTransform() const +{ + return QTransform(m11(), m12(), m14(), m21(), m22(), m24(), m41(), m42(), m44()); +} + +AffineTransform::operator QTransform() const +{ + return QTransform(a(), b(), c(), d(), e(), f()); +} + +} + +// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/qt/TransparencyLayer.h b/Source/WebCore/platform/graphics/qt/TransparencyLayer.h new file mode 100644 index 0000000..5b2f8b2 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/TransparencyLayer.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * Copyright (C) 2006 George Staikos <staikos@kde.org> + * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org> + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> + * + * 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. + */ + +#ifndef TransparencyLayer_h +#define TransparencyLayer_h + +#include <QPaintEngine> +#include <QPainter> +#include <QPixmap> + +namespace WebCore { + +struct TransparencyLayer : FastAllocBase { + TransparencyLayer(const QPainter* p, const QRect &rect, qreal opacity, QPixmap& alphaMask) + : pixmap(rect.width(), rect.height()) + , opacity(opacity) + , alphaMask(alphaMask) + , saveCounter(1) // see the comment for saveCounter + { + offset = rect.topLeft(); + pixmap.fill(Qt::transparent); + painter.begin(&pixmap); + painter.setRenderHints(p->renderHints()); + painter.translate(-offset); + painter.setPen(p->pen()); + painter.setBrush(p->brush()); + painter.setTransform(p->transform(), true); + painter.setOpacity(p->opacity()); + painter.setFont(p->font()); + if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff)) + painter.setCompositionMode(p->compositionMode()); + } + + TransparencyLayer() + { + } + + QPixmap pixmap; + QPoint offset; + QPainter painter; + qreal opacity; + // for clipToImageBuffer + QPixmap alphaMask; + // saveCounter is only used in combination with alphaMask + // otherwise, its value is unspecified + int saveCounter; +private: + TransparencyLayer(const TransparencyLayer &) {} + TransparencyLayer & operator=(const TransparencyLayer &) { return *this; } +}; + +} // namespace WebCore + +#endif // TransparencyLayer_h |