1/*
2 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include "QtWebPageSGNode.h"
23
24#include <QtGui/QPolygonF>
25#include <QtQuick/QQuickItem>
26#include <QtQuick/QQuickWindow>
27#include <QtQuick/QSGSimpleRectNode>
28#include <WebCore/CoordinatedGraphicsScene.h>
29#include <WebCore/TransformationMatrix.h>
30#include <private/qsgrendernode_p.h>
31
32using namespace WebCore;
33
34namespace WebKit {
35
36class ContentsSGNode : public QSGRenderNode {
37public:
38    ContentsSGNode(PassRefPtr<CoordinatedGraphicsScene> scene)
39        : m_scene(scene)
40    {
41        coordinatedGraphicsScene()->setActive(true);
42    }
43
44    virtual StateFlags changedStates()
45    {
46        return StateFlags(StencilState) | ColorState | BlendState;
47    }
48
49    virtual void render(const RenderState& state)
50    {
51        TransformationMatrix renderMatrix;
52        if (pageNode()->devicePixelRatio() != 1.0) {
53            renderMatrix.scale(pageNode()->devicePixelRatio());
54            if (matrix())
55                renderMatrix.multiply(*matrix());
56        } else if (matrix())
57            renderMatrix = *matrix();
58
59        // When rendering to an intermediate surface, Qt will
60        // mirror the projection matrix to fit on the destination coordinate system.
61        const QMatrix4x4* projection = state.projectionMatrix;
62        bool mirrored = projection && (*projection)(0, 0) * (*projection)(1, 1) - (*projection)(0, 1) * (*projection)(1, 0) > 0;
63
64        // FIXME: Support non-rectangular clippings.
65        coordinatedGraphicsScene()->paintToCurrentGLContext(renderMatrix, inheritedOpacity(), clipRect(), mirrored ? TextureMapper::PaintingMirrored : 0);
66    }
67
68    ~ContentsSGNode()
69    {
70        coordinatedGraphicsScene()->purgeGLResources();
71    }
72
73    const QtWebPageSGNode* pageNode() const
74    {
75        const QtWebPageSGNode* parent = static_cast<QtWebPageSGNode*>(this->parent());
76        ASSERT(parent);
77        return parent;
78    }
79
80    WebCore::CoordinatedGraphicsScene* coordinatedGraphicsScene() const { return m_scene.get(); }
81
82private:
83    QRectF clipRect() const
84    {
85        // Start with an invalid rect.
86        QRectF resultRect(0, 0, -1, -1);
87
88        for (const QSGClipNode* clip = clipList(); clip; clip = clip->clipList()) {
89            QMatrix4x4 clipMatrix;
90            if (pageNode()->devicePixelRatio() != 1.0) {
91                clipMatrix.scale(pageNode()->devicePixelRatio());
92                if (clip->matrix())
93                    clipMatrix *= (*clip->matrix());
94            } else if (clip->matrix())
95                clipMatrix = *clip->matrix();
96
97            QRectF currentClip;
98
99            if (clip->isRectangular())
100                currentClip = clipMatrix.mapRect(clip->clipRect());
101            else {
102                const QSGGeometry* geometry = clip->geometry();
103                // Assume here that clipNode has only coordinate data.
104                const QSGGeometry::Point2D* geometryPoints = geometry->vertexDataAsPoint2D();
105
106                // Clip region should be at least triangle to make valid clip.
107                if (geometry->vertexCount() < 3)
108                    continue;
109
110                QPolygonF polygon;
111
112                for (int i = 0; i < geometry->vertexCount(); i++)
113                    polygon.append(clipMatrix.map(QPointF(geometryPoints[i].x, geometryPoints[i].y)));
114                currentClip = polygon.boundingRect();
115            }
116
117            if (currentClip.isEmpty())
118                continue;
119
120            if (resultRect.isValid())
121                resultRect &= currentClip;
122            else
123                resultRect = currentClip;
124        }
125
126        return resultRect;
127    }
128
129    RefPtr<WebCore::CoordinatedGraphicsScene> m_scene;
130};
131
132QtWebPageSGNode::QtWebPageSGNode()
133    : m_contentsNode(0)
134    , m_backgroundNode(new QSGSimpleRectNode)
135    , m_devicePixelRatio(1)
136{
137    appendChildNode(m_backgroundNode);
138}
139
140void QtWebPageSGNode::setBackground(const QRectF& rect, const QColor& color)
141{
142    m_backgroundNode->setRect(rect);
143    m_backgroundNode->setColor(color);
144}
145
146void QtWebPageSGNode::setScale(float scale)
147{
148    QMatrix4x4 matrix;
149    matrix.scale(scale);
150    setMatrix(matrix);
151}
152
153void QtWebPageSGNode::setCoordinatedGraphicsScene(PassRefPtr<WebCore::CoordinatedGraphicsScene> scene)
154{
155    if (m_contentsNode && m_contentsNode->coordinatedGraphicsScene() == scene)
156        return;
157
158    delete m_contentsNode;
159    m_contentsNode = new ContentsSGNode(scene);
160    // This sets the parent node of the content to QtWebPageSGNode.
161    appendChildNode(m_contentsNode);
162}
163
164} // namespace WebKit
165