diff options
author | Steve Block <steveblock@google.com> | 2010-04-27 16:31:00 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-05-11 14:42:12 +0100 |
commit | dcc8cf2e65d1aa555cce12431a16547e66b469ee (patch) | |
tree | 92a8d65cd5383bca9749f5327fb5e440563926e6 /WebCore/platform/graphics/qt | |
parent | ccac38a6b48843126402088a309597e682f40fe6 (diff) | |
download | external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.zip external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.tar.gz external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.tar.bz2 |
Merge webkit.org at r58033 : Initial merge by git
Change-Id: If006c38561af287c50cd578d251629b51e4d8cd1
Diffstat (limited to 'WebCore/platform/graphics/qt')
17 files changed, 2984 insertions, 207 deletions
diff --git a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp index 0a1075f..0565deb 100644 --- a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp +++ b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -58,6 +58,9 @@ FontPlatformData::FontPlatformData(const FontDescription& description, const Ato 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(); m_data->size = font.pointSizeF(); diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index 9ff7c1a..974c179 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - Copyright (C) 2008 Holger Hans Peter Freyther + 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 @@ -134,7 +134,7 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float clip.adjust(dx1, dx2, dy1, dy2); } p->save(); - p->setClipRect(clip.toRect()); + p->setClipRect(clip.toRect(), Qt::IntersectClip); QPointF pt(point.x(), point.y() - ascent); if (hasShadow) { p->save(); @@ -169,17 +169,24 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float p->drawText(pt, string, flags, run.padding()); } -float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*) const +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const { if (!run.length()) return 0; + if (run.length() == 1 && treatAsSpace(run[0])) + return QFontMetrics(font()).width(run[0]) - m_wordSpacing + run.padding(); + String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); QString string = fromRawDataWithoutRef(sanitized); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + int w = int(line.horizontalAdvance()); +#else int w = int(line.naturalTextWidth()); +#endif // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does) if (treatAsSpace(run[0])) w -= m_wordSpacing; @@ -216,8 +223,10 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& QFont Font::font() const { QFont f = primaryFont()->getQtFont(); - f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing); - f.setWordSpacing(m_wordSpacing); + if (m_letterSpacing != 0) + f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing); + if (m_wordSpacing != 0) + f.setWordSpacing(m_wordSpacing); return f; } diff --git a/WebCore/platform/graphics/qt/GradientQt.cpp b/WebCore/platform/graphics/qt/GradientQt.cpp index 9b9acc2..8b9e2d7 100644 --- a/WebCore/platform/graphics/qt/GradientQt.cpp +++ b/WebCore/platform/graphics/qt/GradientQt.cpp @@ -51,6 +51,8 @@ QGradient* Gradient::platformGradient() else m_gradient = new QLinearGradient(m_p0.x(), m_p0.y(), m_p1.x(), m_p1.y()); + sortStopsIfNecessary(); + QColor stopColor; Vector<ColorStop>::iterator stopIterator = m_stops.begin(); qreal lastStop(0.0); @@ -64,9 +66,17 @@ QGradient* Gradient::platformGradient() if (m_radial && m_r0) lastStop = m_r0 / m_r1 + lastStop * (1.0f - m_r0 / m_r1); m_gradient->setColorAt(lastStop, 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); diff --git a/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp b/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp new file mode 100644 index 0000000..b0dd289 --- /dev/null +++ b/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp @@ -0,0 +1,1651 @@ +/* + 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 "CanvasObject.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include "HostWindow.h" +#include "ImageBuffer.h" +#include "NotImplemented.h" +#include "QWebPageClient.h" +#include "WebGLActiveInfo.h" +#include "WebGLArray.h" +#include "WebGLBuffer.h" +#include "WebGLFloatArray.h" +#include "WebGLFramebuffer.h" +#include "WebGLIntArray.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLRenderingContext.h" +#include "WebGLShader.h" +#include "WebGLTexture.h" +#include "WebGLUnsignedByteArray.h" +#include <QAbstractScrollArea> +#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 + +typedef ptrdiff_t GLsizeiptrType; +typedef ptrdiff_t GLintptrType; + +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 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; } + + + + 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; + 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; + QGLWidget* m_glWidget; + GLuint m_texture; + GLuint m_mainFbo; + GLuint m_currentFbo; + GLuint m_depthBuffer; + QImage m_pixels; + ListHashSet<unsigned long> m_syntheticErrors; + +private: + + QGLWidget* getOwnerGLWidget(QWebPageClient* webPageClient); + 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 + +GraphicsContext3DInternal::GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow) + : m_attrs(attrs) + , 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); + 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) +{ + OwnPtr<GraphicsContext3D> context(new GraphicsContext3D(attrs, hostWindow)); + return context->m_internal ? context.release() : 0; +} + +GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow) + : 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::beginPaint(WebGLRenderingContext* context) +{ + m_internal->m_glWidget->makeCurrent(); + + HTMLCanvasElement* canvas = context->canvas(); + ImageBuffer* imageBuffer = canvas->buffer(); + + 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()); + + QPainter* p = imageBuffer->context()->platformContext(); + p->drawImage(/* x */ 0, /* y */ 0, m_internal->m_pixels.transformed(QMatrix().rotate(180))); + + m_internal->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_internal->m_currentFbo); +} + +void GraphicsContext3D::endPaint() +{ +} + +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(); +} + +void GraphicsContext3D::activeTexture(unsigned long texture) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->activeTexture(texture); +} + +void GraphicsContext3D::attachShader(WebGLProgram* program, WebGLShader* shader) +{ + ASSERT(program); + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->attachShader((GLuint) program->object(), (GLuint) shader->object()); +} + +void GraphicsContext3D::bindAttribLocation(WebGLProgram* program, unsigned long index, const String& name) +{ + ASSERT(program); + m_internal->m_glWidget->makeCurrent(); + m_internal->bindAttribLocation((GLuint) program->object(), index, name.utf8().data()); +} + +void GraphicsContext3D::bindBuffer(unsigned long target, WebGLBuffer* buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bindBuffer(target, buffer ? (GLuint) buffer->object() : 0); +} + +void GraphicsContext3D::bindFramebuffer(unsigned long target, WebGLFramebuffer* buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->m_currentFbo = (buffer && buffer->object()) ? (GLuint) buffer->object() : m_internal->m_mainFbo; + m_internal->bindFramebuffer(target, m_internal->m_currentFbo); +} + +void GraphicsContext3D::bindRenderbuffer(unsigned long target, WebGLRenderbuffer* renderbuffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bindRenderbuffer(target, renderbuffer ? (GLuint) renderbuffer->object() : 0); +} + +void GraphicsContext3D::bindTexture(unsigned long target, WebGLTexture* texture) +{ + m_internal->m_glWidget->makeCurrent(); + glBindTexture(target, texture ? (GLuint) texture->object() : 0); +} + +void GraphicsContext3D::blendColor(double red, double green, double blue, double alpha) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendColor(static_cast<float>(red), static_cast<float>(green), static_cast<float>(blue), static_cast<float>(alpha)); +} + +void GraphicsContext3D::blendEquation(unsigned long mode) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendEquation(mode); +} + +void GraphicsContext3D::blendEquationSeparate(unsigned long modeRGB, unsigned long modeAlpha) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendEquationSeparate(modeRGB, modeAlpha); +} + +void GraphicsContext3D::blendFunc(unsigned long sfactor, unsigned long dfactor) +{ + m_internal->m_glWidget->makeCurrent(); + glBlendFunc(sfactor, dfactor); +} + +void GraphicsContext3D::blendFuncSeparate(unsigned long srcRGB, unsigned long dstRGB, unsigned long srcAlpha, unsigned long dstAlpha) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); +} + +void GraphicsContext3D::bufferData(unsigned long target, int size, unsigned long usage) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->bufferData(target, size, /* data */ 0, usage); +} + +void GraphicsContext3D::bufferData(unsigned long target, WebGLArray* array, unsigned long usage) +{ + if (!array || !array->length()) + return; + + m_internal->m_glWidget->makeCurrent(); + m_internal->bufferData(target, array->byteLength(), array->baseAddress(), usage); +} + +void GraphicsContext3D::bufferSubData(unsigned long target, long offset, WebGLArray* array) +{ + if (!array || !array->length()) + return; + + m_internal->m_glWidget->makeCurrent(); + m_internal->bufferSubData(target, offset, array->byteLength(), array->baseAddress()); +} + +unsigned long GraphicsContext3D::checkFramebufferStatus(unsigned long target) +{ + m_internal->m_glWidget->makeCurrent(); + return m_internal->checkFramebufferStatus(target); +} + +void GraphicsContext3D::clearColor(double r, double g, double b, double a) +{ + m_internal->m_glWidget->makeCurrent(); + glClearColor(static_cast<float>(r), static_cast<float>(g), static_cast<float>(b), static_cast<float>(a)); +} + +void GraphicsContext3D::clear(unsigned long mask) +{ + m_internal->m_glWidget->makeCurrent(); + glClear(mask); +} + +void GraphicsContext3D::clearDepth(double depth) +{ + m_internal->m_glWidget->makeCurrent(); +#if defined(QT_OPENGL_ES_2) + glClearDepthf(depth); +#else + glClearDepth(depth); +#endif +} + +void GraphicsContext3D::clearStencil(long s) +{ + m_internal->m_glWidget->makeCurrent(); + glClearStencil(s); +} + +void GraphicsContext3D::colorMask(bool red, bool green, bool blue, bool alpha) +{ + m_internal->m_glWidget->makeCurrent(); + glColorMask(red, green, blue, alpha); +} + +void GraphicsContext3D::compileShader(WebGLShader* shader) +{ + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->compileShader((GLuint) shader->object()); +} + +void GraphicsContext3D::copyTexImage2D(unsigned long target, long level, unsigned long internalformat, long x, long y, unsigned long width, unsigned long height, long border) +{ + m_internal->m_glWidget->makeCurrent(); + glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); +} + +void GraphicsContext3D::copyTexSubImage2D(unsigned long target, long level, long xoffset, long yoffset, long x, long y, unsigned long width, unsigned long height) +{ + m_internal->m_glWidget->makeCurrent(); + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); +} + +void GraphicsContext3D::cullFace(unsigned long mode) +{ + m_internal->m_glWidget->makeCurrent(); + glCullFace(mode); +} + +void GraphicsContext3D::depthFunc(unsigned long func) +{ + m_internal->m_glWidget->makeCurrent(); + glDepthFunc(func); +} + +void GraphicsContext3D::depthMask(bool flag) +{ + m_internal->m_glWidget->makeCurrent(); + glDepthMask(flag); +} + +void GraphicsContext3D::depthRange(double zNear, double zFar) +{ + m_internal->m_glWidget->makeCurrent(); +#if defined(QT_OPENGL_ES_2) + glDepthRangef(zNear, zFar); +#else + glDepthRange(zNear, zFar); +#endif +} + +void GraphicsContext3D::detachShader(WebGLProgram* program, WebGLShader* shader) +{ + ASSERT(program); + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->detachShader((GLuint) program->object(), (GLuint) shader->object()); +} + +void GraphicsContext3D::disable(unsigned long cap) +{ + m_internal->m_glWidget->makeCurrent(); + glDisable(cap); +} + +void GraphicsContext3D::disableVertexAttribArray(unsigned long index) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->disableVertexAttribArray(index); +} + +void GraphicsContext3D::drawArrays(unsigned long mode, long first, long count) +{ + m_internal->m_glWidget->makeCurrent(); + glDrawArrays(mode, first, count); +} + +void GraphicsContext3D::drawElements(unsigned long mode, unsigned long count, unsigned long type, long offset) +{ + m_internal->m_glWidget->makeCurrent(); + glDrawElements(mode, count, type, reinterpret_cast<void*>(static_cast<intptr_t>(offset))); +} + +void GraphicsContext3D::enable(unsigned long cap) +{ + m_internal->m_glWidget->makeCurrent(); + glEnable(cap); +} + +void GraphicsContext3D::enableVertexAttribArray(unsigned long 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(unsigned long target, unsigned long attachment, unsigned long renderbuffertarget, WebGLRenderbuffer* buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->framebufferRenderbuffer(target, attachment, renderbuffertarget, buffer ? (GLuint) buffer->object() : 0); +} + +void GraphicsContext3D::framebufferTexture2D(unsigned long target, unsigned long attachment, unsigned long textarget, WebGLTexture* texture, long level) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->framebufferTexture2D(target, attachment, textarget, texture ? (GLuint) texture->object() : 0, level); +} + +void GraphicsContext3D::frontFace(unsigned long mode) +{ + m_internal->m_glWidget->makeCurrent(); + glFrontFace(mode); +} + +void GraphicsContext3D::generateMipmap(unsigned long target) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->generateMipmap(target); +} + +bool GraphicsContext3D::getActiveAttrib(WebGLProgram* program, unsigned long index, ActiveInfo& info) +{ + if (!program->object()) { + synthesizeGLError(INVALID_VALUE); + return false; + } + + m_internal->m_glWidget->makeCurrent(); + + GLint maxLength; + m_internal->getProgramiv(static_cast<GLuint>(program->object()), GraphicsContext3D::ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength); + + GLchar* name = (GLchar*) fastMalloc(maxLength); + GLsizei nameLength; + GLint size; + GLenum type; + + m_internal->getActiveAttrib(static_cast<GLuint>(program->object()), 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(WebGLProgram* program, unsigned long index, ActiveInfo& info) +{ + if (!program->object()) { + synthesizeGLError(INVALID_VALUE); + return false; + } + + m_internal->m_glWidget->makeCurrent(); + + GLint maxLength; + m_internal->getProgramiv(static_cast<GLuint>(program->object()), GraphicsContext3D::ACTIVE_UNIFORM_MAX_LENGTH, &maxLength); + + GLchar* name = (GLchar*) fastMalloc(maxLength); + GLsizei nameLength; + GLint size; + GLenum type; + + m_internal->getActiveUniform(static_cast<GLuint>(program->object()), 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(WebGLProgram* program, const String& name) +{ + if (!program) + return -1; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->getAttribLocation((GLuint) program->object(), name.utf8().data()); +} + +GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes() +{ + return m_internal->m_attrs; +} + +unsigned long GraphicsContext3D::getError() +{ + if (m_internal->m_syntheticErrors.size() > 0) { + ListHashSet<unsigned long>::iterator iter = m_internal->m_syntheticErrors.begin(); + unsigned long err = *iter; + m_internal->m_syntheticErrors.remove(iter); + return err; + } + + m_internal->m_glWidget->makeCurrent(); + return glGetError(); +} + +String GraphicsContext3D::getString(unsigned long name) +{ + m_internal->m_glWidget->makeCurrent(); + return String((const char*) glGetString(name)); +} + +void GraphicsContext3D::hint(unsigned long target, unsigned long mode) +{ + m_internal->m_glWidget->makeCurrent(); + glHint(target, mode); +} + +bool GraphicsContext3D::isBuffer(WebGLBuffer* buffer) +{ + if (!buffer) + return false; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isBuffer((GLuint) buffer->object()); +} + +bool GraphicsContext3D::isEnabled(unsigned long cap) +{ + m_internal->m_glWidget->makeCurrent(); + return glIsEnabled(cap); +} + +bool GraphicsContext3D::isFramebuffer(WebGLFramebuffer* framebuffer) +{ + if (!framebuffer) + return false; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isFramebuffer((GLuint) framebuffer->object()); +} + +bool GraphicsContext3D::isProgram(WebGLProgram* program) +{ + if (!program) + return false; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isProgram((GLuint) program->object()); +} + +bool GraphicsContext3D::isRenderbuffer(WebGLRenderbuffer* renderbuffer) +{ + if (!renderbuffer) + return false; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isRenderbuffer((GLuint) renderbuffer->object()); +} + +bool GraphicsContext3D::isShader(WebGLShader* shader) +{ + if (!shader) + return false; + + m_internal->m_glWidget->makeCurrent(); + return m_internal->isShader((GLuint) shader->object()); +} + +bool GraphicsContext3D::isTexture(WebGLTexture* texture) +{ + if (!texture) + return false; + + m_internal->m_glWidget->makeCurrent(); + return glIsTexture((GLuint) texture->object()); +} + +void GraphicsContext3D::lineWidth(double width) +{ + m_internal->m_glWidget->makeCurrent(); + glLineWidth(static_cast<float>(width)); +} + +void GraphicsContext3D::linkProgram(WebGLProgram* program) +{ + ASSERT(program); + m_internal->m_glWidget->makeCurrent(); + m_internal->linkProgram((GLuint) program->object()); +} + +void GraphicsContext3D::pixelStorei(unsigned long paramName, long param) +{ + m_internal->m_glWidget->makeCurrent(); + glPixelStorei(paramName, param); +} + +void GraphicsContext3D::polygonOffset(double factor, double units) +{ + m_internal->m_glWidget->makeCurrent(); + glPolygonOffset(static_cast<float>(factor), static_cast<float>(units)); +} + +void GraphicsContext3D::readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type, void* data) +{ + m_internal->m_glWidget->makeCurrent(); + + if (type != GraphicsContext3D::UNSIGNED_BYTE || format != GraphicsContext3D::RGBA) + return; + + glReadPixels(x, y, width, height, format, type, (GLvoid*) data); +} + +void GraphicsContext3D::releaseShaderCompiler() +{ + m_internal->m_glWidget->makeCurrent(); + notImplemented(); +} + +void GraphicsContext3D::renderbufferStorage(unsigned long target, unsigned long internalformat, unsigned long width, unsigned long height) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->renderbufferStorage(target, internalformat, width, height); +} + +void GraphicsContext3D::sampleCoverage(double value, bool invert) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->sampleCoverage(static_cast<float>(value), invert); +} + +void GraphicsContext3D::scissor(long x, long y, unsigned long width, unsigned long height) +{ + m_internal->m_glWidget->makeCurrent(); + glScissor(x, y, width, height); +} + +void GraphicsContext3D::shaderSource(WebGLShader* shader, const String& source) +{ + ASSERT(shader); + + m_internal->m_glWidget->makeCurrent(); + + CString sourceCS = source.utf8(); + const char* data = sourceCS.data(); + int length = source.length(); + m_internal->shaderSource((GLuint) shader->object(), /* count */ 1, &data, &length); +} + +void GraphicsContext3D::stencilFunc(unsigned long func, long ref, unsigned long mask) +{ + m_internal->m_glWidget->makeCurrent(); + glStencilFunc(func, ref, mask); +} + +void GraphicsContext3D::stencilFuncSeparate(unsigned long face, unsigned long func, long ref, unsigned long mask) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->stencilFuncSeparate(face, func, ref, mask); +} + +void GraphicsContext3D::stencilMask(unsigned long mask) +{ + m_internal->m_glWidget->makeCurrent(); + glStencilMask(mask); +} + +void GraphicsContext3D::stencilMaskSeparate(unsigned long face, unsigned long mask) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->stencilMaskSeparate(face, mask); +} + +void GraphicsContext3D::stencilOp(unsigned long fail, unsigned long zfail, unsigned long zpass) +{ + m_internal->m_glWidget->makeCurrent(); + glStencilOp(fail, zfail, zpass); +} + +void GraphicsContext3D::stencilOpSeparate(unsigned long face, unsigned long fail, unsigned long zfail, unsigned long zpass) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->stencilOpSeparate(face, fail, zfail, zpass); +} + +void GraphicsContext3D::texParameterf(unsigned target, unsigned paramName, float value) +{ + m_internal->m_glWidget->makeCurrent(); + glTexParameterf(target, paramName, static_cast<float>(value)); +} + +void GraphicsContext3D::texParameteri(unsigned target, unsigned paramName, int value) +{ + m_internal->m_glWidget->makeCurrent(); + glTexParameteri(target, paramName, static_cast<float>(value)); +} + +void GraphicsContext3D::uniform1f(long location, float v0) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1f(location, v0); +} + +void GraphicsContext3D::uniform1fv(long location, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1fv(location, size, array); +} + +void GraphicsContext3D::uniform2f(long location, float v0, float v1) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2f(location, v0, v1); +} + +void GraphicsContext3D::uniform2fv(long location, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2fv(location, size, array); +} + +void GraphicsContext3D::uniform3f(long location, float v0, float v1, float v2) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3f(location, v0, v1, v2); +} + +void GraphicsContext3D::uniform3fv(long location, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3fv(location, size, array); +} + +void GraphicsContext3D::uniform4f(long location, float v0, float v1, float v2, float v3) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4f(location, v0, v1, v2, v3); +} + +void GraphicsContext3D::uniform4fv(long location, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4fv(location, size, array); +} + +void GraphicsContext3D::uniform1i(long location, int v0) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1i(location, v0); +} + +void GraphicsContext3D::uniform1iv(long location, int* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform1iv(location, size, array); +} + +void GraphicsContext3D::uniform2i(long location, int v0, int v1) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2i(location, v0, v1); +} + +void GraphicsContext3D::uniform2iv(long location, int* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform2iv(location, size, array); +} + +void GraphicsContext3D::uniform3i(long location, int v0, int v1, int v2) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3i(location, v0, v1, v2); +} + +void GraphicsContext3D::uniform3iv(long location, int* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform3iv(location, size, array); +} + +void GraphicsContext3D::uniform4i(long location, int v0, int v1, int v2, int v3) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4i(location, v0, v1, v2, v3); +} + +void GraphicsContext3D::uniform4iv(long location, int* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniform4iv(location, size, array); +} + +void GraphicsContext3D::uniformMatrix2fv(long location, bool transpose, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniformMatrix2fv(location, size, transpose, array); +} + +void GraphicsContext3D::uniformMatrix3fv(long location, bool transpose, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniformMatrix3fv(location, size, transpose, array); +} + +void GraphicsContext3D::uniformMatrix4fv(long location, bool transpose, float* array, int size) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->uniformMatrix4fv(location, size, transpose, array); +} + +void GraphicsContext3D::useProgram(WebGLProgram* program) +{ + ASSERT(program); + + m_internal->m_glWidget->makeCurrent(); + m_internal->useProgram((GLuint) program->object()); +} + +void GraphicsContext3D::validateProgram(WebGLProgram* program) +{ + ASSERT(program); + + m_internal->m_glWidget->makeCurrent(); + m_internal->validateProgram((GLuint) program->object()); +} + +void GraphicsContext3D::vertexAttrib1f(unsigned long indx, float v0) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib1f(indx, v0); +} + +void GraphicsContext3D::vertexAttrib1fv(unsigned long indx, float* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib1fv(indx, array); +} + +void GraphicsContext3D::vertexAttrib2f(unsigned long indx, float v0, float v1) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib2f(indx, v0, v1); +} + +void GraphicsContext3D::vertexAttrib2fv(unsigned long indx, float* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib2fv(indx, array); +} + +void GraphicsContext3D::vertexAttrib3f(unsigned long indx, float v0, float v1, float v2) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib3f(indx, v0, v1, v2); +} + +void GraphicsContext3D::vertexAttrib3fv(unsigned long indx, float* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib3fv(indx, array); +} + +void GraphicsContext3D::vertexAttrib4f(unsigned long indx, float v0, float v1, float v2, float v3) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib4f(indx, v0, v1, v2, v3); +} + +void GraphicsContext3D::vertexAttrib4fv(unsigned long indx, float* array) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttrib4fv(indx, array); +} + +void GraphicsContext3D::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, unsigned long stride, unsigned long offset) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->vertexAttribPointer(indx, size, type, normalized, stride, reinterpret_cast<void*>(static_cast<intptr_t>(offset))); +} + +void GraphicsContext3D::viewport(long x, long y, unsigned long width, unsigned long height) +{ + m_internal->m_glWidget->makeCurrent(); + glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height)); +} + +void GraphicsContext3D::getBooleanv(unsigned long paramName, unsigned char* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetBooleanv(paramName, value); +} + +void GraphicsContext3D::getBufferParameteriv(unsigned long target, unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getBufferParameteriv(target, paramName, value); +} + +void GraphicsContext3D::getFloatv(unsigned long paramName, float* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetFloatv(paramName, value); +} + +void GraphicsContext3D::getFramebufferAttachmentParameteriv(unsigned long target, unsigned long attachment, unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getFramebufferAttachmentParameteriv(target, attachment, paramName, value); +} + +void GraphicsContext3D::getIntegerv(unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetIntegerv(paramName, value); +} + +void GraphicsContext3D::getProgramiv(WebGLProgram* program, unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getProgramiv((GLuint) program->object(), paramName, value); +} + +String GraphicsContext3D::getProgramInfoLog(WebGLProgram* program) +{ + m_internal->m_glWidget->makeCurrent(); + + GLint length; + m_internal->getProgramiv((GLuint) program->object(), GraphicsContext3D::INFO_LOG_LENGTH, &length); + + GLsizei size; + + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + + m_internal->getProgramInfoLog((GLuint) program->object(), length, &size, info); + + String result(info); + fastFree(info); + + return result; +} + +void GraphicsContext3D::getRenderbufferParameteriv(unsigned long target, unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getRenderbufferParameteriv(target, paramName, value); +} + +void GraphicsContext3D::getShaderiv(WebGLShader* shader, unsigned long paramName, int* value) +{ + ASSERT(shader); + m_internal->m_glWidget->makeCurrent(); + m_internal->getShaderiv((GLuint) shader->object(), paramName, value); +} + +String GraphicsContext3D::getShaderInfoLog(WebGLShader* shader) +{ + m_internal->m_glWidget->makeCurrent(); + + GLint length; + m_internal->getShaderiv((GLuint) shader->object(), GraphicsContext3D::INFO_LOG_LENGTH, &length); + + GLsizei size; + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + + m_internal->getShaderInfoLog((GLuint) shader->object(), length, &size, info); + + String result(info); + fastFree(info); + + return result; +} + +String GraphicsContext3D::getShaderSource(WebGLShader* shader) +{ + m_internal->m_glWidget->makeCurrent(); + + GLint length; + m_internal->getShaderiv((GLuint) shader->object(), GraphicsContext3D::SHADER_SOURCE_LENGTH, &length); + + GLsizei size; + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + + m_internal->getShaderSource((GLuint) shader->object(), length, &size, info); + + String result(info); + fastFree(info); + + return result; +} + +void GraphicsContext3D::getTexParameterfv(unsigned long target, unsigned long paramName, float* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetTexParameterfv(target, paramName, value); +} + +void GraphicsContext3D::getTexParameteriv(unsigned long target, unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + glGetTexParameteriv(target, paramName, value); +} + +void GraphicsContext3D::getUniformfv(WebGLProgram* program, long location, float* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getUniformfv((GLuint) program->object(), location, value); +} + +void GraphicsContext3D::getUniformiv(WebGLProgram* program, long location, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getUniformiv((GLuint) program->object(), location, value); +} + +long GraphicsContext3D::getUniformLocation(WebGLProgram* program, const String& name) +{ + ASSERT(program); + + m_internal->m_glWidget->makeCurrent(); + return m_internal->getUniformLocation((GLuint) program->object(), name.utf8().data()); +} + +void GraphicsContext3D::getVertexAttribfv(unsigned long index, unsigned long paramName, float* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getVertexAttribfv(index, paramName, value); +} + +void GraphicsContext3D::getVertexAttribiv(unsigned long index, unsigned long paramName, int* value) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->getVertexAttribiv(index, paramName, value); +} + +long GraphicsContext3D::getVertexAttribOffset(unsigned long index, unsigned long paramName) +{ + m_internal->m_glWidget->makeCurrent(); + + void* pointer; + m_internal->getVertexAttribPointerv(index, paramName, &pointer); + return reinterpret_cast<long>(pointer); +} + +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, void* pixels) +{ + glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + return 0; +} + +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, bool flipY, bool premultiplyAlpha) +{ + ASSERT(image); + + m_internal->m_glWidget->makeCurrent(); + + Vector<uint8_t> imageData; + GLuint format; + GLuint internalFormat; + + if (!extractImageData(image, flipY, premultiplyAlpha, imageData, &format, &internalFormat)) { + LOG_ERROR("GraphicsContext3D::texImage2D: could not extract Image data"); + return -1; + } + + glTexImage2D(target, level, internalFormat, image->width(), image->height(), + /* border */ 0, format, GraphicsContext3D::UNSIGNED_BYTE, imageData.data()); + + return 0; +} + +int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, unsigned format, unsigned type, void* pixels) +{ + glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels); + return 0; +} + +int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, Image* image, bool flipY, bool premultiplyAlpha) +{ + ASSERT(image); + + Vector<uint8_t> imageData; + GLuint format; + GLuint internalFormat; + + if (!extractImageData(image, flipY, premultiplyAlpha, imageData, &format, &internalFormat)) { + LOG_ERROR("GraphicsContext3D::texSubImage2D: could not extract Image data"); + return -1; + } + + glTexSubImage2D(target, level, xoff, yoff, image->width(), image->height(), + format, GraphicsContext3D::UNSIGNED_BYTE, imageData.data()); + + return 0; +} + +unsigned GraphicsContext3D::createBuffer() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle; + m_internal->genBuffers(/* count */ 1, &handle); + return handle; +} + +unsigned GraphicsContext3D::createFramebuffer() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle; + m_internal->genFramebuffers(/* count */ 1, &handle); + return handle; +} + +unsigned GraphicsContext3D::createProgram() +{ + m_internal->m_glWidget->makeCurrent(); + return m_internal->createProgram(); +} + +unsigned GraphicsContext3D::createRenderbuffer() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle; + m_internal->genRenderbuffers(/* count */ 1, &handle); + return handle; +} + +unsigned GraphicsContext3D::createShader(unsigned long type) +{ + m_internal->m_glWidget->makeCurrent(); + return m_internal->createShader((type == FRAGMENT_SHADER) ? GraphicsContext3D::FRAGMENT_SHADER : GraphicsContext3D::VERTEX_SHADER); +} + +unsigned GraphicsContext3D::createTexture() +{ + m_internal->m_glWidget->makeCurrent(); + GLuint handle; + glGenTextures(1, &handle); + return handle; +} + +void GraphicsContext3D::deleteBuffer(unsigned buffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteBuffers(1, &buffer); +} + +void GraphicsContext3D::deleteFramebuffer(unsigned framebuffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteFramebuffers(1, &framebuffer); +} + +void GraphicsContext3D::deleteProgram(unsigned program) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteProgram(program); +} + +void GraphicsContext3D::deleteRenderbuffer(unsigned renderbuffer) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteRenderbuffers(1, &renderbuffer); +} + +void GraphicsContext3D::deleteShader(unsigned shader) +{ + m_internal->m_glWidget->makeCurrent(); + m_internal->deleteShader(shader); +} + +void GraphicsContext3D::deleteTexture(unsigned texture) +{ + m_internal->m_glWidget->makeCurrent(); + glDeleteTextures(1, &texture); +} + +int GraphicsContext3D::sizeInBytes(int 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(unsigned long error) +{ + m_internal->m_syntheticErrors.add(error); +} + +bool GraphicsContext3D::getImageData(Image* image, + Vector<uint8_t>& outputVector, + bool premultiplyAlpha, + bool* hasAlphaChannel, + AlphaOp* neededAlphaOp, + unsigned int* format) +{ + QImage::Format imageFormat = (!premultiplyAlpha) ? + QImage::Format_ARGB32 : + QImage::Format_ARGB32_Premultiplied; + + QPixmap* nativePixmap = image->nativeImageForCurrentFrame(); + + *hasAlphaChannel = true; + *neededAlphaOp = kAlphaDoNothing; + *format = GraphicsContext3D::RGBA; + + QImage nativeImage = nativePixmap->toImage().convertToFormat(imageFormat); + outputVector.append(nativeImage.bits(), nativeImage.byteCount()); + + return true; +} + +} + +#endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 8bcda2e..edac268 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -312,14 +312,16 @@ void GraphicsContext::drawRect(const IntRect& rect) const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); - IntSize shadowSize; - int shadowBlur; - Color shadowColor; - if (getShadow(shadowSize, shadowBlur, shadowColor)) { - IntRect shadowRect = rect; - shadowRect.move(shadowSize.width(), shadowSize.height()); - shadowRect.inflate(static_cast<int>(p->pen().widthF())); - p->fillRect(shadowRect, QColor(shadowColor)); + if (m_common->state.shadowColor.isValid()) { + IntSize shadowSize; + int shadowBlur; + Color shadowColor; + if (getShadow(shadowSize, shadowBlur, shadowColor)) { + IntRect shadowRect = rect; + shadowRect.move(shadowSize.width(), shadowSize.height()); + shadowRect.inflate(static_cast<int>(p->pen().widthF())); + p->fillRect(shadowRect, QColor(shadowColor)); + } } p->drawRect(rect); @@ -410,7 +412,7 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) patternOffset = patWidth / 2; } else { if (remainder) - patternOffset = (patWidth - remainder)/2; + patternOffset = (patWidth - remainder) / 2; } } @@ -614,10 +616,20 @@ void GraphicsContext::fillRect(const FloatRect& rect) QPainter* p = m_data->p(); if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) { - drawBorderlessRectShadow(this, p, rect); + if (m_common->state.shadowColor.isValid()) + drawBorderlessRectShadow(this, p, rect); if (m_common->state.fillPattern) { AffineTransform affine; - p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine))); + FloatRect rectM(rect); + QBrush brush(m_common->state.fillPattern->createPlatformPattern(affine)); + QPixmap* image = m_common->state.fillPattern->tileImage()->nativeImageForCurrentFrame(); + + if (!m_common->state.fillPattern->repeatX() && image) + rectM.setWidth(image->width()); + if (!m_common->state.fillPattern->repeatY() && image) + rectM.setHeight(image->height()); + p->fillRect(rectM, brush); + } else if (m_common->state.fillGradient) { QBrush brush(*m_common->state.fillGradient->platformGradient()); brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform()); @@ -636,7 +648,8 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& c, ColorSpace m_data->solidColor.setColor(c); QPainter* p = m_data->p(); - drawBorderlessRectShadow(this, p, rect); + if (m_common->state.shadowColor.isValid()) + drawBorderlessRectShadow(this, p, rect); p->fillRect(rect, m_data->solidColor); } @@ -1006,11 +1019,11 @@ void GraphicsContext::rotate(float radians) if (paintingDisabled()) return; - m_data->p()->rotate(180/M_PI*radians); + m_data->p()->rotate(180 / M_PI*radians); if (!m_data->currentPath.isEmpty()) { QTransform matrix; - m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians); + m_data->currentPath = m_data->currentPath * matrix.rotate(-180 / M_PI*radians); m_common->state.pathTransform.rotate(radians); } } diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index 0fd0f1a..834cd4f 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -29,6 +29,7 @@ #include "UnitBezier.h" #include <QtCore/qabstractanimation.h> #include <QtCore/qdebug.h> +#include <QtCore/qmetaobject.h> #include <QtCore/qset.h> #include <QtCore/qtimer.h> #include <QtGui/qbitmap.h> @@ -101,31 +102,37 @@ public: // 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), + GeometryOrientationChange = (1L << 8), ContentsOrientationChange = (1L << 9), OpacityChange = (1L << 10), ContentsRectChange = (1L << 11), + Preserves3DChange = (1L << 12), MasksToBoundsChange = (1L << 13), DrawsContentChange = (1L << 14), ContentsOpaqueChange = (1L << 15), + BackfaceVisibilityChange = (1L << 16), ChildrenTransformChange = (1L << 17), DisplayChange = (1L << 18), BackgroundColorChange = (1L << 19), - ParentChange = (1L << 20), - DistributesOpacityChange = (1L << 21) + + DistributesOpacityChange = (1L << 20) }; // the compositor lets us special-case images and colors, so we try to do so - enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType}; + enum StaticContentType { HTMLContentType, PixmapContentType, ColorContentType, MediaContentType}; GraphicsLayerQtImpl(GraphicsLayerQt* newLayer); virtual ~GraphicsLayerQtImpl(); @@ -149,10 +156,6 @@ public: // or ChromeClientQt::scheduleCompositingLayerSync (meaning the sync will happen ASAP) void flushChanges(bool recursive = true, bool forceTransformUpdate = false); - // optimization: when we have an animation running on an element with no contents, that has child-elements with contents, - // ALL of them have to have ItemCoordinateCache and not DeviceCoordinateCache - void adjustCachingRecursively(bool animationIsRunning); - // optimization: returns true if this or an ancestor has a transform animation running. // this enables us to use ItemCoordinatesCache while the animation is running, otherwise we have to recache for every frame bool isTransformAnimationRunning() const; @@ -161,6 +164,9 @@ public slots: // we need to notify the client (aka 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: we don't want to use QTimer::singleShot void notifyAnimationStartedAsync(); @@ -179,6 +185,7 @@ public: bool updateAll; QColor contentsBackgroundColor; QColor backgroundColor; + QWeakPointer<QGraphicsObject> mediaLayer; StaticContentType contentType; float opacity; ContentData() @@ -196,7 +203,9 @@ public: int m_changeMask; QSizeF m_size; +#ifndef QT_NO_ANIMATION QList<QWeakPointer<QAbstractAnimation> > m_animations; +#endif QTimer m_suspendTimer; struct State { @@ -227,7 +236,9 @@ public: } } m_state; +#ifndef QT_NO_ANIMATION friend class AnimationQtBase; +#endif }; GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) @@ -245,7 +256,7 @@ GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) setEnabled(true); // we'll set the cache when we know what's going on - setCacheMode(NoCache); + setCacheMode(ItemCoordinateCache); connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection); } @@ -263,26 +274,13 @@ GraphicsLayerQtImpl::~GraphicsLayerQtImpl() item->setParentItem(0); } } - + +#ifndef QT_NO_ANIMATION // we do, however, own the animations... for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_animations.begin(); it != m_animations.end(); ++it) if (QAbstractAnimation* anim = it->data()) delete anim; -} - -void GraphicsLayerQtImpl::adjustCachingRecursively(bool animationIsRunning) -{ - // optimization: we make sure all our children have ItemCoordinateCache - - // otherwise we end up re-rendering them during the animation - const QList<QGraphicsItem*> children = childItems(); - - for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) { - if (QGraphicsItem* item = *it) - if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject())) { - if (layer->m_layer->drawsContent() && layer->m_currentContent.contentType == HTMLContentType) - layer->setCacheMode(animationIsRunning ? QGraphicsItem::ItemCoordinateCache : QGraphicsItem::DeviceCoordinateCache); - } - } +#endif } void GraphicsLayerQtImpl::updateTransform() @@ -307,29 +305,26 @@ QTransform GraphicsLayerQtImpl::computeTransform(const TransformationMatrix& bas // this has to do with how WebCore implements -webkit-perspective and -webkit-perspective-origin, which are the CSS // attribute that call setChildrenTransform QPointF offset = -pos() - boundingRect().bottomRight() / 2; - const GraphicsLayerQtImpl* ancestor = this; - while ((ancestor = qobject_cast<GraphicsLayerQtImpl*>(ancestor->parentObject()))) { + + for (const GraphicsLayerQtImpl* ancestor = this; (ancestor = qobject_cast<GraphicsLayerQtImpl*>(ancestor->parentObject())); ) { if (!ancestor->m_state.childrenTransform.isIdentity()) { - offset += ancestor->boundingRect().bottomRight() / 2; + const QPointF offset = mapFromItem(ancestor, QPointF(ancestor->m_size.width() / 2, ancestor->m_size.height() / 2)); computedTransform .translate(offset.x(), offset.y()) .multLeft(ancestor->m_state.childrenTransform) .translate(-offset.x(), -offset.y()); break; } - offset -= ancestor->pos(); } - computedTransform.multLeft(baseTransform); - // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint, here we convert // we have to manage this ourselves because QGraphicsView's transformOrigin is incompatible const qreal originX = m_state.anchorPoint.x() * m_size.width(); const qreal originY = m_state.anchorPoint.y() * m_size.height(); - computedTransform = TransformationMatrix() - .translate(originX, originY) - .multiply(computedTransform) - .translate(-originX, -originY); + computedTransform + .translate3d(originX, originY, m_state.anchorPoint.z()) + .multLeft(baseTransform) + .translate3d(-originX, -originY, -m_state.anchorPoint.z()); // now we project to 2D return QTransform(computedTransform); @@ -353,6 +348,7 @@ QPainterPath GraphicsLayerQtImpl::opaqueArea() const 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); @@ -369,14 +365,14 @@ QRectF GraphicsLayerQtImpl::boundingRect() const void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { if (m_currentContent.backgroundColor.isValid()) - painter->fillRect(option->exposedRect, QColor(m_currentContent.backgroundColor)); + painter->fillRect(option->rect, QColor(m_currentContent.backgroundColor)); switch (m_currentContent.contentType) { case HTMLContentType: if (m_state.drawsContent) { // this is the expensive bit. we try to minimize calls to this area by proper caching GraphicsContext gc(painter); - m_layer->paintGraphicsLayerContents(gc, option->exposedRect.toAlignedRect()); + m_layer->paintGraphicsLayerContents(gc, option->rect); } break; case PixmapContentType: @@ -385,19 +381,25 @@ void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsIte 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; } } -void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) +void GraphicsLayerQtImpl::notifySyncRequired() { - Q_ASSERT(this); - - m_changeMask |= changeMask; - if (m_layer->client()) m_layer->client()->notifySyncRequired(m_layer); } +void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) +{ + m_changeMask |= changeMask; + static QMetaMethod syncMethod = staticMetaObject.method(staticMetaObject.indexOfMethod("notifySyncRequired()")); + syncMethod.invoke(this, Qt::QueuedConnection); +} + void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform) { // this is the bulk of the work. understanding what the compositor is trying to achieve, @@ -433,7 +435,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform w->setParentItem(this); for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it) - if (QGraphicsItem* w = *it) + if (GraphicsLayerQtImpl* w = qobject_cast<GraphicsLayerQtImpl*>((*it)->toGraphicsObject())) w->setParentItem(0); // children are ordered by z-value, let graphics-view know. @@ -450,7 +452,6 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform if (m_layer->maskLayer()) { if (GraphicsLayerQtImpl* mask = qobject_cast<GraphicsLayerQtImpl*>(m_layer->maskLayer()->platformLayer()->toGraphicsObject())) { mask->m_maskEffect = new MaskEffectQt(this, mask); - mask->setCacheMode(NoCache); setGraphicsEffect(mask->m_maskEffect.data()); } } @@ -465,11 +466,11 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform 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()) - scene()->update(); + if (scene()) + scene()->update(); if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange)) { // due to the differences between the way WebCore handles transforms and the way Qt handles transforms, @@ -483,11 +484,19 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform update(); setFlag(ItemHasNoContents, false); + // no point in caching a directly composited pixmap into another pixmap + setCacheMode(NoCache); + + break; + case MediaContentType: + setFlag(ItemHasNoContents, true); + setCacheMode(NoCache); + m_pendingContent.mediaLayer.data()->setParentItem(this); break; case ColorContentType: // no point in caching a solid-color rectangle - setCacheMode(m_layer->maskLayer() ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache); + setCacheMode(NoCache); if (m_pendingContent.contentType != m_currentContent.contentType || m_pendingContent.contentsBackgroundColor != m_currentContent.contentsBackgroundColor) update(); m_state.drawsContent = false; @@ -500,19 +509,11 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform case HTMLContentType: if (m_pendingContent.contentType != m_currentContent.contentType) update(); - if (!m_state.drawsContent && m_layer->drawsContent()) + if (!m_state.drawsContent && m_layer->drawsContent()) { update(); - if (m_layer->drawsContent() && !m_maskEffect) { - const QGraphicsItem::CacheMode mewCacheMode = isTransformAnimationRunning() ? ItemCoordinateCache : DeviceCoordinateCache; - - // optimization: QGraphicsItem doesn't always perform this test - if (mewCacheMode != cacheMode()) - setCacheMode(mewCacheMode); - - // HTML content: we want to use exposedRect so we don't use WebCore rendering if we don't have to - setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); - } - else + if (m_layer->drawsContent() && !m_maskEffect) + setCacheMode(ItemCoordinateCache); + } else if (!m_layer->drawsContent()) setCacheMode(NoCache); setFlag(ItemHasNoContents, !m_layer->drawsContent()); @@ -520,7 +521,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform } } - if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity()) + if ((m_changeMask & OpacityChange) && m_state.opacity != m_layer->opacity() && !m_opacityAnimationRunning) setOpacity(m_layer->opacity()); if (m_changeMask & ContentsRectChange) { @@ -569,6 +570,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform 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.regionToUpdate |= m_pendingContent.regionToUpdate; m_currentContent.contentsBackgroundColor = m_pendingContent.contentsBackgroundColor; @@ -707,116 +709,140 @@ void GraphicsLayerQt::removeFromParent() } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setMaskLayer(GraphicsLayer* layer) +void GraphicsLayerQt::setMaskLayer(GraphicsLayer* value) { - GraphicsLayer::setMaskLayer(layer); + if (value == maskLayer()) + return; + GraphicsLayer::setMaskLayer(value); m_impl->notifyChange(GraphicsLayerQtImpl::MaskLayerChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setPosition(const FloatPoint& p) +void GraphicsLayerQt::setPosition(const FloatPoint& value) { - if (position() != p) - m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange); - GraphicsLayer::setPosition(p); + if (value == position()) + return; + GraphicsLayer::setPosition(value); + m_impl->notifyChange(GraphicsLayerQtImpl::PositionChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& p) +void GraphicsLayerQt::setAnchorPoint(const FloatPoint3D& value) { - if (anchorPoint() != p) - m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange); - GraphicsLayer::setAnchorPoint(p); + if (value == anchorPoint()) + return; + GraphicsLayer::setAnchorPoint(value); + m_impl->notifyChange(GraphicsLayerQtImpl::AnchorPointChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setSize(const FloatSize& size) +void GraphicsLayerQt::setSize(const FloatSize& value) { - if (this->size() != size) - m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange); - GraphicsLayer::setSize(size); + if (value == size()) + return; + GraphicsLayer::setSize(value); + m_impl->notifyChange(GraphicsLayerQtImpl::SizeChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setTransform(const TransformationMatrix& t) +void GraphicsLayerQt::setTransform(const TransformationMatrix& value) { - if (!m_impl->m_transformAnimationRunning && transform() != t) - m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange); - GraphicsLayer::setTransform(t); + if (value == transform()) + return; + GraphicsLayer::setTransform(value); + m_impl->notifyChange(GraphicsLayerQtImpl::TransformChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& t) +void GraphicsLayerQt::setChildrenTransform(const TransformationMatrix& value) { - GraphicsLayer::setChildrenTransform(t); + if (value == childrenTransform()) + return; + GraphicsLayer::setChildrenTransform(value); m_impl->notifyChange(GraphicsLayerQtImpl::ChildrenTransformChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setPreserves3D(bool b) +void GraphicsLayerQt::setPreserves3D(bool value) { - if (b != preserves3D()); - m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange); - GraphicsLayer::setPreserves3D(b); + if (value == preserves3D()) + return; + GraphicsLayer::setPreserves3D(value); + m_impl->notifyChange(GraphicsLayerQtImpl::Preserves3DChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setMasksToBounds(bool b) +void GraphicsLayerQt::setMasksToBounds(bool value) { - GraphicsLayer::setMasksToBounds(b); + if (value == masksToBounds()) + return; + GraphicsLayer::setMasksToBounds(value); m_impl->notifyChange(GraphicsLayerQtImpl::MasksToBoundsChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setDrawsContent(bool b) +void GraphicsLayerQt::setDrawsContent(bool value) { + if (value == drawsContent()) + return; m_impl->notifyChange(GraphicsLayerQtImpl::DrawsContentChange); - GraphicsLayer::setDrawsContent(b); + GraphicsLayer::setDrawsContent(value); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setBackgroundColor(const Color& c) +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); - m_impl->m_pendingContent.backgroundColor = c; - GraphicsLayer::setBackgroundColor(c); } // reimp from GraphicsLayer.h void GraphicsLayerQt::clearBackgroundColor() { + if (!m_impl->m_pendingContent.backgroundColor.isValid()) + return; m_impl->m_pendingContent.backgroundColor = QColor(); - m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); GraphicsLayer::clearBackgroundColor(); + m_impl->notifyChange(GraphicsLayerQtImpl::BackgroundColorChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setContentsOpaque(bool b) +void GraphicsLayerQt::setContentsOpaque(bool value) { + if (value == contentsOpaque()) + return; m_impl->notifyChange(GraphicsLayerQtImpl::ContentsOpaqueChange); - GraphicsLayer::setContentsOpaque(b); + GraphicsLayer::setContentsOpaque(value); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setBackfaceVisibility(bool b) +void GraphicsLayerQt::setBackfaceVisibility(bool value) { + if (value == backfaceVisibility()) + return; + GraphicsLayer::setBackfaceVisibility(value); m_impl->notifyChange(GraphicsLayerQtImpl::BackfaceVisibilityChange); - GraphicsLayer::setBackfaceVisibility(b); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setOpacity(float o) +void GraphicsLayerQt::setOpacity(float value) { - if (!m_impl->m_opacityAnimationRunning && opacity() != o) - m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); - GraphicsLayer::setOpacity(o); + if (value == opacity()) + return; + GraphicsLayer::setOpacity(value); + m_impl->notifyChange(GraphicsLayerQtImpl::OpacityChange); } // reimp from GraphicsLayer.h -void GraphicsLayerQt::setContentsRect(const IntRect& r) +void GraphicsLayerQt::setContentsRect(const IntRect& value) { + if (value == contentsRect()) + return; + GraphicsLayer::setContentsRect(value); m_impl->notifyChange(GraphicsLayerQtImpl::ContentsRectChange); - GraphicsLayer::setContentsRect(r); } // reimp from GraphicsLayer.h @@ -845,6 +871,19 @@ void GraphicsLayerQt::setContentsBackgroundColor(const Color& color) GraphicsLayer::setContentsBackgroundColor(color); } +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 from GraphicsLayer.h void GraphicsLayerQt::setGeometryOrientation(CompositingCoordinatesOrientation orientation) { @@ -913,18 +952,18 @@ static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, q // 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 -static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, int duration) -{ - if (timingFunction.type() == LinearTimingFunction) - return progress; - if (timingFunction.type() == CubicBezierTimingFunction) { - return solveCubicBezierFunction(timingFunction.x1(), - timingFunction.y1(), - timingFunction.x2(), - timingFunction.y2(), - double(progress), double(duration) / 1000); - } +static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, double duration) +{ + if (timingFunction.type() == LinearTimingFunction) return progress; + if (timingFunction.type() == CubicBezierTimingFunction) { + return solveCubicBezierFunction(timingFunction.x1(), + timingFunction.y1(), + timingFunction.x2(), + timingFunction.y2(), + double(progress), double(duration) / 1000); + } + return progress; } // helper functions to safely get a value out of WebCore's AnimationValue* @@ -943,6 +982,7 @@ static void webkitAnimationToQtAnimationValue(const AnimationValue* animationVal 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: @@ -953,7 +993,9 @@ public: , m_duration(anim->duration() * 1000) , m_isAlternate(anim->direction() == Animation::AnimationDirectionAlternate) , m_webkitPropertyID(values.property()) + , m_webkitAnimation(anim) , m_keyframesName(name) + , m_fillsForwards(false) { } @@ -973,7 +1015,11 @@ public: 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; @@ -992,6 +1038,8 @@ public: KeyframeValueQt<T> keyframeValue; if (animationValue->timingFunction()) keyframeValue.timingFunction = *animationValue->timingFunction(); + else + keyframeValue.timingFunction = anim->timingFunction(); webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value); m_keyframeValues[animationValue->keyTime()] = keyframeValue; } @@ -1028,7 +1076,7 @@ protected: typename QMap<qreal, KeyframeValueQt<T> >::iterator it2 = it+1; if (it2 == m_keyframeValues.end()) - it2 = m_keyframeValues.begin(); + it2 = it; const KeyframeValueQt<T>& fromKeyframe = it.value(); const KeyframeValueQt<T>& toKeyframe = it2.value(); @@ -1040,7 +1088,7 @@ protected: // 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() / 1000); + : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration()); applyFrame(fromValue, toValue, progress); } @@ -1056,10 +1104,10 @@ public: ~TransformAnimationQt() { - // this came up during the compositing/animation LayoutTests - // when the animation dies, the transform has to go back to default - if (m_layer) - m_layer.data()->updateTransform(); + if (m_fillsForwards) + setCurrentTime(1); + else if (m_layer && m_layer.data()->m_layer) + m_layer.data()->setBaseTransform(m_layer.data()->m_layer->transform()); } // the idea is that we let WebCore manage the transform-operations @@ -1077,15 +1125,29 @@ public: sourceOperations.apply(m_boxSize, sourceMatrix); transformMatrix = m_sourceMatrix; transformMatrix.blend(sourceMatrix, 1 - progress); - } else if (targetOperations.size() != sourceOperations.size()) { - transformMatrix = m_sourceMatrix; - targetOperations.apply(m_boxSize, transformMatrix); - transformMatrix.blend(m_sourceMatrix, progress); } else { - for (size_t i = 0; i < targetOperations.size(); ++i) - targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize); + 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()->setBaseTransform(transformMatrix); + if (m_fillsForwards) + m_layer.data()->m_layer->setTransform(m_layer.data()->m_baseTransform); } virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) @@ -1100,10 +1162,8 @@ public: if (newState == QAbstractAnimation::Running) { m_sourceMatrix = m_layer.data()->m_layer->transform(); m_layer.data()->m_transformAnimationRunning = true; - m_layer.data()->adjustCachingRecursively(true); - } else { + } else if (newState == QAbstractAnimation::Stopped) { m_layer.data()->m_transformAnimationRunning = false; - m_layer.data()->adjustCachingRecursively(false); } } @@ -1117,9 +1177,25 @@ public: { } + ~OpacityAnimationQt() + { + if (m_fillsForwards) + setCurrentTime(1); + else if (m_layer && m_layer.data()->m_layer) + m_layer.data()->setOpacity(m_layer.data()->m_layer->opacity()); + } virtual void applyFrame(const qreal& fromValue, const qreal& toValue, qreal progress) { - m_layer.data()->setOpacity(qMin<qreal>(qMax<qreal>(fromValue + (toValue-fromValue)*progress, 0), 1)); + 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()->opacity() && opacity) + m_layer.data()->scene()->update(); + + m_layer.data()->setOpacity(opacity); + if (m_fillsForwards) + m_layer.data()->m_layer->setOpacity(opacity); } virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) @@ -1136,33 +1212,51 @@ bool GraphicsLayerQt::addAnimation(const KeyframeValueList& values, const IntSiz if (!anim->duration() || !anim->iterationCount()) return false; - QAbstractAnimation* newAnim; + AnimationQtBase* newAnim = 0; - 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; + // fixed: we might already have the Qt animation object associated with this WebCore::Animation object + for (QList<QWeakPointer<QAbstractAnimation> >::iterator 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; + } } - // we make sure WebCore::Animation and QAnimation are on the same terms - newAnim->setLoopCount(anim->iterationCount()); - m_impl->m_animations.append(QWeakPointer<QAbstractAnimation>(newAnim)); - QObject::connect(&m_impl->m_suspendTimer, SIGNAL(timeout()), newAnim, SLOT(resume())); - timeOffset += anim->delay(); + 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 or flicker... m_impl->flushChanges(false); - if (timeOffset) - QTimer::singleShot(timeOffset * 1000, newAnim, SLOT(start())); + // when fill-mode is backwards/both, we set the value to 0 before the delay takes place + if (anim->fillsBackwards()) + newAnim->setCurrentTime(0); + + if (anim->delay()) + QTimer::singleShot(anim->delay() * 1000, newAnim, SLOT(start())); else 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. @@ -1204,8 +1298,11 @@ void GraphicsLayerQt::pauseAnimation(const String& name, double timeOffset) continue; AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data()); - if (anim && anim->m_keyframesName == QString(name)) - QTimer::singleShot(timeOffset * 1000, anim, SLOT(pause())); + if (anim && anim->m_keyframesName == QString(name)) { + // we synchronize the animation's clock to WebCore's timeOffset + anim->setCurrentTime(timeOffset * 1000); + anim->pause(); + } } } @@ -1235,6 +1332,7 @@ void GraphicsLayerQt::resumeAnimations() } } +#endif // QT_NO_ANIMATION } #include <GraphicsLayerQt.moc> diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.h b/WebCore/platform/graphics/qt/GraphicsLayerQt.h index 3a53bd9..9e5832f 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.h +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.h @@ -63,13 +63,16 @@ public: 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 setContentsToMedia(PlatformLayer*); virtual void setContentsBackgroundColor(const Color&); virtual void setGeometryOrientation(CompositingCoordinatesOrientation orientation); virtual void setContentsOrientation(CompositingCoordinatesOrientation orientation); diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index d831566..4b85a18 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -28,11 +28,11 @@ #include "config.h" #include "ImageBuffer.h" -#include "CString.h" #include "GraphicsContext.h" #include "ImageData.h" #include "MIMETypeRegistry.h" #include "StillImageQt.h" +#include <wtf/text/CString.h> #include <QBuffer> #include <QColor> diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index 18e7f08..b48b278 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -57,7 +57,7 @@ ImageDecoderQt::~ImageDecoderQt() void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) { - if (m_failed) + if (failed()) return; // No progressive loading possible @@ -75,7 +75,7 @@ void ImageDecoderQt::setData(SharedBuffer* data, bool allDataReceived) QByteArray imageData = QByteArray::fromRawData(m_data->data(), m_data->size()); m_buffer.set(new QBuffer); m_buffer->setData(imageData); - m_buffer->open(QBuffer::ReadOnly); + 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 @@ -106,9 +106,8 @@ size_t ImageDecoderQt::frameCount() forceLoadEverything(); else m_frameBufferCache.resize(imageCount); - } else { + } else m_frameBufferCache.resize(1); - } } return m_frameBufferCache.size(); @@ -116,8 +115,21 @@ size_t ImageDecoderQt::frameCount() int ImageDecoderQt::repetitionCount() const { - if (m_reader && m_reader->supportsAnimation()) - m_repetitionCount = qMax(0, m_reader->loopCount()); + if (m_reader && m_reader->supportsAnimation()) { + m_repetitionCount = m_reader->loopCount(); + + // Qt and WebCore have a incompatible understanding of + // the loop count and we can not completely map everything. + // Qt | WebCore | description + // -1 | 0 | infinite animation + // 0 | cAnimationLoopOnce | show every frame once + // n | n | no idea if that is supported + // n/a | cAnimationNone | show only the first frame + if (m_repetitionCount == -1) + m_repetitionCount = 0; + else if (m_repetitionCount == 0) + m_repetitionCount = cAnimationLoopOnce; + } return m_repetitionCount; } @@ -133,7 +145,7 @@ RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index) // yet how many images we are going to have and need to // find that out now. size_t count = m_frameBufferCache.size(); - if (!m_failed && !count) { + if (!failed() && !count) { internalDecodeSize(); count = frameCount(); } @@ -157,8 +169,10 @@ void ImageDecoderQt::internalDecodeSize() // If we have a QSize() something failed QSize size = m_reader->size(); - if (size.isEmpty()) - return failRead(); + if (size.isEmpty()) { + setFailed(); + return clearPointers(); + } setSize(size.width(), size.height()); } @@ -169,26 +183,33 @@ void ImageDecoderQt::internalReadImage(size_t frameIndex) if (m_reader->supportsAnimation()) m_reader->jumpToImage(frameIndex); - else if (frameIndex != 0) - return failRead(); + else if (frameIndex) { + setFailed(); + return clearPointers(); + } - internalHandleCurrentImage(frameIndex); + if (!internalHandleCurrentImage(frameIndex)) + setFailed(); // Attempt to return some memory - for (int i = 0; i < m_frameBufferCache.size(); ++i) + for (int i = 0; i < m_frameBufferCache.size(); ++i) { if (m_frameBufferCache[i].status() != RGBA32Buffer::FrameComplete) return; + } - m_reader.clear(); - m_buffer.clear(); + clearPointers(); } -void ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) +bool ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) { // Now get the QImage from Qt and place it in the RGBA32Buffer QImage img; - if (!m_reader->read(&img)) - return failRead(); + if (!m_reader->read(&img)) { + frameCount(); + repetitionCount(); + clearPointers(); + return false; + } // now into the RGBA32Buffer - even if the image is not QSize imageSize = img.size(); @@ -197,6 +218,7 @@ void ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) buffer->setStatus(RGBA32Buffer::FrameComplete); buffer->setDuration(m_reader->nextImageDelay()); buffer->setDecodedImage(img); + return true; } // The QImageIOHandler is not able to tell us how many frames @@ -204,8 +226,8 @@ void ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) // increasing the m_frameBufferCache by one and try to parse // the image. We stop when QImage::read fails and then need // to resize the m_frameBufferCache to the final size and update -// the m_failed. In case we failed to decode the first image -// we want to keep m_failed set to true. +// 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() @@ -214,20 +236,19 @@ void ImageDecoderQt::forceLoadEverything() do { m_frameBufferCache.resize(++imageCount); - internalHandleCurrentImage(imageCount - 1); - } while (!m_failed); + } while (internalHandleCurrentImage(imageCount - 1)); // If we failed decoding the first image we actually - // have no images and need to keep m_failed set to - // true otherwise we want to reset it and forget about + // 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); - m_failed = imageCount == 1; + if (imageCount == 1) + setFailed(); } -void ImageDecoderQt::failRead() +void ImageDecoderQt::clearPointers() { - setFailed(); m_reader.clear(); m_buffer.clear(); } diff --git a/WebCore/platform/graphics/qt/ImageDecoderQt.h b/WebCore/platform/graphics/qt/ImageDecoderQt.h index be9a9b0..ceef884 100644 --- a/WebCore/platform/graphics/qt/ImageDecoderQt.h +++ b/WebCore/platform/graphics/qt/ImageDecoderQt.h @@ -61,9 +61,9 @@ private: private: void internalDecodeSize(); void internalReadImage(size_t); - void internalHandleCurrentImage(size_t); + bool internalHandleCurrentImage(size_t); void forceLoadEverything(); - void failRead(); + void clearPointers(); private: QByteArray m_format; diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index 3274db5..21e670c 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -23,23 +23,25 @@ #include <limits> -#include "CString.h" #include "FrameView.h" #include "GraphicsContext.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 <QMetaEnum> #include <QUrl> -#include <QEvent> -#include <phonon/path.h> #include <phonon/audiooutput.h> +#include <phonon/backendcapabilities.h> +#include <phonon/path.h> #include <phonon/mediaobject.h> #include <phonon/videowidget.h> @@ -143,15 +145,62 @@ MediaPlayerPrivate::~MediaPlayerPrivate() m_mediaObject = 0; } -void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&) +HashSet<String>& MediaPlayerPrivate::supportedTypesCache() { - notImplemented(); + 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; } -MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String&, const String&) +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) { - // FIXME: do the real thing - notImplemented(); + types = supportedTypesCache(); +} + +MediaPlayer::SupportsType MediaPlayerPrivate::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; } diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h index e7630a1..ff6a01c 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -132,6 +132,7 @@ namespace WebCore { 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(); diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp new file mode 100644 index 0000000..bdac2a4 --- /dev/null +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp @@ -0,0 +1,571 @@ +/* + 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 "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 <QTime> +#include <QTimer> +#include <QUrl> +#include <limits> +#include <wtf/HashSet.h> +#include <wtf/text/CString.h> + +using namespace WTF; + +namespace WebCore { + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + registrar(create, getSupportedTypes, supportsType); +} + +void MediaPlayerPrivate::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 MediaPlayerPrivate::supportsType(const String& mime, const String& codec) +{ + if (!mime.startsWith("audio/") && !mime.startsWith("video/")) + return MediaPlayer::IsNotSupported; + + if (QMediaPlayer::hasSupport(mime, QStringList(codec)) >= QtMultimedia::ProbablySupported) + return MediaPlayer::IsSupported; + + return MediaPlayer::MayBeSupported; +} + +MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) + : m_player(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_isVisible(false) + , m_isSeeking(false) + , m_composited(false) + , m_queuedSeek(-1) +{ + m_videoItem->setMediaObject(m_mediaPlayer); + 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(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 + QMediaService* service = m_mediaPlayer->service(); + if (service) { + m_mediaPlayerControl = qobject_cast<QMediaPlayerControl *>( + service->control(QMediaPlayerControl_iid)); + } +} + +MediaPlayerPrivate::~MediaPlayerPrivate() +{ + delete m_mediaPlayer; + delete m_videoScene; +} + +bool MediaPlayerPrivate::hasVideo() const +{ + return m_mediaPlayer->isVideoAvailable(); +} + +bool MediaPlayerPrivate::hasAudio() const +{ + return true; +} + +void MediaPlayerPrivate::load(const String& url) +{ + // 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(); + } + + const QUrl rUrl = QUrl(QString(url)); + const QString scheme = rUrl.scheme().toLower(); + + // Grab the client media element + HTMLMediaElement* element = static_cast<HTMLMediaElement*>(m_player->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; + FrameLoaderClientQt* frameLoader = frame ? static_cast<FrameLoaderClientQt*>(frame->loader()->client()) : 0; + QNetworkAccessManager* manager = frameLoader ? frameLoader->webFrame()->page()->networkAccessManager() : 0; + + if (document && manager) { + // Set the cookies + QNetworkCookieJar* jar = manager->cookieJar(); + QList<QNetworkCookie> cookies = jar->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)); +} + +void MediaPlayerPrivate::cancelLoad() +{ + m_mediaPlayer->setMedia(QMediaContent()); + updateStates(); +} + +void MediaPlayerPrivate::play() +{ + if (m_mediaPlayer->state() != QMediaPlayer::PlayingState) + m_mediaPlayer->play(); +} + +void MediaPlayerPrivate::pause() +{ + if (m_mediaPlayer->state() == QMediaPlayer::PlayingState) + m_mediaPlayer->pause(); +} + +bool MediaPlayerPrivate::paused() const +{ + return (m_mediaPlayer->state() != QMediaPlayer::PlayingState); +} + +void MediaPlayerPrivate::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 MediaPlayerPrivate::seeking() const +{ + return m_isSeeking; +} + +float MediaPlayerPrivate::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 MediaPlayerPrivate::currentTime() const +{ + float currentTime = m_mediaPlayer->position() / 1000.0f; + return currentTime; +} + +PassRefPtr<TimeRanges> MediaPlayerPrivate::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 MediaPlayerPrivate::maxTimeSeekable() const +{ + if (!m_mediaPlayerControl) + return 0; + + return static_cast<float>(m_mediaPlayerControl->availablePlaybackRanges().latestTime()) / 1000.0f; +} + +unsigned MediaPlayerPrivate::bytesLoaded() const +{ + unsigned percentage = m_mediaPlayer->bufferStatus(); + + if (percentage == 100) { + if (m_networkState != MediaPlayer::Idle) { + m_networkState = MediaPlayer::Idle; + m_player->networkStateChanged(); + } + if (m_readyState != MediaPlayer::HaveEnoughData) { + m_readyState = MediaPlayer::HaveEnoughData; + m_player->readyStateChanged(); + } + } + + QLatin1String bytesLoadedKey("bytes-loaded"); + if (m_mediaPlayer->availableExtendedMetaData().contains(bytesLoadedKey)) + return m_mediaPlayer->extendedMetaData(bytesLoadedKey).toInt(); + + return percentage; +} + +unsigned MediaPlayerPrivate::totalBytes() const +{ + if (m_mediaPlayer->availableMetaData().contains(QtMultimedia::Size)) + return m_mediaPlayer->metaData(QtMultimedia::Size).toInt(); + + return 100; +} + +void MediaPlayerPrivate::setRate(float rate) +{ + m_mediaPlayer->setPlaybackRate(rate); +} + +void MediaPlayerPrivate::setVolume(float volume) +{ + m_mediaPlayer->setVolume(static_cast<int>(volume * 100.0)); +} + +bool MediaPlayerPrivate::supportsMuting() const +{ + return true; +} + +void MediaPlayerPrivate::setMuted(bool muted) +{ + m_mediaPlayer->setMuted(muted); +} + +MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const +{ + return m_networkState; +} + +MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const +{ + return m_readyState; +} + +void MediaPlayerPrivate::setVisible(bool visible) +{ + m_isVisible = visible; +} + +void MediaPlayerPrivate::mediaStatusChanged(QMediaPlayer::MediaStatus) +{ + updateStates(); +} + +void MediaPlayerPrivate::handleError(QMediaPlayer::Error) +{ + updateStates(); +} + +void MediaPlayerPrivate::stateChanged(QMediaPlayer::State state) +{ + if (state != QMediaPlayer::PlayingState && m_isSeeking && m_queuedSeek >= 0) { + m_mediaPlayer->setPosition(m_queuedSeek); + m_queuedSeek = -1; + } +} + +void MediaPlayerPrivate::nativeSizeChanged(const QSizeF&) +{ + m_player->sizeChanged(); +} + +void MediaPlayerPrivate::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 MediaPlayerPrivate::seekTimeout() +{ + // If we haven't heard anything, assume the seek succeeded + if (m_isSeeking) { + m_player->timeChanged(); + m_isSeeking = false; + } +} + +void MediaPlayerPrivate::positionChanged(qint64) +{ + // Only propogate this event if we are seeking + if (m_isSeeking && m_queuedSeek == -1) { + m_player->timeChanged(); + m_isSeeking = false; + } +} + +void MediaPlayerPrivate::durationChanged(qint64) +{ + m_player->durationChanged(); +} + +void MediaPlayerPrivate::volumeChanged(int volume) +{ + m_player->volumeChanged(static_cast<float>(volume) / 100.0); +} + +void MediaPlayerPrivate::mutedChanged(bool muted) +{ + m_player->muteChanged(muted); +} + +void MediaPlayerPrivate::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_player->readyStateChanged(); + + if (m_networkState != oldNetworkState) + m_player->networkStateChanged(); +} + +void MediaPlayerPrivate::setSize(const IntSize& size) +{ + if (size == m_currentSize) + return; + + m_currentSize = size; + m_videoItem->setSize(QSizeF(QSize(size))); +} + +IntSize MediaPlayerPrivate::naturalSize() const +{ + if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) + return IntSize(); + + return IntSize(m_videoItem->nativeSize().toSize()); +} + +void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_composited) + return; +#endif + if (context->paintingDisabled()) + return; + + if (!m_isVisible) + return; + + // Grab the painter and widget + QPainter* painter = context->platformContext(); + + // Render the video + m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect()); +} + +void MediaPlayerPrivate::repaint() +{ + m_player->repaint(); +} + +#if USE(ACCELERATED_COMPOSITING) +void MediaPlayerPrivate::acceleratedRenderingStateChanged() +{ + bool composited = m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player); + if (composited == m_composited) + return; + + m_composited = composited; + if (composited) + m_videoScene->removeItem(m_videoItem); + else + m_videoScene->addItem(m_videoItem); +} + +PlatformLayer* MediaPlayerPrivate::platformLayer() const +{ + return m_composited ? m_videoItem : 0; +} +#endif + +} // namespace WebCore + +#include "moc_MediaPlayerPrivateQt.cpp" diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h new file mode 100644 index 0000000..d72404c --- /dev/null +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h @@ -0,0 +1,133 @@ +/* + 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 MediaPlayerPrivate : public QObject, public MediaPlayerPrivateInterface { + + Q_OBJECT + +public: + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + ~MediaPlayerPrivate(); + + 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 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); + + bool supportsMuting() const; + 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); + + IntSize naturalSize() const; + void setSize(const IntSize&); + + void paint(GraphicsContext*, const IntRect&); + + bool supportsFullscreen() const { return false; } + +#if USE(ACCELERATED_COMPOSITING) + // 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; +#endif + +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 volumeChanged(int); + void mutedChanged(bool); + void repaint(); + +private: + void updateStates(); + +private: + MediaPlayerPrivate(MediaPlayer*); + + MediaPlayer* m_player; + QMediaPlayer* m_mediaPlayer; + QMediaPlayerControl* m_mediaPlayerControl; + QGraphicsVideoItem* m_videoItem; + QGraphicsScene* m_videoScene; + + mutable MediaPlayer::NetworkState m_networkState; + mutable MediaPlayer::ReadyState m_readyState; + + IntSize m_currentSize; + bool m_isVisible; + bool m_isSeeking; + bool m_composited; + qint64 m_queuedSeek; +}; +} + +#endif // MediaPlayerPrivateQt_h diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index 507f029..ee4af7f 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -269,10 +269,12 @@ void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool antic span += ea - sa; } - m_path.moveTo(QPointF(xc + radius * cos(sar), + // connect to the previous point by a straight line + m_path.lineTo(QPointF(xc + radius * cos(sar), yc - radius * sin(sar))); m_path.arcTo(xs, ys, width, height, sa, span); + } void Path::addRect(const FloatRect& r) diff --git a/WebCore/platform/graphics/qt/StillImageQt.cpp b/WebCore/platform/graphics/qt/StillImageQt.cpp index 1db04a7..4653c58 100644 --- a/WebCore/platform/graphics/qt/StillImageQt.cpp +++ b/WebCore/platform/graphics/qt/StillImageQt.cpp @@ -57,8 +57,43 @@ void StillImage::draw(GraphicsContext* ctxt, const FloatRect& dst, ctxt->save(); ctxt->setCompositeOperation(op); + + // To support width or height is negative + float sx = src.x(); + float sy = src.y(); + float sw = src.width(); + float sh = src.height(); + + if (sw < 0) { + sx = sx + sw; + sw = -sw; + } + + if (sh < 0) { + sy = sy + sh; + sh = -sh; + } + + float dx = dst.x(); + float dy = dst.y(); + float dw = dst.width(); + float dh = dst.height(); + + if (dw < 0) { + dx = dx + dw; + dw = -dw; + } + + if (dh < 0) { + dy = dy + dh; + dh = -dh; + } + + FloatRect srcM(sx, sy, sw, sh); + FloatRect dstM(dx, dy, dw, dh); QPainter* painter(ctxt->platformContext()); - painter->drawPixmap(dst, m_pixmap, src); + + painter->drawPixmap(dstM, m_pixmap, srcM); ctxt->restore(); } diff --git a/WebCore/platform/graphics/qt/TileQt.cpp b/WebCore/platform/graphics/qt/TileQt.cpp new file mode 100644 index 0000000..9628448 --- /dev/null +++ b/WebCore/platform/graphics/qt/TileQt.cpp @@ -0,0 +1,178 @@ +/* + 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()); + 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 |