diff options
Diffstat (limited to 'Source/WebCore/platform/graphics')
515 files changed, 47246 insertions, 21830 deletions
diff --git a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp index abfb06dc6..3c0d36740 100644 --- a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp +++ b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,7 +25,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "ANGLEWebKitBridge.h" #include "Logging.h" @@ -33,114 +33,94 @@ namespace WebCore { -// Temporary typedef to support an incompatible change in the ANGLE API. -#if !defined(ANGLE_SH_VERSION) || ANGLE_SH_VERSION < 108 -typedef int ANGLEGetInfoType; -#else -typedef size_t ANGLEGetInfoType; -#endif +// FIXME: This is awful. Get rid of ANGLEWebKitBridge completely and call the libANGLE API directly to validate shaders. -inline static ANGLEGetInfoType getValidationResultValue(const ShHandle compiler, ShShaderInfo shaderInfo) +static void appendSymbol(const sh::ShaderVariable& variable, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, const std::string& name, const std::string& mappedName) { - ANGLEGetInfoType value = 0; - ShGetInfo(compiler, shaderInfo, &value); - return value; + LOG(WebGL, "Map shader symbol %s -> %s\n", name.c_str(), mappedName.c_str()); + + sh::ShaderVariable variableToAppend = variable; + variableToAppend.name = name; + variableToAppend.mappedName = mappedName; + symbols.append(std::make_pair(symbolType, variableToAppend)); + + if (variable.isArray()) { + for (unsigned i = 0; i < variable.elementCount(); i++) { + std::string arrayBrackets = "[" + std::to_string(i) + "]"; + std::string arrayName = name + arrayBrackets; + std::string arrayMappedName = mappedName + arrayBrackets; + LOG(WebGL, "Map shader symbol %s -> %s\n", arrayName.c_str(), arrayMappedName.c_str()); + variableToAppend.name = arrayName; + variableToAppend.mappedName = arrayMappedName; + symbols.append(std::make_pair(symbolType, variableToAppend)); + } + } +} + +static void getStructInfo(const sh::ShaderVariable& field, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, const std::string& namePrefix, const std::string& mappedNamePrefix) +{ + std::string name = namePrefix + '.' + field.name; + std::string mappedName = mappedNamePrefix + '.' + field.mappedName; + + if (field.isStruct()) { + for (const auto& subfield : field.fields) { + // ANGLE restricts the depth of structs, which prevents stack overflow errors in this recursion. + getStructInfo(subfield, symbolType, symbols, name, mappedName); + } + } else + appendSymbol(field, symbolType, symbols, name, mappedName); } -static bool getSymbolInfo(ShHandle compiler, ShShaderInfo symbolType, Vector<ANGLEShaderSymbol>& symbols) +static void getSymbolInfo(const sh::ShaderVariable& variable, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols) { - ShShaderInfo symbolMaxNameLengthType; + if (variable.isStruct()) { + if (variable.isArray()) { + for (unsigned i = 0; i < variable.elementCount(); i++) { + std::string arrayBrackets = "[" + std::to_string(i) + "]"; + std::string arrayName = variable.name + arrayBrackets; + std::string arrayMappedName = variable.mappedName + arrayBrackets; + for (const auto& field : variable.fields) + getStructInfo(field, symbolType, symbols, arrayName, arrayMappedName); + } + } else { + for (const auto& field : variable.fields) + getStructInfo(field, symbolType, symbols, variable.name, variable.mappedName); + } + } else + appendSymbol(variable, symbolType, symbols, variable.name, variable.mappedName); +} +static bool getSymbolInfo(ShHandle compiler, ANGLEShaderSymbolType symbolType, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols) +{ switch (symbolType) { - case SH_ACTIVE_ATTRIBUTES: - symbolMaxNameLengthType = SH_ACTIVE_ATTRIBUTE_MAX_LENGTH; + case SHADER_SYMBOL_TYPE_UNIFORM: { + auto uniforms = ShGetUniforms(compiler); + if (!uniforms) + return false; + for (const auto& uniform : *uniforms) + getSymbolInfo(uniform, symbolType, symbols); break; - case SH_ACTIVE_UNIFORMS: - symbolMaxNameLengthType = SH_ACTIVE_UNIFORM_MAX_LENGTH; + } + case SHADER_SYMBOL_TYPE_VARYING: { + auto varyings = ShGetVaryings(compiler); + if (!varyings) + return false; + for (const auto& varying : *varyings) + getSymbolInfo(varying, symbolType, symbols); break; - case SH_VARYINGS: - symbolMaxNameLengthType = SH_VARYING_MAX_LENGTH; + } + case SHADER_SYMBOL_TYPE_ATTRIBUTE: { + auto attributes = ShGetAttributes(compiler); + if (!attributes) + return false; + for (const auto& attribute : *attributes) + getSymbolInfo(attribute, symbolType, symbols); break; + } default: ASSERT_NOT_REACHED(); return false; } - - ANGLEGetInfoType numSymbols = getValidationResultValue(compiler, symbolType); - - ANGLEGetInfoType maxNameLength = getValidationResultValue(compiler, symbolMaxNameLengthType); - if (maxNameLength <= 1) - return false; - - ANGLEGetInfoType maxMappedNameLength = getValidationResultValue(compiler, SH_MAPPED_NAME_MAX_LENGTH); - if (maxMappedNameLength <= 1) - return false; - - // The maximum allowed symbol name length is 256 characters. - Vector<char, 256> nameBuffer(maxNameLength); - Vector<char, 256> mappedNameBuffer(maxMappedNameLength); - - for (ANGLEGetInfoType i = 0; i < numSymbols; ++i) { - ANGLEShaderSymbol symbol; - ANGLEGetInfoType nameLength = 0; - ShPrecisionType precision; - int staticUse; - switch (symbolType) { - case SH_ACTIVE_ATTRIBUTES: - symbol.symbolType = SHADER_SYMBOL_TYPE_ATTRIBUTE; - ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data()); - break; - case SH_ACTIVE_UNIFORMS: - symbol.symbolType = SHADER_SYMBOL_TYPE_UNIFORM; - ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data()); - break; - case SH_VARYINGS: - symbol.symbolType = SHADER_SYMBOL_TYPE_VARYING; - ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data()); - break; - default: - ASSERT_NOT_REACHED(); - return false; - } - if (!nameLength) - return false; - - // The ShGetActive* calls above are guaranteed to produce null-terminated strings for - // nameBuffer and mappedNameBuffer. Also, the character set for symbol names - // is a subset of Latin-1 as specified by the OpenGL ES Shading Language, Section 3.1 and - // WebGL, Section "Characters Outside the GLSL Source Character Set". - - String name = String(nameBuffer.data()); - String mappedName = String(mappedNameBuffer.data()); - LOG(WebGL, "Map shader symbol %s -> %s\n", name.utf8().data(), mappedName.utf8().data()); - - // ANGLE returns array names in the format "array[0]". - // The only way to know if a symbol is an array is to check if it ends with "[0]". - // We can't check the size because regular symbols and arrays of length 1 both have a size of 1. - symbol.isArray = name.endsWith("[0]") && mappedName.endsWith("[0]"); - if (symbol.isArray) { - // Add a symbol for the array name without the "[0]" suffix. - name.truncate(name.length() - 3); - mappedName.truncate(mappedName.length() - 3); - } - - symbol.name = name; - symbol.mappedName = mappedName; - symbol.precision = precision; - symbol.staticUse = staticUse; - symbols.append(symbol); - - if (symbol.isArray) { - // Add symbols for each array element. - symbol.isArray = false; - for (int i = 0; i < symbol.size; i++) { - String arrayBrackets = "[" + String::number(i) + "]"; - symbol.name = name + arrayBrackets; - symbol.mappedName = mappedName + arrayBrackets; - symbols.append(symbol); - } - } - } return true; } @@ -164,15 +144,15 @@ void ANGLEWebKitBridge::cleanupCompilers() { if (m_fragmentCompiler) ShDestruct(m_fragmentCompiler); - m_fragmentCompiler = 0; + m_fragmentCompiler = nullptr; if (m_vertexCompiler) ShDestruct(m_vertexCompiler); - m_vertexCompiler = 0; + m_vertexCompiler = nullptr; builtCompilers = false; } -void ANGLEWebKitBridge::setResources(ShBuiltInResources resources) +void ANGLEWebKitBridge::setResources(const ShBuiltInResources& resources) { // Resources are (possibly) changing - cleanup compilers if we had them already cleanupCompilers(); @@ -180,11 +160,11 @@ void ANGLEWebKitBridge::setResources(ShBuiltInResources resources) m_resources = resources; } -bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<ANGLEShaderSymbol>& symbols, int extraCompileOptions) +bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, int extraCompileOptions) { if (!builtCompilers) { - m_fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, m_shaderSpec, m_shaderOutput, &m_resources); - m_vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, m_shaderSpec, m_shaderOutput, &m_resources); + m_fragmentCompiler = ShConstructCompiler(GL_FRAGMENT_SHADER, m_shaderSpec, m_shaderOutput, &m_resources); + m_vertexCompiler = ShConstructCompiler(GL_VERTEX_SHADER, m_shaderSpec, m_shaderOutput, &m_resources); if (!m_fragmentCompiler || !m_vertexCompiler) { cleanupCompilers(); return false; @@ -204,31 +184,21 @@ bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShade bool validateSuccess = ShCompile(compiler, shaderSourceStrings, 1, SH_OBJECT_CODE | SH_VARIABLES | extraCompileOptions); if (!validateSuccess) { - int logSize = getValidationResultValue(compiler, SH_INFO_LOG_LENGTH); - if (logSize > 1) { - auto logBuffer = std::make_unique<char[]>(logSize); - if (logBuffer) { - ShGetInfoLog(compiler, logBuffer.get()); - shaderValidationLog = logBuffer.get(); - } - } + const std::string& log = ShGetInfoLog(compiler); + if (log.length()) + shaderValidationLog = log.c_str(); return false; } - int translationLength = getValidationResultValue(compiler, SH_OBJECT_CODE_LENGTH); - if (translationLength > 1) { - auto translationBuffer = std::make_unique<char[]>(translationLength); - if (!translationBuffer) - return false; - ShGetObjectCode(compiler, translationBuffer.get()); - translatedShaderSource = translationBuffer.get(); - } + const std::string& objectCode = ShGetObjectCode(compiler); + if (objectCode.length()) + translatedShaderSource = objectCode.c_str(); - if (!getSymbolInfo(compiler, SH_ACTIVE_ATTRIBUTES, symbols)) + if (!getSymbolInfo(compiler, SHADER_SYMBOL_TYPE_ATTRIBUTE, symbols)) return false; - if (!getSymbolInfo(compiler, SH_ACTIVE_UNIFORMS, symbols)) + if (!getSymbolInfo(compiler, SHADER_SYMBOL_TYPE_UNIFORM, symbols)) return false; - if (!getSymbolInfo(compiler, SH_VARYINGS, symbols)) + if (!getSymbolInfo(compiler, SHADER_SYMBOL_TYPE_VARYING, symbols)) return false; return true; @@ -236,4 +206,4 @@ bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShade } -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h index 449ab453b..9114e76c7 100644 --- a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h +++ b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,22 +26,29 @@ #ifndef ANGLEWebKitBridge_h #define ANGLEWebKitBridge_h +#include <ANGLE/ShaderLang.h> #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> -#if !PLATFORM(GTK) && !PLATFORM(EFL) && !PLATFORM(WIN) -#include "ANGLE/ShaderLang.h" +#if PLATFORM(IOS) +#import <OpenGLES/ES2/glext.h> +#elif PLATFORM(MAC) +#include <OpenGL/gl.h> #elif PLATFORM(WIN) -#include "GLSLANG/ShaderLang.h" +#include "OpenGLESShims.h" +#elif PLATFORM(GTK) +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> #else -#include "ShaderLang.h" +#include "OpenGLShims.h" +#endif #endif namespace WebCore { enum ANGLEShaderType { - SHADER_TYPE_VERTEX = SH_VERTEX_SHADER, - SHADER_TYPE_FRAGMENT = SH_FRAGMENT_SHADER, + SHADER_TYPE_VERTEX = GL_VERTEX_SHADER, + SHADER_TYPE_FRAGMENT = GL_FRAGMENT_SHADER, }; enum ANGLEShaderSymbolType { @@ -50,36 +57,16 @@ enum ANGLEShaderSymbolType { SHADER_SYMBOL_TYPE_VARYING }; -struct ANGLEShaderSymbol { - ANGLEShaderSymbolType symbolType; - String name; - String mappedName; - ShDataType dataType; - int size; - bool isArray; - ShPrecisionType precision; - int staticUse; - - bool isSampler() const - { - return symbolType == SHADER_SYMBOL_TYPE_UNIFORM - && (dataType == SH_SAMPLER_2D - || dataType == SH_SAMPLER_CUBE - || dataType == SH_SAMPLER_2D_RECT_ARB - || dataType == SH_SAMPLER_EXTERNAL_OES); - } -}; - class ANGLEWebKitBridge { public: - ANGLEWebKitBridge(ShShaderOutput = SH_GLSL_OUTPUT, ShShaderSpec = SH_WEBGL_SPEC); + ANGLEWebKitBridge(ShShaderOutput = SH_GLSL_COMPATIBILITY_OUTPUT, ShShaderSpec = SH_WEBGL_SPEC); ~ANGLEWebKitBridge(); - ShBuiltInResources getResources() { return m_resources; } - void setResources(ShBuiltInResources); + const ShBuiltInResources& getResources() { return m_resources; } + void setResources(const ShBuiltInResources&); - bool compileShaderSource(const char* shaderSource, ANGLEShaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<ANGLEShaderSymbol>& symbols, int extraCompileOptions = 0); + bool compileShaderSource(const char* shaderSource, ANGLEShaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>>& symbols, int extraCompileOptions = 0); private: diff --git a/Source/WebCore/platform/graphics/AudioTrackPrivate.h b/Source/WebCore/platform/graphics/AudioTrackPrivate.h index ed0212924..710d17020 100644 --- a/Source/WebCore/platform/graphics/AudioTrackPrivate.h +++ b/Source/WebCore/platform/graphics/AudioTrackPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AudioTrackPrivate_h -#define AudioTrackPrivate_h +#pragma once #include "TrackPrivateBase.h" @@ -36,18 +35,18 @@ class AudioTrackPrivate; class AudioTrackPrivateClient : public TrackPrivateBaseClient { public: - virtual void enabledChanged(AudioTrackPrivate*, bool) = 0; + virtual void enabledChanged(bool) = 0; }; class AudioTrackPrivate : public TrackPrivateBase { public: - static PassRefPtr<AudioTrackPrivate> create() + static Ref<AudioTrackPrivate> create() { - return adoptRef(new AudioTrackPrivate()); + return adoptRef(*new AudioTrackPrivate); } void setClient(AudioTrackPrivateClient* client) { m_client = client; } - virtual AudioTrackPrivateClient* client() const override { return m_client; } + AudioTrackPrivateClient* client() const override { return m_client; } virtual void setEnabled(bool enabled) { @@ -55,26 +54,22 @@ public: return; m_enabled = enabled; if (m_client) - m_client->enabledChanged(this, enabled); - }; - virtual bool enabled() const { return m_enabled; } + m_client->enabledChanged(enabled); + } + + bool enabled() const { return m_enabled; } enum Kind { Alternative, Description, Main, MainDesc, Translation, Commentary, None }; virtual Kind kind() const { return None; } protected: - AudioTrackPrivate() - : m_client(0) - , m_enabled(false) - { - } + AudioTrackPrivate() = default; private: - AudioTrackPrivateClient* m_client; - bool m_enabled; + AudioTrackPrivateClient* m_client { nullptr }; + bool m_enabled { false }; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/platform/graphics/BitmapImage.cpp b/Source/WebCore/platform/graphics/BitmapImage.cpp index 29f9d5c1f..d7de7f828 100644 --- a/Source/WebCore/platform/graphics/BitmapImage.cpp +++ b/Source/WebCore/platform/graphics/BitmapImage.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2008, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,27 +11,29 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "BitmapImage.h" #include "FloatRect.h" +#include "GraphicsContext.h" #include "ImageBuffer.h" #include "ImageObserver.h" #include "IntRect.h" -#include "MIMETypeRegistry.h" +#include "Logging.h" +#include "TextStream.h" #include "Timer.h" #include <wtf/CurrentTime.h> #include <wtf/Vector.h> @@ -43,730 +45,373 @@ namespace WebCore { -// FIXME: We should better integrate the iOS and non-iOS code in this class. Unlike other ports, the -// iOS port caches the metadata for a frame without decoding the image. BitmapImage::BitmapImage(ImageObserver* observer) : Image(observer) - , m_currentFrame(0) - , m_frames(0) - , m_repetitionCount(cAnimationNone) - , m_repetitionCountStatus(Unknown) - , m_repetitionsComplete(0) - , m_desiredFrameStartTime(0) - , m_decodedSize(0) - , m_decodedPropertiesSize(0) - , m_frameCount(0) -#if PLATFORM(IOS) - // FIXME: We should expose a setting to enable/disable progressive loading remove the PLATFORM(IOS)-guard. - , m_progressiveLoadChunkTime(0) - , m_progressiveLoadChunkCount(0) -#endif - , m_isSolidColor(false) - , m_checkedForSolidColor(false) - , m_animationFinished(false) - , m_allDataReceived(false) - , m_haveSize(false) - , m_sizeAvailable(false) - , m_hasUniformFrameSize(true) - , m_haveFrameCount(false) - , m_cachedImage(0) + , m_source(this) { } -BitmapImage::~BitmapImage() +BitmapImage::BitmapImage(NativeImagePtr&& image, ImageObserver* observer) + : Image(observer) + , m_source(WTFMove(image)) { - invalidatePlatformData(); - stopAnimation(); } -bool BitmapImage::hasSingleSecurityOrigin() const +BitmapImage::~BitmapImage() { - return true; + invalidatePlatformData(); + stopAnimation(); } void BitmapImage::destroyDecodedData(bool destroyAll) { - unsigned frameBytesCleared = 0; - const size_t clearBeforeFrame = destroyAll ? m_frames.size() : m_currentFrame; - - // Because we can advance frames without always needing to decode the actual - // bitmap data, |m_currentFrame| may be larger than m_frames.size(); - // make sure not to walk off the end of the container in this case. - for (size_t i = 0; i < std::min(clearBeforeFrame, m_frames.size()); ++i) { - // The underlying frame isn't actually changing (we're just trying to - // save the memory for the framebuffer data), so we don't need to clear - // the metadata. - unsigned frameBytes = m_frames[i].m_frameBytes; - if (m_frames[i].clear(false)) - frameBytesCleared += frameBytes; - } + if (!destroyAll) + m_source.destroyDecodedDataBeforeFrame(m_currentFrame); + else if (m_source.hasDecodingQueue()) + m_source.destroyAllDecodedDataExcludeFrame(m_currentFrame); + else + m_source.destroyAllDecodedData(); - destroyMetadataAndNotify(frameBytesCleared); + // There's no need to throw away the decoder unless we're explicitly asked + // to destroy all of the frames. + if (!destroyAll || m_source.hasDecodingQueue()) + m_source.clearFrameBufferCache(m_currentFrame); + else + m_source.clear(data()); - m_source.clear(destroyAll, clearBeforeFrame, data(), m_allDataReceived); - return; + invalidatePlatformData(); } void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll) { - // Animated images >5MB are considered large enough that we'll only hang on - // to one frame at a time. -#if PLATFORM(IOS) - static const unsigned cLargeAnimationCutoff = 2097152; - - // If we have decoded frames but there is no encoded data, we shouldn't destroy - // the decoded image since we won't be able to reconstruct it later. - if (!data() && m_frames.size()) - return; -#else - static const unsigned cLargeAnimationCutoff = 5242880; -#endif - // If we have decoded frames but there is no encoded data, we shouldn't destroy // the decoded image since we won't be able to reconstruct it later. - if (!data() && m_frames.size()) + if (!data() && frameCount()) return; - unsigned allFrameBytes = 0; - for (size_t i = 0; i < m_frames.size(); ++i) - allFrameBytes += m_frames[i].m_frameBytes; + if (m_source.decodedSize() < LargeAnimationCutoff) + return; - if (allFrameBytes > cLargeAnimationCutoff) - destroyDecodedData(destroyAll); + destroyDecodedData(destroyAll); } -void BitmapImage::destroyMetadataAndNotify(unsigned frameBytesCleared) +bool BitmapImage::dataChanged(bool allDataReceived) { - m_isSolidColor = false; - m_checkedForSolidColor = false; - invalidatePlatformData(); - - ASSERT(m_decodedSize >= frameBytesCleared); - m_decodedSize -= frameBytesCleared; - if (frameBytesCleared > 0) { - frameBytesCleared += m_decodedPropertiesSize; - m_decodedPropertiesSize = 0; - } - if (frameBytesCleared && imageObserver()) - imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesCleared)); + return m_source.dataChanged(data(), allDataReceived); } -#if PLATFORM(IOS) -void BitmapImage::cacheFrame(size_t index, float scaleHint) -#else -void BitmapImage::cacheFrame(size_t index) -#endif +NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext) { - size_t numFrames = frameCount(); - ASSERT(m_decodedSize == 0 || numFrames > 1); - - if (m_frames.size() < numFrames) - m_frames.grow(numFrames); - -#if PLATFORM(IOS) - m_frames[index].m_frame = m_source.createFrameAtIndex(index, &scaleHint); - m_frames[index].m_scale = scaleHint; -#else - m_frames[index].m_frame = m_source.createFrameAtIndex(index); -#endif - if (numFrames == 1 && m_frames[index].m_frame) - checkForSolidColor(); - - m_frames[index].m_orientation = m_source.orientationAtIndex(index); - m_frames[index].m_haveMetadata = true; - m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index); - if (repetitionCount(false) != cAnimationNone) - m_frames[index].m_duration = m_source.frameDurationAtIndex(index); - m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); - m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index); - - const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size); - if (frameSize != m_size) - m_hasUniformFrameSize = false; - if (m_frames[index].m_frame) { - int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes); - m_decodedSize += deltaBytes; - // The fully-decoded frame will subsume the partially decoded data used - // to determine image properties. - deltaBytes -= m_decodedPropertiesSize; - m_decodedPropertiesSize = 0; - if (imageObserver()) - imageObserver()->decodedSizeChanged(this, deltaBytes); + if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing)) { + LOG(Images, "BitmapImage::%s - %p - url: %s [subsamplingLevel was %d, resampling]", __FUNCTION__, this, sourceURL().characters8(), static_cast<int>(frameSubsamplingLevelAtIndex(index))); + invalidatePlatformData(); } + + return m_source.frameImageAtIndex(index, subsamplingLevel, sizeForDrawing, targetContext); } -#if PLATFORM(IOS) -void BitmapImage::cacheFrameInfo(size_t index) +NativeImagePtr BitmapImage::nativeImage(const GraphicsContext* targetContext) { - size_t numFrames = frameCount(); - - if (m_frames.size() < numFrames) - m_frames.resize(numFrames); - - ASSERT(!m_frames[index].m_haveInfo); - - if (shouldAnimate()) - m_frames[index].m_duration = m_source.frameDurationAtIndex(index); - m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); - m_frames[index].m_haveInfo = true; + return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext); } -#endif -void BitmapImage::didDecodeProperties() const +NativeImagePtr BitmapImage::nativeImageForCurrentFrame(const GraphicsContext* targetContext) { - if (m_decodedSize) - return; - size_t updatedSize = m_source.bytesDecodedToDetermineProperties(); - if (m_decodedPropertiesSize == updatedSize) - return; - int deltaBytes = updatedSize - m_decodedPropertiesSize; -#if !ASSERT_DISABLED - bool overflow = updatedSize > m_decodedPropertiesSize && deltaBytes < 0; - bool underflow = updatedSize < m_decodedPropertiesSize && deltaBytes > 0; - ASSERT(!overflow && !underflow); -#endif - m_decodedPropertiesSize = updatedSize; - if (imageObserver()) - imageObserver()->decodedSizeChanged(this, deltaBytes); + return frameImageAtIndex(m_currentFrame, SubsamplingLevel::Default, { }, targetContext); } -void BitmapImage::updateSize(ImageOrientationDescription description) const +#if USE(CG) +NativeImagePtr BitmapImage::nativeImageOfSize(const IntSize& size, const GraphicsContext* targetContext) { - if (!m_sizeAvailable || m_haveSize) - return; + size_t count = frameCount(); - m_size = m_source.size(description); - m_sizeRespectingOrientation = m_source.size(ImageOrientationDescription(RespectImageOrientation, description.imageOrientation())); - m_imageOrientation = static_cast<unsigned>(description.imageOrientation()); - m_shouldRespectImageOrientation = static_cast<unsigned>(description.respectImageOrientation()); -#if PLATFORM(IOS) - m_originalSize = m_source.originalSize(); - m_originalSizeRespectingOrientation = m_source.originalSize(RespectImageOrientation); -#endif - m_haveSize = true; - didDecodeProperties(); -} + for (size_t i = 0; i < count; ++i) { + auto image = frameImageAtIndex(i, SubsamplingLevel::Default, { }, targetContext); + if (image && nativeImageSize(image) == size) + return image; + } -IntSize BitmapImage::size() const -{ - updateSize(); - return m_size; + // Fallback to the first frame image if we can't find the right size + return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext); } -IntSize BitmapImage::sizeRespectingOrientation(ImageOrientationDescription description) const +Vector<NativeImagePtr> BitmapImage::framesNativeImages() { - updateSize(description); - return m_sizeRespectingOrientation; -} + Vector<NativeImagePtr> images; + size_t count = frameCount(); -#if PLATFORM(IOS) -IntSize BitmapImage::originalSize() const -{ - updateSize(); - return m_originalSize; -} + for (size_t i = 0; i < count; ++i) { + if (auto image = frameImageAtIndex(i)) + images.append(image); + } -IntSize BitmapImage::originalSizeRespectingOrientation() const -{ - updateSize(); - return m_originalSizeRespectingOrientation; + return images; } #endif -IntSize BitmapImage::currentFrameSize() const +#if !ASSERT_DISABLED +bool BitmapImage::notSolidColor() { - if (!m_currentFrame || m_hasUniformFrameSize) - return size(); - IntSize frameSize = m_source.frameSizeAtIndex(m_currentFrame); - didDecodeProperties(); - return frameSize; + return size().width() != 1 || size().height() != 1 || frameCount() > 1; } +#endif -bool BitmapImage::getHotSpot(IntPoint& hotSpot) const +void BitmapImage::draw(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode mode, ImageOrientationDescription description) { - bool result = m_source.getHotSpot(hotSpot); - didDecodeProperties(); - return result; -} + if (destRect.isEmpty() || srcRect.isEmpty()) + return; -bool BitmapImage::dataChanged(bool allDataReceived) -{ - // Because we're modifying the current frame, clear its (now possibly - // inaccurate) metadata as well. -#if !PLATFORM(IOS) - // Clear all partially-decoded frames. For most image formats, there is only - // one frame, but at least GIF and ICO can have more. With GIFs, the frames - // come in order and we ask to decode them in order, waiting to request a - // subsequent frame until the prior one is complete. Given that we clear - // incomplete frames here, this means there is at most one incomplete frame - // (even if we use destroyDecodedData() -- since it doesn't reset the - // metadata), and it is after all the complete frames. - // - // With ICOs, on the other hand, we may ask for arbitrary frames at - // different times (e.g. because we're displaying a higher-resolution image - // in the content area and using a lower-resolution one for the favicon), - // and the frames aren't even guaranteed to appear in the file in the same - // order as in the directory, so an arbitrary number of the frames might be - // incomplete (if we ask for frames for which we've not yet reached the - // start of the frame data), and any or none of them might be the particular - // frame affected by appending new data here. Thus we have to clear all the - // incomplete frames to be safe. - unsigned frameBytesCleared = 0; - for (size_t i = 0; i < m_frames.size(); ++i) { - // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to - // decode any uncached (i.e. never-decoded or - // cleared-on-a-previous-pass) frames! - unsigned frameBytes = m_frames[i].m_frameBytes; - if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete) - frameBytesCleared += (m_frames[i].clear(true) ? frameBytes : 0); - } - destroyMetadataAndNotify(frameBytesCleared); -#else - int deltaBytes = 0; - if (!m_frames.isEmpty()) { - int bytes = m_frames[m_frames.size() - 1].m_frameBytes; - if (m_frames[m_frames.size() - 1].clear(true)) { - deltaBytes += bytes; - deltaBytes += m_decodedPropertiesSize; - m_decodedPropertiesSize = 0; - } - } - destroyMetadataAndNotify(deltaBytes); -#endif - - // Feed all the data we've seen so far to the image decoder. - m_allDataReceived = allDataReceived; -#if PLATFORM(IOS) - // FIXME: We should expose a setting to enable/disable progressive loading and make this - // code conditional on it. Then we can remove the PLATFORM(IOS)-guard. - static const double chunkLoadIntervals[] = {0, 1, 3, 6, 15}; - double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, static_cast<uint16_t>(4))]; - - bool needsUpdate = false; - if (currentTime() - m_progressiveLoadChunkTime > interval) { // The first time through, the chunk time will be 0 and the image will get an update. - needsUpdate = true; - m_progressiveLoadChunkTime = currentTime(); - ASSERT(m_progressiveLoadChunkCount <= std::numeric_limits<uint16_t>::max()); - ++m_progressiveLoadChunkCount; + m_sizeForDrawing = enclosingIntRect(destRect).size(); + StartAnimationResult result = internalStartAnimation(); + + Color color; + if (result == StartAnimationResult::DecodingActive && showDebugBackground()) + color = Color::yellow; + else + color = singlePixelSolidColor(); + + if (color.isValid()) { + fillWithSolidColor(context, destRect, color, op); + return; } - if (needsUpdate || allDataReceived) - m_source.setData(data(), allDataReceived); -#else - m_source.setData(data(), allDataReceived); -#endif - m_haveFrameCount = false; - m_hasUniformFrameSize = true; - return isSizeAvailable(); -} + float scale = subsamplingScale(context, destRect, srcRect); + m_currentSubsamplingLevel = allowSubsampling() ? m_source.subsamplingLevelForScale(scale) : SubsamplingLevel::Default; + LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld subsamplingLevel = %d scale = %.4f]", __FUNCTION__, this, sourceURL().characters8(), m_currentFrame, static_cast<int>(m_currentSubsamplingLevel), scale); -String BitmapImage::filenameExtension() const -{ - return m_source.filenameExtension(); + ASSERT_IMPLIES(result == StartAnimationResult::DecodingActive, m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing)); + auto image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing, &context); + if (!image) // If it's too early we won't have an image yet. + return; + + ImageOrientation orientation(description.imageOrientation()); + if (description.respectImageOrientation() == RespectImageOrientation) + orientation = frameOrientationAtIndex(m_currentFrame); + + drawNativeImage(image, context, destRect, srcRect, IntSize(size()), op, mode, orientation); + + if (imageObserver()) + imageObserver()->didDraw(this); } -size_t BitmapImage::frameCount() +void BitmapImage::drawPattern(GraphicsContext& ctxt, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& transform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) { - if (!m_haveFrameCount) { - m_frameCount = m_source.frameCount(); - // If decoder is not initialized yet, m_source.frameCount() returns 0. - if (m_frameCount) { - didDecodeProperties(); - m_haveFrameCount = true; - } + if (tileRect.isEmpty()) + return; + + if (!ctxt.drawLuminanceMask()) { + Image::drawPattern(ctxt, destRect, tileRect, transform, phase, spacing, op, blendMode); + return; } - return m_frameCount; -} -bool BitmapImage::isSizeAvailable() -{ - if (m_sizeAvailable) - return true; + if (!m_cachedImage) { + auto buffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(tileRect.size()), ColorSpaceSRGB, ctxt); + if (!buffer) + return; - m_sizeAvailable = m_source.isSizeAvailable(); - didDecodeProperties(); + ImageObserver* observer = imageObserver(); - return m_sizeAvailable; -} + // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. + setImageObserver(nullptr); -#if !PLATFORM(IOS) -bool BitmapImage::ensureFrameIsCached(size_t index) -{ - if (index >= frameCount()) - return false; + draw(buffer->context(), tileRect, tileRect, op, blendMode, ImageOrientationDescription()); - if (index >= m_frames.size() || !m_frames[index].m_frame) - cacheFrame(index); - return true; -} -#else -bool BitmapImage::ensureFrameInfoIsCached(size_t index) -{ - if (index >= frameCount()) - return false; + setImageObserver(observer); + buffer->convertToLuminanceMask(); - if (index >= m_frames.size() || !m_frames[index].m_haveInfo) - cacheFrameInfo(index); - return true; -} -#endif + m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled); + if (!m_cachedImage) + return; + } -PassNativeImagePtr BitmapImage::frameAtIndex(size_t index) -{ -#if PLATFORM(IOS) - return frameAtIndex(index, 1.0f); -#else - if (!ensureFrameIsCached(index)) - return nullptr; - return m_frames[index].m_frame; -#endif + ctxt.setDrawLuminanceMask(false); + m_cachedImage->drawPattern(ctxt, destRect, tileRect, transform, phase, spacing, op, blendMode); } -#if PLATFORM(IOS) -PassNativeImagePtr BitmapImage::frameAtIndex(size_t index, float scaleHint) +bool BitmapImage::shouldAnimate() { - if (index >= frameCount()) - return nullptr; - - if (index >= m_frames.size() || !m_frames[index].m_frame) - cacheFrame(index, scaleHint); - else if (std::min(1.0f, scaleHint) > m_frames[index].m_scale) { - // If the image is already cached, but at too small a size, re-decode a larger version. - int sizeChange = -m_frames[index].m_frameBytes; - ASSERT(static_cast<int>(m_decodedSize) + sizeChange >= 0); - m_frames[index].clear(true); - invalidatePlatformData(); - m_decodedSize += sizeChange; - if (imageObserver()) - imageObserver()->decodedSizeChanged(this, sizeChange); - - cacheFrame(index, scaleHint); - } - return m_frames[index].m_frame; + return repetitionCount() && !m_animationFinished && imageObserver(); } -#endif -bool BitmapImage::frameIsCompleteAtIndex(size_t index) +bool BitmapImage::canAnimate() { -#if PLATFORM(IOS) - // FIXME: cacheFrameInfo does not set m_isComplete. Should it? - if (!ensureFrameInfoIsCached(index)) - return false; -#else - if (!ensureFrameIsCached(index)) - return false; -#endif - return m_frames[index].m_isComplete; + return shouldAnimate() && frameCount() > 1; } -float BitmapImage::frameDurationAtIndex(size_t index) +bool BitmapImage::isLargeImageAsyncDecodingRequired() { -#if PLATFORM(IOS) - if (!ensureFrameInfoIsCached(index)) - return 0; -#else - if (!ensureFrameIsCached(index)) - return 0; -#endif - return m_frames[index].m_duration; + return !canAnimate() && allowLargeImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired()); } -PassNativeImagePtr BitmapImage::nativeImageForCurrentFrame() +bool BitmapImage::isAnimatedImageAsyncDecodingRequired() { - return frameAtIndex(currentFrame()); + return canAnimate() && allowAnimatedImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired()); } -bool BitmapImage::frameHasAlphaAtIndex(size_t index) +void BitmapImage::clearTimer() { -#if PLATFORM(IOS) - if (!ensureFrameInfoIsCached(index)) - return true; // FIXME: Why would an invalid index return true here? -#else - if (m_frames.size() <= index) - return true; -#endif - if (m_frames[index].m_haveMetadata) - return m_frames[index].m_hasAlpha; - - return m_source.frameHasAlphaAtIndex(index); + m_frameTimer = nullptr; } -bool BitmapImage::currentFrameKnownToBeOpaque() +void BitmapImage::startTimer(double delay) { - return !frameHasAlphaAtIndex(currentFrame()); + ASSERT(!m_frameTimer); + m_frameTimer = std::make_unique<Timer>(*this, &BitmapImage::advanceAnimation); + m_frameTimer->startOneShot(delay); } -ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index) +BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation() { -#if PLATFORM(IOS) - // FIXME: cacheFrameInfo does not set m_orientation. Should it? - if (!ensureFrameInfoIsCached(index)) - return DefaultImageOrientation; -#else - if (!ensureFrameIsCached(index)) - return DefaultImageOrientation; -#endif + if (!canAnimate()) + return StartAnimationResult::CannotStart; - if (m_frames[index].m_haveMetadata) - return m_frames[index].m_orientation; + if (m_frameTimer) + return StartAnimationResult::TimerActive; + + // Don't start a new animation until we draw the frame that is currently being decoded. + size_t nextFrame = (m_currentFrame + 1) % frameCount(); + if (frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing)) { + LOG(Images, "BitmapImage::%s - %p - url: %s [nextFrame = %ld is being decoded]", __FUNCTION__, this, sourceURL().characters8(), nextFrame); + return StartAnimationResult::DecodingActive; + } - return m_source.orientationAtIndex(index); -} + if (m_currentFrame >= frameCount() - 1) { + // Don't advance past the last frame if we haven't decoded the whole image + // yet and our repetition count is potentially unset. The repetition count + // in a GIF can potentially come after all the rest of the image data, so + // wait on it. + if (!m_source.isAllDataReceived() && repetitionCount() == RepetitionCountOnce) + return StartAnimationResult::IncompleteData; -#if !ASSERT_DISABLED -bool BitmapImage::notSolidColor() -{ - return size().width() != 1 || size().height() != 1 || frameCount() > 1; -} -#endif + ++m_repetitionsComplete; -int BitmapImage::repetitionCount(bool imageKnownToBeComplete) -{ - if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Uncertain) && imageKnownToBeComplete)) { - // Snag the repetition count. If |imageKnownToBeComplete| is false, the - // repetition count may not be accurate yet for GIFs; in this case the - // decoder will default to cAnimationLoopOnce, and we'll try and read - // the count again once the whole image is decoded. - m_repetitionCount = m_source.repetitionCount(); - didDecodeProperties(); - m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount == cAnimationNone) ? Certain : Uncertain; + // Check for the end of animation. + if (repetitionCount() != RepetitionCountInfinite && m_repetitionsComplete >= repetitionCount()) { + m_animationFinished = true; + destroyDecodedDataIfNecessary(false); + return StartAnimationResult::CannotStart; + } + + destroyDecodedDataIfNecessary(true); } - return m_repetitionCount; -} -bool BitmapImage::shouldAnimate() -{ - return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver()); -} + // Don't advance the animation to an incomplete frame. + if (!m_source.isAllDataReceived() && !frameIsCompleteAtIndex(nextFrame)) + return StartAnimationResult::IncompleteData; -void BitmapImage::startAnimation(bool catchUpIfNecessary) -{ - if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) - return; + double time = monotonicallyIncreasingTime(); - // If we aren't already animating, set now as the animation start time. - const double time = monotonicallyIncreasingTime(); + // Handle initial state. if (!m_desiredFrameStartTime) m_desiredFrameStartTime = time; - // Don't advance the animation to an incomplete frame. - size_t nextFrame = (m_currentFrame + 1) % frameCount(); - if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame)) - return; - - // Don't advance past the last frame if we haven't decoded the whole image - // yet and our repetition count is potentially unset. The repetition count - // in a GIF can potentially come after all the rest of the image data, so - // wait on it. - if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1)) - return; - - // Determine time for next frame to start. By ignoring paint and timer lag - // in this calculation, we make the animation appear to run at its desired - // rate regardless of how fast it's being repainted. - const double currentDuration = frameDurationAtIndex(m_currentFrame); - m_desiredFrameStartTime += currentDuration; - -#if !PLATFORM(IOS) - // When an animated image is more than five minutes out of date, the - // user probably doesn't care about resyncing and we could burn a lot of - // time looping through frames below. Just reset the timings. - const double cAnimationResyncCutoff = 5 * 60; - if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff) - m_desiredFrameStartTime = time + currentDuration; + // Setting 'm_desiredFrameStartTime' to 'time' means we are late; otherwise we are early. + m_desiredFrameStartTime = std::max(time, m_desiredFrameStartTime + frameDurationAtIndex(m_currentFrame)); + + // Request async decoding for nextFrame only if this is required. If nextFrame is not in the frameCache, + // it will be decoded on a separate work queue. When decoding nextFrame finishes, we will be notified + // through the callback newFrameNativeImageAvailableAtIndex(). Otherwise, advanceAnimation() will be called + // when the timer fires and m_currentFrame will be advanced to nextFrame since it is not being decoded. + if (m_sizeForDrawing && isAnimatedImageAsyncDecodingRequired()) { + bool isAsyncDecode = m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel, *m_sizeForDrawing); + +#if !LOG_DISABLED + if (isAsyncDecode) + LOG(Images, "BitmapImage::%s - %p - url: %s [requesting async decoding for nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), nextFrame); + else + LOG(Images, "BitmapImage::%s - %p - url: %s [cachedFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_cachedFrameCount, nextFrame); #else - // Maintaining frame-to-frame delays is more important than - // maintaining absolute animation timing, so reset the timings each frame. - m_desiredFrameStartTime = time + currentDuration; + UNUSED_PARAM(isAsyncDecode); #endif - // The image may load more slowly than it's supposed to animate, so that by - // the time we reach the end of the first repetition, we're well behind. - // Clamp the desired frame start time in this case, so that we don't skip - // frames (or whole iterations) trying to "catch up". This is a tradeoff: - // It guarantees users see the whole animation the second time through and - // don't miss any repetitions, and is closer to what other browsers do; on - // the other hand, it makes animations "less accurate" for pages that try to - // sync an image and some other resource (e.g. audio), especially if users - // switch tabs (and thus stop drawing the animation, which will pause it) - // during that initial loop, then switch back later. - if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time) - m_desiredFrameStartTime = time; + m_desiredFrameDecodeTimeForTesting = time + std::max(m_frameDecodingDurationForTesting, 0.0f); + } - if (!catchUpIfNecessary || time < m_desiredFrameStartTime) { - // Haven't yet reached time for next frame to start; delay until then. - m_frameTimer = std::make_unique<Timer<BitmapImage>>(this, &BitmapImage::advanceAnimation); - m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.)); - } else { - // We've already reached or passed the time for the next frame to start. - // See if we've also passed the time for frames after that to start, in - // case we need to skip some frames entirely. Remember not to advance - // to an incomplete frame. - for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) { - // Should we skip the next frame? - double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame); - if (time < frameAfterNextStartTime) - break; - - // Yes; skip over it without notifying our observers. - if (!internalAdvanceAnimation(true)) - return; - m_desiredFrameStartTime = frameAfterNextStartTime; - nextFrame = frameAfterNext; - } + ASSERT(!m_frameTimer); + startTimer(m_desiredFrameStartTime - time); + return StartAnimationResult::Started; +} - // Draw the next frame immediately. Note that m_desiredFrameStartTime - // may be in the past, meaning the next time through this function we'll - // kick off the next advancement sooner than this frame's duration would - // suggest. - if (internalAdvanceAnimation(false)) { - // The image region has been marked dirty, but once we return to our - // caller, draw() will clear it, and nothing will cause the - // animation to advance again. We need to start the timer for the - // next frame running, or the animation can hang. (Compare this - // with when advanceAnimation() is called, and the region is dirtied - // while draw() is not in the callstack, meaning draw() gets called - // to update the region and thus startAnimation() is reached again.) - // NOTE: For large images with slow or heavily-loaded systems, - // throwing away data as we go (see destroyDecodedData()) means we - // can spend so much time re-decoding data above that by the time we - // reach here we're behind again. If we let startAnimation() run - // the catch-up code again, we can get long delays without painting - // as we race the timer, or even infinite recursion. In this - // situation the best we can do is to simply change frames as fast - // as possible, so force startAnimation() to set a zero-delay timer - // and bail out if we're not caught up. - startAnimation(false); +void BitmapImage::advanceAnimation() +{ + clearTimer(); + + // Pretend as if decoding nextFrame has taken m_frameDecodingDurationForTesting from + // the time this decoding was requested. + if (isAsyncDecodingForcedForTesting()) { + double time = monotonicallyIncreasingTime(); + // Start a timer with the remaining time from now till the m_desiredFrameDecodeTime. + if (m_desiredFrameDecodeTimeForTesting > std::max(time, m_desiredFrameStartTime)) { + startTimer(m_desiredFrameDecodeTimeForTesting - time); + return; } } + + // Don't advance to nextFrame unless its decoding has finished or was not required. + size_t nextFrame = (m_currentFrame + 1) % frameCount(); + if (!frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing)) + internalAdvanceAnimation(); + else { + // Force repaint if showDebugBackground() is on. + if (showDebugBackground()) + imageObserver()->changedInRect(this); + LOG(Images, "BitmapImage::%s - %p - url: %s [lateFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_lateFrameCount, nextFrame); + } +} + +void BitmapImage::internalAdvanceAnimation() +{ + m_currentFrame = (m_currentFrame + 1) % frameCount(); + ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame, m_sizeForDrawing)); + + destroyDecodedDataIfNecessary(false); + + if (imageObserver()) + imageObserver()->animationAdvanced(this); + + LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), m_currentFrame); } void BitmapImage::stopAnimation() { - // This timer is used to animate all occurrences of this image. Don't invalidate + // This timer is used to animate all occurrences of this image. Don't invalidate // the timer unless all renderers have stopped drawing. - m_frameTimer = nullptr; + clearTimer(); + m_source.stopAsyncDecodingQueue(); } void BitmapImage::resetAnimation() { stopAnimation(); m_currentFrame = 0; - m_repetitionsComplete = 0; + m_repetitionsComplete = RepetitionCountNone; m_desiredFrameStartTime = 0; m_animationFinished = false; - + // For extremely large animations, when the animation is reset, we just throw everything away. destroyDecodedDataIfNecessary(true); } -void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& transform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) +void BitmapImage::newFrameNativeImageAvailableAtIndex(size_t index) { - if (tileRect.isEmpty()) - return; - - if (!ctxt->drawLuminanceMask()) { - Image::drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode); - return; - } - if (!m_cachedImage) { - std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(tileRect.size())); - ASSERT(buffer.get()); - - ImageObserver* observer = imageObserver(); - ASSERT(observer); - - // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. - setImageObserver(0); - - draw(buffer->context(), tileRect, tileRect, styleColorSpace, op, blendMode, ImageOrientationDescription()); + UNUSED_PARAM(index); + ASSERT(index == (m_currentFrame + 1) % frameCount()); - setImageObserver(observer); - buffer->convertToLuminanceMask(); - - m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled); - m_cachedImage->setSpaceSize(spaceSize()); - - setImageObserver(observer); - } - - ctxt->setDrawLuminanceMask(false); - m_cachedImage->drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode); + // Don't advance to nextFrame unless the timer was fired before its decoding finishes. + if (canAnimate() && !m_frameTimer) + internalAdvanceAnimation(); + else + LOG(Images, "BitmapImage::%s - %p - url: %s [earlyFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_earlyFrameCount, index); } - -void BitmapImage::advanceAnimation(Timer<BitmapImage>&) +void BitmapImage::dump(TextStream& ts) const { - internalAdvanceAnimation(false); - // At this point the image region has been marked dirty, and if it's - // onscreen, we'll soon make a call to draw(), which will call - // startAnimation() again to keep the animation moving. -} - -bool BitmapImage::internalAdvanceAnimation(bool skippingFrames) -{ - // Stop the animation. - stopAnimation(); + Image::dump(ts); -#if !PLATFORM(IOS) - // See if anyone is still paying attention to this animation. If not, we don't - // advance and will remain suspended at the current frame until the animation is resumed. - if (!skippingFrames && imageObserver()->shouldPauseAnimation(this)) - return false; -#endif - - ++m_currentFrame; - bool advancedAnimation = true; - bool destroyAll = false; - if (m_currentFrame >= frameCount()) { - ++m_repetitionsComplete; - - // Get the repetition count again. If we weren't able to get a - // repetition count before, we should have decoded the whole image by - // now, so it should now be available. - // Note that we don't need to special-case cAnimationLoopOnce here - // because it is 0 (see comments on its declaration in ImageSource.h). - if (repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsComplete > m_repetitionCount) { - m_animationFinished = true; - m_desiredFrameStartTime = 0; - --m_currentFrame; - advancedAnimation = false; - } else { - m_currentFrame = 0; - destroyAll = true; - } - } - destroyDecodedDataIfNecessary(destroyAll); - - // We need to draw this frame if we advanced to it while not skipping, or if - // while trying to skip frames we hit the last frame and thus had to stop. - if (skippingFrames != advancedAnimation) - imageObserver()->animationAdvanced(this); - return advancedAnimation; -} - -bool BitmapImage::mayFillWithSolidColor() -{ - if (!m_checkedForSolidColor && frameCount() > 0) { - checkForSolidColor(); - // WINCE PORT: checkForSolidColor() doesn't set m_checkedForSolidColor until - // it gets enough information to make final decision. -#if !OS(WINCE) - ASSERT(m_checkedForSolidColor); -#endif - } - return m_isSolidColor && !m_currentFrame; -} - -Color BitmapImage::solidColor() const -{ - return m_solidColor; -} + if (isAnimated()) + ts.dumpProperty("current-frame", m_currentFrame); -bool BitmapImage::canAnimate() -{ - return shouldAnimate() && frameCount() > 1; + m_source.dump(ts); } } diff --git a/Source/WebCore/platform/graphics/BitmapImage.h b/Source/WebCore/platform/graphics/BitmapImage.h index c2161b61a..30add02af 100644 --- a/Source/WebCore/platform/graphics/BitmapImage.h +++ b/Source/WebCore/platform/graphics/BitmapImage.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2013 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc. All rights reserved. * Copyright (C) 2008-2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,16 +25,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BitmapImage_h -#define BitmapImage_h +#pragma once #include "Image.h" #include "Color.h" +#include "ImageObserver.h" #include "ImageOrientation.h" #include "ImageSource.h" #include "IntSize.h" +#include "URL.h" -#if PLATFORM(MAC) +#if USE(CG) || USE(APPKIT) #include <wtf/RetainPtr.h> #endif @@ -47,310 +48,183 @@ typedef struct HBITMAP__ *HBITMAP; #endif namespace WebCore { - struct FrameData; -} -namespace WTF { - template<> struct VectorTraits<WebCore::FrameData> : public SimpleClassVectorTraits { - static const bool canInitializeWithMemset = false; // Not all FrameData members initialize to 0. - }; -} - -namespace WebCore { - -template <typename T> class Timer; - -// ================================================ -// FrameData Class -// ================================================ - -struct FrameData { - WTF_MAKE_NONCOPYABLE(FrameData); -public: - FrameData() - : m_frame(0) - , m_orientation(DefaultImageOrientation) -#if PLATFORM(IOS) - , m_scale(0) - , m_haveInfo(false) -#endif - , m_duration(0) - , m_haveMetadata(false) - , m_isComplete(false) - , m_hasAlpha(true) - , m_frameBytes(0) - { - } - - ~FrameData() - { - clear(true); - } - - // Clear the cached image data on the frame, and (optionally) the metadata. - // Returns whether there was cached image data to clear. - bool clear(bool clearMetadata); - - NativeImagePtr m_frame; - ImageOrientation m_orientation; -#if PLATFORM(IOS) - float m_scale; - bool m_haveInfo; -#endif - float m_duration; - bool m_haveMetadata : 1; - bool m_isComplete : 1; - bool m_hasAlpha : 1; - unsigned m_frameBytes; -}; - -// ================================================= -// BitmapImage Class -// ================================================= - -// FIXME: We should better integrate the iOS and non-iOS code in this class. Unlike other ports, the -// iOS port caches the metadata for a frame without decoding the image. +class Timer; class BitmapImage final : public Image { - friend class GeneratedImage; - friend class CrossfadeGeneratedImage; - friend class GradientImage; - friend class GraphicsContext; public: - static PassRefPtr<BitmapImage> create(PassNativeImagePtr nativeImage, ImageObserver* observer = 0) + static Ref<BitmapImage> create(NativeImagePtr&& nativeImage, ImageObserver* observer = nullptr) { - return adoptRef(new BitmapImage(nativeImage, observer)); + return adoptRef(*new BitmapImage(WTFMove(nativeImage), observer)); } - static PassRefPtr<BitmapImage> create(ImageObserver* observer = 0) + static Ref<BitmapImage> create(ImageObserver* observer = nullptr) { - return adoptRef(new BitmapImage(observer)); + return adoptRef(*new BitmapImage(observer)); } #if PLATFORM(WIN) - static PassRefPtr<BitmapImage> create(HBITMAP); + WEBCORE_EXPORT static RefPtr<BitmapImage> create(HBITMAP); #endif virtual ~BitmapImage(); - virtual bool isBitmapImage() const override { return true; } + bool hasSingleSecurityOrigin() const override { return true; } - virtual bool hasSingleSecurityOrigin() const override; + bool dataChanged(bool allDataReceived) override; + unsigned decodedSize() const { return m_source.decodedSize(); } - virtual IntSize size() const override; - IntSize sizeRespectingOrientation(ImageOrientationDescription = ImageOrientationDescription()) const; -#if PLATFORM(IOS) - virtual IntSize originalSize() const; - IntSize originalSizeRespectingOrientation() const; -#endif - IntSize currentFrameSize() const; - virtual bool getHotSpot(IntPoint&) const override; + bool isSizeAvailable() const { return m_source.isSizeAvailable(); } + size_t frameCount() const { return m_source.frameCount(); } + RepetitionCount repetitionCount() const { return m_source.repetitionCount(); } + String filenameExtension() const override { return m_source.filenameExtension(); } + std::optional<IntPoint> hotSpot() const override { return m_source.hotSpot(); } - unsigned decodedSize() const { return m_decodedSize; } + // FloatSize due to override. + FloatSize size() const override { return m_source.size(); } + IntSize sizeRespectingOrientation() const { return m_source.sizeRespectingOrientation(); } + Color singlePixelSolidColor() const override { return m_source.singlePixelSolidColor(); } - virtual bool dataChanged(bool allDataReceived) override; - virtual String filenameExtension() const override; + bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameIsBeingDecodedAtIndex(index, sizeForDrawing); } + bool frameHasDecodedNativeImage(size_t index) const { return m_source.frameHasDecodedNativeImage(index); } + bool frameIsCompleteAtIndex(size_t index) const { return m_source.frameIsCompleteAtIndex(index); } + bool frameHasAlphaAtIndex(size_t index) const { return m_source.frameHasAlphaAtIndex(index); } + bool frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); } + SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) const { return m_source.frameSubsamplingLevelAtIndex(index); } - // It may look unusual that there is no start animation call as public API. This is because - // we start and stop animating lazily. Animation begins whenever someone draws the image. It will - // automatically pause once all observers no longer want to render the image anywhere. - virtual void stopAnimation() override; - virtual void resetAnimation() override; + float frameDurationAtIndex(size_t index) const { return m_source.frameDurationAtIndex(index); } + ImageOrientation frameOrientationAtIndex(size_t index) const { return m_source.frameOrientationAtIndex(index); } - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal) override; + size_t currentFrame() const { return m_currentFrame; } + bool currentFrameKnownToBeOpaque() const override { return !frameHasAlphaAtIndex(currentFrame()); } + ImageOrientation orientationForCurrentFrame() const override { return frameOrientationAtIndex(currentFrame()); } - // Accessors for native image formats. + bool isAsyncDecodingForcedForTesting() const { return m_frameDecodingDurationForTesting > 0; } + void setFrameDecodingDurationForTesting(float duration) { m_frameDecodingDurationForTesting = duration; } + bool isLargeImageAsyncDecodingRequired(); + bool isAnimatedImageAsyncDecodingRequired(); + // Accessors for native image formats. #if USE(APPKIT) - virtual NSImage* getNSImage() override; -#endif - -#if PLATFORM(MAC) - virtual CFDataRef getTIFFRepresentation() override; + NSImage *nsImage() override; + RetainPtr<NSImage> snapshotNSImage() override; #endif -#if USE(CG) - virtual CGImageRef getCGImageRef() override; - virtual CGImageRef getFirstCGImageRefOfSize(const IntSize&) override; - virtual RetainPtr<CFArrayRef> getCGImageArray() override; +#if PLATFORM(COCOA) + CFDataRef tiffRepresentation() override; #endif #if PLATFORM(WIN) - virtual bool getHBITMAP(HBITMAP) override; - virtual bool getHBITMAPOfSize(HBITMAP, const IntSize*) override; + bool getHBITMAP(HBITMAP) override; + bool getHBITMAPOfSize(HBITMAP, const IntSize*) override; #endif #if PLATFORM(GTK) - virtual GdkPixbuf* getGdkPixbuf() override; + GdkPixbuf* getGdkPixbuf() override; #endif -#if PLATFORM(EFL) - virtual Evas_Object* getEvasObject(Evas*) override; + WEBCORE_EXPORT NativeImagePtr nativeImage(const GraphicsContext* = nullptr) override; + NativeImagePtr nativeImageForCurrentFrame(const GraphicsContext* = nullptr) override; +#if USE(CG) + NativeImagePtr nativeImageOfSize(const IntSize&, const GraphicsContext* = nullptr) override; + Vector<NativeImagePtr> framesNativeImages() override; #endif - virtual PassNativeImagePtr nativeImageForCurrentFrame() override; - virtual ImageOrientation orientationForCurrentFrame() override { return frameOrientationAtIndex(currentFrame()); } - - virtual bool currentFrameKnownToBeOpaque() override; - - bool canAnimate(); - -private: - void updateSize(ImageOrientationDescription = ImageOrientationDescription()) const; - protected: - enum RepetitionCountStatus { - Unknown, // We haven't checked the source's repetition count. - Uncertain, // We have a repetition count, but it might be wrong (some GIFs have a count after the image data, and will report "loop once" until all data has been decoded). - Certain // The repetition count is known to be correct. - }; + WEBCORE_EXPORT BitmapImage(NativeImagePtr&&, ImageObserver* = nullptr); + WEBCORE_EXPORT BitmapImage(ImageObserver* = nullptr); - BitmapImage(PassNativeImagePtr, ImageObserver* = 0); - BitmapImage(ImageObserver* = 0); + NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* = nullptr); -#if PLATFORM(WIN) - virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, ColorSpace styleColorSpace, CompositeOperator) override; -#endif - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override; - -#if USE(WINGDI) - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect); -#endif - - size_t currentFrame() const { return m_currentFrame; } -#if PLATFORM(IOS) - PassNativeImagePtr frameAtIndex(size_t, float scaleHint); - PassNativeImagePtr copyUnscaledFrameAtIndex(size_t); -#endif - size_t frameCount(); - PassNativeImagePtr frameAtIndex(size_t); - bool frameIsCompleteAtIndex(size_t); - float frameDurationAtIndex(size_t); - bool frameHasAlphaAtIndex(size_t); - ImageOrientation frameOrientationAtIndex(size_t); - - // Decodes and caches a frame. Never accessed except internally. -#if PLATFORM(IOS) - void cacheFrame(size_t index, float scaleHint); - - // Cache frame metadata without decoding image. - void cacheFrameInfo(size_t index); - // Called before accessing m_frames[index] for info without decoding. Returns false on index out of bounds. - bool ensureFrameInfoIsCached(size_t index); -#else - void cacheFrame(size_t index); - // Called before accessing m_frames[index]. Returns false on index out of bounds. - bool ensureFrameIsCached(size_t index); -#endif + String sourceURL() const { return imageObserver() ? imageObserver()->sourceUrl().string() : emptyString(); } + bool allowSubsampling() const { return imageObserver() && imageObserver()->allowSubsampling(); } + bool allowLargeImageAsyncDecoding() const { return imageObserver() && imageObserver()->allowLargeImageAsyncDecoding(); } + bool allowAnimatedImageAsyncDecoding() const { return imageObserver() && imageObserver()->allowAnimatedImageAsyncDecoding(); } + bool showDebugBackground() const { return imageObserver() && imageObserver()->showDebugBackground(); } - // Called to invalidate cached data. When |destroyAll| is true, we wipe out + // Called to invalidate cached data. When |destroyAll| is true, we wipe out // the entire frame buffer cache and tell the image source to destroy // everything; this is used when e.g. we want to free some room in the image - // cache. If |destroyAll| is false, we only delete frames up to the current + // cache. If |destroyAll| is false, we only delete frames up to the current // one; this is used while animating large images to keep memory footprint // low without redecoding the whole image on every frame. - virtual void destroyDecodedData(bool destroyAll = true) override; + void destroyDecodedData(bool destroyAll = true) override; // If the image is large enough, calls destroyDecodedData() and passes // |destroyAll| along. - void destroyDecodedDataIfNecessary(bool destroyAll); + void destroyDecodedDataIfNecessary(bool destroyAll = true); - // Generally called by destroyDecodedData(), destroys whole-image metadata - // and notifies observers that the memory footprint has (hopefully) - // decreased by |frameBytesCleared|. - void destroyMetadataAndNotify(unsigned frameBytesCleared); - - // Whether or not size is available yet. - bool isSizeAvailable(); - - // Called after asking the source for any information that may require - // decoding part of the image (e.g., the image size). We need to report - // the partially decoded data to our observer so it has an accurate - // account of the BitmapImage's memory usage. - void didDecodeProperties() const; + void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override; + void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal) override; +#if PLATFORM(WIN) + void drawFrameMatchingSourceSize(GraphicsContext&, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) override; +#endif // Animation. - int repetitionCount(bool imageKnownToBeComplete); // |imageKnownToBeComplete| should be set if the caller knows the entire image has been decoded. + enum class StartAnimationResult { CannotStart, IncompleteData, TimerActive, DecodingActive, Started }; + bool isAnimated() const override { return m_source.frameCount() > 1; } bool shouldAnimate(); - virtual void startAnimation(bool catchUpIfNecessary = true) override; - void advanceAnimation(Timer<BitmapImage>&); + bool canAnimate(); + void startAnimation() override { internalStartAnimation(); } + StartAnimationResult internalStartAnimation(); + void advanceAnimation(); + void internalAdvanceAnimation(); - // Function that does the real work of advancing the animation. When - // skippingFrames is true, we're in the middle of a loop trying to skip over - // a bunch of animation frames, so we should not do things like decode each - // one or notify our observers. - // Returns whether the animation was advanced. - bool internalAdvanceAnimation(bool skippingFrames); + // It may look unusual that there is no start animation call as public API. This is because + // we start and stop animating lazily. Animation begins whenever someone draws the image. It will + // automatically pause once all observers no longer want to render the image anywhere. + void stopAnimation() override; + void resetAnimation() override; + void newFrameNativeImageAvailableAtIndex(size_t) override; // Handle platform-specific data void invalidatePlatformData(); - // Checks to see if the image is a 1x1 solid color. We optimize these images and just do a fill rect instead. - // This check should happen regardless whether m_checkedForSolidColor is already set, as the frame may have - // changed. - void checkForSolidColor(); - - virtual bool mayFillWithSolidColor() override; - virtual Color solidColor() const override; - #if !ASSERT_DISABLED - virtual bool notSolidColor() override; + bool notSolidColor() override; #endif -private: - ImageSource m_source; - mutable IntSize m_size; // The size to use for the overall image (will just be the size of the first image). - mutable IntSize m_sizeRespectingOrientation; - mutable unsigned m_imageOrientation : 4; // ImageOrientationEnum - mutable unsigned m_shouldRespectImageOrientation : 1; // RespectImageOrientationEnum - -#if PLATFORM(IOS) - mutable IntSize m_originalSize; // The size of the unsubsampled image. - mutable IntSize m_originalSizeRespectingOrientation; +#if PLATFORM(COCOA) + RetainPtr<CFDataRef> tiffRepresentation(const Vector<NativeImagePtr>&); #endif - size_t m_currentFrame; // The index of the current frame of animation. - Vector<FrameData, 1> m_frames; // An array of the cached frames of the animation. We have to ref frames to pin them in the cache. - - std::unique_ptr<Timer<BitmapImage>> m_frameTimer; - int m_repetitionCount; // How many total animation loops we should do. This will be cAnimationNone if this image type is incapable of animation. - RepetitionCountStatus m_repetitionCountStatus; - int m_repetitionsComplete; // How many repetitions we've finished. - double m_desiredFrameStartTime; // The system time at which we hope to see the next call to startAnimation(). -#if USE(APPKIT) - mutable RetainPtr<NSImage> m_nsImage; // A cached NSImage of frame 0. Only built lazily if someone actually queries for one. -#endif -#if USE(CG) - mutable RetainPtr<CFDataRef> m_tiffRep; // Cached TIFF rep for frame 0. Only built lazily if someone queries for one. +private: + void clearTimer(); + void startTimer(double delay); + bool isBitmapImage() const override { return true; } + void dump(TextStream&) const override; + + // Animated images over a certain size are considered large enough that we'll only hang on to one frame at a time. +#if !PLATFORM(IOS) + static const unsigned LargeAnimationCutoff = 5242880; +#else + static const unsigned LargeAnimationCutoff = 2097152; #endif - Color m_solidColor; // If we're a 1x1 solid color, this is the color to use to fill. + mutable ImageSource m_source; - unsigned m_decodedSize; // The current size of all decoded frames. - mutable unsigned m_decodedPropertiesSize; // The size of data decoded by the source to determine image properties (e.g. size, frame count, etc). - size_t m_frameCount; + size_t m_currentFrame { 0 }; // The index of the current frame of animation. + SubsamplingLevel m_currentSubsamplingLevel { SubsamplingLevel::Default }; + std::optional<IntSize> m_sizeForDrawing; + std::unique_ptr<Timer> m_frameTimer; + RepetitionCount m_repetitionsComplete { RepetitionCountNone }; // How many repetitions we've finished. + double m_desiredFrameStartTime { 0 }; // The system time at which we hope to see the next call to startAnimation(). + bool m_animationFinished { false }; -#if PLATFORM(IOS) - // FIXME: We should expose a setting to enable/disable progressive loading remove the PLATFORM(IOS)-guard. - double m_progressiveLoadChunkTime; - uint16_t m_progressiveLoadChunkCount; + float m_frameDecodingDurationForTesting { 0 }; + double m_desiredFrameDecodeTimeForTesting { 0 }; +#if !LOG_DISABLED + size_t m_lateFrameCount { 0 }; + size_t m_earlyFrameCount { 0 }; + size_t m_cachedFrameCount { 0 }; #endif - bool m_isSolidColor : 1; // Whether or not we are a 1x1 solid image. - bool m_checkedForSolidColor : 1; // Whether we've checked the frame for solid color. - - bool m_animationFinished : 1; // Whether or not we've completed the entire animation. - - bool m_allDataReceived : 1; // Whether or not we've received all our data. - mutable bool m_haveSize : 1; // Whether or not our |m_size| member variable has the final overall image size yet. - bool m_sizeAvailable : 1; // Whether or not we can obtain the size of the first image frame yet from ImageIO. - mutable bool m_hasUniformFrameSize : 1; - mutable bool m_haveFrameCount : 1; - +#if USE(APPKIT) + mutable RetainPtr<NSImage> m_nsImage; // A cached NSImage of all the frames. Only built lazily if someone actually queries for one. +#endif +#if USE(CG) + mutable RetainPtr<CFDataRef> m_tiffRep; // Cached TIFF rep for all the frames. Only built lazily if someone queries for one. +#endif RefPtr<Image> m_cachedImage; }; -IMAGE_TYPE_CASTS(BitmapImage) +} // namespace WebCore -} - -#endif +SPECIALIZE_TYPE_TRAITS_IMAGE(BitmapImage) diff --git a/Source/WebCore/platform/graphics/Color.cpp b/Source/WebCore/platform/graphics/Color.cpp index 7de24ebdc..4c0d15606 100644 --- a/Source/WebCore/platform/graphics/Color.cpp +++ b/Source/WebCore/platform/graphics/Color.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,7 +26,9 @@ #include "config.h" #include "Color.h" +#include "AnimationUtilities.h" #include "HashTools.h" +#include "TextStream.h" #include <wtf/Assertions.h> #include <wtf/DecimalNumber.h> #include <wtf/HexNumber.h> @@ -47,6 +49,16 @@ const RGBA32 Color::transparent; static const RGBA32 lightenedBlack = 0xFF545454; static const RGBA32 darkenedWhite = 0xFFABABAB; +static inline unsigned premultipliedChannel(unsigned c, unsigned a, bool ceiling = true) +{ + return fastDivideBy255(ceiling ? c * a + 254 : c * a); +} + +static inline unsigned unpremultipliedChannel(unsigned c, unsigned a) +{ + return (fastMultiplyBy255(c) + a - 1) / a; +} + RGBA32 makeRGB(int r, int g, int b) { return 0xFF000000 | std::max(0, std::min(r, 255)) << 16 | std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255)); @@ -57,6 +69,16 @@ RGBA32 makeRGBA(int r, int g, int b, int a) return std::max(0, std::min(a, 255)) << 24 | std::max(0, std::min(r, 255)) << 16 | std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255)); } +RGBA32 makePremultipliedRGBA(int r, int g, int b, int a, bool ceiling) +{ + return makeRGBA(premultipliedChannel(r, a, ceiling), premultipliedChannel(g, a, ceiling), premultipliedChannel(b, a, ceiling), a); +} + +RGBA32 makeUnPremultipliedRGBA(int r, int g, int b, int a) +{ + return makeRGBA(unpremultipliedChannel(r, a), unpremultipliedChannel(g, a), unpremultipliedChannel(b, a), a); +} + static int colorFloatToRGBAByte(float f) { // We use lroundf and 255 instead of nextafterf(256, 0) to match CG's rounding @@ -126,7 +148,7 @@ RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a) template <typename CharacterType> static inline bool parseHexColorInternal(const CharacterType* name, unsigned length, RGBA32& rgb) { - if (length != 3 && length != 6) + if (length != 3 && length != 4 && length != 6 && length != 8) return false; unsigned value = 0; for (unsigned i = 0; i < length; ++i) { @@ -139,6 +161,20 @@ static inline bool parseHexColorInternal(const CharacterType* name, unsigned len rgb = 0xFF000000 | value; return true; } + if (length == 8) { + // We parsed the values into RGBA order, but the RGBA32 type + // expects them to be in ARGB order, so we right rotate eight bits. + rgb = value << 24 | value >> 8; + return true; + } + if (length == 4) { + // #abcd converts to ddaabbcc in RGBA32. + rgb = (value & 0xF) << 28 | (value & 0xF) << 24 + | (value & 0xF000) << 8 | (value & 0xF000) << 4 + | (value & 0xF00) << 4 | (value & 0xF00) + | (value & 0xF0) | (value & 0xF0) >> 4; + return true; + } // #abc converts to #aabbcc rgb = 0xFF000000 | (value & 0xF00) << 12 | (value & 0xF00) << 8 @@ -165,42 +201,153 @@ bool Color::parseHexColor(const String& name, RGBA32& rgb) return false; if (name.is8Bit()) return parseHexColor(name.characters8(), name.length(), rgb); - return parseHexColor(name.deprecatedCharacters(), name.length(), rgb); + return parseHexColor(name.characters16(), name.length(), rgb); +} + +bool Color::parseHexColor(const StringView& name, RGBA32& rgb) +{ + unsigned length = name.length(); + if (!length) + return false; + if (name.is8Bit()) + return parseHexColor(name.characters8(), name.length(), rgb); + return parseHexColor(name.characters16(), name.length(), rgb); } int differenceSquared(const Color& c1, const Color& c2) { - int dR = c1.red() - c2.red(); - int dG = c1.green() - c2.green(); - int dB = c1.blue() - c2.blue(); + // FIXME: This is assuming that the colors are in the same colorspace. + // FIXME: This should probably return a floating point number, but many of the call + // sites have picked comparison values based on feel. We'd need to break out + // our logarithm tables to change them :) + int c1Red = c1.isExtended() ? c1.asExtended().red() * 255 : c1.red(); + int c1Green = c1.isExtended() ? c1.asExtended().green() * 255 : c1.green(); + int c1Blue = c1.isExtended() ? c1.asExtended().blue() * 255 : c1.blue(); + int c2Red = c2.isExtended() ? c2.asExtended().red() * 255 : c2.red(); + int c2Green = c2.isExtended() ? c2.asExtended().green() * 255 : c2.green(); + int c2Blue = c2.isExtended() ? c2.asExtended().blue() * 255 : c2.blue(); + int dR = c1Red - c2Red; + int dG = c1Green - c2Green; + int dB = c1Blue - c2Blue; return dR * dR + dG * dG + dB * dB; } +static inline const NamedColor* findNamedColor(const String& name) +{ + char buffer[64]; // easily big enough for the longest color name + unsigned length = name.length(); + if (length > sizeof(buffer) - 1) + return nullptr; + for (unsigned i = 0; i < length; ++i) { + UChar c = name[i]; + if (!c || !WTF::isASCII(c)) + return nullptr; + buffer[i] = toASCIILower(static_cast<char>(c)); + } + buffer[length] = '\0'; + return findColor(buffer, length); +} + Color::Color(const String& name) { if (name[0] == '#') { + RGBA32 color; + bool valid; + if (name.is8Bit()) - m_valid = parseHexColor(name.characters8() + 1, name.length() - 1, m_color); + valid = parseHexColor(name.characters8() + 1, name.length() - 1, color); else - m_valid = parseHexColor(name.deprecatedCharacters() + 1, name.length() - 1, m_color); - } else - setNamedColor(name); + valid = parseHexColor(name.characters16() + 1, name.length() - 1, color); + + if (valid) + setRGB(color); + } else { + if (auto* foundColor = findNamedColor(name)) + setRGB(foundColor->ARGBValue); + else + m_colorData.rgbaAndFlags = invalidRGBAColor; + } } Color::Color(const char* name) { + RGBA32 color; + bool valid; if (name[0] == '#') - m_valid = parseHexColor(&name[1], m_color); + valid = parseHexColor((String)&name[1], color); else { const NamedColor* foundColor = findColor(name, strlen(name)); - m_color = foundColor ? foundColor->ARGBValue : 0; - m_valid = foundColor; + color = foundColor ? foundColor->ARGBValue : 0; + valid = foundColor; } + + if (valid) + setRGB(color); +} + +Color::Color(const Color& other) + : m_colorData(other.m_colorData) +{ + if (isExtended()) + m_colorData.extendedColor->ref(); +} + +Color::Color(Color&& other) +{ + *this = WTFMove(other); +} + +Color::Color(float r, float g, float b, float a, ColorSpace colorSpace) +{ + // Zero the union, just in case a 32-bit system only assigns the + // top 32 bits when copying the extendedColor pointer below. + m_colorData.rgbaAndFlags = 0; + auto extendedColorRef = ExtendedColor::create(r, g, b, a, colorSpace); + m_colorData.extendedColor = &extendedColorRef.leakRef(); + ASSERT(isExtended()); +} + +Color::~Color() +{ + if (isExtended()) + m_colorData.extendedColor->deref(); +} + +Color& Color::operator=(const Color& other) +{ + if (*this == other) + return *this; + + if (isExtended()) + m_colorData.extendedColor->deref(); + + m_colorData = other.m_colorData; + + if (isExtended()) + m_colorData.extendedColor->ref(); + return *this; +} + +Color& Color::operator=(Color&& other) +{ + if (*this == other) + return *this; + + if (isExtended()) + m_colorData.extendedColor->deref(); + + m_colorData = other.m_colorData; + other.m_colorData.rgbaAndFlags = invalidRGBAColor; + + return *this; } String Color::serialized() const { - if (!hasAlpha()) { + if (isExtended()) + return asExtended().cssText(); + + if (isOpaque()) { StringBuilder builder; builder.reserveCapacity(7); builder.append('#'); @@ -210,65 +357,54 @@ String Color::serialized() const return builder.toString(); } - Vector<LChar> result; - result.reserveInitialCapacity(28); - const char commaSpace[] = ", "; - const char rgbaParen[] = "rgba("; - - result.append(rgbaParen, 5); - appendNumber(result, red()); - result.append(commaSpace, 2); - appendNumber(result, green()); - result.append(commaSpace, 2); - appendNumber(result, blue()); - result.append(commaSpace, 2); - - if (!alpha()) - result.append('0'); - else { - NumberToLStringBuffer buffer; - unsigned length = DecimalNumber(alpha() / 255.0).toStringDecimal(buffer, WTF::NumberToStringBufferLength); - result.append(buffer, length); - } - - result.append(')'); - return String::adopt(result); + return cssText(); } -String Color::nameForRenderTreeAsText() const +String Color::cssText() const { - if (alpha() < 0xFF) - return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha()); - return String::format("#%02X%02X%02X", red(), green(), blue()); -} + if (isExtended()) + return asExtended().cssText(); + + StringBuilder builder; + builder.reserveCapacity(28); + bool colorHasAlpha = !isOpaque(); + if (colorHasAlpha) + builder.appendLiteral("rgba("); + else + builder.appendLiteral("rgb("); -static inline const NamedColor* findNamedColor(const String& name) -{ - char buffer[64]; // easily big enough for the longest color name - unsigned length = name.length(); - if (length > sizeof(buffer) - 1) - return 0; - for (unsigned i = 0; i < length; ++i) { - UChar c = name[i]; - if (!c || c > 0x7F) - return 0; - buffer[i] = toASCIILower(static_cast<char>(c)); + builder.appendNumber(static_cast<unsigned char>(red())); + builder.appendLiteral(", "); + + builder.appendNumber(static_cast<unsigned char>(green())); + builder.appendLiteral(", "); + + + builder.appendNumber(static_cast<unsigned char>(blue())); + if (colorHasAlpha) { + builder.appendLiteral(", "); + + NumberToStringBuffer buffer; + bool shouldTruncateTrailingZeros = true; + builder.append(numberToFixedPrecisionString(alpha() / 255.0f, 6, buffer, shouldTruncateTrailingZeros)); } - buffer[length] = '\0'; - return findColor(buffer, length); + + builder.append(')'); + return builder.toString(); } -void Color::setNamedColor(const String& name) +String Color::nameForRenderTreeAsText() const { - const NamedColor* foundColor = findNamedColor(name); - m_color = foundColor ? foundColor->ARGBValue : 0; - m_valid = foundColor; + // FIXME: Handle ExtendedColors. + if (alpha() < 0xFF) + return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha()); + return String::format("#%02X%02X%02X", red(), green(), blue()); } Color Color::light() const { // Hardcode this common case for speed. - if (m_color == black) + if (rgb() == black) return lightenedBlack; const float scaleFactor = nextafterf(256.0f, 0.0f); @@ -293,7 +429,7 @@ Color Color::light() const Color Color::dark() const { // Hardcode this common case for speed. - if (m_color == white) + if (rgb() == white) return darkenedWhite; const float scaleFactor = nextafterf(256.0f, 0.0f); @@ -336,7 +472,7 @@ const int cAlphaIncrement = 17; // Increments in between. Color Color::blend(const Color& source) const { - if (!alpha() || !source.hasAlpha()) + if (!isVisible() || source.isOpaque()) return source; if (!source.alpha()) @@ -353,7 +489,7 @@ Color Color::blend(const Color& source) const Color Color::blendWithWhite() const { // If the color contains alpha already, we leave it alone. - if (hasAlpha()) + if (!isOpaque()) return *this; Color newColor; @@ -372,6 +508,21 @@ Color Color::blendWithWhite() const return newColor; } +Color Color::colorWithAlphaMultipliedBy(float amount) const +{ + float newAlpha = amount * (isExtended() ? m_colorData.extendedColor->alpha() : static_cast<float>(alpha()) / 255); + return colorWithAlpha(newAlpha); +} + +Color Color::colorWithAlpha(float alpha) const +{ + if (isExtended()) + return Color { m_colorData.extendedColor->red(), m_colorData.extendedColor->green(), m_colorData.extendedColor->blue(), alpha, m_colorData.extendedColor->colorSpace() }; + + int newAlpha = alpha * 255; + return Color { red(), green(), blue(), newAlpha }; +} + void Color::getRGBA(float& r, float& g, float& b, float& a) const { r = red() / 255.0f; @@ -398,15 +549,16 @@ void Color::getHSL(double& hue, double& saturation, double& lightness) const double b = static_cast<double>(blue()) / 255.0; double max = std::max(std::max(r, g), b); double min = std::min(std::min(r, g), b); + double chroma = max - min; - if (max == min) + if (!chroma) hue = 0.0; else if (max == r) - hue = (60.0 * ((g - b) / (max - min))) + 360.0; + hue = (60.0 * ((g - b) / chroma)) + 360.0; else if (max == g) - hue = (60.0 * ((b - r) / (max - min))) + 120.0; + hue = (60.0 * ((b - r) / chroma)) + 120.0; else - hue = (60.0 * ((r - g) / (max - min))) + 240.0; + hue = (60.0 * ((r - g) / chroma)) + 240.0; if (hue >= 360.0) hue -= 360.0; @@ -415,42 +567,113 @@ void Color::getHSL(double& hue, double& saturation, double& lightness) const hue /= 360.0; lightness = 0.5 * (max + min); - if (max == min) + if (!chroma) saturation = 0.0; else if (lightness <= 0.5) - saturation = ((max - min) / (max + min)); + saturation = (chroma / (max + min)); else - saturation = ((max - min) / (2.0 - (max + min))); + saturation = (chroma / (2.0 - (max + min))); +} + +void Color::getHSV(double& hue, double& saturation, double& value) const +{ + double r = static_cast<double>(red()) / 255.0; + double g = static_cast<double>(green()) / 255.0; + double b = static_cast<double>(blue()) / 255.0; + double max = std::max(std::max(r, g), b); + double min = std::min(std::min(r, g), b); + double chroma = max - min; + + if (!chroma) + hue = 0.0; + else if (max == r) + hue = (60.0 * ((g - b) / chroma)) + 360.0; + else if (max == g) + hue = (60.0 * ((b - r) / chroma)) + 120.0; + else + hue = (60.0 * ((r - g) / chroma)) + 240.0; + + if (hue >= 360.0) + hue -= 360.0; + + hue /= 360.0; + + if (!max) + saturation = 0; + else + saturation = chroma / max; + + value = max; } Color colorFromPremultipliedARGB(RGBA32 pixelColor) { int alpha = alphaChannel(pixelColor); - if (alpha && alpha < 255) { - return Color::createUnchecked( - redChannel(pixelColor) * 255 / alpha, - greenChannel(pixelColor) * 255 / alpha, - blueChannel(pixelColor) * 255 / alpha, - alpha); - } else - return Color(pixelColor); + if (alpha && alpha < 255) + pixelColor = makeUnPremultipliedRGBA(redChannel(pixelColor), greenChannel(pixelColor), blueChannel(pixelColor), alpha); + return Color(pixelColor); } RGBA32 premultipliedARGBFromColor(const Color& color) { - unsigned pixelColor; + if (color.isOpaque()) { + if (color.isExtended()) + return makeRGB(color.asExtended().red() * 255, color.asExtended().green() * 255, color.asExtended().blue() * 255); + return color.rgb(); + } - unsigned alpha = color.alpha(); - if (alpha < 255) { - pixelColor = Color::createUnchecked( - fastDivideBy255(color.red() * alpha + 254), - fastDivideBy255(color.green() * alpha + 254), - fastDivideBy255(color.blue() * alpha + 254), - alpha).rgb(); - } else - pixelColor = color.rgb(); + if (color.isExtended()) + return makePremultipliedRGBA(color.asExtended().red() * 255, color.asExtended().green() * 255, color.asExtended().blue() * 255, color.asExtended().alpha() * 255); - return pixelColor; + return makePremultipliedRGBA(color.red(), color.green(), color.blue(), color.alpha()); +} + +Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied) +{ + // FIXME: ExtendedColor - needs to handle color spaces. + // We need to preserve the state of the valid flag at the end of the animation + if (progress == 1 && !to.isValid()) + return Color(); + + if (blendPremultiplied) { + // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor(). + // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that. + Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0; + Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0; + + Color premultBlended(blend(premultFrom.red(), premultTo.red(), progress), + blend(premultFrom.green(), premultTo.green(), progress), + blend(premultFrom.blue(), premultTo.blue(), progress), + blend(premultFrom.alpha(), premultTo.alpha(), progress)); + + return Color(colorFromPremultipliedARGB(premultBlended.rgb())); + } + + return Color(blend(from.red(), to.red(), progress), + blend(from.green(), to.green(), progress), + blend(from.blue(), to.blue(), progress), + blend(from.alpha(), to.alpha(), progress)); +} + +TextStream& operator<<(TextStream& ts, const Color& color) +{ + return ts << color.nameForRenderTreeAsText(); +} + +void Color::tagAsValid() +{ + m_colorData.rgbaAndFlags |= validRGBAColor; +} + +bool Color::isExtended() const +{ + return !(m_colorData.rgbaAndFlags & invalidRGBAColor); +} + +ExtendedColor& Color::asExtended() const +{ + ASSERT(isExtended()); + return *m_colorData.extendedColor; } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/Color.h b/Source/WebCore/platform/graphics/Color.h index 2047ad24c..83b2a3945 100644 --- a/Source/WebCore/platform/graphics/Color.h +++ b/Source/WebCore/platform/graphics/Color.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,20 +23,30 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Color_h -#define Color_h +#pragma once -#include "AnimationUtilities.h" -#include <wtf/FastMalloc.h> +#include "ColorSpace.h" +#include "ExtendedColor.h" +#include "PlatformExportMacros.h" +#include <algorithm> +#include <cmath> +#include <unicode/uchar.h> #include <wtf/Forward.h> -#include <wtf/unicode/Unicode.h> +#include <wtf/HashFunctions.h> +#include <wtf/Optional.h> +#include <wtf/text/LChar.h> #if USE(CG) -#include "ColorSpace.h" typedef struct CGColor* CGColorRef; -#if PLATFORM(IOS) -typedef struct CGColorSpace* CGColorSpaceRef; -#endif // PLATFORM(IOS) +#endif + +#if PLATFORM(WIN) +struct _D3DCOLORVALUE; +typedef _D3DCOLORVALUE D3DCOLORVALUE; +typedef D3DCOLORVALUE D2D_COLOR_F; +typedef D2D_COLOR_F D2D1_COLOR_F; +struct D2D_VECTOR_4F; +typedef D2D_VECTOR_4F D2D1_VECTOR_4F; #endif #if PLATFORM(GTK) @@ -48,39 +58,123 @@ typedef struct _GdkRGBA GdkRGBA; namespace WebCore { -class Color; +class TextStream; -typedef unsigned RGBA32; // RGBA quadruplet +typedef unsigned RGBA32; // Deprecated: Type for an RGBA quadruplet. Use RGBA class instead. -RGBA32 makeRGB(int r, int g, int b); -RGBA32 makeRGBA(int r, int g, int b, int a); +WEBCORE_EXPORT RGBA32 makeRGB(int r, int g, int b); +WEBCORE_EXPORT RGBA32 makeRGBA(int r, int g, int b, int a); -RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha); -RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a); +RGBA32 makePremultipliedRGBA(int r, int g, int b, int a, bool ceiling = true); +RGBA32 makeUnPremultipliedRGBA(int r, int g, int b, int a); + +WEBCORE_EXPORT RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha); +RGBA32 colorWithOverrideAlpha(RGBA32 color, std::optional<float> overrideAlpha); + +WEBCORE_EXPORT RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a); RGBA32 makeRGBAFromHSLA(double h, double s, double l, double a); RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a); -int differenceSquared(const Color&, const Color&); - inline int redChannel(RGBA32 color) { return (color >> 16) & 0xFF; } inline int greenChannel(RGBA32 color) { return (color >> 8) & 0xFF; } inline int blueChannel(RGBA32 color) { return color & 0xFF; } inline int alphaChannel(RGBA32 color) { return (color >> 24) & 0xFF; } +uint8_t roundAndClampColorChannel(int); +uint8_t roundAndClampColorChannel(float); + +class RGBA { +public: + RGBA(); // all channels zero, including alpha + RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); + RGBA(uint8_t red, uint8_t green, uint8_t blue); // opaque, alpha of 1 + + uint8_t red() const; + uint8_t green() const; + uint8_t blue() const; + uint8_t alpha() const; + + bool hasAlpha() const; + +private: + friend class Color; + + unsigned m_integer { 0 }; +}; + +bool operator==(const RGBA&, const RGBA&); +bool operator!=(const RGBA&, const RGBA&); + class Color { WTF_MAKE_FAST_ALLOCATED; public: - Color() : m_color(0), m_valid(false) { } - Color(RGBA32 color, bool valid = true) : m_color(color), m_valid(valid) { ASSERT(!m_color || m_valid); } - Color(int r, int g, int b) : m_color(makeRGB(r, g, b)), m_valid(true) { } - Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)), m_valid(true) { } - // Color is currently limited to 32bit RGBA, perhaps some day we'll support better colors - Color(float r, float g, float b, float a) : m_color(makeRGBA32FromFloats(r, g, b, a)), m_valid(true) { } + Color() { } + + // FIXME: Remove all these constructors and creation functions and replace the ones that are still needed with free functions. + + Color(RGBA32 color, bool valid = true) + { + if (valid) + setRGB(color); + } + + Color(int r, int g, int b) + { + setRGB(r, g, b); + } + + Color(int r, int g, int b, int a) + { + setRGB(makeRGBA(r, g, b, a)); + } + + Color(float r, float g, float b, float a) + { + setRGB(makeRGBA32FromFloats(r, g, b, a)); + } + // Creates a new color from the specific CMYK and alpha values. - Color(float c, float m, float y, float k, float a) : m_color(makeRGBAFromCMYKA(c, m, y, k, a)), m_valid(true) { } - explicit Color(const String&); + Color(float c, float m, float y, float k, float a) + { + setRGB(makeRGBAFromCMYKA(c, m, y, k, a)); + } + + WEBCORE_EXPORT explicit Color(const String&); explicit Color(const char*); + explicit Color(WTF::HashTableDeletedValueType) + { + static_assert(deletedHashValue & invalidRGBAColor, "Color's deleted hash value must not look like an ExtendedColor"); + static_assert(!(deletedHashValue & validRGBAColorBit), "Color's deleted hash value must not look like a valid RGBA32 Color"); + static_assert(deletedHashValue & (1 << 4), "Color's deleted hash value must have some bits set that an RGBA32 Color wouldn't have"); + m_colorData.rgbaAndFlags = deletedHashValue; + ASSERT(!isExtended()); + } + + bool isHashTableDeletedValue() const + { + return m_colorData.rgbaAndFlags == deletedHashValue; + } + + explicit Color(WTF::HashTableEmptyValueType) + { + static_assert(emptyHashValue & invalidRGBAColor, "Color's empty hash value must not look like an ExtendedColor"); + static_assert(emptyHashValue & (1 << 4), "Color's deleted hash value must have some bits set that an RGBA32 Color wouldn't have"); + m_colorData.rgbaAndFlags = emptyHashValue; + ASSERT(!isExtended()); + } + + // This creates an ExtendedColor. + // FIXME: If the colorSpace is sRGB and the values can all be + // converted exactly to integers, we should make a normal Color. + WEBCORE_EXPORT Color(float r, float g, float b, float a, ColorSpace colorSpace); + + Color(RGBA, ColorSpace); + WEBCORE_EXPORT Color(const Color&); + WEBCORE_EXPORT Color(Color&&); + + WEBCORE_EXPORT ~Color(); + static Color createUnchecked(int r, int g, int b) { RGBA32 color = 0xFF000000 | r << 16 | g << 8 | b; @@ -93,30 +187,36 @@ public: } // Returns the color serialized according to HTML5 - // - http://www.whatwg.org/specs/web-apps/current-work/#serialization-of-a-color - String serialized() const; + // <https://html.spec.whatwg.org/multipage/scripting.html#fill-and-stroke-styles> (10 September 2015) + WEBCORE_EXPORT String serialized() const; + + WEBCORE_EXPORT String cssText() const; // Returns the color serialized as either #RRGGBB or #RRGGBBAA - // The latter format is not a valid CSS color, and should only be seen in DRT dumps. String nameForRenderTreeAsText() const; - void setNamedColor(const String&); + bool isValid() const { return isExtended() || (m_colorData.rgbaAndFlags & validRGBAColorBit); } - bool isValid() const { return m_valid; } + bool isOpaque() const { return isValid() && (isExtended() ? asExtended().alpha() == 1.0 : alpha() == 255); } + bool isVisible() const { return isValid() && (isExtended() ? asExtended().alpha() > 0.0 : alpha() > 0); } - bool hasAlpha() const { return alpha() < 255; } + int red() const { return redChannel(rgb()); } + int green() const { return greenChannel(rgb()); } + int blue() const { return blueChannel(rgb()); } + int alpha() const { return alphaChannel(rgb()); } - int red() const { return redChannel(m_color); } - int green() const { return greenChannel(m_color); } - int blue() const { return blueChannel(m_color); } - int alpha() const { return alphaChannel(m_color); } - - RGBA32 rgb() const { return m_color; } // Preserve the alpha. - void setRGB(int r, int g, int b) { m_color = makeRGB(r, g, b); m_valid = true; } - void setRGB(RGBA32 rgb) { m_color = rgb; m_valid = true; } - void getRGBA(float& r, float& g, float& b, float& a) const; - void getRGBA(double& r, double& g, double& b, double& a) const; - void getHSL(double& h, double& s, double& l) const; + float alphaAsFloat() const { return isExtended() ? asExtended().alpha() : static_cast<float>(alphaChannel(rgb())) / 255; } + + RGBA32 rgb() const; + + // FIXME: Like operator==, this will give different values for ExtendedColors that + // should be identical, since the respective pointer will be different. + unsigned hash() const { return WTF::intHash(m_colorData.rgbaAndFlags); } + + WEBCORE_EXPORT void getRGBA(float& r, float& g, float& b, float& a) const; + WEBCORE_EXPORT void getRGBA(double& r, double& g, double& b, double& a) const; + WEBCORE_EXPORT void getHSL(double& h, double& s, double& l) const; + WEBCORE_EXPORT void getHSV(double& h, double& s, double& v) const; Color light() const; Color dark() const; @@ -127,6 +227,12 @@ public: Color blend(const Color&) const; Color blendWithWhite() const; + Color colorWithAlphaMultipliedBy(float) const; + + // Returns a color that has the same RGB values, but with the given A. + Color colorWithAlpha(float) const; + Color opaqueColor() const { return colorWithAlpha(1.0f); } + #if PLATFORM(GTK) Color(const GdkColor&); // We can't sensibly go back to GdkColor without losing the alpha value @@ -137,37 +243,136 @@ public: #endif #if USE(CG) - Color(CGColorRef); + WEBCORE_EXPORT Color(CGColorRef); +#endif + +#if PLATFORM(WIN) + WEBCORE_EXPORT Color(D2D1_COLOR_F); + WEBCORE_EXPORT operator D2D1_COLOR_F() const; + WEBCORE_EXPORT operator D2D1_VECTOR_4F() const; #endif static bool parseHexColor(const String&, RGBA32&); + static bool parseHexColor(const StringView&, RGBA32&); static bool parseHexColor(const LChar*, unsigned, RGBA32&); static bool parseHexColor(const UChar*, unsigned, RGBA32&); static const RGBA32 black = 0xFF000000; - static const RGBA32 white = 0xFFFFFFFF; + WEBCORE_EXPORT static const RGBA32 white = 0xFFFFFFFF; static const RGBA32 darkGray = 0xFF808080; static const RGBA32 gray = 0xFFA0A0A0; static const RGBA32 lightGray = 0xFFC0C0C0; - static const RGBA32 transparent = 0x00000000; + WEBCORE_EXPORT static const RGBA32 transparent = 0x00000000; static const RGBA32 cyan = 0xFF00FFFF; + static const RGBA32 yellow = 0xFFFFFF00; #if PLATFORM(IOS) - static const RGBA32 tap = 0x4D1A1A1A; - - // FIXME: This color shouldn't be iOS-specific. Once we fix up its usage in InlineTextBox::paintCompositionBackground() - // we should move it outside the PLATFORM(IOS)-guard. See <https://bugs.webkit.org/show_bug.cgi?id=126296>. static const RGBA32 compositionFill = 0x3CAFC0E3; +#else + static const RGBA32 compositionFill = 0xFFE1DD55; #endif + WEBCORE_EXPORT bool isExtended() const; + WEBCORE_EXPORT ExtendedColor& asExtended() const; + + WEBCORE_EXPORT Color& operator=(const Color&); + WEBCORE_EXPORT Color& operator=(Color&&); + + friend bool operator==(const Color& a, const Color& b); + + static bool isBlackColor(const Color&); + static bool isWhiteColor(const Color&); + private: - RGBA32 m_color; - bool m_valid; + void setRGB(int r, int g, int b) { setRGB(makeRGB(r, g, b)); } + void setRGB(RGBA32); + + // 0x_______00 is an ExtendedColor pointer. + // 0x_______01 is an invalid RGBA32. + // 0x_______11 is a valid RGBA32. + static const uint64_t extendedColor = 0x0; + static const uint64_t invalidRGBAColor = 0x1; + static const uint64_t validRGBAColorBit = 0x2; + static const uint64_t validRGBAColor = 0x3; + + static const uint64_t deletedHashValue = 0xFFFFFFFFFFFFFFFD; + static const uint64_t emptyHashValue = 0xFFFFFFFFFFFFFFFB; + + WEBCORE_EXPORT void tagAsValid(); + + union { + uint64_t rgbaAndFlags { invalidRGBAColor }; + ExtendedColor* extendedColor; + } m_colorData; }; +// FIXME: These do not work for ExtendedColor because +// they become just pointer comparison. +bool operator==(const Color&, const Color&); +bool operator!=(const Color&, const Color&); + +Color colorFromPremultipliedARGB(RGBA32); +RGBA32 premultipliedARGBFromColor(const Color&); + +Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied = true); + +int differenceSquared(const Color&, const Color&); + +uint16_t fastMultiplyBy255(uint16_t value); +uint16_t fastDivideBy255(uint16_t); + +#if USE(CG) +WEBCORE_EXPORT CGColorRef cachedCGColor(const Color&); +#endif + +inline RGBA::RGBA() +{ +} + +inline RGBA::RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) + : m_integer(alpha << 24 | red << 16 | green << 8 | blue) +{ +} + +inline RGBA::RGBA(uint8_t red, uint8_t green, uint8_t blue) + : m_integer(0xFF000000 | red << 16 | green << 8 | blue) +{ +} + +inline uint8_t RGBA::red() const +{ + return m_integer >> 16; +} + +inline uint8_t RGBA::green() const +{ + return m_integer >> 8; +} + +inline uint8_t RGBA::blue() const +{ + return m_integer; +} + +inline uint8_t RGBA::alpha() const +{ + return m_integer >> 24; +} + +inline bool RGBA::hasAlpha() const +{ + return (m_integer & 0xFF000000) != 0xFF000000; +} + +inline Color::Color(RGBA color, ColorSpace space) +{ + setRGB(color.m_integer); + ASSERT_UNUSED(space, space == ColorSpaceSRGB); +} + inline bool operator==(const Color& a, const Color& b) { - return a.rgb() == b.rgb() && a.isValid() == b.isValid(); + return a.m_colorData.rgbaAndFlags == b.m_colorData.rgbaAndFlags; } inline bool operator!=(const Color& a, const Color& b) @@ -175,50 +380,69 @@ inline bool operator!=(const Color& a, const Color& b) return !(a == b); } -Color colorFromPremultipliedARGB(RGBA32); -RGBA32 premultipliedARGBFromColor(const Color&); +inline uint8_t roundAndClampColorChannel(int value) +{ + return std::max(0, std::min(255, value)); +} -inline Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied = true) +inline uint8_t roundAndClampColorChannel(float value) { - // We need to preserve the state of the valid flag at the end of the animation - if (progress == 1 && !to.isValid()) - return Color(); - - if (blendPremultiplied) { - // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor(). - // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that. - Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0; - Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0; - - Color premultBlended(blend(premultFrom.red(), premultTo.red(), progress), - blend(premultFrom.green(), premultTo.green(), progress), - blend(premultFrom.blue(), premultTo.blue(), progress), - blend(premultFrom.alpha(), premultTo.alpha(), progress)); - - return Color(colorFromPremultipliedARGB(premultBlended.rgb())); - } + return std::max(0.f, std::min(255.f, std::round(value))); +} - return Color(blend(from.red(), to.red(), progress), - blend(from.green(), to.green(), progress), - blend(from.blue(), to.blue(), progress), - blend(from.alpha(), to.alpha(), progress)); +inline uint16_t fastMultiplyBy255(uint16_t value) +{ + return (value << 8) - value; } inline uint16_t fastDivideBy255(uint16_t value) { - // This is an approximate algorithm for division by 255, but it gives accurate results for 16bit values. + // While this is an approximate algorithm for division by 255, it gives perfectly accurate results for 16-bit values. + // FIXME: Since this gives accurate results for 16-bit values, we should get this optimization into compilers like clang. uint16_t approximation = value >> 8; uint16_t remainder = value - (approximation * 255) + 1; return approximation + (remainder >> 8); } -#if USE(CG) -CGColorRef cachedCGColor(const Color&, ColorSpace); -#if PLATFORM(IOS) -CGColorRef createCGColorWithDeviceWhite(CGFloat white, CGFloat alpha); -#endif // PLATFORM(IOS) -#endif +inline RGBA32 colorWithOverrideAlpha(RGBA32 color, std::optional<float> overrideAlpha) +{ + return overrideAlpha ? colorWithOverrideAlpha(color, overrideAlpha.value()) : color; +} -} // namespace WebCore +inline RGBA32 Color::rgb() const +{ + // FIXME: We should ASSERT(!isExtended()) here, or produce + // an RGBA32 equivalent for an ExtendedColor. Ideally the former, + // so we can audit all the rgb() call sites to handle extended. + return static_cast<RGBA32>(m_colorData.rgbaAndFlags >> 32); +} + +inline void Color::setRGB(RGBA32 rgb) +{ + m_colorData.rgbaAndFlags = static_cast<uint64_t>(rgb) << 32; + tagAsValid(); +} + +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const Color&); + +inline bool Color::isBlackColor(const Color& color) +{ + if (color.isExtended()) { + const ExtendedColor& extendedColor = color.asExtended(); + return !extendedColor.red() && !extendedColor.green() && !extendedColor.blue() && extendedColor.alpha() == 1; + } -#endif // Color_h + return color.isValid() && color.rgb() == Color::black; +} + +inline bool Color::isWhiteColor(const Color& color) +{ + if (color.isExtended()) { + const ExtendedColor& extendedColor = color.asExtended(); + return extendedColor.red() == 1 && extendedColor.green() == 1 && extendedColor.blue() == 1 && extendedColor.alpha() == 1; + } + + return color.isValid() && color.rgb() == Color::white; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ColorHash.h b/Source/WebCore/platform/graphics/ColorHash.h new file mode 100644 index 000000000..f08119351 --- /dev/null +++ b/Source/WebCore/platform/graphics/ColorHash.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Color.h" +#include <wtf/HashTraits.h> + +namespace WTF { + +struct ColorHash { + static unsigned hash(const WebCore::Color& key) { return key.hash(); } + static bool equal(const WebCore::Color& a, const WebCore::Color& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +template<> struct DefaultHash<WebCore::Color> { + using Hash = ColorHash; +}; + +template<> struct HashTraits<WebCore::Color> : GenericHashTraits<WebCore::Color> { + static const bool emptyValueIsZero = false; + static WebCore::Color emptyValue() { return WebCore::Color(HashTableEmptyValue); } + + static void constructDeletedValue(WebCore::Color& slot) { new (NotNull, &slot) WebCore::Color(HashTableDeletedValue); } + static bool isDeletedValue(const WebCore::Color& value) { return value.isHashTableDeletedValue(); } +}; + +} // namespace WTF diff --git a/Source/WebCore/platform/graphics/ColorSpace.h b/Source/WebCore/platform/graphics/ColorSpace.h index 7622c47b2..e12c40adc 100644 --- a/Source/WebCore/platform/graphics/ColorSpace.h +++ b/Source/WebCore/platform/graphics/ColorSpace.h @@ -31,7 +31,8 @@ namespace WebCore { enum ColorSpace { ColorSpaceDeviceRGB, ColorSpaceSRGB, - ColorSpaceLinearRGB + ColorSpaceLinearRGB, + ColorSpaceDisplayP3 }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ComplexTextController.cpp b/Source/WebCore/platform/graphics/ComplexTextController.cpp new file mode 100644 index 000000000..507765f42 --- /dev/null +++ b/Source/WebCore/platform/graphics/ComplexTextController.cpp @@ -0,0 +1,893 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ComplexTextController.h" + +#include "CharacterProperties.h" +#include "FloatSize.h" +#include "FontCascade.h" +#include "RenderBlock.h" +#include "RenderText.h" +#include "TextRun.h" +#include <unicode/ubrk.h> +#include <wtf/Optional.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/TextBreakIterator.h> +#include <wtf/unicode/CharacterNames.h> + +#if PLATFORM(IOS) +#include <CoreText/CoreText.h> +#endif + +#if PLATFORM(MAC) +#include <ApplicationServices/ApplicationServices.h> +#endif + +namespace WebCore { + +class TextLayout { + WTF_MAKE_FAST_ALLOCATED; +public: + static bool isNeeded(RenderText& text, const FontCascade& font) + { + TextRun run = RenderBlock::constructTextRun(text, text.style()); + return font.codePath(run) == FontCascade::Complex; + } + + TextLayout(RenderText& text, const FontCascade& font, float xPos) + : m_font(font) + , m_run(constructTextRun(text, xPos)) + , m_controller(std::make_unique<ComplexTextController>(m_font, m_run, true)) + { + } + + float width(unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts) + { + m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts); + float beforeWidth = m_controller->runWidthSoFar(); + if (m_font.wordSpacing() && from && FontCascade::treatAsSpace(m_run[from])) + beforeWidth += m_font.wordSpacing(); + m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts); + float afterWidth = m_controller->runWidthSoFar(); + return afterWidth - beforeWidth; + } + +private: + static TextRun constructTextRun(RenderText& text, float xPos) + { + TextRun run = RenderBlock::constructTextRun(text, text.style()); + run.setCharactersLength(text.textLength()); + ASSERT(run.charactersLength() >= run.length()); + run.setXPos(xPos); + return run; + } + + // ComplexTextController has only references to its FontCascade and TextRun so they must be kept alive here. + FontCascade m_font; + TextRun m_run; + std::unique_ptr<ComplexTextController> m_controller; +}; + +void TextLayoutDeleter::operator()(TextLayout* layout) const +{ +#if PLATFORM(COCOA) + delete layout; +#else + ASSERT_UNUSED(layout, !layout); +#endif +} + +std::unique_ptr<TextLayout, TextLayoutDeleter> FontCascade::createLayout(RenderText& text, float xPos, bool collapseWhiteSpace) const +{ +#if PLATFORM(COCOA) + if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this)) + return nullptr; + return std::unique_ptr<TextLayout, TextLayoutDeleter>(new TextLayout(text, *this, xPos)); +#else + UNUSED_PARAM(text); + UNUSED_PARAM(xPos); + UNUSED_PARAM(collapseWhiteSpace); + return nullptr; +#endif +} + +float FontCascade::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts) +{ +#if PLATFORM(COCOA) + return layout.width(from, len, fallbackFonts); +#else + UNUSED_PARAM(layout); + UNUSED_PARAM(from); + UNUSED_PARAM(len); + UNUSED_PARAM(fallbackFonts); + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +void ComplexTextController::computeExpansionOpportunity() +{ + if (!m_expansion) + m_expansionPerOpportunity = 0; + else { + unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, m_run.expansionBehavior()).first; + + if (!expansionOpportunityCount) + m_expansionPerOpportunity = 0; + else + m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; + } +} + +ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const Font*>* fallbackFonts, bool forTextEmphasis) + : m_fallbackFonts(fallbackFonts) + , m_font(font) + , m_run(run) + , m_end(run.length()) + , m_expansion(run.expansion()) + , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) + , m_forTextEmphasis(forTextEmphasis) +{ + computeExpansionOpportunity(); + + collectComplexTextRuns(); + + finishConstruction(); +} + +ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, Vector<Ref<ComplexTextRun>>& runs) + : m_font(font) + , m_run(run) + , m_end(run.length()) + , m_expansion(run.expansion()) +{ + computeExpansionOpportunity(); + + for (auto& run : runs) + m_complexTextRuns.append(run.ptr()); + + finishConstruction(); +} + +void ComplexTextController::finishConstruction() +{ + adjustGlyphsAndAdvances(); + + if (!m_isLTROnly) { + m_runIndices.reserveInitialCapacity(m_complexTextRuns.size()); + + m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size()); + unsigned glyphCountSoFar = 0; + for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) { + m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar); + glyphCountSoFar += m_complexTextRuns[i]->glyphCount(); + } + } +} + +unsigned ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs) +{ + if (h >= m_totalWidth) + return m_run.ltr() ? m_end : 0; + + if (h < 0) + return m_run.ltr() ? 0 : m_end; + + float x = h; + + size_t runCount = m_complexTextRuns.size(); + unsigned offsetIntoAdjustedGlyphs = 0; + + for (size_t r = 0; r < runCount; ++r) { + const ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; + for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) { + unsigned index = offsetIntoAdjustedGlyphs + j; + float adjustedAdvance = m_adjustedBaseAdvances[index].width(); + if (x < adjustedAdvance) { + unsigned hitGlyphStart = complexTextRun.indexAt(j); + unsigned hitGlyphEnd; + if (m_run.ltr()) + hitGlyphEnd = std::max(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.indexEnd()); + else + hitGlyphEnd = std::max(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.indexEnd()); + + // FIXME: Instead of dividing the glyph's advance equally between the characters, this + // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions(). + unsigned hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance); + unsigned stringLength = complexTextRun.stringLength(); + UBreakIterator* cursorPositionIterator = cursorMovementIterator(StringView(complexTextRun.characters(), stringLength)); + unsigned clusterStart; + if (ubrk_isBoundary(cursorPositionIterator, hitIndex)) + clusterStart = hitIndex; + else { + int preceeding = ubrk_preceding(cursorPositionIterator, hitIndex); + clusterStart = preceeding == UBRK_DONE ? 0 : preceeding; + } + + if (!includePartialGlyphs) + return complexTextRun.stringLocation() + clusterStart; + + int following = ubrk_following(cursorPositionIterator, hitIndex); + unsigned clusterEnd = following == UBRK_DONE ? stringLength : following; + + float clusterWidth; + // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns + // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no + // reordering and no font fallback should occur within a CTLine. + if (clusterEnd - clusterStart > 1) { + clusterWidth = adjustedAdvance; + if (j) { + unsigned firstGlyphBeforeCluster = j - 1; + while (complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { + float width = m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width(); + clusterWidth += width; + x += width; + if (!firstGlyphBeforeCluster) + break; + firstGlyphBeforeCluster--; + } + } + unsigned firstGlyphAfterCluster = j + 1; + while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { + clusterWidth += m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width(); + firstGlyphAfterCluster++; + } + } else { + clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart); + x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1); + } + if (x <= clusterWidth / 2) + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); + } + x -= adjustedAdvance; + } + offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +// FIXME: We should consider reimplementing this function using ICU to advance by grapheme. +// The current implementation only considers explicitly emoji sequences and emoji variations. +static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount) +{ + ASSERT(iterator < end); + + markCount = 0; + + unsigned i = 0; + unsigned remainingCharacters = end - iterator; + U16_NEXT(iterator, i, remainingCharacters, baseCharacter); + iterator = iterator + i; + if (U_IS_SURROGATE(baseCharacter)) + return false; + + // Consume marks. + bool sawEmojiGroupCandidate = isEmojiGroupCandidate(baseCharacter); + bool sawJoiner = false; + while (iterator < end) { + UChar32 nextCharacter; + unsigned markLength = 0; + bool shouldContinue = false; + ASSERT(end >= iterator); + U16_NEXT(iterator, markLength, static_cast<unsigned>(end - iterator), nextCharacter); + + if (isVariationSelector(nextCharacter) || isEmojiFitzpatrickModifier(nextCharacter)) + shouldContinue = true; + + if (sawJoiner && isEmojiGroupCandidate(nextCharacter)) + shouldContinue = true; + + sawJoiner = false; + if (sawEmojiGroupCandidate && nextCharacter == zeroWidthJoiner) { + sawJoiner = true; + shouldContinue = true; + } + + if (!shouldContinue && !(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) + break; + + markCount += markLength; + iterator += markLength; + } + + return true; +} + +// FIXME: Capitalization is language-dependent and context-dependent and should operate on grapheme clusters instead of codepoints. +static inline std::optional<UChar32> capitalized(UChar32 baseCharacter) +{ + if (U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) + return std::nullopt; + + UChar32 uppercaseCharacter = u_toupper(baseCharacter); + ASSERT(uppercaseCharacter == baseCharacter || (U_IS_BMP(baseCharacter) == U_IS_BMP(uppercaseCharacter))); + if (uppercaseCharacter != baseCharacter) + return uppercaseCharacter; + return std::nullopt; +} + +static bool shouldSynthesize(bool dontSynthesizeSmallCaps, const Font* nextFont, UChar32 baseCharacter, std::optional<UChar32> capitalizedBase, FontVariantCaps fontVariantCaps, bool engageAllSmallCapsProcessing) +{ + if (dontSynthesizeSmallCaps) + return false; + if (!nextFont || nextFont == Font::systemFallback()) + return false; + if (engageAllSmallCapsProcessing && isASCIISpace(baseCharacter)) + return false; + if (!engageAllSmallCapsProcessing && !capitalizedBase) + return false; + return !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter); +} + +void ComplexTextController::collectComplexTextRuns() +{ + if (!m_end) + return; + + // We break up glyph run generation for the string by Font. + const UChar* cp; + + if (m_run.is8Bit()) { + String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length()); + m_stringsFor8BitRuns.append(WTFMove(stringFor8BitRun)); + cp = m_stringsFor8BitRuns.last().characters16(); + } else + cp = m_run.characters16(); + + auto fontVariantCaps = m_font.fontDescription().variantCaps(); + bool dontSynthesizeSmallCaps = !static_cast<bool>(m_font.fontDescription().fontSynthesis() & FontSynthesisSmallCaps); + bool engageAllSmallCapsProcessing = fontVariantCaps == FontVariantCaps::AllSmall || fontVariantCaps == FontVariantCaps::AllPetite; + bool engageSmallCapsProcessing = engageAllSmallCapsProcessing || fontVariantCaps == FontVariantCaps::Small || fontVariantCaps == FontVariantCaps::Petite; + + if (engageAllSmallCapsProcessing || engageSmallCapsProcessing) + m_smallCapsBuffer.resize(m_end); + + unsigned indexOfFontTransition = 0; + const UChar* curr = cp; + const UChar* end = cp + m_end; + + const Font* font; + const Font* nextFont; + const Font* synthesizedFont = nullptr; + const Font* smallSynthesizedFont = nullptr; + + unsigned markCount; + UChar32 baseCharacter; + if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount)) + return; + + nextFont = m_font.fontForCombiningCharacterSequence(cp, curr - cp); + + bool isSmallCaps = false; + bool nextIsSmallCaps = false; + + auto capitalizedBase = capitalized(baseCharacter); + if (shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) { + synthesizedFont = &nextFont->noSynthesizableFeaturesFont(); + smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription()); + UChar32 characterToWrite = capitalizedBase ? capitalizedBase.value() : cp[0]; + unsigned characterIndex = 0; + U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, characterToWrite); + for (unsigned i = characterIndex; cp + i < curr; ++i) + m_smallCapsBuffer[i] = cp[i]; + nextIsSmallCaps = true; + } + + while (curr < end) { + font = nextFont; + isSmallCaps = nextIsSmallCaps; + unsigned index = curr - cp; + + if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount)) + return; + + if (synthesizedFont) { + if (auto capitalizedBase = capitalized(baseCharacter)) { + unsigned characterIndex = index; + U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, capitalizedBase.value()); + for (unsigned i = 0; i < markCount; ++i) + m_smallCapsBuffer[i + characterIndex] = cp[i + characterIndex]; + nextIsSmallCaps = true; + } else { + if (engageAllSmallCapsProcessing) { + for (unsigned i = 0; i < curr - cp - index; ++i) + m_smallCapsBuffer[index + i] = cp[index + i]; + } + nextIsSmallCaps = engageAllSmallCapsProcessing; + } + } + + if (baseCharacter == zeroWidthJoiner) + nextFont = font; + else + nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index); + + capitalizedBase = capitalized(baseCharacter); + if (!synthesizedFont && shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) { + // Rather than synthesize each character individually, we should synthesize the entire "run" if any character requires synthesis. + synthesizedFont = &nextFont->noSynthesizableFeaturesFont(); + smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription()); + nextIsSmallCaps = true; + curr = cp + indexOfFontTransition; + continue; + } + + if (nextFont != font || nextIsSmallCaps != isSmallCaps) { + unsigned itemLength = index - indexOfFontTransition; + if (itemLength) { + unsigned itemStart = indexOfFontTransition; + if (synthesizedFont) { + if (isSmallCaps) + collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont); + else + collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont); + } else + collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, font); + if (nextFont != font) { + synthesizedFont = nullptr; + smallSynthesizedFont = nullptr; + nextIsSmallCaps = false; + } + } + indexOfFontTransition = index; + } + } + + ASSERT(m_end >= indexOfFontTransition); + unsigned itemLength = m_end - indexOfFontTransition; + if (itemLength) { + unsigned itemStart = indexOfFontTransition; + if (synthesizedFont) { + if (nextIsSmallCaps) + collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont); + else + collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont); + } else + collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, nextFont); + } + + if (!m_run.ltr()) + m_complexTextRuns.reverse(); +} + +unsigned ComplexTextController::ComplexTextRun::indexAt(unsigned i) const +{ + ASSERT(i < m_glyphCount); + + return m_coreTextIndices[i]; +} + +void ComplexTextController::ComplexTextRun::setIsNonMonotonic() +{ + ASSERT(m_isMonotonic); + m_isMonotonic = false; + + Vector<bool, 64> mappedIndices(m_stringLength, false); + for (unsigned i = 0; i < m_glyphCount; ++i) { + ASSERT(indexAt(i) < m_stringLength); + mappedIndices[indexAt(i)] = true; + } + + m_glyphEndOffsets.grow(m_glyphCount); + for (unsigned i = 0; i < m_glyphCount; ++i) { + unsigned nextMappedIndex = m_indexEnd; + for (unsigned j = indexAt(i) + 1; j < m_stringLength; ++j) { + if (mappedIndices[j]) { + nextMappedIndex = j; + break; + } + } + m_glyphEndOffsets[i] = nextMappedIndex; + } +} + +unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph) +{ + leftmostGlyph = 0; + + size_t runCount = m_complexTextRuns.size(); + if (m_currentRun >= runCount) + return runCount; + + if (m_isLTROnly) { + for (unsigned i = 0; i < m_currentRun; ++i) + leftmostGlyph += m_complexTextRuns[i]->glyphCount(); + return m_currentRun; + } + + if (m_runIndices.isEmpty()) { + unsigned firstRun = 0; + unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]); + for (unsigned i = 1; i < runCount; ++i) { + unsigned offset = stringBegin(*m_complexTextRuns[i]); + if (offset < firstRunOffset) { + firstRun = i; + firstRunOffset = offset; + } + } + m_runIndices.uncheckedAppend(firstRun); + } + + while (m_runIndices.size() <= m_currentRun) { + unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]); + + for (unsigned i = 0; i < runCount; ++i) { + if (offset == stringBegin(*m_complexTextRuns[i])) { + m_runIndices.uncheckedAppend(i); + break; + } + } + } + + unsigned currentRunIndex = m_runIndices[m_currentRun]; + leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex]; + return currentRunIndex; +} + +unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph) +{ + if (m_isLTROnly) { + leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount(); + return m_currentRun; + } + + m_currentRun++; + leftmostGlyph = 0; + return indexOfCurrentRun(leftmostGlyph); +} + +float ComplexTextController::runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle iterationStyle) const +{ + // FIXME: Instead of dividing the glyph's advance equally between the characters, this + // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions(). + if (glyphStartOffset == glyphEndOffset) { + // When there are multiple glyphs per character we need to advance by the full width of the glyph. + ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph); + return 1; + } + + if (iterationStyle == ByWholeGlyphs) { + if (!oldCharacterInCurrentGlyph) + return 1; + return 0; + } + + return static_cast<float>(m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset); +} + +void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const Font*>* fallbackFonts) +{ + if (offset > m_end) + offset = m_end; + + if (offset <= m_currentCharacter) { + m_runWidthSoFar = 0; + m_numGlyphsSoFar = 0; + m_currentRun = 0; + m_glyphInCurrentRun = 0; + m_characterInCurrentGlyph = 0; + } + + m_currentCharacter = offset; + + size_t runCount = m_complexTextRuns.size(); + + unsigned indexOfLeftmostGlyphInCurrentRun = 0; // Relative to the beginning of ComplexTextController. + unsigned currentRunIndex = indexOfCurrentRun(indexOfLeftmostGlyphInCurrentRun); + while (m_currentRun < runCount) { + const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex]; + bool ltr = complexTextRun.isLTR(); + unsigned glyphCount = complexTextRun.glyphCount(); + unsigned glyphIndexIntoCurrentRun = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun; + unsigned glyphIndexIntoComplexTextController = indexOfLeftmostGlyphInCurrentRun + glyphIndexIntoCurrentRun; + if (fallbackFonts && &complexTextRun.font() != &m_font.primaryFont()) + fallbackFonts->add(&complexTextRun.font()); + + // We must store the initial advance for the first glyph we are going to draw. + // When leftmostGlyph is 0, it represents the first glyph to draw, taking into + // account the text direction. + if (!indexOfLeftmostGlyphInCurrentRun && glyphBuffer) + glyphBuffer->setInitialAdvance(GlyphBufferAdvance(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height())); + + while (m_glyphInCurrentRun < glyphCount) { + unsigned glyphStartOffset = complexTextRun.indexAt(glyphIndexIntoCurrentRun); + unsigned glyphEndOffset; + if (complexTextRun.isMonotonic()) { + if (ltr) + glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun + 1 < glyphCount ? complexTextRun.indexAt(glyphIndexIntoCurrentRun + 1) : complexTextRun.indexEnd()); + else + glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun > 0 ? complexTextRun.indexAt(glyphIndexIntoCurrentRun - 1) : complexTextRun.indexEnd()); + } else + glyphEndOffset = complexTextRun.endOffsetAt(glyphIndexIntoCurrentRun); + + FloatSize adjustedBaseAdvance = m_adjustedBaseAdvances[glyphIndexIntoComplexTextController]; + + if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter) + return; + + if (glyphBuffer && !m_characterInCurrentGlyph) { + auto currentGlyphOrigin = glyphOrigin(glyphIndexIntoComplexTextController); + GlyphBufferAdvance paintAdvance(adjustedBaseAdvance); + if (!glyphIndexIntoCurrentRun) { + // The first layout advance of every run includes the "initial layout advance." However, here, we need + // paint advances, so subtract it out before transforming the layout advance into a paint advance. + paintAdvance.setWidth(paintAdvance.width() - (complexTextRun.initialAdvance().width() - currentGlyphOrigin.x())); + paintAdvance.setHeight(paintAdvance.height() - (complexTextRun.initialAdvance().height() - currentGlyphOrigin.y())); + } + paintAdvance.setWidth(paintAdvance.width() + glyphOrigin(glyphIndexIntoComplexTextController + 1).x() - currentGlyphOrigin.x()); + paintAdvance.setHeight(paintAdvance.height() + glyphOrigin(glyphIndexIntoComplexTextController + 1).y() - currentGlyphOrigin.y()); + if (glyphIndexIntoCurrentRun == glyphCount - 1 && currentRunIndex + 1 < runCount) { + // Our paint advance points to the end of the run. However, the next run may have an + // initial advance, and our paint advance needs to point to the location of the next + // glyph. So, we need to add in the next run's initial advance. + paintAdvance.setWidth(paintAdvance.width() - glyphOrigin(glyphIndexIntoComplexTextController + 1).x() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().width()); + paintAdvance.setHeight(paintAdvance.height() - glyphOrigin(glyphIndexIntoComplexTextController + 1).y() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().height()); + } + paintAdvance.setHeight(-paintAdvance.height()); // Increasing y points down + glyphBuffer->add(m_adjustedGlyphs[glyphIndexIntoComplexTextController], &complexTextRun.font(), paintAdvance, complexTextRun.indexAt(m_glyphInCurrentRun)); + } + + unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; + m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset; + m_runWidthSoFar += adjustedBaseAdvance.width() * runWidthSoFarFraction(glyphStartOffset, glyphEndOffset, oldCharacterInCurrentGlyph, iterationStyle); + + if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter) + return; + + m_numGlyphsSoFar++; + m_glyphInCurrentRun++; + m_characterInCurrentGlyph = 0; + if (ltr) { + glyphIndexIntoCurrentRun++; + glyphIndexIntoComplexTextController++; + } else { + glyphIndexIntoCurrentRun--; + glyphIndexIntoComplexTextController--; + } + } + currentRunIndex = incrementCurrentRun(indexOfLeftmostGlyphInCurrentRun); + m_glyphInCurrentRun = 0; + } +} + +static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion) +{ + bool expandLeft = ideograph; + bool expandRight = ideograph; + if (treatAsSpace) { + if (ltr) + expandRight = true; + else + expandLeft = true; + } + if (isAfterExpansion) + expandLeft = false; + ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion); + ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion); + if (forbidLeadingExpansion) + expandLeft = false; + if (forbidTrailingExpansion) + expandRight = false; + if (forceLeadingExpansion) + expandLeft = true; + if (forceTrailingExpansion) + expandRight = true; + return std::make_pair(expandLeft, expandRight); +} + +void ComplexTextController::adjustGlyphsAndAdvances() +{ + bool afterExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion; + size_t runCount = m_complexTextRuns.size(); + bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled(); + bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion; + bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion; + bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion; + bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion; + + // We are iterating in glyph order, not string order. Compare this to WidthIterator::advanceInternal() + for (size_t runIndex = 0; runIndex < runCount; ++runIndex) { + ComplexTextRun& complexTextRun = *m_complexTextRuns[runIndex]; + unsigned glyphCount = complexTextRun.glyphCount(); + const Font& font = complexTextRun.font(); + + if (!complexTextRun.isLTR()) + m_isLTROnly = false; + + const CGGlyph* glyphs = complexTextRun.glyphs(); + const FloatSize* advances = complexTextRun.baseAdvances(); + + float spaceWidth = font.spaceWidth() - font.syntheticBoldOffset(); + const UChar* cp = complexTextRun.characters(); + FloatPoint glyphOrigin; + unsigned lastCharacterIndex = m_run.ltr() ? std::numeric_limits<unsigned>::min() : std::numeric_limits<unsigned>::max(); + bool isMonotonic = true; + + for (unsigned i = 0; i < glyphCount; i++) { + unsigned characterIndex = complexTextRun.indexAt(i); + if (m_run.ltr()) { + if (characterIndex < lastCharacterIndex) + isMonotonic = false; + } else { + if (characterIndex > lastCharacterIndex) + isMonotonic = false; + } + UChar ch = *(cp + characterIndex); + + bool treatAsSpace = FontCascade::treatAsSpace(ch); + CGGlyph glyph = treatAsSpace ? font.spaceGlyph() : glyphs[i]; + FloatSize advance = treatAsSpace ? FloatSize(spaceWidth, advances[i].height()) : advances[i]; + + if (ch == '\t' && m_run.allowTabs()) + advance.setWidth(m_font.tabWidth(font, m_run.tabSize(), m_run.xPos() + m_totalWidth)); + else if (FontCascade::treatAsZeroWidthSpace(ch) && !treatAsSpace) { + advance.setWidth(0); + glyph = font.spaceGlyph(); + } + + if (!i) { + advance.expand(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height()); + if (auto* origins = complexTextRun.glyphOrigins()) + advance.expand(-origins[0].x(), -origins[0].y()); + } + + advance.expand(font.syntheticBoldOffset(), 0); + + if (hasExtraSpacing) { + // If we're a glyph with an advance, add in letter-spacing. + // That way we weed out zero width lurkers. This behavior matches the fast text code path. + if (advance.width()) + advance.expand(m_font.letterSpacing(), 0); + + unsigned characterIndexInRun = characterIndex + complexTextRun.stringLocation(); + bool isFirstCharacter = !(characterIndex + complexTextRun.stringLocation()); + bool isLastCharacter = characterIndexInRun + 1 == m_run.length() || (U16_IS_LEAD(ch) && characterIndexInRun + 2 == m_run.length() && U16_IS_TRAIL(*(cp + characterIndex + 1))); + + bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr() + bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr() + bool forbidLeadingExpansion = false; + bool forbidTrailingExpansion = false; + if (runForcesLeadingExpansion) + forceLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter; + if (runForcesTrailingExpansion) + forceTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter; + if (runForbidsLeadingExpansion) + forbidLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter; + if (runForbidsTrailingExpansion) + forbidTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter; + // Handle justification and word-spacing. + bool ideograph = FontCascade::isCJKIdeographOrSymbol(ch); + if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) { + // Distribute the run's total expansion evenly over all expansion opportunities in the run. + if (m_expansion) { + bool expandLeft, expandRight; + std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), afterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion); + if (expandLeft) { + m_expansion -= m_expansionPerOpportunity; + // Increase previous width + if (m_adjustedBaseAdvances.isEmpty()) { + advance.expand(m_expansionPerOpportunity, 0); + complexTextRun.growInitialAdvanceHorizontally(m_expansionPerOpportunity); + } else { + m_adjustedBaseAdvances.last().expand(m_expansionPerOpportunity, 0); + m_totalWidth += m_expansionPerOpportunity; + } + } + if (expandRight) { + m_expansion -= m_expansionPerOpportunity; + advance.expand(m_expansionPerOpportunity, 0); + afterExpansion = true; + } + } else + afterExpansion = false; + + // Account for word-spacing. + if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || runIndex > 0) && m_font.wordSpacing()) + advance.expand(m_font.wordSpacing(), 0); + } else + afterExpansion = false; + } + + m_totalWidth += advance.width(); + + // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space. + if (m_forTextEmphasis && (!FontCascade::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK))) + glyph = 0; + + m_adjustedBaseAdvances.append(advance); + if (auto* origins = complexTextRun.glyphOrigins()) { + ASSERT(m_glyphOrigins.size() < m_adjustedBaseAdvances.size()); + m_glyphOrigins.grow(m_adjustedBaseAdvances.size()); + m_glyphOrigins[m_glyphOrigins.size() - 1] = origins[i]; + ASSERT(m_glyphOrigins.size() == m_adjustedBaseAdvances.size()); + } + m_adjustedGlyphs.append(glyph); + + FloatRect glyphBounds = font.boundsForGlyph(glyph); + glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); + m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x()); + m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); + m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y()); + m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); + glyphOrigin.move(advance); + + lastCharacterIndex = characterIndex; + } + if (!isMonotonic) + complexTextRun.setIsNonMonotonic(); + } +} + +// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on +// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path. +ComplexTextController::ComplexTextRun::ComplexTextRun(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr) + : m_font(font) + , m_characters(characters) + , m_stringLength(stringLength) + , m_indexBegin(indexBegin) + , m_indexEnd(indexEnd) + , m_stringLocation(stringLocation) + , m_isLTR(ltr) +{ + auto runLengthInCodeUnits = m_indexEnd - m_indexBegin; + m_coreTextIndices.reserveInitialCapacity(runLengthInCodeUnits); + unsigned r = m_indexBegin; + while (r < m_indexEnd) { + m_coreTextIndices.uncheckedAppend(r); + UChar32 character; + U16_NEXT(m_characters, r, m_stringLength, character); + } + m_glyphCount = m_coreTextIndices.size(); + if (!ltr) { + for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) + std::swap(m_coreTextIndices[r], m_coreTextIndices[end]); + } + + // Synthesize a run of missing glyphs. + m_glyphs.fill(0, m_glyphCount); + m_baseAdvances.fill(FloatSize(m_font.widthForGlyph(0), 0), m_glyphCount); +} + +ComplexTextController::ComplexTextRun::ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr) + : m_baseAdvances(advances) + , m_glyphOrigins(origins) + , m_glyphs(glyphs) + , m_coreTextIndices(stringIndices) + , m_initialAdvance(initialAdvance) + , m_font(font) + , m_characters(characters) + , m_stringLength(stringLength) + , m_indexBegin(indexBegin) + , m_indexEnd(indexEnd) + , m_glyphCount(glyphs.size()) + , m_stringLocation(stringLocation) + , m_isLTR(ltr) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ComplexTextController.h b/Source/WebCore/platform/graphics/ComplexTextController.h new file mode 100644 index 000000000..169d891b2 --- /dev/null +++ b/Source/WebCore/platform/graphics/ComplexTextController.h @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "FloatPoint.h" +#include "GlyphBuffer.h" +#include <wtf/HashSet.h> +#include <wtf/RefCounted.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +#define USE_LAYOUT_SPECIFIC_ADVANCES ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000)) + +typedef unsigned short CGGlyph; + +typedef const struct __CTRun * CTRunRef; +typedef const struct __CTLine * CTLineRef; + +namespace WebCore { + +class FontCascade; +class Font; +class TextRun; + +enum GlyphIterationStyle { IncludePartialGlyphs, ByWholeGlyphs }; + +// See https://trac.webkit.org/wiki/ComplexTextController for more information about ComplexTextController. +class ComplexTextController { + WTF_MAKE_FAST_ALLOCATED; +public: + ComplexTextController(const FontCascade&, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const Font*>* fallbackFonts = 0, bool forTextEmphasis = false); + + class ComplexTextRun; + WEBCORE_EXPORT ComplexTextController(const FontCascade&, const TextRun&, Vector<Ref<ComplexTextRun>>&); + + // Advance and emit glyphs up to the specified character. + WEBCORE_EXPORT void advance(unsigned to, GlyphBuffer* = nullptr, GlyphIterationStyle = IncludePartialGlyphs, HashSet<const Font*>* fallbackFonts = nullptr); + + // Compute the character offset for a given x coordinate. + unsigned offsetForPosition(float x, bool includePartialGlyphs); + + // Returns the width of everything we've consumed so far. + float runWidthSoFar() const { return m_runWidthSoFar; } + + float totalWidth() const { return m_totalWidth; } + + float minGlyphBoundingBoxX() const { return m_minGlyphBoundingBoxX; } + float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; } + float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; } + float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; } + + class ComplexTextRun : public RefCounted<ComplexTextRun> { + public: + static Ref<ComplexTextRun> create(CTRunRef ctRun, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd) + { + return adoptRef(*new ComplexTextRun(ctRun, font, characters, stringLocation, stringLength, indexBegin, indexEnd)); + } + + static Ref<ComplexTextRun> create(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr) + { + return adoptRef(*new ComplexTextRun(font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr)); + } + + static Ref<ComplexTextRun> create(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr) + { + return adoptRef(*new ComplexTextRun(advances, origins, glyphs, stringIndices, initialAdvance, font, characters, stringLocation, stringLength, indexBegin, indexEnd, ltr)); + } + + unsigned glyphCount() const { return m_glyphCount; } + const Font& font() const { return m_font; } + const UChar* characters() const { return m_characters; } + unsigned stringLocation() const { return m_stringLocation; } + unsigned stringLength() const { return m_stringLength; } + ALWAYS_INLINE unsigned indexAt(unsigned) const; + unsigned indexBegin() const { return m_indexBegin; } + unsigned indexEnd() const { return m_indexEnd; } + unsigned endOffsetAt(unsigned i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; } + const CGGlyph* glyphs() const { return m_glyphs.data(); } + + /* + * This is the format of the information CoreText gives us about each run: + * + * ----->X (Paint glyph position) X (Paint glyph position) X (Paint glyph position) + * / 7 7 7 + * / / / / + * (Initial advance) / / (Glyph origin) / (Glyph origin) / (Glyph origin) + * ------------------- / / / + * / / / / + * X X--------------------------X--------------------------X--------------------------X + * (text position ^) (base advance) (base advance) (base advance) + * + * + * + * + * + * And here is the output we transform this into (for each run): + * + * ----->X------------------------->X------------------------->X + * / (Paint advance) (Paint advance) \ + * / \ + * (Initial advance) / \ (Paint advance) + * ------------------- ---------------- + * / \ + * X--------------------------------------------------X--------------------------X--------------------------X + * (text position ^) (layout advance) (layout advance) (layout advance) + */ + void growInitialAdvanceHorizontally(float delta) { m_initialAdvance.expand(delta, 0); } + FloatSize initialAdvance() const { return m_initialAdvance; } + const FloatSize* baseAdvances() const { return m_baseAdvances.data(); } + const FloatPoint* glyphOrigins() const { return m_glyphOrigins.size() == glyphCount() ? m_glyphOrigins.data() : nullptr; } + bool isLTR() const { return m_isLTR; } + bool isMonotonic() const { return m_isMonotonic; } + void setIsNonMonotonic(); + + private: + ComplexTextRun(CTRunRef, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd); + ComplexTextRun(const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr); + WEBCORE_EXPORT ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font&, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr); + + Vector<FloatSize, 64> m_baseAdvances; + Vector<FloatPoint, 64> m_glyphOrigins; + Vector<CGGlyph, 64> m_glyphs; + Vector<unsigned, 64> m_glyphEndOffsets; + Vector<unsigned, 64> m_coreTextIndices; + FloatSize m_initialAdvance; + const Font& m_font; + const UChar* m_characters; + unsigned m_stringLength; + unsigned m_indexBegin; + unsigned m_indexEnd; + unsigned m_glyphCount; + unsigned m_stringLocation; + bool m_isLTR; + bool m_isMonotonic { true }; + }; +private: + void computeExpansionOpportunity(); + void finishConstruction(); + + static unsigned stringBegin(const ComplexTextRun& run) { return run.stringLocation() + run.indexBegin(); } + static unsigned stringEnd(const ComplexTextRun& run) { return run.stringLocation() + run.indexEnd(); } + + void collectComplexTextRuns(); + + void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const Font*); + void adjustGlyphsAndAdvances(); + + unsigned indexOfCurrentRun(unsigned& leftmostGlyph); + unsigned incrementCurrentRun(unsigned& leftmostGlyph); + + float runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle) const; + + FloatPoint glyphOrigin(unsigned index) const { return index < m_glyphOrigins.size() ? m_glyphOrigins[index] : FloatPoint(); } + + Vector<FloatSize, 256> m_adjustedBaseAdvances; + Vector<FloatPoint, 256> m_glyphOrigins; + Vector<CGGlyph, 256> m_adjustedGlyphs; + + Vector<UChar, 256> m_smallCapsBuffer; + + // There is a 3-level hierarchy here. At the top, we are interested in m_run.string(). We partition that string + // into Lines, each of which is a sequence of characters which should use the same Font. Core Text then partitions + // the Line into ComplexTextRuns. + // ComplexTextRun::stringLocation() and ComplexTextRun::stringLength() refer to the offset and length of the Line + // relative to m_run.string(). ComplexTextRun::indexAt() returns to the offset of a codepoint relative to + // its Line. ComplexTextRun::glyphs() and ComplexTextRun::advances() refer to glyphs relative to the ComplexTextRun. + // The length of the entire TextRun is m_run.length() + Vector<RefPtr<ComplexTextRun>, 16> m_complexTextRuns; + + // The initial capacity of these vectors was selected as being the smallest power of two greater than + // the average (3.5) plus one standard deviation (7.5) of nonzero sizes used on Arabic Wikipedia. + Vector<unsigned, 16> m_runIndices; + Vector<unsigned, 16> m_glyphCountFromStartToIndex; + +#if PLATFORM(COCOA) + Vector<RetainPtr<CTLineRef>> m_coreTextLines; +#endif + + Vector<String> m_stringsFor8BitRuns; + + HashSet<const Font*>* m_fallbackFonts { nullptr }; + + const FontCascade& m_font; + const TextRun& m_run; + + unsigned m_currentCharacter { 0 }; + unsigned m_end { 0 }; + + float m_totalWidth { 0 }; + float m_runWidthSoFar { 0 }; + unsigned m_numGlyphsSoFar { 0 }; + unsigned m_currentRun { 0 }; + unsigned m_glyphInCurrentRun { 0 }; + unsigned m_characterInCurrentGlyph { 0 }; + float m_expansion { 0 }; + float m_expansionPerOpportunity { 0 }; + + float m_minGlyphBoundingBoxX { std::numeric_limits<float>::max() }; + float m_maxGlyphBoundingBoxX { std::numeric_limits<float>::min() }; + float m_minGlyphBoundingBoxY { std::numeric_limits<float>::max() }; + float m_maxGlyphBoundingBoxY { std::numeric_limits<float>::min() }; + + bool m_isLTROnly { true }; + bool m_mayUseNaturalWritingDirection { false }; + bool m_forTextEmphasis { false }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp index bf44bffab..3a229c61b 100644 --- a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp +++ b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,10 +29,11 @@ #include "FloatRect.h" #include "GraphicsContext.h" #include "ImageBuffer.h" +#include "TextStream.h" namespace WebCore { -CrossfadeGeneratedImage::CrossfadeGeneratedImage(Image* fromImage, Image* toImage, float percentage, IntSize crossfadeSize, const IntSize& size) +CrossfadeGeneratedImage::CrossfadeGeneratedImage(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize& size) : m_fromImage(fromImage) , m_toImage(toImage) , m_percentage(percentage) @@ -41,73 +42,83 @@ CrossfadeGeneratedImage::CrossfadeGeneratedImage(Image* fromImage, Image* toImag setContainerSize(size); } -static void drawCrossfadeSubimage(GraphicsContext* context, Image* image, CompositeOperator operation, float opacity, IntSize targetSize) +static void drawCrossfadeSubimage(GraphicsContext& context, Image& image, CompositeOperator operation, float opacity, const FloatSize& targetSize) { - IntSize imageSize = image->size(); + FloatSize imageSize = image.size(); // SVGImage resets the opacity when painting, so we have to use transparency layers to accurately paint one at a given opacity. - bool useTransparencyLayer = image->isSVGImage(); + bool useTransparencyLayer = image.isSVGImage(); - GraphicsContextStateSaver stateSaver(*context); - - context->setCompositeOperation(operation); + GraphicsContextStateSaver stateSaver(context); + + CompositeOperator drawImageOperation = operation; - if (useTransparencyLayer) - context->beginTransparencyLayer(opacity); - else - context->setAlpha(opacity); + if (useTransparencyLayer) { + context.setCompositeOperation(operation); + context.beginTransparencyLayer(opacity); + drawImageOperation = CompositeSourceOver; + } else + context.setAlpha(opacity); if (targetSize != imageSize) - context->scale(FloatSize(static_cast<float>(targetSize.width()) / imageSize.width(), - static_cast<float>(targetSize.height()) / imageSize.height())); - context->drawImage(image, ColorSpaceDeviceRGB, IntPoint()); + context.scale(FloatSize(targetSize.width() / imageSize.width(), targetSize.height() / imageSize.height())); + + context.drawImage(image, IntPoint(), ImagePaintingOptions(drawImageOperation)); if (useTransparencyLayer) - context->endTransparencyLayer(); + context.endTransparencyLayer(); } -void CrossfadeGeneratedImage::drawCrossfade(GraphicsContext* context) +void CrossfadeGeneratedImage::drawCrossfade(GraphicsContext& context) { // Draw nothing if either of the images hasn't loaded yet. - if (m_fromImage == Image::nullImage() || m_toImage == Image::nullImage()) + if (m_fromImage.ptr() == Image::nullImage() || m_toImage.ptr() == Image::nullImage()) return; - GraphicsContextStateSaver stateSaver(*context); + GraphicsContextStateSaver stateSaver(context); - context->clip(IntRect(IntPoint(), m_crossfadeSize)); - context->beginTransparencyLayer(1); + context.clip(FloatRect(FloatPoint(), m_crossfadeSize)); + context.beginTransparencyLayer(1); - drawCrossfadeSubimage(context, m_fromImage, CompositeSourceOver, 1 - m_percentage, m_crossfadeSize); - drawCrossfadeSubimage(context, m_toImage, CompositePlusLighter, m_percentage, m_crossfadeSize); + drawCrossfadeSubimage(context, m_fromImage.get(), CompositeSourceOver, 1 - m_percentage, m_crossfadeSize); + drawCrossfadeSubimage(context, m_toImage.get(), CompositePlusLighter, m_percentage, m_crossfadeSize); - context->endTransparencyLayer(); + context.endTransparencyLayer(); } -void CrossfadeGeneratedImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) +void CrossfadeGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) { - GraphicsContextStateSaver stateSaver(*context); - context->setCompositeOperation(compositeOp, blendMode); - context->clip(dstRect); - context->translate(dstRect.x(), dstRect.y()); + GraphicsContextStateSaver stateSaver(context); + context.setCompositeOperation(compositeOp, blendMode); + context.clip(dstRect); + context.translate(dstRect.x(), dstRect.y()); if (dstRect.size() != srcRect.size()) - context->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); - context->translate(-srcRect.x(), -srcRect.y()); + context.scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); + context.translate(-srcRect.x(), -srcRect.y()); drawCrossfade(context); } -void CrossfadeGeneratedImage::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode) +void CrossfadeGeneratedImage::drawPattern(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, BlendMode blendMode) { - std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(size(), 1, ColorSpaceDeviceRGB, context->isAcceleratedContext() ? Accelerated : Unaccelerated); + std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(size(), context.renderingMode()); if (!imageBuffer) return; // Fill with the cross-faded image. - GraphicsContext* graphicsContext = imageBuffer->context(); + GraphicsContext& graphicsContext = imageBuffer->context(); drawCrossfade(graphicsContext); // Tile the image buffer into the context. - imageBuffer->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, compositeOp, dstRect, blendMode); + imageBuffer->drawPattern(context, dstRect, srcRect, patternTransform, phase, spacing, compositeOp, blendMode); +} + +void CrossfadeGeneratedImage::dump(TextStream& ts) const +{ + GeneratedImage::dump(ts); + ts.dumpProperty("from-image", m_fromImage.get()); + ts.dumpProperty("to-image", m_toImage.get()); + ts.dumpProperty("percentage", m_percentage); } } diff --git a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h index ee965d446..c7849a715 100644 --- a/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h +++ b/Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,44 +26,45 @@ #ifndef CrossfadeGeneratedImage_h #define CrossfadeGeneratedImage_h +#include "FloatSize.h" #include "GeneratedImage.h" #include "Image.h" #include "ImageObserver.h" -#include "IntSize.h" #include <wtf/RefPtr.h> namespace WebCore { -class CSSCrossfadeValue; - class CrossfadeGeneratedImage final : public GeneratedImage { public: - static PassRefPtr<CrossfadeGeneratedImage> create(Image* fromImage, Image* toImage, float percentage, IntSize crossfadeSize, const IntSize& size) + static Ref<CrossfadeGeneratedImage> create(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize& size) { - return adoptRef(new CrossfadeGeneratedImage(fromImage, toImage, percentage, crossfadeSize, size)); + return adoptRef(*new CrossfadeGeneratedImage(fromImage, toImage, percentage, crossfadeSize, size)); } - virtual void setContainerSize(const IntSize&) override { } - virtual bool usesContainerSize() const override { return false; } - virtual bool hasRelativeWidth() const override { return false; } - virtual bool hasRelativeHeight() const override { return false; } + void setContainerSize(const FloatSize&) override { } + bool usesContainerSize() const override { return false; } + bool hasRelativeWidth() const override { return false; } + bool hasRelativeHeight() const override { return false; } - virtual IntSize size() const override { return m_crossfadeSize; } + FloatSize size() const override { return m_crossfadeSize; } protected: - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override; - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& dstRect, BlendMode) override; + void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override; + void drawPattern(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override; - CrossfadeGeneratedImage(Image* fromImage, Image* toImage, float percentage, IntSize crossfadeSize, const IntSize&); + CrossfadeGeneratedImage(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize&); private: - void drawCrossfade(GraphicsContext*); + bool isCrossfadeGeneratedImage() const override { return true; } + void dump(TextStream&) const override; + + void drawCrossfade(GraphicsContext&); - Image* m_fromImage; - Image* m_toImage; + Ref<Image> m_fromImage; + Ref<Image> m_toImage; float m_percentage; - IntSize m_crossfadeSize; + FloatSize m_crossfadeSize; }; } diff --git a/Source/WebCore/platform/graphics/DashArray.h b/Source/WebCore/platform/graphics/DashArray.h index 39596d423..5a1b31733 100644 --- a/Source/WebCore/platform/graphics/DashArray.h +++ b/Source/WebCore/platform/graphics/DashArray.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp index 5641864a6..2cbefcc95 100644 --- a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp +++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,45 +24,48 @@ */ #include "config.h" +#include "DisplayRefreshMonitor.h" #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) -#include "DisplayRefreshMonitor.h" -#include <wtf/CurrentTime.h> -#include <wtf/Ref.h> +#include "DisplayRefreshMonitorClient.h" +#include "DisplayRefreshMonitorManager.h" -namespace WebCore { +#if PLATFORM(IOS) +#include "DisplayRefreshMonitorIOS.h" +#else +#include "DisplayRefreshMonitorMac.h" +#endif -DisplayRefreshMonitorClient::DisplayRefreshMonitorClient() - : m_scheduled(false) - , m_displayIDIsSet(false) -{ -} +namespace WebCore { -DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient() +RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(PlatformDisplayID displayID) { - DisplayRefreshMonitorManager::sharedManager()->unregisterClient(this); +#if PLATFORM(MAC) + return DisplayRefreshMonitorMac::create(displayID); +#endif +#if PLATFORM(IOS) + return DisplayRefreshMonitorIOS::create(displayID); +#endif + return nullptr; } -void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(double timestamp) +RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::create(DisplayRefreshMonitorClient& client) { - if (m_scheduled) { - m_scheduled = false; - displayRefreshFired(timestamp); - } + return client.createDisplayRefreshMonitor(client.displayID()); } DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID) - : m_monotonicAnimationStartTime(0) - , m_active(true) + : m_active(true) , m_scheduled(false) , m_previousFrameDone(true) , m_unscheduledFireCount(0) , m_displayID(displayID) , m_clientsToBeNotified(nullptr) -#if PLATFORM(MAC) - , m_displayLink(0) -#endif +{ +} + +DisplayRefreshMonitor::~DisplayRefreshMonitor() { } @@ -72,138 +75,57 @@ void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* monitor->displayDidRefresh(); } -void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient* client) +void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient& client) { - m_clients.add(client); + m_clients.add(&client); } -bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient* client) +bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient& client) { if (m_clientsToBeNotified) - m_clientsToBeNotified->remove(client); - return m_clients.remove(client); + m_clientsToBeNotified->remove(&client); + return m_clients.remove(&client); } void DisplayRefreshMonitor::displayDidRefresh() { - double monotonicAnimationStartTime; { - MutexLocker lock(m_mutex); + LockHolder lock(m_mutex); if (!m_scheduled) ++m_unscheduledFireCount; else m_unscheduledFireCount = 0; m_scheduled = false; - monotonicAnimationStartTime = m_monotonicAnimationStartTime; } // The call back can cause all our clients to be unregistered, so we need to protect // against deletion until the end of the method. - Ref<DisplayRefreshMonitor> protect(*this); + Ref<DisplayRefreshMonitor> protectedThis(*this); // Copy the hash table and remove clients from it one by one so we don't notify // any client twice, but can respond to removal of clients during the delivery process. HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients; m_clientsToBeNotified = &clientsToBeNotified; while (!clientsToBeNotified.isEmpty()) { - // Take a random client out of the set. Ordering doesn't matter. - // FIXME: Would read more cleanly if HashSet had a take function. - auto it = clientsToBeNotified.begin(); - DisplayRefreshMonitorClient* client = *it; - clientsToBeNotified.remove(it); - - client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime); + DisplayRefreshMonitorClient* client = clientsToBeNotified.takeAny(); + client->fireDisplayRefreshIfNeeded(); // This checks if this function was reentered. In that case, stop iterating // since it's not safe to use the set any more. if (m_clientsToBeNotified != &clientsToBeNotified) break; } + if (m_clientsToBeNotified == &clientsToBeNotified) m_clientsToBeNotified = nullptr; { - MutexLocker lock(m_mutex); + LockHolder lock(m_mutex); m_previousFrameDone = true; } - DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this); -} - -DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager() -{ - DEFINE_STATIC_LOCAL(DisplayRefreshMonitorManager, manager, ()); - return &manager; -} - -DisplayRefreshMonitor* DisplayRefreshMonitorManager::ensureMonitorForClient(DisplayRefreshMonitorClient* client) -{ - DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID); - if (it == m_monitors.end()) { - RefPtr<DisplayRefreshMonitor> monitor = DisplayRefreshMonitor::create(client->m_displayID); - monitor->addClient(client); - DisplayRefreshMonitor* result = monitor.get(); - m_monitors.add(client->m_displayID, monitor.release()); - return result; - } - it->value->addClient(client); - return it->value.get(); -} - -void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client) -{ - if (!client->m_displayIDIsSet) - return; - - ensureMonitorForClient(client); -} - -void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client) -{ - if (!client->m_displayIDIsSet) - return; - - DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID); - if (it == m_monitors.end()) - return; - - DisplayRefreshMonitor* monitor = it->value.get(); - if (monitor->removeClient(client)) { - if (!monitor->hasClients()) - m_monitors.remove(it); - } -} - -bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient* client) -{ - if (!client->m_displayIDIsSet) - return false; - - DisplayRefreshMonitor* monitor = ensureMonitorForClient(client); - - client->m_scheduled = true; - return monitor->requestRefreshCallback(); -} - -void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor* monitor) -{ - if (monitor->shouldBeTerminated()) { - ASSERT(m_monitors.contains(monitor->displayID())); - m_monitors.remove(monitor->displayID()); - } -} - -void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client) -{ - if (client->m_displayIDIsSet && client->m_displayID == displayID) - return; - - unregisterClient(client); - client->setDisplayID(displayID); - registerClient(client); - if (client->m_scheduled) - scheduleAnimation(client); + DisplayRefreshMonitorManager::sharedManager().displayDidRefresh(*this); } } diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h index 648a19119..12183267b 100644 --- a/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h +++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2014, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,70 +29,29 @@ #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) #include "PlatformScreen.h" -#include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/Lock.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> -#include <wtf/Threading.h> - -#if PLATFORM(MAC) -typedef struct __CVDisplayLink *CVDisplayLinkRef; -#endif namespace WebCore { class DisplayAnimationClient; -class DisplayRefreshMonitor; -class DisplayRefreshMonitorManager; - -// -// Abstract virtual client which receives refresh fired messages on the main thread -// -class DisplayRefreshMonitorClient { - friend class DisplayRefreshMonitor; - friend class DisplayRefreshMonitorManager; - -public: - DisplayRefreshMonitorClient(); - virtual ~DisplayRefreshMonitorClient(); - - virtual void displayRefreshFired(double timestamp) = 0; - -private: - void fireDisplayRefreshIfNeeded(double timestamp); - - void setDisplayID(PlatformDisplayID displayID) - { - m_displayID = displayID; - m_displayIDIsSet = true; - } - - bool m_scheduled; - bool m_displayIDIsSet; - PlatformDisplayID m_displayID; -}; - -// -// Monitor for display refresh messages for a given screen -// +class DisplayRefreshMonitorClient; class DisplayRefreshMonitor : public RefCounted<DisplayRefreshMonitor> { public: - static PassRefPtr<DisplayRefreshMonitor> create(PlatformDisplayID displayID) - { - return adoptRef(new DisplayRefreshMonitor(displayID)); - } - - ~DisplayRefreshMonitor(); + static RefPtr<DisplayRefreshMonitor> create(DisplayRefreshMonitorClient&); + WEBCORE_EXPORT virtual ~DisplayRefreshMonitor(); // Return true if callback request was scheduled, false if it couldn't be // (e.g., hardware refresh is not available) - bool requestRefreshCallback(); + virtual bool requestRefreshCallback() = 0; void windowScreenDidChange(PlatformDisplayID); bool hasClients() const { return m_clients.size(); } - void addClient(DisplayRefreshMonitorClient*); - bool removeClient(DisplayRefreshMonitorClient*); + void addClient(DisplayRefreshMonitorClient&); + bool removeClient(DisplayRefreshMonitorClient&); PlatformDisplayID displayID() const { return m_displayID; } @@ -101,67 +60,36 @@ public: const int maxInactiveFireCount = 10; return !m_scheduled && m_unscheduledFireCount > maxInactiveFireCount; } - -private: - explicit DisplayRefreshMonitor(PlatformDisplayID); + bool isActive() const { return m_active; } + void setIsActive(bool active) { m_active = active; } + + bool isScheduled() const { return m_scheduled; } + void setIsScheduled(bool scheduled) { m_scheduled = scheduled; } + + bool isPreviousFrameDone() const { return m_previousFrameDone; } + void setIsPreviousFrameDone(bool done) { m_previousFrameDone = done; } + + Lock& mutex() { return m_mutex; } + + static RefPtr<DisplayRefreshMonitor> createDefaultDisplayRefreshMonitor(PlatformDisplayID); + +protected: + WEBCORE_EXPORT explicit DisplayRefreshMonitor(PlatformDisplayID); + WEBCORE_EXPORT static void handleDisplayRefreshedNotificationOnMainThread(void* data); + +private: void displayDidRefresh(); - static void handleDisplayRefreshedNotificationOnMainThread(void* data); - double m_monotonicAnimationStartTime; bool m_active; bool m_scheduled; bool m_previousFrameDone; int m_unscheduledFireCount; // Number of times the display link has fired with no clients. PlatformDisplayID m_displayID; - Mutex m_mutex; + Lock m_mutex; HashSet<DisplayRefreshMonitorClient*> m_clients; HashSet<DisplayRefreshMonitorClient*>* m_clientsToBeNotified; - -#if PLATFORM(MAC) && !PLATFORM(IOS) -public: - void displayLinkFired(double nowSeconds, double outputTimeSeconds); -private: - CVDisplayLinkRef m_displayLink; -#endif - -#if PLATFORM(IOS) -public: - void displayLinkFired(double nowSeconds); -private: - void* m_displayLink; -#endif -}; - -// -// Singleton manager for all the DisplayRefreshMonitors. This is the interface to the -// outside world. It distributes requests to the appropriate monitor. When the display -// refresh event fires, the passed DisplayRefreshMonitorClient is called directly on -// the main thread. -// -class DisplayRefreshMonitorManager { -public: - static DisplayRefreshMonitorManager* sharedManager(); - - void registerClient(DisplayRefreshMonitorClient*); - void unregisterClient(DisplayRefreshMonitorClient*); - - bool scheduleAnimation(DisplayRefreshMonitorClient*); - void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient*); - -private: - friend class DisplayRefreshMonitor; - void displayDidRefresh(DisplayRefreshMonitor*); - - DisplayRefreshMonitorManager() { } - DisplayRefreshMonitor* ensureMonitorForClient(DisplayRefreshMonitorClient*); - - // We know nothing about the values of PlatformDisplayIDs, so use UnsignedWithZeroKeyHashTraits. - // FIXME: Since we know nothing about these values, this is not sufficient. - // Even with UnsignedWithZeroKeyHashTraits, there are still two special values used for empty and deleted hash table slots. - typedef HashMap<uint64_t, RefPtr<DisplayRefreshMonitor>, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> DisplayRefreshMonitorMap; - DisplayRefreshMonitorMap m_monitors; }; } diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp new file mode 100644 index 000000000..b08a3a898 --- /dev/null +++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DisplayRefreshMonitorClient.h" + +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + +#include "DisplayRefreshMonitor.h" +#include "DisplayRefreshMonitorManager.h" + +namespace WebCore { + +DisplayRefreshMonitorClient::DisplayRefreshMonitorClient() +{ +} + +DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient() +{ + DisplayRefreshMonitorManager::sharedManager().unregisterClient(*this); +} + +void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded() +{ + if (!m_scheduled) + return; + + m_scheduled = false; + displayRefreshFired(); +} + +} + +#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h new file mode 100644 index 000000000..8fa114c45 --- /dev/null +++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + +#include "PlatformScreen.h" +#include <wtf/Forward.h> +#include <wtf/Optional.h> + +namespace WebCore { + +class DisplayRefreshMonitor; + +class DisplayRefreshMonitorClient { +public: + DisplayRefreshMonitorClient(); + virtual ~DisplayRefreshMonitorClient(); + + // Always called on the main thread. + virtual void displayRefreshFired() = 0; + + virtual RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const = 0; + + PlatformDisplayID displayID() const { return m_displayID.value(); } + bool hasDisplayID() const { return !!m_displayID; } + void setDisplayID(PlatformDisplayID displayID) { m_displayID = displayID; } + + void setIsScheduled(bool isScheduled) { m_scheduled = isScheduled; } + bool isScheduled() const { return m_scheduled; } + + void fireDisplayRefreshIfNeeded(); + +private: + bool m_scheduled { false }; + std::optional<PlatformDisplayID> m_displayID; +}; + +} + +#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp new file mode 100644 index 000000000..f08aebe72 --- /dev/null +++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DisplayRefreshMonitorManager.h" + +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + +#include "DisplayRefreshMonitor.h" +#include "DisplayRefreshMonitorClient.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +DisplayRefreshMonitorManager::~DisplayRefreshMonitorManager() +{ +} + +DisplayRefreshMonitorManager& DisplayRefreshMonitorManager::sharedManager() +{ + static NeverDestroyed<DisplayRefreshMonitorManager> manager; + return manager.get(); +} + +DisplayRefreshMonitor* DisplayRefreshMonitorManager::createMonitorForClient(DisplayRefreshMonitorClient& client) +{ + PlatformDisplayID clientDisplayID = client.displayID(); + for (const RefPtr<DisplayRefreshMonitor>& monitor : m_monitors) { + if (monitor->displayID() != clientDisplayID) + continue; + monitor->addClient(client); + return monitor.get(); + } + + auto monitor = DisplayRefreshMonitor::create(client); + if (!monitor) + return nullptr; + + monitor->addClient(client); + DisplayRefreshMonitor* result = monitor.get(); + m_monitors.append(WTFMove(monitor)); + return result; +} + +void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient& client) +{ + if (!client.hasDisplayID()) + return; + + createMonitorForClient(client); +} + +void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient& client) +{ + if (!client.hasDisplayID()) + return; + + PlatformDisplayID clientDisplayID = client.displayID(); + for (size_t i = 0; i < m_monitors.size(); ++i) { + RefPtr<DisplayRefreshMonitor> monitor = m_monitors[i]; + if (monitor->displayID() != clientDisplayID) + continue; + if (monitor->removeClient(client)) { + if (!monitor->hasClients()) + m_monitors.remove(i); + } + return; + } +} + +bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient& client) +{ + if (!client.hasDisplayID()) + return false; + + DisplayRefreshMonitor* monitor = createMonitorForClient(client); + if (!monitor) + return false; + + client.setIsScheduled(true); + return monitor->requestRefreshCallback(); +} + +void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor& monitor) +{ + if (!monitor.shouldBeTerminated()) + return; + + size_t monitorIndex = m_monitors.find(&monitor); + if (monitorIndex != notFound) + m_monitors.remove(monitorIndex); +} + +void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient& client) +{ + if (client.hasDisplayID() && client.displayID() == displayID) + return; + + unregisterClient(client); + client.setDisplayID(displayID); + registerClient(client); + if (client.isScheduled()) + scheduleAnimation(client); +} + +} + +#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) diff --git a/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h new file mode 100644 index 000000000..801811bbc --- /dev/null +++ b/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DisplayRefreshMonitorManager_h +#define DisplayRefreshMonitorManager_h + +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + +#include "DisplayRefreshMonitor.h" +#include "PlatformScreen.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class DisplayRefreshMonitorManager { + friend class NeverDestroyed<DisplayRefreshMonitorManager>; +public: + static DisplayRefreshMonitorManager& sharedManager(); + + void registerClient(DisplayRefreshMonitorClient&); + void unregisterClient(DisplayRefreshMonitorClient&); + + bool scheduleAnimation(DisplayRefreshMonitorClient&); + void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient&); + +private: + friend class DisplayRefreshMonitor; + void displayDidRefresh(DisplayRefreshMonitor&); + + DisplayRefreshMonitorManager() { } + virtual ~DisplayRefreshMonitorManager(); + + DisplayRefreshMonitor* createMonitorForClient(DisplayRefreshMonitorClient&); + + Vector<RefPtr<DisplayRefreshMonitor>> m_monitors; +}; + +} + +#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + +#endif diff --git a/Source/WebCore/platform/graphics/ExtendedColor.cpp b/Source/WebCore/platform/graphics/ExtendedColor.cpp new file mode 100644 index 000000000..50e3c5c8a --- /dev/null +++ b/Source/WebCore/platform/graphics/ExtendedColor.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ExtendedColor.h" + +#include "ColorSpace.h" +#include <wtf/MathExtras.h> +#include <wtf/dtoa.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +Ref<ExtendedColor> ExtendedColor::create(float r, float g, float b, float a, ColorSpace colorSpace) +{ + return adoptRef(*new ExtendedColor(r, g, b, a, colorSpace)); +} + +String ExtendedColor::cssText() const +{ + StringBuilder builder; + builder.reserveCapacity(40); + builder.appendLiteral("color("); + + switch (m_colorSpace) { + case ColorSpaceSRGB: + builder.appendLiteral("srgb "); + break; + case ColorSpaceDisplayP3: + builder.appendLiteral("display-p3 "); + break; + default: + ASSERT_NOT_REACHED(); + return WTF::emptyString(); + } + + NumberToStringBuffer buffer; + bool shouldTruncateTrailingZeros = true; + + builder.append(numberToFixedPrecisionString(red(), 6, buffer, shouldTruncateTrailingZeros)); + builder.append(' '); + + builder.append(numberToFixedPrecisionString(green(), 6, buffer, shouldTruncateTrailingZeros)); + builder.append(' '); + + builder.append(numberToFixedPrecisionString(blue(), 6, buffer, shouldTruncateTrailingZeros)); + if (!WTF::areEssentiallyEqual(alpha(), 1.0f)) { + builder.appendLiteral(" / "); + builder.append(numberToFixedPrecisionString(alpha(), 6, buffer, shouldTruncateTrailingZeros)); + } + builder.append(')'); + + return builder.toString(); +} + +} diff --git a/Source/WebCore/platform/graphics/ExtendedColor.h b/Source/WebCore/platform/graphics/ExtendedColor.h new file mode 100644 index 000000000..37c68edc3 --- /dev/null +++ b/Source/WebCore/platform/graphics/ExtendedColor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ColorSpace.h" + +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class ExtendedColor : public RefCounted<ExtendedColor> { +public: + static Ref<ExtendedColor> create(float r, float g, float b, float a, ColorSpace = ColorSpace::ColorSpaceSRGB); + + float red() const { return m_red; } + float green() const { return m_green; } + float blue() const { return m_blue; } + float alpha() const { return m_alpha; } + + ColorSpace colorSpace() const { return m_colorSpace; } + + WEBCORE_EXPORT String cssText() const; + +private: + ExtendedColor(float r, float g, float b, float a, ColorSpace colorSpace) + : m_red(r) + , m_green(g) + , m_blue(b) + , m_alpha(a) + , m_colorSpace(colorSpace) + { } + + float m_red { 0 }; + float m_green { 0 }; + float m_blue { 0 }; + float m_alpha { 0 }; + + ColorSpace m_colorSpace { ColorSpace::ColorSpaceSRGB }; +}; + +} diff --git a/Source/WebCore/platform/graphics/Extensions3D.h b/Source/WebCore/platform/graphics/Extensions3D.h index 2454e40ea..87ee55d7e 100644 --- a/Source/WebCore/platform/graphics/Extensions3D.h +++ b/Source/WebCore/platform/graphics/Extensions3D.h @@ -52,6 +52,7 @@ public: // GL_ARB_texture_non_power_of_two / GL_OES_texture_npot // GL_EXT_packed_depth_stencil / GL_OES_packed_depth_stencil // GL_ANGLE_framebuffer_blit / GL_ANGLE_framebuffer_multisample + // GL_IMG_multisampled_render_to_texture // GL_OES_texture_float // GL_OES_texture_float_linear // GL_OES_texture_half_float @@ -71,9 +72,8 @@ public: // GL_IMG_texture_compression_pvrtc // EXT_texture_filter_anisotropic // GL_EXT_debug_marker - // GL_CHROMIUM_copy_texture - // GL_CHROMIUM_flipy // GL_ARB_draw_buffers / GL_EXT_draw_buffers + // GL_ANGLE_instanced_arrays // Takes full name of extension; for example, // "GL_EXT_texture_format_BGRA8888". @@ -90,6 +90,16 @@ public: virtual bool isEnabled(const String&) = 0; enum ExtensionsEnumType { + // EXT_sRGB formats + SRGB_EXT = 0x8C40, + SRGB_ALPHA_EXT = 0x8C42, + SRGB8_ALPHA8_EXT = 0x8C43, + FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT = 0x8210, + + // EXT_blend_minmax enums + MIN_EXT = 0x8007, + MAX_EXT = 0x8008, + // GL_EXT_texture_format_BGRA8888 enums BGRA_EXT = 0x80E1, @@ -112,6 +122,12 @@ public: FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56, MAX_SAMPLES = 0x8D57, + // GL_IMG_multisampled_render_to_texture + RENDERBUFFER_SAMPLES_IMG = 0x9133, + FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG = 0x9134, + MAX_SAMPLES_IMG = 0x9135, + TEXTURE_SAMPLES_IMG = 0x9136, + // GL_OES_standard_derivatives names FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B, @@ -154,13 +170,6 @@ public: TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE, MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF, - // GL_CHROMIUM_flipy - UNPACK_FLIP_Y_CHROMIUM = 0x9240, - - // GL_CHROMIUM_copy_texture - UNPACK_PREMULTIPLY_ALPHA_CHROMIUM = 0x9241, - UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM = 0x9242, - // GL_ARB_draw_buffers / GL_EXT_draw_buffers MAX_DRAW_BUFFERS_EXT = 0x8824, DRAW_BUFFER0_EXT = 0x8825, @@ -222,9 +231,6 @@ public: // GL_ANGLE_translated_shader_source virtual String getTranslatedShaderSourceANGLE(Platform3DObject) = 0; - // GL_CHROMIUM_copy_texture - virtual void copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum) = 0; - // EXT Robustness - uses getGraphicsResetStatusARB virtual void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data) = 0; virtual void getnUniformfvEXT(GC3Duint program, int location, GC3Dsizei bufSize, float *params) = 0; @@ -238,21 +244,22 @@ public: // GL_ARB_draw_buffers / GL_EXT_draw_buffers virtual void drawBuffersEXT(GC3Dsizei n, const GC3Denum* bufs) = 0; + // GL_ANGLE_instanced_arrays + virtual void drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) = 0; + virtual void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount) = 0; + virtual void vertexAttribDivisor(GC3Duint index, GC3Duint divisor) = 0; + virtual bool isNVIDIA() = 0; virtual bool isAMD() = 0; virtual bool isIntel() = 0; + virtual bool isImagination() = 0; virtual String vendor() = 0; - // If this method returns false then the system *definitely* does not support multisampling. - // It does not necessarily say the system does support it - callers must attempt to construct - // multisampled renderbuffers and check framebuffer completeness. - // Ports should implement this to return false on configurations where it is known - // that multisampling is not available. - virtual bool maySupportMultisampling() = 0; - // Some configurations have bugs regarding built-in functions in their OpenGL drivers - // that must be avoided. Ports should implement this flag such configurations. + // that must be avoided. Ports should implement these flags on such configurations. virtual bool requiresBuiltInFunctionEmulation() = 0; + virtual bool requiresRestrictedMaximumTextureSize() = 0; + }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FloatPoint.cpp b/Source/WebCore/platform/graphics/FloatPoint.cpp index ca1b8786b..7a1343c72 100644 --- a/Source/WebCore/platform/graphics/FloatPoint.cpp +++ b/Source/WebCore/platform/graphics/FloatPoint.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,10 +30,10 @@ #include "AffineTransform.h" #include "FloatConversion.h" #include "IntPoint.h" +#include "TextStream.h" #include "TransformationMatrix.h" #include <limits> #include <math.h> -#include <wtf/PrintStream.h> namespace WebCore { @@ -41,6 +41,14 @@ FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y()) { } +FloatPoint FloatPoint::constrainedBetween(const FloatPoint& min, const FloatPoint& max) const +{ + return { + std::max(min.x(), std::min(max.x(), m_x)), + std::max(min.y(), std::min(max.y(), m_y)) + }; +} + void FloatPoint::normalize() { float tempLength = length(); @@ -80,48 +88,10 @@ FloatPoint FloatPoint::narrowPrecision(double x, double y) return FloatPoint(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)); } -float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c) -{ - if (p2.x() == p1.x()) - return std::numeric_limits<float>::infinity(); - - // y = mx + c - float slope = (p2.y() - p1.y()) / (p2.x() - p1.x()); - c = p1.y() - slope * p1.x(); - return slope; -} - -bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection) -{ - float pOffset = 0; - float pSlope = findSlope(p1, p2, pOffset); - - float dOffset = 0; - float dSlope = findSlope(d1, d2, dOffset); - - if (dSlope == pSlope) - return false; - - if (pSlope == std::numeric_limits<float>::infinity()) { - intersection.setX(p1.x()); - intersection.setY(dSlope * intersection.x() + dOffset); - return true; - } - if (dSlope == std::numeric_limits<float>::infinity()) { - intersection.setX(d1.x()); - intersection.setY(pSlope * intersection.x() + pOffset); - return true; - } - - // Find x at intersection, where ys overlap; x = (c' - c) / (m - m') - intersection.setX((dOffset - pOffset) / (pSlope - dSlope)); - intersection.setY(pSlope * intersection.x() + pOffset); - return true; -} - -void FloatPoint::dump(PrintStream& out) const +TextStream& operator<<(TextStream& ts, const FloatPoint& p) { - out.printf("(%f, %f)", x(), y()); + // FIXME: callers should use the NumberRespectingIntegers flag. + return ts << "(" << TextStream::FormatNumberRespectingIntegers(p.x()) << "," << TextStream::FormatNumberRespectingIntegers(p.y()) << ")"; } } diff --git a/Source/WebCore/platform/graphics/FloatPoint.h b/Source/WebCore/platform/graphics/FloatPoint.h index 62c37bd36..7a417b4b1 100644 --- a/Source/WebCore/platform/graphics/FloatPoint.h +++ b/Source/WebCore/platform/graphics/FloatPoint.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004-2016 Apple Inc. All rights reserved. * Copyright (C) 2005 Nokia. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,17 +31,26 @@ #include "IntPoint.h" #include <wtf/MathExtras.h> +#if PLATFORM(MAC) && defined __OBJC__ +#import <Foundation/NSGeometry.h> +#endif + #if USE(CG) typedef struct CGPoint CGPoint; #endif -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGPoint NSPoint; #else typedef struct _NSPoint NSPoint; #endif -#endif // PLATFORM(MAC) && !PLATFORM(IOS) +#endif // PLATFORM(MAC) + +#if PLATFORM(WIN) +struct D2D_POINT_2F; +typedef D2D_POINT_2F D2D1_POINT_2F; +#endif namespace WebCore { @@ -49,12 +58,13 @@ class AffineTransform; class TransformationMatrix; class IntPoint; class IntSize; +class TextStream; class FloatPoint { public: - FloatPoint() : m_x(0), m_y(0) { } + FloatPoint() { } FloatPoint(float x, float y) : m_x(x), m_y(y) { } - FloatPoint(const IntPoint&); + WEBCORE_EXPORT FloatPoint(const IntPoint&); explicit FloatPoint(const FloatSize& size) : m_x(size.width()), m_y(size.height()) { } static FloatPoint zero() { return FloatPoint(); } @@ -66,43 +76,56 @@ public: void setX(float x) { m_x = x; } void setY(float y) { m_y = y; } + void set(float x, float y) { m_x = x; m_y = y; } + void move(float dx, float dy) { m_x += dx; m_y += dy; } + void move(const IntSize& a) { m_x += a.width(); m_y += a.height(); } + void move(const FloatSize& a) { m_x += a.width(); m_y += a.height(); } + void moveBy(const IntPoint& a) { m_x += a.x(); m_y += a.y(); } + void moveBy(const FloatPoint& a) { m_x += a.x(); m_y += a.y(); } + + void scale(float scale) + { + m_x *= scale; + m_y *= scale; + } + void scale(float sx, float sy) { m_x *= sx; m_y *= sy; } - void normalize(); + WEBCORE_EXPORT void normalize(); float dot(const FloatPoint& a) const { @@ -111,40 +134,50 @@ public: float slopeAngleRadians() const; float length() const; + float lengthSquared() const { return m_x * m_x + m_y * m_y; } + WEBCORE_EXPORT FloatPoint constrainedBetween(const FloatPoint& min, const FloatPoint& max) const; + + FloatPoint shrunkTo(const FloatPoint& other) const + { + return { std::min(m_x, other.m_x), std::min(m_y, other.m_y) }; + } + FloatPoint expandedTo(const FloatPoint& other) const { - return FloatPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y)); + return { std::max(m_x, other.m_x), std::max(m_y, other.m_y) }; } FloatPoint transposedPoint() const { - return FloatPoint(m_y, m_x); + return { m_y, m_x }; } #if USE(CG) - FloatPoint(const CGPoint&); - operator CGPoint() const; + WEBCORE_EXPORT FloatPoint(const CGPoint&); + WEBCORE_EXPORT operator CGPoint() const; #endif -#if !PLATFORM(IOS) #if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) - FloatPoint(const NSPoint&); - operator NSPoint() const; + WEBCORE_EXPORT FloatPoint(const NSPoint&); + WEBCORE_EXPORT operator NSPoint() const; #endif -#endif // !PLATFORM(IOS) - FloatPoint matrixTransform(const TransformationMatrix&) const; - FloatPoint matrixTransform(const AffineTransform&) const; +#if PLATFORM(WIN) + WEBCORE_EXPORT FloatPoint(const D2D1_POINT_2F&); + WEBCORE_EXPORT operator D2D1_POINT_2F() const; +#endif - void dump(PrintStream& out) const; + WEBCORE_EXPORT FloatPoint matrixTransform(const TransformationMatrix&) const; + WEBCORE_EXPORT FloatPoint matrixTransform(const AffineTransform&) const; private: - float m_x, m_y; + float m_x { 0 }; + float m_y { 0 }; }; @@ -207,6 +240,11 @@ inline float operator*(const FloatPoint& a, const FloatPoint& b) return a.dot(b); } +inline IntSize flooredIntSize(const FloatPoint& p) +{ + return IntSize(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y()))); +} + inline IntPoint roundedIntPoint(const FloatPoint& p) { return IntPoint(clampToInteger(roundf(p.x())), clampToInteger(roundf(p.y()))); @@ -222,9 +260,14 @@ inline IntPoint ceiledIntPoint(const FloatPoint& p) return IntPoint(clampToInteger(ceilf(p.x())), clampToInteger(ceilf(p.y()))); } -inline IntSize flooredIntSize(const FloatPoint& p) +inline FloatPoint floorPointToDevicePixels(const FloatPoint& p, float deviceScaleFactor) { - return IntSize(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y()))); + return FloatPoint(floorf(p.x() * deviceScaleFactor) / deviceScaleFactor, floorf(p.y() * deviceScaleFactor) / deviceScaleFactor); +} + +inline FloatPoint ceilPointToDevicePixels(const FloatPoint& p, float deviceScaleFactor) +{ + return FloatPoint(ceilf(p.x() * deviceScaleFactor) / deviceScaleFactor, ceilf(p.y() * deviceScaleFactor) / deviceScaleFactor); } inline FloatSize toFloatSize(const FloatPoint& a) @@ -232,10 +275,17 @@ inline FloatSize toFloatSize(const FloatPoint& a) return FloatSize(a.x(), a.y()); } -float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c); +inline FloatPoint toFloatPoint(const FloatSize& a) +{ + return FloatPoint(a.width(), a.height()); +} + +inline bool areEssentiallyEqual(const FloatPoint& a, const FloatPoint& b) +{ + return WTF::areEssentiallyEqual(a.x(), b.x()) && WTF::areEssentiallyEqual(a.y(), b.y()); +} -// Find point where lines through the two pairs of points intersect. Returns false if the lines don't intersect. -bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatPoint&); } diff --git a/Source/WebCore/platform/graphics/FloatPoint3D.cpp b/Source/WebCore/platform/graphics/FloatPoint3D.cpp index ed1816bdd..c6fbdab72 100644 --- a/Source/WebCore/platform/graphics/FloatPoint3D.cpp +++ b/Source/WebCore/platform/graphics/FloatPoint3D.cpp @@ -20,9 +20,9 @@ */ #include "config.h" - #include "FloatPoint3D.h" +#include "TextStream.h" #include <math.h> namespace WebCore { @@ -38,5 +38,10 @@ void FloatPoint3D::normalize() } } +TextStream& operator<<(TextStream& ts, const FloatPoint3D& point) +{ + return ts << point.x() << " " << point.y() << " " << point.z(); +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FloatPoint3D.h b/Source/WebCore/platform/graphics/FloatPoint3D.h index ba0ee9d94..3c6e2a77c 100644 --- a/Source/WebCore/platform/graphics/FloatPoint3D.h +++ b/Source/WebCore/platform/graphics/FloatPoint3D.h @@ -182,6 +182,8 @@ inline float FloatPoint3D::distanceTo(const FloatPoint3D& a) const return (*this - a).length(); } +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatPoint3D&); + } // namespace WebCore #endif // FloatPoint3D_h diff --git a/Source/WebCore/platform/graphics/FloatPolygon.cpp b/Source/WebCore/platform/graphics/FloatPolygon.cpp index c39e285ab..d91359111 100644 --- a/Source/WebCore/platform/graphics/FloatPolygon.cpp +++ b/Source/WebCore/platform/graphics/FloatPolygon.cpp @@ -79,8 +79,8 @@ static unsigned findNextEdgeVertexIndex(const FloatPolygon& polygon, unsigned ve return vertexIndex2; } -FloatPolygon::FloatPolygon(PassOwnPtr<Vector<FloatPoint>> vertices, WindRule fillRule) - : m_vertices(vertices) +FloatPolygon::FloatPolygon(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule) + : m_vertices(WTFMove(vertices)) , m_fillRule(fillRule) { unsigned nVertices = numberOfVertices(); diff --git a/Source/WebCore/platform/graphics/FloatPolygon.h b/Source/WebCore/platform/graphics/FloatPolygon.h index 5666574b0..aeaef25d6 100644 --- a/Source/WebCore/platform/graphics/FloatPolygon.h +++ b/Source/WebCore/platform/graphics/FloatPolygon.h @@ -35,8 +35,7 @@ #include "PODIntervalTree.h" #include "ValueToString.h" #include "WindRule.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include <memory> #include <wtf/Vector.h> namespace WebCore { @@ -45,7 +44,7 @@ class FloatPolygonEdge; class FloatPolygon { public: - FloatPolygon(PassOwnPtr<Vector<FloatPoint>> vertices, WindRule fillRule); + FloatPolygon(std::unique_ptr<Vector<FloatPoint>> vertices, WindRule fillRule); const FloatPoint& vertexAt(unsigned index) const { return (*m_vertices)[index]; } unsigned numberOfVertices() const { return m_vertices->size(); } @@ -67,7 +66,7 @@ private: bool containsNonZero(const FloatPoint&) const; bool containsEvenOdd(const FloatPoint&) const; - OwnPtr<Vector<FloatPoint>> m_vertices; + std::unique_ptr<Vector<FloatPoint>> m_vertices; WindRule m_fillRule; FloatRect m_boundingBox; bool m_empty; @@ -95,13 +94,13 @@ public: class FloatPolygonEdge : public VertexPair { friend class FloatPolygon; public: - virtual const FloatPoint& vertex1() const override + const FloatPoint& vertex1() const override { ASSERT(m_polygon); return m_polygon->vertexAt(m_vertexIndex1); } - virtual const FloatPoint& vertex2() const override + const FloatPoint& vertex2() const override { ASSERT(m_polygon); return m_polygon->vertexAt(m_vertexIndex2); diff --git a/Source/WebCore/platform/graphics/FloatQuad.cpp b/Source/WebCore/platform/graphics/FloatQuad.cpp index 655beabf3..c5687767c 100644 --- a/Source/WebCore/platform/graphics/FloatQuad.cpp +++ b/Source/WebCore/platform/graphics/FloatQuad.cpp @@ -12,7 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -33,6 +33,7 @@ #include <algorithm> #include <limits> +#include <wtf/MathExtras.h> namespace WebCore { @@ -90,15 +91,10 @@ FloatRect FloatQuad::boundingBox() const return FloatRect(left, top, right - left, bottom - top); } -static inline bool withinEpsilon(float a, float b) -{ - return fabs(a - b) < std::numeric_limits<float>::epsilon(); -} - bool FloatQuad::isRectilinear() const { - return (withinEpsilon(m_p1.x(), m_p2.x()) && withinEpsilon(m_p2.y(), m_p3.y()) && withinEpsilon(m_p3.x(), m_p4.x()) && withinEpsilon(m_p4.y(), m_p1.y())) - || (withinEpsilon(m_p1.y(), m_p2.y()) && withinEpsilon(m_p2.x(), m_p3.x()) && withinEpsilon(m_p3.y(), m_p4.y()) && withinEpsilon(m_p4.x(), m_p1.x())); + return (WTF::areEssentiallyEqual(m_p1.x(), m_p2.x()) && WTF::areEssentiallyEqual(m_p2.y(), m_p3.y()) && WTF::areEssentiallyEqual(m_p3.x(), m_p4.x()) && WTF::areEssentiallyEqual(m_p4.y(), m_p1.y())) + || (WTF::areEssentiallyEqual(m_p1.y(), m_p2.y()) && WTF::areEssentiallyEqual(m_p2.x(), m_p3.x()) && WTF::areEssentiallyEqual(m_p3.y(), m_p4.y()) && WTF::areEssentiallyEqual(m_p4.x(), m_p1.x())); } bool FloatQuad::containsPoint(const FloatPoint& p) const diff --git a/Source/WebCore/platform/graphics/FloatQuad.h b/Source/WebCore/platform/graphics/FloatQuad.h index a9d9096f2..793eb34c1 100644 --- a/Source/WebCore/platform/graphics/FloatQuad.h +++ b/Source/WebCore/platform/graphics/FloatQuad.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -39,6 +39,7 @@ namespace WebCore { // mapping a rectangle through transforms. When initialized from a rect, the // points are in clockwise order from top left. class FloatQuad { + WTF_MAKE_FAST_ALLOCATED; public: FloatQuad() { @@ -78,15 +79,15 @@ public: // that is, if two edges are parallel to the x-axis and the other two // are parallel to the y-axis. If this method returns true, the // corresponding FloatRect can be retrieved with boundingBox(). - bool isRectilinear() const; + WEBCORE_EXPORT bool isRectilinear() const; // Tests whether the given point is inside, or on an edge or corner of this quad. - bool containsPoint(const FloatPoint&) const; + WEBCORE_EXPORT bool containsPoint(const FloatPoint&) const; // Tests whether the four corners of other are inside, or coincident with the sides of this quad. // Note that this only works for convex quads, but that includes all quads that originate // from transformed rects. - bool containsQuad(const FloatQuad&) const; + WEBCORE_EXPORT bool containsQuad(const FloatQuad&) const; // Tests whether any part of the rectangle intersects with this quad. // This only works for convex quads. @@ -104,7 +105,7 @@ public: (m_p1.y() + m_p2.y() + m_p3.y() + m_p4.y()) / 4.0); } - FloatRect boundingBox() const; + WEBCORE_EXPORT FloatRect boundingBox() const; IntRect enclosingBoundingBox() const { return enclosingIntRect(boundingBox()); @@ -125,6 +126,11 @@ public: m_p3.move(dx, dy); m_p4.move(dx, dy); } + + void scale(float s) + { + scale(s, s); + } void scale(float dx, float dy) { diff --git a/Source/WebCore/platform/graphics/FloatRect.cpp b/Source/WebCore/platform/graphics/FloatRect.cpp index 2fae4e7b4..5556c28c4 100644 --- a/Source/WebCore/platform/graphics/FloatRect.cpp +++ b/Source/WebCore/platform/graphics/FloatRect.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,10 +29,10 @@ #include "FloatConversion.h" #include "IntRect.h" +#include "TextStream.h" #include <algorithm> #include <math.h> #include <wtf/MathExtras.h> -#include <wtf/PrintStream.h> namespace WebCore { @@ -147,17 +147,6 @@ void FloatRect::scale(float sx, float sy) m_size.setHeight(height() * sy); } -FloatRect unionRect(const Vector<FloatRect>& rects) -{ - FloatRect result; - - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) - result.unite(rects[i]); - - return result; -} - void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1) { float left = std::min(p0.x(), p1.x()); @@ -217,22 +206,18 @@ void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const Fl setLocationAndSizeFromEdges(left, top, right, bottom); } -IntRect enclosingIntRect(const FloatRect& rect) +FloatRect encloseRectToDevicePixels(const FloatRect& rect, float deviceScaleFactor) { - IntPoint location = flooredIntPoint(rect.minXMinYCorner()); - IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); - - return IntRect(location, maxPoint - location); + FloatPoint location = floorPointToDevicePixels(rect.minXMinYCorner(), deviceScaleFactor); + FloatPoint maxPoint = ceilPointToDevicePixels(rect.maxXMaxYCorner(), deviceScaleFactor); + return FloatRect(location, maxPoint - location); } -IntRect enclosedIntRect(const FloatRect& rect) +IntRect enclosingIntRect(const FloatRect& rect) { - IntPoint location = ceiledIntPoint(rect.minXMinYCorner()); - IntPoint maxPoint = flooredIntPoint(rect.maxXMaxYCorner()); - IntSize size = maxPoint - location; - size.clampNegativeToZero(); - - return IntRect(location, size); + FloatPoint location = flooredIntPoint(rect.minXMinYCorner()); + FloatPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); + return IntRect(IntPoint(location), IntSize(maxPoint - location)); } IntRect roundedIntRect(const FloatRect& rect) @@ -240,21 +225,15 @@ IntRect roundedIntRect(const FloatRect& rect) return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size())); } -FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect) +TextStream& operator<<(TextStream& ts, const FloatRect &r) { - if (srcRect.width() == 0 || srcRect.height() == 0) - return FloatRect(); - - float widthScale = destRect.width() / srcRect.width(); - float heightScale = destRect.height() / srcRect.height(); - return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale, - destRect.y() + (r.y() - srcRect.y()) * heightScale, - r.width() * widthScale, r.height() * heightScale); -} + if (ts.hasFormattingFlag(TextStream::Formatting::SVGStyleRect)) { + // FIXME: callers should use the NumberRespectingIntegers flag. + return ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.x()) << "," << TextStream::FormatNumberRespectingIntegers(r.y()) + << ") size " << TextStream::FormatNumberRespectingIntegers(r.width()) << "x" << TextStream::FormatNumberRespectingIntegers(r.height()); + } -void FloatRect::dump(PrintStream& out) const -{ - out.print(location(), " ", size()); + return ts << r.location() << " " << r.size(); } } diff --git a/Source/WebCore/platform/graphics/FloatRect.h b/Source/WebCore/platform/graphics/FloatRect.h index 8056686d8..cf6dccfd9 100644 --- a/Source/WebCore/platform/graphics/FloatRect.h +++ b/Source/WebCore/platform/graphics/FloatRect.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * Copyright (C) 2005 Nokia. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,32 +28,33 @@ #define FloatRect_h #include "FloatPoint.h" -#include <wtf/Vector.h> - -#if PLATFORM(IOS) -#include <CoreGraphics/CGGeometry.h> -#endif #if USE(CG) typedef struct CGRect CGRect; #endif -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGRect NSRect; #else typedef struct _NSRect NSRect; #endif -#endif // PLATFORM(MAC) && !PLATFORM(IOS) +#endif // PLATFORM(MAC) #if USE(CAIRO) typedef struct _cairo_rectangle cairo_rectangle_t; #endif +#if PLATFORM(WIN) +struct D2D_RECT_F; +typedef D2D_RECT_F D2D1_RECT_F; +#endif + namespace WebCore { class IntRect; class IntPoint; +class TextStream; class FloatRect { public: @@ -67,7 +68,9 @@ public: : m_location(location), m_size(size) { } FloatRect(float x, float y, float width, float height) : m_location(FloatPoint(x, y)), m_size(FloatSize(width, height)) { } - FloatRect(const IntRect&); + FloatRect(const FloatPoint& topLeft, const FloatPoint& bottomRight) + : m_location(topLeft), m_size(FloatSize(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())) { } + WEBCORE_EXPORT FloatRect(const IntRect&); static FloatRect narrowPrecision(double x, double y, double width, double height); @@ -132,21 +135,24 @@ public: FloatPoint minXMaxYCorner() const { return FloatPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft FloatPoint maxXMaxYCorner() const { return FloatPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight - bool intersects(const FloatRect&) const; - bool contains(const FloatRect&) const; - bool contains(const FloatPoint&, ContainsMode = InsideOrOnStroke) const; + WEBCORE_EXPORT bool intersects(const FloatRect&) const; + WEBCORE_EXPORT bool contains(const FloatRect&) const; + WEBCORE_EXPORT bool contains(const FloatPoint&, ContainsMode = InsideOrOnStroke) const; - void intersect(const FloatRect&); - void unite(const FloatRect&); + WEBCORE_EXPORT void intersect(const FloatRect&); + WEBCORE_EXPORT void unite(const FloatRect&); void uniteEvenIfEmpty(const FloatRect&); void uniteIfNonZero(const FloatRect&); - void extend(const FloatPoint&); + WEBCORE_EXPORT void extend(const FloatPoint&); // Note, this doesn't match what IntRect::contains(IntPoint&) does; the int version // is really checking for containment of 1x1 rect, but that doesn't make sense with floats. bool contains(float px, float py) const { return px >= x() && px <= maxX() && py >= y() && py <= maxY(); } + bool overlapsYRange(float y1, float y2) const { return !isEmpty() && y2 >= y1 && y2 >= y() && y1 <= maxY(); } + bool overlapsXRange(float x1, float x2) const { return !isEmpty() && x2 >= x1 && x2 >= x() && x1 <= maxX(); } + void inflateX(float dx) { m_location.setX(m_location.x() - dx); m_size.setWidth(m_size.width() + dx + dx); @@ -157,33 +163,34 @@ public: } void inflate(float d) { inflateX(d); inflateY(d); } void scale(float s) { scale(s, s); } - void scale(float sx, float sy); + WEBCORE_EXPORT void scale(float sx, float sy); FloatRect transposedRect() const { return FloatRect(m_location.transposedPoint(), m_size.transposedSize()); } // Re-initializes this rectangle to fit the sets of passed points. - void fitToPoints(const FloatPoint& p0, const FloatPoint& p1); - void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2); - void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3); + WEBCORE_EXPORT void fitToPoints(const FloatPoint& p0, const FloatPoint& p1); + WEBCORE_EXPORT void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2); + WEBCORE_EXPORT void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3); #if USE(CG) - FloatRect(const CGRect&); - operator CGRect() const; + WEBCORE_EXPORT FloatRect(const CGRect&); + WEBCORE_EXPORT operator CGRect() const; #endif -#if !PLATFORM(IOS) #if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) - FloatRect(const NSRect&); - operator NSRect() const; + WEBCORE_EXPORT FloatRect(const NSRect&); + WEBCORE_EXPORT operator NSRect() const; #endif -#endif // !PLATFORM(IOS) #if USE(CAIRO) FloatRect(const cairo_rectangle_t&); operator cairo_rectangle_t() const; #endif - void dump(PrintStream& out) const; +#if PLATFORM(WIN) + WEBCORE_EXPORT FloatRect(const D2D1_RECT_F&); + WEBCORE_EXPORT operator D2D1_RECT_F() const; +#endif static FloatRect infiniteRect(); bool isInfinite() const; @@ -214,8 +221,6 @@ inline FloatRect unionRect(const FloatRect& a, const FloatRect& b) return c; } -FloatRect unionRect(const Vector<FloatRect>&); - inline FloatRect& operator+=(FloatRect& a, const FloatRect& b) { a.move(b.x(), b.y()); @@ -252,15 +257,11 @@ inline bool FloatRect::isInfinite() const return *this == infiniteRect(); } -IntRect enclosingIntRect(const FloatRect&); - -// Returns a valid IntRect contained within the given FloatRect. -IntRect enclosedIntRect(const FloatRect&); - -IntRect roundedIntRect(const FloatRect&); +WEBCORE_EXPORT FloatRect encloseRectToDevicePixels(const FloatRect&, float deviceScaleFactor); +WEBCORE_EXPORT IntRect enclosingIntRect(const FloatRect&); +WEBCORE_EXPORT IntRect roundedIntRect(const FloatRect&); -// Map rect r from srcRect to an equivalent rect in destRect. -FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatRect&); } diff --git a/Source/WebCore/platform/graphics/FloatRoundedRect.cpp b/Source/WebCore/platform/graphics/FloatRoundedRect.cpp index 19ac93186..20a88b8b6 100644 --- a/Source/WebCore/platform/graphics/FloatRoundedRect.cpp +++ b/Source/WebCore/platform/graphics/FloatRoundedRect.cpp @@ -30,10 +30,17 @@ #include "config.h" #include "FloatRoundedRect.h" +#include "TextStream.h" #include <algorithm> namespace WebCore { +FloatRoundedRect::FloatRoundedRect(const RoundedRect& rect) + : m_rect(rect.rect()) + , m_radii(rect.radii()) +{ +} + FloatRoundedRect::FloatRoundedRect(float x, float y, float width, float height) : m_rect(x, y, width, height) { @@ -56,25 +63,37 @@ bool FloatRoundedRect::Radii::isZero() const return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero(); } +bool FloatRoundedRect::Radii::isUniformCornerRadius() const +{ + return WTF::areEssentiallyEqual(m_topLeft.width(), m_topLeft.height()) + && areEssentiallyEqual(m_topLeft, m_topRight) + && areEssentiallyEqual(m_topLeft, m_bottomLeft) + && areEssentiallyEqual(m_topLeft, m_bottomRight); +} + void FloatRoundedRect::Radii::scale(float factor) { - if (factor == 1) + scale(factor, factor); +} + +void FloatRoundedRect::Radii::scale(float horizontalFactor, float verticalFactor) +{ + if (horizontalFactor == 1 && verticalFactor == 1) return; // If either radius on a corner becomes zero, reset both radii on that corner. - m_topLeft.scale(factor); + m_topLeft.scale(horizontalFactor, verticalFactor); if (!m_topLeft.width() || !m_topLeft.height()) m_topLeft = FloatSize(); - m_topRight.scale(factor); + m_topRight.scale(horizontalFactor, verticalFactor); if (!m_topRight.width() || !m_topRight.height()) m_topRight = FloatSize(); - m_bottomLeft.scale(factor); + m_bottomLeft.scale(horizontalFactor, verticalFactor); if (!m_bottomLeft.width() || !m_bottomLeft.height()) m_bottomLeft = FloatSize(); - m_bottomRight.scale(factor); + m_bottomRight.scale(horizontalFactor, verticalFactor); if (!m_bottomRight.width() || !m_bottomRight.height()) m_bottomRight = FloatSize(); - } void FloatRoundedRect::Radii::expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth) @@ -137,4 +156,71 @@ bool FloatRoundedRect::xInterceptsAtY(float y, float& minXIntercept, float& maxX return true; } +bool FloatRoundedRect::isRenderable() const +{ + return m_radii.topLeft().width() >= 0 && m_radii.topLeft().height() >= 0 + && m_radii.bottomLeft().width() >= 0 && m_radii.bottomLeft().height() >= 0 + && m_radii.topRight().width() >= 0 && m_radii.topRight().height() >= 0 + && m_radii.bottomRight().width() >= 0 && m_radii.bottomRight().height() >= 0 + && m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width() + && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width() + && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height() + && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height(); +} + +void FloatRoundedRect::inflateWithRadii(float size) +{ + FloatRect old = m_rect; + + m_rect.inflate(size); + // Considering the inflation factor of shorter size to scale the radii seems appropriate here + float factor; + if (m_rect.width() < m_rect.height()) + factor = old.width() ? m_rect.width() / old.width() : 0; + else + factor = old.height() ? m_rect.height() / old.height() : 0; + + m_radii.scale(factor); +} + +void FloatRoundedRect::adjustRadii() +{ + float maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width()); + float maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height()); + + if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) { + m_radii.scale(0.0f); + return; + } + float widthRatio = m_rect.width() / maxRadiusWidth; + float heightRatio = m_rect.height() / maxRadiusHeight; + m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio); +} + +// This is conservative; it does not test intrusion into the corner rects. +bool FloatRoundedRect::intersectionIsRectangular(const FloatRect& rect) const +{ + return !(rect.intersects(topLeftCorner()) || rect.intersects(topRightCorner()) || rect.intersects(bottomLeftCorner()) || rect.intersects(bottomRightCorner())); +} + +TextStream& operator<<(TextStream& ts, const FloatRoundedRect& roundedRect) +{ + ts << roundedRect.rect().x() << " " << roundedRect.rect().y() << " " << roundedRect.rect().width() << " " << roundedRect.rect().height() << "\n"; + + ts.increaseIndent(); + ts.writeIndent(); + ts << "topLeft=" << roundedRect.topLeftCorner().width() << " " << roundedRect.topLeftCorner().height() << "\n"; + ts.writeIndent(); + ts << "topRight=" << roundedRect.topRightCorner().width() << " " << roundedRect.topRightCorner().height() << "\n"; + ts.writeIndent(); + ts << "bottomLeft=" << roundedRect.bottomLeftCorner().width() << " " << roundedRect.bottomLeftCorner().height() << "\n"; + ts.writeIndent(); + ts << "bottomRight=" << roundedRect.bottomRightCorner().width() << " " << roundedRect.bottomRightCorner().height(); + ts.decreaseIndent(); + + return ts; +} + + + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FloatRoundedRect.h b/Source/WebCore/platform/graphics/FloatRoundedRect.h index fbd449be8..eed0e355f 100644 --- a/Source/WebCore/platform/graphics/FloatRoundedRect.h +++ b/Source/WebCore/platform/graphics/FloatRoundedRect.h @@ -37,6 +37,7 @@ namespace WebCore { class FloatRoundedRect { + WTF_MAKE_FAST_ALLOCATED; public: class Radii { public: @@ -57,6 +58,14 @@ public: { } + explicit Radii(float uniformRadius) + : m_topLeft(uniformRadius, uniformRadius) + , m_topRight(uniformRadius, uniformRadius) + , m_bottomLeft(uniformRadius, uniformRadius) + , m_bottomRight(uniformRadius, uniformRadius) + { + } + void setTopLeft(const FloatSize& size) { m_topLeft = size; } void setTopRight(const FloatSize& size) { m_topRight = size; } void setBottomLeft(const FloatSize& size) { m_bottomLeft = size; } @@ -67,8 +76,10 @@ public: const FloatSize& bottomRight() const { return m_bottomRight; } bool isZero() const; + bool isUniformCornerRadius() const; // Including no radius. void scale(float factor); + void scale(float horizontalFactor, float verticalFactor); void expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth); void expand(float size) { expand(size, size, size, size); } void shrink(float topWidth, float bottomWidth, float leftWidth, float rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); } @@ -81,7 +92,8 @@ public: FloatSize m_bottomRight; }; - explicit FloatRoundedRect(const FloatRect&, const Radii& = Radii()); + WEBCORE_EXPORT explicit FloatRoundedRect(const FloatRect& = FloatRect(), const Radii& = Radii()); + explicit FloatRoundedRect(const RoundedRect&); FloatRoundedRect(float x, float y, float width, float height); FloatRoundedRect(const FloatRect&, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight); @@ -97,6 +109,8 @@ public: void inflate(float size) { m_rect.inflate(size); } void expandRadii(float size) { m_radii.expand(size); } void shrinkRadii(float size) { m_radii.shrink(size); } + void inflateWithRadii(float size); + void adjustRadii(); FloatRect topLeftCorner() const { @@ -115,8 +129,11 @@ public: return FloatRect(m_rect.maxX() - m_radii.bottomRight().width(), m_rect.maxY() - m_radii.bottomRight().height(), m_radii.bottomRight().width(), m_radii.bottomRight().height()); } + bool isRenderable() const; bool xInterceptsAtY(float y, float& minXIntercept, float& maxXIntercept) const; + bool intersectionIsRectangular(const FloatRect&) const; + private: FloatRect m_rect; Radii m_radii; @@ -127,11 +144,55 @@ inline bool operator==(const FloatRoundedRect::Radii& a, const FloatRoundedRect: return a.topLeft() == b.topLeft() && a.topRight() == b.topRight() && a.bottomLeft() == b.bottomLeft() && a.bottomRight() == b.bottomRight(); } +inline bool operator!=(const FloatRoundedRect::Radii& a, const FloatRoundedRect::Radii& b) +{ + return !(a == b); +} + inline bool operator==(const FloatRoundedRect& a, const FloatRoundedRect& b) { return a.rect() == b.rect() && a.radii() == b.radii(); } +inline bool operator!=(const FloatRoundedRect& a, const FloatRoundedRect& b) +{ + return !(a == b); +} + +inline float calcBorderRadiiConstraintScaleFor(const FloatRect& rect, const FloatRoundedRect::Radii& radii) +{ + // Constrain corner radii using CSS3 rules: + // http://www.w3.org/TR/css3-background/#the-border-radius + + float factor = 1; + float radiiSum; + + // top + radiiSum = radii.topLeft().width() + radii.topRight().width(); // Casts to avoid integer overflow. + if (radiiSum > rect.width()) + factor = std::min(rect.width() / radiiSum, factor); + + // bottom + radiiSum = radii.bottomLeft().width() + radii.bottomRight().width(); + if (radiiSum > rect.width()) + factor = std::min(rect.width() / radiiSum, factor); + + // left + radiiSum = radii.topLeft().height() + radii.bottomLeft().height(); + if (radiiSum > rect.height()) + factor = std::min(rect.height() / radiiSum, factor); + + // right + radiiSum = radii.topRight().height() + radii.bottomRight().height(); + if (radiiSum > rect.height()) + factor = std::min(rect.height() / radiiSum, factor); + + ASSERT(factor <= 1); + return factor; +} + +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatRoundedRect&); + } // namespace WebCore #endif // FloatRoundedRect_h diff --git a/Source/WebCore/platform/graphics/FloatSize.cpp b/Source/WebCore/platform/graphics/FloatSize.cpp index 60ae586a2..73eed5440 100644 --- a/Source/WebCore/platform/graphics/FloatSize.cpp +++ b/Source/WebCore/platform/graphics/FloatSize.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * Copyright (C) 2005 Nokia. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,15 +29,26 @@ #include "FloatConversion.h" #include "IntSize.h" +#include "TextStream.h" #include <limits> #include <math.h> namespace WebCore { -FloatSize::FloatSize(const IntSize& size) : m_width(size.width()), m_height(size.height()) +FloatSize::FloatSize(const IntSize& size) + : m_width(size.width()) + , m_height(size.height()) { } +FloatSize FloatSize::constrainedBetween(const FloatSize& min, const FloatSize& max) const +{ + return { + std::max(min.width(), std::min(max.width(), m_width)), + std::max(min.height(), std::min(max.height(), m_height)) + }; +} + float FloatSize::diagonalLength() const { return sqrtf(diagonalLengthSquared()); @@ -58,9 +69,10 @@ FloatSize FloatSize::narrowPrecision(double width, double height) return FloatSize(narrowPrecisionToFloat(width), narrowPrecisionToFloat(height)); } -void FloatSize::dump(PrintStream& out) const +TextStream& operator<<(TextStream& ts, const FloatSize& size) { - out.printf("(%f x %f)", width(), height()); + return ts << "width=" << TextStream::FormatNumberRespectingIntegers(size.width()) + << " height=" << TextStream::FormatNumberRespectingIntegers(size.height()); } } diff --git a/Source/WebCore/platform/graphics/FloatSize.h b/Source/WebCore/platform/graphics/FloatSize.h index d7aa6df75..e48d0805b 100644 --- a/Source/WebCore/platform/graphics/FloatSize.h +++ b/Source/WebCore/platform/graphics/FloatSize.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * Copyright (C) 2005 Nokia. All rights reserved. * 2008 Eric Seidel <eric@webkit.org> * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -39,23 +39,29 @@ typedef struct CGSize CGSize; #endif -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGSize NSSize; #else typedef struct _NSSize NSSize; #endif -#endif // PLATFORM(MAC) && !PLATFORM(IOS) +#endif // PLATFORM(MAC) + +#if PLATFORM(WIN) +struct D2D_SIZE_F; +typedef D2D_SIZE_F D2D1_SIZE_F; +#endif namespace WebCore { class IntSize; +class TextStream; class FloatSize { public: - FloatSize() : m_width(0), m_height(0) { } + FloatSize() { } FloatSize(float width, float height) : m_width(width), m_height(height) { } - FloatSize(const IntSize&); + WEBCORE_EXPORT FloatSize(const IntSize&); static FloatSize narrowPrecision(double width, double height); @@ -66,7 +72,7 @@ public: void setHeight(float height) { m_height = height; } bool isEmpty() const { return m_width <= 0 || m_height <= 0; } - bool isZero() const; + WEBCORE_EXPORT bool isZero() const; bool isExpressibleAsIntSize() const; float aspectRatio() const { return m_width / m_height; } @@ -77,7 +83,11 @@ public: m_height += height; } - void scale(float s) { scale(s, s); } + void scale(float s) + { + m_width *= s; + m_height *= s; + } void scale(float scaleX, float scaleY) { @@ -85,6 +95,8 @@ public: m_height *= scaleY; } + WEBCORE_EXPORT FloatSize constrainedBetween(const FloatSize& min, const FloatSize& max) const; + FloatSize expandedTo(const FloatSize& other) const { return FloatSize(m_width > other.m_width ? m_width : other.m_width, @@ -97,11 +109,17 @@ public: m_height < other.m_height ? m_height : other.m_height); } - float diagonalLength() const; + WEBCORE_EXPORT float diagonalLength() const; + float diagonalLengthSquared() const { return m_width * m_width + m_height * m_height; } + + float area() const + { + return m_width * m_height; + } FloatSize transposedSize() const { @@ -109,21 +127,23 @@ public: } #if USE(CG) - explicit FloatSize(const CGSize&); // don't do this implicitly since it's lossy - operator CGSize() const; + WEBCORE_EXPORT explicit FloatSize(const CGSize&); // don't do this implicitly since it's lossy + WEBCORE_EXPORT operator CGSize() const; #endif -#if !PLATFORM(IOS) -#if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) - explicit FloatSize(const NSSize &); // don't do this implicitly since it's lossy +#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) + WEBCORE_EXPORT explicit FloatSize(const NSSize&); // don't do this implicitly since it's lossy operator NSSize() const; #endif -#endif // !PLATFORM(IOS) - void dump(PrintStream& out) const; +#if PLATFORM(WIN) + WEBCORE_EXPORT FloatSize(const D2D1_SIZE_F&); + operator D2D1_SIZE_F() const; +#endif private: - float m_width, m_height; + float m_width { 0 }; + float m_height { 0 }; }; inline FloatSize& operator+=(FloatSize& a, const FloatSize& b) @@ -155,21 +175,41 @@ inline FloatSize operator-(const FloatSize& size) return FloatSize(-size.width(), -size.height()); } -inline FloatSize operator*(const FloatSize& a, const float b) +inline FloatSize operator*(const FloatSize& a, float b) { return FloatSize(a.width() * b, a.height() * b); } -inline FloatSize operator*(const float a, const FloatSize& b) +inline FloatSize operator*(float a, const FloatSize& b) { return FloatSize(a * b.width(), a * b.height()); } +inline FloatSize operator*(const FloatSize& a, const FloatSize& b) +{ + return FloatSize(a.width() * b.width(), a.height() * b.height()); +} + +inline FloatSize operator/(const FloatSize& a, float b) +{ + return FloatSize(a.width() / b, a.height() / b); +} + +inline FloatSize operator/(float a, const FloatSize& b) +{ + return FloatSize(a / b.width(), a / b.height()); +} + inline bool operator==(const FloatSize& a, const FloatSize& b) { return a.width() == b.width() && a.height() == b.height(); } +inline bool areEssentiallyEqual(const FloatSize& a, const FloatSize& b) +{ + return WTF::areEssentiallyEqual(a.width(), b.width()) && WTF::areEssentiallyEqual(a.height(), b.height()); +} + inline bool operator!=(const FloatSize& a, const FloatSize& b) { return a.width() != b.width() || a.height() != b.height(); @@ -195,6 +235,8 @@ inline IntPoint flooredIntPoint(const FloatSize& p) return IntPoint(clampToInteger(floorf(p.width())), clampToInteger(floorf(p.height()))); } +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FloatSize&); + } // namespace WebCore #endif // FloatSize_h diff --git a/Source/WebCore/platform/graphics/FloatSizeHash.h b/Source/WebCore/platform/graphics/FloatSizeHash.h new file mode 100644 index 000000000..c5a3966b1 --- /dev/null +++ b/Source/WebCore/platform/graphics/FloatSizeHash.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FloatSizeHash_h +#define FloatSizeHash_h + +#include "FloatSize.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> + +namespace WTF { + +template<> struct FloatHash<WebCore::FloatSize> { + static unsigned hash(const WebCore::FloatSize& key) { return pairIntHash(DefaultHash<float>::Hash::hash(key.width()), DefaultHash<float>::Hash::hash(key.height())); } + static bool equal(const WebCore::FloatSize& a, const WebCore::FloatSize& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +template<> struct DefaultHash<WebCore::FloatSize> { + typedef FloatHash<WebCore::FloatSize> Hash; +}; + +template<> struct HashTraits<WebCore::FloatSize> : GenericHashTraits<WebCore::FloatSize> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(WebCore::FloatSize& slot) { new (NotNull, &slot) WebCore::FloatSize(-1, -1); } + static bool isDeletedValue(const WebCore::FloatSize& value) { return value.width() == -1 && value.height() == -1; } +}; + +} // namespace WTF + +#endif diff --git a/Source/WebCore/platform/graphics/Font.cpp b/Source/WebCore/platform/graphics/Font.cpp index 456f26c0d..ac8fa39c5 100644 --- a/Source/WebCore/platform/graphics/Font.cpp +++ b/Source/WebCore/platform/graphics/Font.cpp @@ -1,960 +1,517 @@ /* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2008, 2010, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov * - * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * 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. + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "Font.h" -#include "FloatRect.h" +#if PLATFORM(COCOA) +#include "CoreTextSPI.h" +#endif #include "FontCache.h" -#include "IntPoint.h" -#include "GlyphBuffer.h" -#include "TextRun.h" -#include "WidthIterator.h" -#include <wtf/MainThread.h> +#include "FontCascade.h" +#include "OpenTypeMathData.h" #include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> #include <wtf/text/AtomicStringHash.h> -#include <wtf/text/StringBuilder.h> - -using namespace WTF; -using namespace Unicode; -namespace WTF { - -// allow compilation of OwnPtr<TextLayout> in source files that don't have access to the TextLayout class definition -template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout* ptr) -{ - WebCore::Font::deleteLayout(ptr); -} +#if ENABLE(OPENTYPE_VERTICAL) +#include "OpenTypeVerticalData.h" +#endif -} +#if USE(DIRECT2D) +#include <dwrite.h> +#endif namespace WebCore { -static PassRef<FontGlyphs> retrieveOrAddCachedFontGlyphs(const FontDescription&, PassRefPtr<FontSelector>); - -const uint8_t Font::s_roundingHackCharacterTable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static bool useBackslashAsYenSignForFamily(const AtomicString& family) -{ - if (family.isEmpty()) - return false; - static HashSet<AtomicString>* set; - if (!set) { - set = new HashSet<AtomicString>; - set->add("MS PGothic"); - UChar unicodeNameMSPGothic[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x30B4, 0x30B7, 0x30C3, 0x30AF}; - set->add(AtomicString(unicodeNameMSPGothic, WTF_ARRAY_LENGTH(unicodeNameMSPGothic))); - - set->add("MS PMincho"); - UChar unicodeNameMSPMincho[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x660E, 0x671D}; - set->add(AtomicString(unicodeNameMSPMincho, WTF_ARRAY_LENGTH(unicodeNameMSPMincho))); - - set->add("MS Gothic"); - UChar unicodeNameMSGothic[] = {0xFF2D, 0xFF33, 0x0020, 0x30B4, 0x30B7, 0x30C3, 0x30AF}; - set->add(AtomicString(unicodeNameMSGothic, WTF_ARRAY_LENGTH(unicodeNameMSGothic))); - - set->add("MS Mincho"); - UChar unicodeNameMSMincho[] = {0xFF2D, 0xFF33, 0x0020, 0x660E, 0x671D}; - set->add(AtomicString(unicodeNameMSMincho, WTF_ARRAY_LENGTH(unicodeNameMSMincho))); - - set->add("Meiryo"); - UChar unicodeNameMeiryo[] = {0x30E1, 0x30A4, 0x30EA, 0x30AA}; - set->add(AtomicString(unicodeNameMeiryo, WTF_ARRAY_LENGTH(unicodeNameMeiryo))); - } - return set->contains(family); -} - -Font::CodePath Font::s_codePath = Auto; - -TypesettingFeatures Font::s_defaultTypesettingFeatures = 0; - -// ============================================================================================ -// Font Implementation (Cross-Platform Portion) -// ============================================================================================ - -Font::Font() - : m_letterSpacing(0) - , m_wordSpacing(0) - , m_useBackslashAsYenSymbol(false) - , m_typesettingFeatures(0) -{ -} - -Font::Font(const FontDescription& fd, float letterSpacing, float wordSpacing) - : m_fontDescription(fd) - , m_letterSpacing(letterSpacing) - , m_wordSpacing(wordSpacing) - , m_useBackslashAsYenSymbol(useBackslashAsYenSignForFamily(fd.firstFamily())) - , m_typesettingFeatures(computeTypesettingFeatures()) -{ -} - -// FIXME: We should make this constructor platform-independent. -Font::Font(const FontPlatformData& fontData, bool isPrinterFont, FontSmoothingMode fontSmoothingMode) - : m_glyphs(FontGlyphs::createForPlatformFont(fontData)) - , m_letterSpacing(0) - , m_wordSpacing(0) - , m_useBackslashAsYenSymbol(false) - , m_typesettingFeatures(computeTypesettingFeatures()) -{ - m_fontDescription.setUsePrinterFont(isPrinterFont); - m_fontDescription.setFontSmoothing(fontSmoothingMode); +unsigned GlyphPage::s_count = 0; + +const float smallCapsFontSizeMultiplier = 0.7f; +const float emphasisMarkFontSizeMultiplier = 0.5f; + +Font::Font(const FontPlatformData& platformData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback) + : m_maxCharWidth(-1) + , m_avgCharWidth(-1) + , m_platformData(platformData) + , m_mathData(nullptr) + , m_treatAsFixedPitch(false) + , m_isCustomFont(isCustomFont) + , m_isLoading(isLoading) + , m_isTextOrientationFallback(isTextOrientationFallback) + , m_isBrokenIdeographFallback(false) + , m_hasVerticalGlyphs(false) + , m_isUsedInSystemFallbackCache(false) #if PLATFORM(IOS) - m_fontDescription.setSpecifiedSize(CTFontGetSize(fontData.font())); - m_fontDescription.setComputedSize(CTFontGetSize(fontData.font())); - m_fontDescription.setItalic(CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitItalic); - m_fontDescription.setWeight((CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitBold) ? FontWeightBold : FontWeightNormal); + , m_shouldNotBeUsedForArabic(false) #endif -} - -// FIXME: We should make this constructor platform-independent. -#if PLATFORM(IOS) -Font::Font(const FontPlatformData& fontData, PassRefPtr<FontSelector> fontSelector) - : m_glyphs(FontGlyphs::createForPlatformFont(fontData)) - , m_letterSpacing(0) - , m_wordSpacing(0) - , m_typesettingFeatures(computeTypesettingFeatures()) { - CTFontRef primaryFont = fontData.font(); - m_fontDescription.setSpecifiedSize(CTFontGetSize(primaryFont)); - m_fontDescription.setComputedSize(CTFontGetSize(primaryFont)); - m_fontDescription.setItalic(CTFontGetSymbolicTraits(primaryFont) & kCTFontTraitItalic); - m_fontDescription.setWeight((CTFontGetSymbolicTraits(primaryFont) & kCTFontTraitBold) ? FontWeightBold : FontWeightNormal); - m_fontDescription.setUsePrinterFont(fontData.isPrinterFont()); - m_glyphs = retrieveOrAddCachedFontGlyphs(m_fontDescription, fontSelector.get()); -} + platformInit(); + platformGlyphInit(); + platformCharWidthInit(); +#if ENABLE(OPENTYPE_VERTICAL) + if (platformData.orientation() == Vertical && !isTextOrientationFallback) { + m_verticalData = FontCache::singleton().verticalData(platformData); + m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics(); + } #endif - -Font::Font(const Font& other) - : m_fontDescription(other.m_fontDescription) - , m_glyphs(other.m_glyphs) - , m_letterSpacing(other.m_letterSpacing) - , m_wordSpacing(other.m_wordSpacing) - , m_useBackslashAsYenSymbol(other.m_useBackslashAsYenSymbol) - , m_typesettingFeatures(computeTypesettingFeatures()) -{ } -Font& Font::operator=(const Font& other) +// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font. +void Font::initCharWidths() { - m_fontDescription = other.m_fontDescription; - m_glyphs = other.m_glyphs; - m_letterSpacing = other.m_letterSpacing; - m_wordSpacing = other.m_wordSpacing; - m_useBackslashAsYenSymbol = other.m_useBackslashAsYenSymbol; - m_typesettingFeatures = other.m_typesettingFeatures; - return *this; -} - -bool Font::operator==(const Font& other) const -{ - // Our FontData don't have to be checked, since checking the font description will be fine. - // FIXME: This does not work if the font was made with the FontPlatformData constructor. - if (loadingCustomFonts() || other.loadingCustomFonts()) - return false; - - if (m_fontDescription != other.m_fontDescription || m_letterSpacing != other.m_letterSpacing || m_wordSpacing != other.m_wordSpacing) - return false; - if (m_glyphs == other.m_glyphs) - return true; - if (!m_glyphs || !other.m_glyphs) - return false; - if (m_glyphs->fontSelector() != other.m_glyphs->fontSelector()) - return false; - // Can these cases actually somehow occur? All fonts should get wiped out by full style recalc. - if (m_glyphs->fontSelectorVersion() != other.m_glyphs->fontSelectorVersion()) - return false; - if (m_glyphs->generation() != other.m_glyphs->generation()) - return false; - return true; -} - -struct FontGlyphsCacheKey { - // This part of the key is shared with the lower level FontCache (caching FontData objects). - FontDescriptionFontDataCacheKey fontDescriptionCacheKey; - Vector<AtomicString, 3> families; - unsigned fontSelectorId; - unsigned fontSelectorVersion; - unsigned fontSelectorFlags; -}; - -struct FontGlyphsCacheEntry { - WTF_MAKE_FAST_ALLOCATED; -public: - FontGlyphsCacheEntry(FontGlyphsCacheKey&& k, PassRef<FontGlyphs> g) : key(k), glyphs(std::move(g)) { } - FontGlyphsCacheKey key; - Ref<FontGlyphs> glyphs; -}; + auto* glyphPageZero = glyphPage(GlyphPage::pageNumberForCodePoint('0')); -typedef HashMap<unsigned, OwnPtr<FontGlyphsCacheEntry>, AlreadyHashed> FontGlyphsCache; - -static bool operator==(const FontGlyphsCacheKey& a, const FontGlyphsCacheKey& b) -{ - if (a.fontDescriptionCacheKey != b.fontDescriptionCacheKey) - return false; - if (a.fontSelectorId != b.fontSelectorId || a.fontSelectorVersion != b.fontSelectorVersion || a.fontSelectorFlags != b.fontSelectorFlags) - return false; - if (a.families.size() != b.families.size()) - return false; - for (unsigned i = 0; i < a.families.size(); ++i) { - if (!equalIgnoringCase(a.families[i].impl(), b.families[i].impl())) - return false; + // Treat the width of a '0' as the avgCharWidth. + if (m_avgCharWidth <= 0.f && glyphPageZero) { + Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph; + if (digitZeroGlyph) + m_avgCharWidth = widthForGlyph(digitZeroGlyph); } - return true; -} -static FontGlyphsCache& fontGlyphsCache() -{ - DEFINE_STATIC_LOCAL(FontGlyphsCache, cache, ()); - return cache; -} + // If we can't retrieve the width of a '0', fall back to the x height. + if (m_avgCharWidth <= 0.f) + m_avgCharWidth = m_fontMetrics.xHeight(); -void invalidateFontGlyphsCache() -{ - fontGlyphsCache().clear(); + if (m_maxCharWidth <= 0.f) + m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); } -void clearWidthCaches() +void Font::platformGlyphInit() { - for (auto it = fontGlyphsCache().begin(), end = fontGlyphsCache().end(); it != end; ++it) - it->value->glyphs.get().widthCache().clear(); -} + auto* glyphPageZero = glyphPage(0); + auto* glyphPageCharacterZero = glyphPage(GlyphPage::pageNumberForCodePoint('0')); + auto* glyphPageSpace = glyphPage(GlyphPage::pageNumberForCodePoint(space)); -static unsigned makeFontSelectorFlags(const FontDescription& description) -{ - return static_cast<unsigned>(description.script()) << 1 | static_cast<unsigned>(description.smallCaps()); -} + // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, + // are mapped to the ZERO WIDTH SPACE glyph. + if (glyphPageZero) + m_zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph; -static void makeFontGlyphsCacheKey(FontGlyphsCacheKey& key, const FontDescription& description, FontSelector* fontSelector) -{ - key.fontDescriptionCacheKey = FontDescriptionFontDataCacheKey(description); - for (unsigned i = 0; i < description.familyCount(); ++i) - key.families.append(description.familyAt(i)); - key.fontSelectorId = fontSelector ? fontSelector->uniqueId() : 0; - key.fontSelectorVersion = fontSelector ? fontSelector->version() : 0; - key.fontSelectorFlags = fontSelector && fontSelector->resolvesFamilyFor(description) ? makeFontSelectorFlags(description) : 0; -} + // Nasty hack to determine if we should round or ceil space widths. + // If the font is monospace or fake monospace we ceil to ensure that + // every character and the space are the same width. Otherwise we round. + if (glyphPageSpace) + m_spaceGlyph = glyphPageSpace->glyphDataForCharacter(space).glyph; + float width = widthForGlyph(m_spaceGlyph); + m_spaceWidth = width; + if (glyphPageCharacterZero) + m_zeroGlyph = glyphPageCharacterZero->glyphDataForCharacter('0').glyph; + m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph)); + determinePitch(); + m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width); -static unsigned computeFontGlyphsCacheHash(const FontGlyphsCacheKey& key) -{ - Vector<unsigned, 7> hashCodes; - hashCodes.reserveInitialCapacity(4 + key.families.size()); - - hashCodes.uncheckedAppend(key.fontDescriptionCacheKey.computeHash()); - hashCodes.uncheckedAppend(key.fontSelectorId); - hashCodes.uncheckedAppend(key.fontSelectorVersion); - hashCodes.uncheckedAppend(key.fontSelectorFlags); - for (unsigned i = 0; i < key.families.size(); ++i) - hashCodes.uncheckedAppend(key.families[i].impl() ? CaseFoldingHash::hash(key.families[i]) : 0); - - return StringHasher::hashMemory(hashCodes.data(), hashCodes.size() * sizeof(unsigned)); -} - -void pruneUnreferencedEntriesFromFontGlyphsCache() -{ - Vector<unsigned, 50> toRemove; - FontGlyphsCache::iterator end = fontGlyphsCache().end(); - for (FontGlyphsCache::iterator it = fontGlyphsCache().begin(); it != end; ++it) { - if (it->value->glyphs.get().hasOneRef()) - toRemove.append(it->key); - } - for (unsigned i = 0; i < toRemove.size(); ++i) - fontGlyphsCache().remove(toRemove[i]); + // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. + // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. + // See <http://bugs.webkit.org/show_bug.cgi?id=13178> and Font::isZeroWidthSpaceGlyph() + if (m_zeroWidthSpaceGlyph == m_spaceGlyph) + m_zeroWidthSpaceGlyph = 0; } -static PassRef<FontGlyphs> retrieveOrAddCachedFontGlyphs(const FontDescription& fontDescription, PassRefPtr<FontSelector> fontSelector) +Font::~Font() { - FontGlyphsCacheKey key; - makeFontGlyphsCacheKey(key, fontDescription, fontSelector.get()); - - unsigned hash = computeFontGlyphsCacheHash(key); - FontGlyphsCache::AddResult addResult = fontGlyphsCache().add(hash, PassOwnPtr<FontGlyphsCacheEntry>()); - if (!addResult.isNewEntry && addResult.iterator->value->key == key) - return addResult.iterator->value->glyphs.get(); - - OwnPtr<FontGlyphsCacheEntry>& newEntry = addResult.iterator->value; - newEntry = adoptPtr(new FontGlyphsCacheEntry(std::move(key), FontGlyphs::create(fontSelector))); - PassRef<FontGlyphs> glyphs = newEntry->glyphs.get(); - - static const unsigned unreferencedPruneInterval = 50; - static const int maximumEntries = 400; - static unsigned pruneCounter; - // Referenced FontGlyphs would exist anyway so pruning them saves little memory. - if (!(++pruneCounter % unreferencedPruneInterval)) - pruneUnreferencedEntriesFromFontGlyphsCache(); - // Prevent pathological growth. - if (fontGlyphsCache().size() > maximumEntries) - fontGlyphsCache().remove(fontGlyphsCache().begin()); - return glyphs; + removeFromSystemFallbackCache(); } -void Font::update(PassRefPtr<FontSelector> fontSelector) const +static bool fillGlyphPage(GlyphPage& pageToFill, UChar* buffer, unsigned bufferLength, const Font& font) { - m_glyphs = retrieveOrAddCachedFontGlyphs(m_fontDescription, fontSelector.get()); - m_useBackslashAsYenSymbol = useBackslashAsYenSignForFamily(firstFamily()); - m_typesettingFeatures = computeTypesettingFeatures(); + bool hasGlyphs = pageToFill.fill(buffer, bufferLength); +#if ENABLE(OPENTYPE_VERTICAL) + if (hasGlyphs && font.verticalData()) + font.verticalData()->substituteWithVerticalGlyphs(&font, &pageToFill); +#else + UNUSED_PARAM(font); +#endif + return hasGlyphs; } -float Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to, CustomFontNotReadyAction customFontNotReadyAction) const +static RefPtr<GlyphPage> createAndFillGlyphPage(unsigned pageNumber, const Font& font) { - // Don't draw anything while we are using custom fonts that are in the process of loading, - // except if the 'force' argument is set to true (in which case it will use a fallback - // font). - if (loadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady) - return 0; - - to = (to == -1 ? run.length() : to); - - if (codePath(run) != Complex) - return drawSimpleText(context, run, point, from, to); +#if PLATFORM(IOS) + // FIXME: Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. See <rdar://problem/9823975>. + // Once we have the fix for <rdar://problem/9823975> then remove this code together with Font::shouldNotBeUsedForArabic() + // in <rdar://problem/12096835>. + if (GlyphPage::pageNumberIsUsedForArabic(pageNumber) && font.shouldNotBeUsedForArabic()) + return nullptr; +#endif - return drawComplexText(context, run, point, from, to); -} + unsigned glyphPageSize = GlyphPage::sizeForPageNumber(pageNumber); + + unsigned start = GlyphPage::startingCodePointInPageNumber(pageNumber); + unsigned end = start + glyphPageSize; + Vector<UChar> buffer(glyphPageSize * 2 + 2); + unsigned bufferLength; + // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for. + if (U_IS_BMP(start)) { + bufferLength = glyphPageSize; + for (unsigned i = 0; i < bufferLength; i++) + buffer[i] = start + i; + + // Code points 0x0 - 0x20 and 0x7F - 0xA0 are control character and shouldn't render. Map them to ZERO WIDTH SPACE. + auto overwriteCodePoints = [&](unsigned minimum, unsigned maximum, UChar newCodePoint) { + unsigned begin = std::max(start, minimum); + unsigned complete = std::min(end, maximum); + for (unsigned i = begin; i < complete; ++i) + buffer[i - start] = newCodePoint; + }; + + auto overwriteCodePoint = [&](UChar codePoint, UChar newCodePoint) { + if (codePoint >= start && codePoint < end) + buffer[codePoint - start] = newCodePoint; + }; + + overwriteCodePoints(0x0, 0x20, zeroWidthSpace); + overwriteCodePoints(0x7F, 0xA0, zeroWidthSpace); + overwriteCodePoint(softHyphen, zeroWidthSpace); + overwriteCodePoint('\n', space); + overwriteCodePoint('\t', space); + overwriteCodePoint(noBreakSpace, space); + overwriteCodePoint(narrowNoBreakSpace, zeroWidthSpace); + overwriteCodePoint(leftToRightMark, zeroWidthSpace); + overwriteCodePoint(rightToLeftMark, zeroWidthSpace); + overwriteCodePoint(leftToRightEmbed, zeroWidthSpace); + overwriteCodePoint(rightToLeftEmbed, zeroWidthSpace); + overwriteCodePoint(leftToRightOverride, zeroWidthSpace); + overwriteCodePoint(rightToLeftOverride, zeroWidthSpace); + overwriteCodePoint(leftToRightIsolate, zeroWidthSpace); + overwriteCodePoint(rightToLeftIsolate, zeroWidthSpace); + overwriteCodePoint(zeroWidthNonJoiner, zeroWidthSpace); + overwriteCodePoint(zeroWidthJoiner, zeroWidthSpace); + overwriteCodePoint(popDirectionalFormatting, zeroWidthSpace); + overwriteCodePoint(popDirectionalIsolate, zeroWidthSpace); + overwriteCodePoint(firstStrongIsolate, zeroWidthSpace); + overwriteCodePoint(objectReplacementCharacter, zeroWidthSpace); + overwriteCodePoint(zeroWidthNoBreakSpace, zeroWidthSpace); + } else { + bufferLength = glyphPageSize * 2; + for (unsigned i = 0; i < glyphPageSize; i++) { + int c = i + start; + buffer[i * 2] = U16_LEAD(c); + buffer[i * 2 + 1] = U16_TRAIL(c); + } + } -void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const -{ - if (loadingCustomFonts()) - return; + // Now that we have a buffer full of characters, we want to get back an array + // of glyph indices. This part involves calling into the platform-specific + // routine of our glyph map for actually filling in the page with the glyphs. + // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data + // for only 128 out of 256 characters. + Ref<GlyphPage> glyphPage = GlyphPage::create(font); - if (to < 0) - to = run.length(); + bool haveGlyphs = fillGlyphPage(glyphPage, buffer.data(), bufferLength, font); + if (!haveGlyphs) + return nullptr; - if (codePath(run) != Complex) - drawEmphasisMarksForSimpleText(context, run, mark, point, from, to); - else - drawEmphasisMarksForComplexText(context, run, mark, point, from, to); + return WTFMove(glyphPage); } -float Font::width(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +const GlyphPage* Font::glyphPage(unsigned pageNumber) const { - CodePath codePathToUse = codePath(run); - if (codePathToUse != Complex) { - // The complex path is more restrictive about returning fallback fonts than the simple path, so we need an explicit test to make their behaviors match. - if (!canReturnFallbackFontsForComplexText()) - fallbackFonts = 0; - // The simple path can optimize the case where glyph overflow is not observable. - if (codePathToUse != SimpleWithGlyphOverflow && (glyphOverflow && !glyphOverflow->computeBounds)) - glyphOverflow = 0; + if (!pageNumber) { + if (!m_glyphPageZero) + m_glyphPageZero = createAndFillGlyphPage(0, *this); + return m_glyphPageZero.get(); } + auto addResult = m_glyphPages.add(pageNumber, nullptr); + if (addResult.isNewEntry) + addResult.iterator->value = createAndFillGlyphPage(pageNumber, *this); - bool hasKerningOrLigatures = typesettingFeatures() & (Kerning | Ligatures); - bool hasWordSpacingOrLetterSpacing = wordSpacing() || letterSpacing(); - float* cacheEntry = m_glyphs->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), hasKerningOrLigatures, hasWordSpacingOrLetterSpacing, glyphOverflow); - if (cacheEntry && !std::isnan(*cacheEntry)) - return *cacheEntry; - - HashSet<const SimpleFontData*> localFallbackFonts; - if (!fallbackFonts) - fallbackFonts = &localFallbackFonts; - - float result; - if (codePathToUse == Complex) - result = floatWidthForComplexText(run, fallbackFonts, glyphOverflow); - else - result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow); - - if (cacheEntry && fallbackFonts->isEmpty()) - *cacheEntry = result; - return result; + return addResult.iterator->value.get(); } -float Font::width(const TextRun& run, int& charsConsumed, String& glyphName) const +Glyph Font::glyphForCharacter(UChar32 character) const { -#if ENABLE(SVG_FONTS) - if (TextRun::RenderingContext* renderingContext = run.renderingContext()) - return renderingContext->floatWidthUsingSVGFont(*this, run, charsConsumed, glyphName); -#endif - - charsConsumed = run.length(); - glyphName = ""; - return width(run); -} - -#if !PLATFORM(MAC) -PassOwnPtr<TextLayout> Font::createLayout(RenderText*, float, bool) const -{ - return nullptr; + auto* page = glyphPage(GlyphPage::pageNumberForCodePoint(character)); + if (!page) + return 0; + return page->glyphForCharacter(character); } -void Font::deleteLayout(TextLayout*) +GlyphData Font::glyphDataForCharacter(UChar32 character) const { + auto* page = glyphPage(GlyphPage::pageNumberForCodePoint(character)); + if (!page) + return GlyphData(); + return page->glyphDataForCharacter(character); } -float Font::width(TextLayout&, unsigned, unsigned, HashSet<const SimpleFontData*>*) +const Font& Font::verticalRightOrientationFont() const { - ASSERT_NOT_REACHED(); - return 0; + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->verticalRightOrientation) { + auto verticalRightPlatformData = FontPlatformData::cloneWithOrientation(m_platformData, Horizontal); + m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, isCustomFont(), false, true); + } + ASSERT(m_derivedFontData->verticalRightOrientation != this); + return *m_derivedFontData->verticalRightOrientation; } -#endif -FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const +const Font& Font::uprightOrientationFont() const { - to = (to == -1 ? run.length() : to); - - if (codePath(run) != Complex) - return selectionRectForSimpleText(run, point, h, from, to); - - return selectionRectForComplexText(run, point, h, from, to); + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->uprightOrientation) + m_derivedFontData->uprightOrientation = create(m_platformData, isCustomFont(), false, true); + ASSERT(m_derivedFontData->uprightOrientation != this); + return *m_derivedFontData->uprightOrientation; } -int Font::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const +const Font* Font::smallCapsFont(const FontDescription& fontDescription) const { - if (codePath(run) != Complex) - return offsetForPositionForSimpleText(run, x, includePartialGlyphs); - - return offsetForPositionForComplexText(run, x, includePartialGlyphs); + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->smallCaps) + m_derivedFontData->smallCaps = createScaledFont(fontDescription, smallCapsFontSizeMultiplier); + ASSERT(m_derivedFontData->smallCaps != this); + return m_derivedFontData->smallCaps.get(); } -template <typename CharacterType> -static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length) +const Font& Font::noSynthesizableFeaturesFont() const { - StringBuilder normalized; - normalized.reserveCapacity(length); - - for (unsigned i = 0; i < length; ++i) - normalized.append(Font::normalizeSpaces(characters[i])); - - return normalized.toString(); +#if PLATFORM(COCOA) + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->noSynthesizableFeatures) + m_derivedFontData->noSynthesizableFeatures = createFontWithoutSynthesizableFeatures(); + ASSERT(m_derivedFontData->noSynthesizableFeatures != this); + return *m_derivedFontData->noSynthesizableFeatures; +#else + return *this; +#endif } -String Font::normalizeSpaces(const LChar* characters, unsigned length) +const Font* Font::emphasisMarkFont(const FontDescription& fontDescription) const { - return normalizeSpacesInternal(characters, length); + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->emphasisMark) + m_derivedFontData->emphasisMark = createScaledFont(fontDescription, emphasisMarkFontSizeMultiplier); + ASSERT(m_derivedFontData->emphasisMark != this); + return m_derivedFontData->emphasisMark.get(); } -String Font::normalizeSpaces(const UChar* characters, unsigned length) +const Font& Font::brokenIdeographFont() const { - return normalizeSpacesInternal(characters, length); + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->brokenIdeograph) { + m_derivedFontData->brokenIdeograph = create(m_platformData, isCustomFont(), false); + m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true; + } + ASSERT(m_derivedFontData->brokenIdeograph != this); + return *m_derivedFontData->brokenIdeograph; } -static bool shouldUseFontSmoothing = true; - -void Font::setShouldUseSmoothing(bool shouldUseSmoothing) +const Font& Font::nonSyntheticItalicFont() const { - ASSERT(isMainThread()); - shouldUseFontSmoothing = shouldUseSmoothing; + if (!m_derivedFontData) + m_derivedFontData = std::make_unique<DerivedFonts>(isCustomFont()); + if (!m_derivedFontData->nonSyntheticItalic) { +#if PLATFORM(COCOA) || USE(CAIRO) + FontPlatformData nonSyntheticItalicFontPlatformData = FontPlatformData::cloneWithSyntheticOblique(m_platformData, false); +#else + FontPlatformData nonSyntheticItalicFontPlatformData(m_platformData); +#endif + m_derivedFontData->nonSyntheticItalic = create(nonSyntheticItalicFontPlatformData, isCustomFont()); + } + ASSERT(m_derivedFontData->nonSyntheticItalic != this); + return *m_derivedFontData->nonSyntheticItalic; } -bool Font::shouldUseSmoothing() +#ifndef NDEBUG +String Font::description() const { - return shouldUseFontSmoothing; -} + if (isCustomFont()) + return "[custom font]"; -void Font::setCodePath(CodePath p) -{ - s_codePath = p; + return platformData().description(); } +#endif -Font::CodePath Font::codePath() +const OpenTypeMathData* Font::mathData() const { - return s_codePath; + if (m_isLoading) + return nullptr; + if (!m_mathData) { + m_mathData = OpenTypeMathData::create(m_platformData); + if (!m_mathData->hasMathData()) + m_mathData = nullptr; + } + return m_mathData.get(); } -void Font::setDefaultTypesettingFeatures(TypesettingFeatures typesettingFeatures) +Font::DerivedFonts::~DerivedFonts() { - s_defaultTypesettingFeatures = typesettingFeatures; } -TypesettingFeatures Font::defaultTypesettingFeatures() +RefPtr<Font> Font::createScaledFont(const FontDescription& fontDescription, float scaleFactor) const { - return s_defaultTypesettingFeatures; + return platformCreateScaledFont(fontDescription, scaleFactor); } -Font::CodePath Font::codePath(const TextRun& run) const +bool Font::applyTransforms(GlyphBufferGlyph* glyphs, GlyphBufferAdvance* advances, size_t glyphCount, bool enableKerning, bool requiresShaping) const { - if (s_codePath != Auto) - return s_codePath; - -#if ENABLE(SVG_FONTS) - if (run.renderingContext()) - return Simple; + // We need to handle transforms on SVG fonts internally, since they are rendered internally. +#if PLATFORM(COCOA) + CTFontTransformOptions options = (enableKerning ? kCTFontTransformApplyPositioning : 0) | (requiresShaping ? kCTFontTransformApplyShaping : 0); + return CTFontTransformGlyphs(m_platformData.ctFont(), glyphs, reinterpret_cast<CGSize*>(advances), glyphCount, options); +#else + UNUSED_PARAM(glyphs); + UNUSED_PARAM(advances); + UNUSED_PARAM(glyphCount); + UNUSED_PARAM(enableKerning); + UNUSED_PARAM(requiresShaping); + return false; #endif - - if (m_fontDescription.featureSettings() && m_fontDescription.featureSettings()->size() > 0) - return Complex; - - if (run.length() > 1 && !WidthIterator::supportsTypesettingFeatures(*this)) - return Complex; - - if (!run.characterScanForCodePath()) - return Simple; - - if (run.is8Bit()) - return Simple; - - // Start from 0 since drawing and highlighting also measure the characters before run->from. - return characterRangeCodePath(run.characters16(), run.length()); } -Font::CodePath Font::characterRangeCodePath(const UChar* characters, unsigned len) -{ - // FIXME: Should use a UnicodeSet in ports where ICU is used. Note that we - // can't simply use UnicodeCharacter Property/class because some characters - // are not 'combining', but still need to go to the complex path. - // Alternatively, we may as well consider binary search over a sorted - // list of ranges. - CodePath result = Simple; - for (unsigned i = 0; i < len; i++) { - const UChar c = characters[i]; - if (c < 0x2E5) // U+02E5 through U+02E9 (Modifier Letters : Tone letters) - continue; - if (c <= 0x2E9) - return Complex; - - if (c < 0x300) // U+0300 through U+036F Combining diacritical marks - continue; - if (c <= 0x36F) - return Complex; - - if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha - continue; - if (c <= 0x05CF) - return Complex; - - // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic, - // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, - // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar - if (c < 0x0600) - continue; - if (c <= 0x109F) - return Complex; - - // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; - // Modern Korean will be precomposed as a result of step A) - if (c < 0x1100) - continue; - if (c <= 0x11FF) - return Complex; - - if (c < 0x135D) // U+135D through U+135F Ethiopic combining marks - continue; - if (c <= 0x135F) - return Complex; - - if (c < 0x1700) // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian - continue; - if (c <= 0x18AF) - return Complex; - - if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) - continue; - if (c <= 0x194F) - return Complex; - - if (c < 0x1980) // U+1980 through U+19DF New Tai Lue - continue; - if (c <= 0x19DF) - return Complex; - - if (c < 0x1A00) // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic - continue; - if (c <= 0x1CFF) - return Complex; - - if (c < 0x1DC0) // U+1DC0 through U+1DFF Comining diacritical mark supplement - continue; - if (c <= 0x1DFF) - return Complex; - - // U+1E00 through U+2000 characters with diacritics and stacked diacritics - if (c <= 0x2000) { - result = SimpleWithGlyphOverflow; - continue; - } - - if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols - continue; - if (c <= 0x20FF) - return Complex; - - if (c < 0x2CEF) // U+2CEF through U+2CF1 Combining marks for Coptic - continue; - if (c <= 0x2CF1) - return Complex; - - if (c < 0x302A) // U+302A through U+302F Ideographic and Hangul Tone marks - continue; - if (c <= 0x302F) - return Complex; - - if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic - continue; - if (c <= 0xA67D) - return Complex; - - if (c < 0xA6F0) // U+A6F0 through U+A6F1 Combining mark for Bamum - continue; - if (c <= 0xA6F1) - return Complex; - - // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended, - // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek, - if (c < 0xA800) - continue; - if (c <= 0xABFF) - return Complex; - - if (c < 0xD7B0) // U+D7B0 through U+D7FF Hangul Jamo Ext. B - continue; - if (c <= 0xD7FF) - return Complex; - - if (c <= 0xDBFF) { - // High surrogate - - if (i == len - 1) - continue; - - UChar next = characters[++i]; - if (!U16_IS_TRAIL(next)) - continue; - - UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next); - - if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols - continue; - if (supplementaryCharacter <= 0x1F1FF) - return Complex; - - if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors. - continue; - if (supplementaryCharacter <= 0xE01EF) - return Complex; - - // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts - // in plane 1 or higher. - - continue; - } - - if (c < 0xFE00) // U+FE00 through U+FE0F Unicode variation selectors - continue; - if (c <= 0xFE0F) - return Complex; - - if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks - continue; - if (c <= 0xFE2F) - return Complex; +class CharacterFallbackMapKey { +public: + CharacterFallbackMapKey() + { } - return result; -} - -bool Font::isCJKIdeograph(UChar32 c) -{ - // The basic CJK Unified Ideographs block. - if (c >= 0x4E00 && c <= 0x9FFF) - return true; - - // CJK Unified Ideographs Extension A. - if (c >= 0x3400 && c <= 0x4DBF) - return true; - - // CJK Radicals Supplement. - if (c >= 0x2E80 && c <= 0x2EFF) - return true; - - // Kangxi Radicals. - if (c >= 0x2F00 && c <= 0x2FDF) - return true; - - // CJK Strokes. - if (c >= 0x31C0 && c <= 0x31EF) - return true; - - // CJK Compatibility Ideographs. - if (c >= 0xF900 && c <= 0xFAFF) - return true; - - // CJK Unified Ideographs Extension B. - if (c >= 0x20000 && c <= 0x2A6DF) - return true; - - // CJK Unified Ideographs Extension C. - if (c >= 0x2A700 && c <= 0x2B73F) - return true; - - // CJK Unified Ideographs Extension D. - if (c >= 0x2B740 && c <= 0x2B81F) - return true; - - // CJK Compatibility Ideographs Supplement. - if (c >= 0x2F800 && c <= 0x2FA1F) - return true; - - return false; -} - -bool Font::isCJKIdeographOrSymbol(UChar32 c) -{ - // 0x2C7 Caron, Mandarin Chinese 3rd Tone - // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone - // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone - // 0x2D9 Dot Above, Mandarin Chinese 5th Tone - if ((c == 0x2C7) || (c == 0x2CA) || (c == 0x2CB) || (c == 0x2D9)) - return true; - - if ((c == 0x2020) || (c == 0x2021) || (c == 0x2030) || (c == 0x203B) || (c == 0x203C) - || (c == 0x2042) || (c == 0x2047) || (c == 0x2048) || (c == 0x2049) || (c == 0x2051) - || (c == 0x20DD) || (c == 0x20DE) || (c == 0x2100) || (c == 0x2103) || (c == 0x2105) - || (c == 0x2109) || (c == 0x210A) || (c == 0x2113) || (c == 0x2116) || (c == 0x2121) - || (c == 0x212B) || (c == 0x213B) || (c == 0x2150) || (c == 0x2151) || (c == 0x2152)) - return true; - - if (c >= 0x2156 && c <= 0x215A) - return true; - - if (c >= 0x2160 && c <= 0x216B) - return true; - - if (c >= 0x2170 && c <= 0x217B) - return true; - - if ((c == 0x217F) || (c == 0x2189) || (c == 0x2307) || (c == 0x2312) || (c == 0x23BE) || (c == 0x23BF)) - return true; - - if (c >= 0x23C0 && c <= 0x23CC) - return true; - - if ((c == 0x23CE) || (c == 0x2423)) - return true; - - if (c >= 0x2460 && c <= 0x2492) - return true; - - if (c >= 0x249C && c <= 0x24FF) - return true; - - if ((c == 0x25A0) || (c == 0x25A1) || (c == 0x25A2) || (c == 0x25AA) || (c == 0x25AB)) - return true; - if ((c == 0x25B1) || (c == 0x25B2) || (c == 0x25B3) || (c == 0x25B6) || (c == 0x25B7) || (c == 0x25BC) || (c == 0x25BD)) - return true; - - if ((c == 0x25C0) || (c == 0x25C1) || (c == 0x25C6) || (c == 0x25C7) || (c == 0x25C9) || (c == 0x25CB) || (c == 0x25CC)) - return true; - - if (c >= 0x25CE && c <= 0x25D3) - return true; - - if (c >= 0x25E2 && c <= 0x25E6) - return true; - - if (c == 0x25EF) - return true; - - if (c >= 0x2600 && c <= 0x2603) - return true; - - if ((c == 0x2605) || (c == 0x2606) || (c == 0x260E) || (c == 0x2616) || (c == 0x2617) || (c == 0x2640) || (c == 0x2642)) - return true; - - if (c >= 0x2660 && c <= 0x266F) - return true; - - if (c >= 0x2672 && c <= 0x267D) - return true; - - if ((c == 0x26A0) || (c == 0x26BD) || (c == 0x26BE) || (c == 0x2713) || (c == 0x271A) || (c == 0x273F) || (c == 0x2740) || (c == 0x2756)) - return true; - - if (c >= 0x2776 && c <= 0x277F) - return true; - - if (c == 0x2B1A) - return true; - - // Ideographic Description Characters. - if (c >= 0x2FF0 && c <= 0x2FFF) - return true; - - // CJK Symbols and Punctuation, excluding 0x3030. - if (c >= 0x3000 && c < 0x3030) - return true; - - if (c > 0x3030 && c <= 0x303F) - return true; - - // Hiragana - if (c >= 0x3040 && c <= 0x309F) - return true; + CharacterFallbackMapKey(const AtomicString& locale, UChar32 character, bool isForPlatformFont) + : locale(locale) + , character(character) + , isForPlatformFont(isForPlatformFont) + { + } - // Katakana - if (c >= 0x30A0 && c <= 0x30FF) - return true; + CharacterFallbackMapKey(WTF::HashTableDeletedValueType) + : character(-1) + { + } - // Bopomofo - if (c >= 0x3100 && c <= 0x312F) - return true; + bool isHashTableDeletedValue() const { return character == -1; } - if (c >= 0x3190 && c <= 0x319F) - return true; + bool operator==(const CharacterFallbackMapKey& other) const + { + return locale == other.locale && character == other.character && isForPlatformFont == other.isForPlatformFont; + } - // Bopomofo Extended - if (c >= 0x31A0 && c <= 0x31BF) - return true; - - // Enclosed CJK Letters and Months. - if (c >= 0x3200 && c <= 0x32FF) - return true; - - // CJK Compatibility. - if (c >= 0x3300 && c <= 0x33FF) - return true; + static const bool emptyValueIsZero = true; - if (c >= 0xF860 && c <= 0xF862) - return true; +private: + friend struct CharacterFallbackMapKeyHash; - // CJK Compatibility Forms. - if (c >= 0xFE30 && c <= 0xFE4F) - return true; + AtomicString locale; + UChar32 character { 0 }; + bool isForPlatformFont { false }; +}; - if ((c == 0xFE10) || (c == 0xFE11) || (c == 0xFE12) || (c == 0xFE19)) - return true; +struct CharacterFallbackMapKeyHash { + static unsigned hash(const CharacterFallbackMapKey& key) + { + IntegerHasher hasher; + hasher.add(key.character); + hasher.add(key.isForPlatformFont); + hasher.add(key.locale.existingHash()); + return hasher.hash(); + } - if ((c == 0xFF0D) || (c == 0xFF1B) || (c == 0xFF1C) || (c == 0xFF1E)) - return false; + static bool equal(const CharacterFallbackMapKey& a, const CharacterFallbackMapKey& b) + { + return a == b; + } - // Halfwidth and Fullwidth Forms - // Usually only used in CJK - if (c >= 0xFF00 && c <= 0xFFEF) - return true; + static const bool safeToCompareToEmptyOrDeleted = true; +}; - // Emoji. - if (c == 0x1F100) - return true; +// Fonts are not ref'd to avoid cycles. +// FIXME: Shouldn't these be WeakPtrs? +typedef HashMap<CharacterFallbackMapKey, Font*, CharacterFallbackMapKeyHash, WTF::SimpleClassHashTraits<CharacterFallbackMapKey>> CharacterFallbackMap; +typedef HashMap<const Font*, CharacterFallbackMap> SystemFallbackCache; - if (c >= 0x1F110 && c <= 0x1F129) - return true; +static SystemFallbackCache& systemFallbackCache() +{ + static NeverDestroyed<SystemFallbackCache> map; + return map.get(); +} - if (c >= 0x1F130 && c <= 0x1F149) - return true; +RefPtr<Font> Font::systemFallbackFontForCharacter(UChar32 character, const FontDescription& description, bool isForPlatformFont) const +{ + auto fontAddResult = systemFallbackCache().add(this, CharacterFallbackMap()); - if (c >= 0x1F150 && c <= 0x1F169) - return true; + if (!character) { + UChar codeUnit = 0; + return FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, &codeUnit, 1); + } - if (c >= 0x1F170 && c <= 0x1F189) - return true; + auto key = CharacterFallbackMapKey(description.locale(), character, isForPlatformFont); + auto characterAddResult = fontAddResult.iterator->value.add(WTFMove(key), nullptr); + + Font*& fallbackFont = characterAddResult.iterator->value; + + if (!fallbackFont) { + UChar codeUnits[2]; + unsigned codeUnitsLength; + if (U_IS_BMP(character)) { + codeUnits[0] = FontCascade::normalizeSpaces(character); + codeUnitsLength = 1; + } else { + codeUnits[0] = U16_LEAD(character); + codeUnits[1] = U16_TRAIL(character); + codeUnitsLength = 2; + } - if (c >= 0x1F200 && c <= 0x1F6C5) - return true; + fallbackFont = FontCache::singleton().systemFallbackForCharacters(description, this, isForPlatformFont, codeUnits, codeUnitsLength).get(); + if (fallbackFont) + fallbackFont->m_isUsedInSystemFallbackCache = true; + } - return isCJKIdeograph(c); + return fallbackFont; } -unsigned Font::expansionOpportunityCount(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) +void Font::removeFromSystemFallbackCache() { - unsigned count = 0; - if (direction == LTR) { - for (size_t i = 0; i < length; ++i) { - if (treatAsSpace(characters[i])) { - count++; - isAfterExpansion = true; - } else - isAfterExpansion = false; - } - } else { - for (size_t i = length; i > 0; --i) { - if (treatAsSpace(characters[i - 1])) { - count++; - isAfterExpansion = true; - } else - isAfterExpansion = false; - } - } - return count; -} + systemFallbackCache().remove(this); -unsigned Font::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) -{ - static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText(); - unsigned count = 0; - if (direction == LTR) { - for (size_t i = 0; i < length; ++i) { - UChar32 character = characters[i]; - if (treatAsSpace(character)) { - count++; - isAfterExpansion = true; - continue; - } - if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) { - character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); - i++; - } - if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { - if (!isAfterExpansion) - count++; - count++; - isAfterExpansion = true; - continue; - } - isAfterExpansion = false; - } - } else { - for (size_t i = length; i > 0; --i) { - UChar32 character = characters[i - 1]; - if (treatAsSpace(character)) { - count++; - isAfterExpansion = true; - continue; - } - if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) { - character = U16_GET_SUPPLEMENTARY(characters[i - 2], character); - i--; - } - if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { - if (!isAfterExpansion) - count++; - count++; - isAfterExpansion = true; - continue; - } - isAfterExpansion = false; + if (!m_isUsedInSystemFallbackCache) + return; + + for (auto& characterMap : systemFallbackCache().values()) { + Vector<CharacterFallbackMapKey, 512> toRemove; + for (auto& entry : characterMap) { + if (entry.value == this) + toRemove.append(entry.key); } + for (auto& key : toRemove) + characterMap.remove(key); } - return count; } -bool Font::canReceiveTextEmphasis(UChar32 c) +#if !PLATFORM(COCOA) +bool Font::variantCapsSupportsCharacterForSynthesis(FontVariantCaps, UChar32) const { - if (U_GET_GC_MASK(c) & (U_GC_Z_MASK | U_GC_CN_MASK | U_GC_CC_MASK | U_GC_CF_MASK)) - return false; - - // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010. - if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot - || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar) - return false; - - return true; + return false; } +#endif -} +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/Font.h b/Source/WebCore/platform/graphics/Font.h index 35151f127..83a20d5e7 100644 --- a/Source/WebCore/platform/graphics/Font.h +++ b/Source/WebCore/platform/graphics/Font.h @@ -1,9 +1,8 @@ /* - * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * (C) 2000 Antti Koivisto (koivisto@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006, 2007, 2010, 2011 Apple Inc. All rights reserved. - * Copyright (C) 2008 Holger Hans Peter Freyther + * This file is part of the internal font implementation. + * + * Copyright (C) 2006, 2008, 2010, 2015-2016 Apple Inc. All rights reserved. + * Copyright (C) 2007-2008 Torch Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,341 +24,347 @@ #ifndef Font_h #define Font_h -#include "DashArray.h" -#include "FontDescription.h" -#include "FontGlyphs.h" -#include "SimpleFontData.h" -#include "TextDirection.h" -#include "TypesettingFeatures.h" -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/MathExtras.h> -#include <wtf/unicode/CharacterNames.h> - -// "X11/X.h" defines Complex to 0 and conflicts -// with Complex value in CodePath enum. -#ifdef Complex -#undef Complex +#include "FloatRect.h" +#include "FontBaseline.h" +#include "FontMetrics.h" +#include "FontPlatformData.h" +#include "GlyphBuffer.h" +#include "GlyphMetricsMap.h" +#include "GlyphPage.h" +#include "OpenTypeMathData.h" +#if ENABLE(OPENTYPE_VERTICAL) +#include "OpenTypeVerticalData.h" +#endif +#include <wtf/BitVector.h> +#include <wtf/Optional.h> +#include <wtf/TypeCasts.h> +#include <wtf/text/StringHash.h> + +#if PLATFORM(COCOA) +#include "WebCoreSystemInterface.h" +#include <wtf/RetainPtr.h> #endif -namespace WebCore { +#if PLATFORM(WIN) +#include <usp10.h> +#endif + +#if USE(CAIRO) +#include <cairo.h> +#endif -class FloatPoint; -class FloatRect; -class FontData; -class FontMetrics; -class FontPlatformData; -class FontSelector; -class GlyphBuffer; -class GraphicsContext; -class RenderText; -class TextLayout; -class TextRun; +#if USE(CG) +#include "CoreGraphicsSPI.h" +#endif + +#if USE(DIRECT2D) +interface IDWriteFactory; +interface IDWriteGdiInterop; +#endif +namespace WebCore { + +class GlyphPage; +class FontDescription; +class SharedBuffer; struct GlyphData; +struct WidthIterator; + +enum FontVariant { AutoVariant, NormalVariant, SmallCapsVariant, EmphasisMarkVariant, BrokenIdeographVariant }; +enum Pitch { UnknownPitch, FixedPitch, VariablePitch }; -struct GlyphOverflow { - GlyphOverflow() - : left(0) - , right(0) - , top(0) - , bottom(0) - , computeBounds(false) +class Font : public RefCounted<Font> { +public: + // Used to create platform fonts. + static Ref<Font> create(const FontPlatformData& platformData, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false) { + return adoptRef(*new Font(platformData, isCustomFont, isLoading, isTextOrientationFallback)); } - int left; - int right; - int top; - int bottom; - bool computeBounds; -}; - + WEBCORE_EXPORT ~Font(); -class Font { -public: - Font(); - Font(const FontDescription&, float letterSpacing, float wordSpacing); - // This constructor is only used if the platform wants to start with a native font. - Font(const FontPlatformData&, bool isPrinting, FontSmoothingMode = AutoSmoothing); + static const Font* systemFallback() { return reinterpret_cast<const Font*>(-1); } - // FIXME: We should make this constructor platform-independent. -#if PLATFORM(IOS) - Font(const FontPlatformData&, PassRefPtr<FontSelector>); + const FontPlatformData& platformData() const { return m_platformData; } + const OpenTypeMathData* mathData() const; +#if ENABLE(OPENTYPE_VERTICAL) + const OpenTypeVerticalData* verticalData() const { return m_verticalData.get(); } #endif - ~Font(); - - Font(const Font&); - Font& operator=(const Font&); - bool operator==(const Font& other) const; - bool operator!=(const Font& other) const { return !(*this == other); } + const Font* smallCapsFont(const FontDescription&) const; + const Font& noSynthesizableFeaturesFont() const; + const Font* emphasisMarkFont(const FontDescription&) const; + const Font& brokenIdeographFont() const; + const Font& nonSyntheticItalicFont() const; - const FontDescription& fontDescription() const { return m_fontDescription; } + const Font* variantFont(const FontDescription& description, FontVariant variant) const + { +#if PLATFORM(COCOA) + ASSERT(variant != SmallCapsVariant); +#endif + switch (variant) { + case SmallCapsVariant: + return smallCapsFont(description); + case EmphasisMarkVariant: + return emphasisMarkFont(description); + case BrokenIdeographVariant: + return &brokenIdeographFont(); + case AutoVariant: + case NormalVariant: + break; + } + ASSERT_NOT_REACHED(); + return const_cast<Font*>(this); + } - int pixelSize() const { return fontDescription().computedPixelSize(); } - float size() const { return fontDescription().computedSize(); } + bool variantCapsSupportsCharacterForSynthesis(FontVariantCaps, UChar32) const; - void update(PassRefPtr<FontSelector>) const; + const Font& verticalRightOrientationFont() const; + const Font& uprightOrientationFont() const; - enum CustomFontNotReadyAction { DoNotPaintIfFontNotReady, UseFallbackIfFontNotReady }; - float drawText(GraphicsContext*, const TextRun&, const FloatPoint&, int from = 0, int to = -1, CustomFontNotReadyAction = DoNotPaintIfFontNotReady) const; - void drawEmphasisMarks(GraphicsContext*, const TextRun&, const AtomicString& mark, const FloatPoint&, int from = 0, int to = -1) const; + bool hasVerticalGlyphs() const { return m_hasVerticalGlyphs; } + bool isTextOrientationFallback() const { return m_isTextOrientationFallback; } - DashArray dashesForIntersectionsWithRect(const TextRun&, const FloatPoint& textOrigin, const FloatRect& lineExtents) const; + FontMetrics& fontMetrics() { return m_fontMetrics; } + const FontMetrics& fontMetrics() const { return m_fontMetrics; } + float sizePerUnit() const { return platformData().size() / (fontMetrics().unitsPerEm() ? fontMetrics().unitsPerEm() : 1); } - float width(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const; - float width(const TextRun&, int& charsConsumed, String& glyphName) const; + float maxCharWidth() const { return m_maxCharWidth; } + void setMaxCharWidth(float maxCharWidth) { m_maxCharWidth = maxCharWidth; } - PassOwnPtr<TextLayout> createLayout(RenderText*, float xPos, bool collapseWhiteSpace) const; - static void deleteLayout(TextLayout*); - static float width(TextLayout&, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts = 0); + float avgCharWidth() const { return m_avgCharWidth; } + void setAvgCharWidth(float avgCharWidth) { m_avgCharWidth = avgCharWidth; } - int offsetForPosition(const TextRun&, float position, bool includePartialGlyphs) const; - FloatRect selectionRectForText(const TextRun&, const FloatPoint&, int h, int from = 0, int to = -1) const; + FloatRect boundsForGlyph(Glyph) const; + float widthForGlyph(Glyph) const; + FloatRect platformBoundsForGlyph(Glyph) const; + float platformWidthForGlyph(Glyph) const; - bool isSmallCaps() const { return m_fontDescription.smallCaps(); } + float spaceWidth() const { return m_spaceWidth; } + float adjustedSpaceWidth() const { return m_adjustedSpaceWidth; } + void setSpaceWidths(float spaceWidth) + { + m_spaceWidth = spaceWidth; + m_adjustedSpaceWidth = spaceWidth; + } - float wordSpacing() const { return m_wordSpacing; } - float letterSpacing() const { return m_letterSpacing; } - void setWordSpacing(float s) { m_wordSpacing = s; } - void setLetterSpacing(float s) { m_letterSpacing = s; } - bool isFixedPitch() const; - bool isPrinterFont() const { return m_fontDescription.usePrinterFont(); } - bool isSVGFont() const { return primaryFont()->isSVGFont(); } - - FontRenderingMode renderingMode() const { return m_fontDescription.renderingMode(); } +#if USE(CG) || USE(DIRECT2D) || USE(CAIRO) + float syntheticBoldOffset() const { return m_syntheticBoldOffset; } +#endif - TypesettingFeatures typesettingFeatures() const { return static_cast<TypesettingFeatures>(m_typesettingFeatures); } + Glyph spaceGlyph() const { return m_spaceGlyph; } + void setSpaceGlyph(Glyph spaceGlyph) { m_spaceGlyph = spaceGlyph; } + Glyph zeroWidthSpaceGlyph() const { return m_zeroWidthSpaceGlyph; } + void setZeroWidthSpaceGlyph(Glyph spaceGlyph) { m_zeroWidthSpaceGlyph = spaceGlyph; } + bool isZeroWidthSpaceGlyph(Glyph glyph) const { return glyph == m_zeroWidthSpaceGlyph && glyph; } + Glyph zeroGlyph() const { return m_zeroGlyph; } + void setZeroGlyph(Glyph zeroGlyph) { m_zeroGlyph = zeroGlyph; } - const AtomicString& firstFamily() const { return m_fontDescription.firstFamily(); } - unsigned familyCount() const { return m_fontDescription.familyCount(); } - const AtomicString& familyAt(unsigned i) const { return m_fontDescription.familyAt(i); } + GlyphData glyphDataForCharacter(UChar32) const; + Glyph glyphForCharacter(UChar32) const; - FontItalic italic() const { return m_fontDescription.italic(); } - FontWeight weight() const { return m_fontDescription.weight(); } - FontWidthVariant widthVariant() const { return m_fontDescription.widthVariant(); } + RefPtr<Font> systemFallbackFontForCharacter(UChar32, const FontDescription&, bool isForPlatformFont) const; - bool isPlatformFont() const { return m_glyphs->isForPlatformFont(); } + const GlyphPage* glyphPage(unsigned pageNumber) const; - const FontMetrics& fontMetrics() const { return primaryFont()->fontMetrics(); } - float spaceWidth() const { return primaryFont()->spaceWidth() + m_letterSpacing; } - float tabWidth(const SimpleFontData&, unsigned tabSize, float position) const; - float tabWidth(unsigned tabSize, float position) const { return tabWidth(*primaryFont(), tabSize, position); } + void determinePitch(); + Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; } - int emphasisMarkAscent(const AtomicString&) const; - int emphasisMarkDescent(const AtomicString&) const; - int emphasisMarkHeight(const AtomicString&) const; + bool isCustomFont() const { return m_isCustomFont; } + bool isLoading() const { return m_isLoading; } - const SimpleFontData* primaryFont() const; - const FontData* fontDataAt(unsigned) const; - GlyphData glyphDataForCharacter(UChar32 c, bool mirror, FontDataVariant variant = AutoVariant) const - { - return glyphDataAndPageForCharacter(c, mirror, variant).first; - } -#if PLATFORM(MAC) - const SimpleFontData* fontDataForCombiningCharacterSequence(const UChar*, size_t length, FontDataVariant) const; +#ifndef NDEBUG + String description() const; #endif - std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCharacter(UChar32 c, bool mirror, FontDataVariant variant) const - { - return m_glyphs->glyphDataAndPageForCharacter(m_fontDescription, c, mirror, variant); - } - bool primaryFontHasGlyphForCharacter(UChar32) const; - - static bool isCJKIdeograph(UChar32); - static bool isCJKIdeographOrSymbol(UChar32); - static unsigned expansionOpportunityCount(const LChar*, size_t length, TextDirection, bool& isAfterExpansion); - static unsigned expansionOpportunityCount(const UChar*, size_t length, TextDirection, bool& isAfterExpansion); +#if PLATFORM(IOS) + bool shouldNotBeUsedForArabic() const { return m_shouldNotBeUsedForArabic; }; +#endif +#if PLATFORM(COCOA) + CTFontRef getCTFont() const { return m_platformData.font(); } + CFDictionaryRef getCFStringAttributes(bool enableKerning, FontOrientation) const; + const BitVector& glyphsSupportedBySmallCaps() const; + const BitVector& glyphsSupportedByAllSmallCaps() const; + const BitVector& glyphsSupportedByPetiteCaps() const; + const BitVector& glyphsSupportedByAllPetiteCaps() const; +#endif - static void setShouldUseSmoothing(bool); - static bool shouldUseSmoothing(); +#if PLATFORM(COCOA) || USE(HARFBUZZ) + bool canRenderCombiningCharacterSequence(const UChar*, size_t) const; +#endif - enum CodePath { Auto, Simple, Complex, SimpleWithGlyphOverflow }; - CodePath codePath(const TextRun&) const; - static CodePath characterRangeCodePath(const LChar*, unsigned) { return Simple; } - static CodePath characterRangeCodePath(const UChar*, unsigned len); + bool applyTransforms(GlyphBufferGlyph*, GlyphBufferAdvance*, size_t glyphCount, bool enableKerning, bool requiresShaping) const; -private: - enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis }; - - // Returns the initial in-stream advance. - float getGlyphsAndAdvancesForSimpleText(const TextRun&, int from, int to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const; - float drawSimpleText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; - void drawEmphasisMarksForSimpleText(GraphicsContext*, const TextRun&, const AtomicString& mark, const FloatPoint&, int from, int to) const; - void drawGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const; - void drawGlyphBuffer(GraphicsContext*, const TextRun&, const GlyphBuffer&, FloatPoint&) const; - void drawEmphasisMarks(GraphicsContext*, const TextRun&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const; - float floatWidthForSimpleText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const; - int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const; - FloatRect selectionRectForSimpleText(const TextRun&, const FloatPoint&, int h, int from, int to) const; - - bool getEmphasisMarkGlyphData(const AtomicString&, GlyphData&) const; - - static bool canReturnFallbackFontsForComplexText(); - static bool canExpandAroundIdeographsInComplexText(); - - // Returns the initial in-stream advance. - float getGlyphsAndAdvancesForComplexText(const TextRun&, int from, int to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const; - float drawComplexText(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const; - void drawEmphasisMarksForComplexText(GraphicsContext*, const TextRun&, const AtomicString& mark, const FloatPoint&, int from, int to) const; - float floatWidthForComplexText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const; - int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const; - FloatRect selectionRectForComplexText(const TextRun&, const FloatPoint&, int h, int from, int to) const; - - friend struct WidthIterator; - friend class SVGTextRunRenderingContext; +#if PLATFORM(WIN) + SCRIPT_FONTPROPERTIES* scriptFontProperties() const; + SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } + static void setShouldApplyMacAscentHack(bool); + static bool shouldApplyMacAscentHack(); + static float ascentConsideringMacAscentHack(const WCHAR*, float ascent, float descent); +#endif -public: -#if ENABLE(IOS_TEXT_AUTOSIZING) - bool equalForTextAutoSizing(const Font& other) const - { - return m_fontDescription.equalForTextAutoSizing(other.m_fontDescription) - && m_letterSpacing == other.m_letterSpacing - && m_wordSpacing == other.m_wordSpacing; - } +#if USE(DIRECT2D) + WEBCORE_EXPORT static IDWriteFactory* systemDWriteFactory(); + WEBCORE_EXPORT static IDWriteGdiInterop* systemDWriteGdiInterop(); #endif - // Useful for debugging the different font rendering code paths. - static void setCodePath(CodePath); - static CodePath codePath(); - static CodePath s_codePath; +private: + Font(const FontPlatformData&, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false); - static void setDefaultTypesettingFeatures(TypesettingFeatures); - static TypesettingFeatures defaultTypesettingFeatures(); + void platformInit(); + void platformGlyphInit(); + void platformCharWidthInit(); + void platformDestroy(); - static const uint8_t s_roundingHackCharacterTable[256]; - static bool isRoundingHackCharacter(UChar32 c) - { - return !(c & ~0xFF) && s_roundingHackCharacterTable[c]; - } + void initCharWidths(); - FontSelector* fontSelector() const; - static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == noBreakSpace; } - static bool treatAsZeroWidthSpace(UChar c) { return treatAsZeroWidthSpaceInComplexScript(c) || c == 0x200c || c == 0x200d; } - static bool treatAsZeroWidthSpaceInComplexScript(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == softHyphen || c == zeroWidthSpace || (c >= 0x200e && c <= 0x200f) || (c >= 0x202a && c <= 0x202e) || c == zeroWidthNoBreakSpace || c == objectReplacementCharacter; } - static bool canReceiveTextEmphasis(UChar32 c); + RefPtr<Font> createFontWithoutSynthesizableFeatures() const; + RefPtr<Font> createScaledFont(const FontDescription&, float scaleFactor) const; + RefPtr<Font> platformCreateScaledFont(const FontDescription&, float scaleFactor) const; - static inline UChar normalizeSpaces(UChar character) - { - if (treatAsSpace(character)) - return space; + void removeFromSystemFallbackCache(); - if (treatAsZeroWidthSpace(character)) - return zeroWidthSpace; +#if PLATFORM(WIN) + void initGDIFont(); + void platformCommonDestroy(); + FloatRect boundsForGDIGlyph(Glyph) const; + float widthForGDIGlyph(Glyph) const; +#endif - return character; - } + FontMetrics m_fontMetrics; + float m_maxCharWidth; + float m_avgCharWidth; - static String normalizeSpaces(const LChar*, unsigned length); - static String normalizeSpaces(const UChar*, unsigned length); + const FontPlatformData m_platformData; - bool useBackslashAsYenSymbol() const { return m_useBackslashAsYenSymbol; } - FontGlyphs* glyphs() const { return m_glyphs.get(); } + mutable RefPtr<GlyphPage> m_glyphPageZero; + mutable HashMap<unsigned, RefPtr<GlyphPage>> m_glyphPages; + mutable std::unique_ptr<GlyphMetricsMap<FloatRect>> m_glyphToBoundsMap; + mutable GlyphMetricsMap<float> m_glyphToWidthMap; -private: - bool loadingCustomFonts() const - { - return m_glyphs && m_glyphs->loadingCustomFonts(); - } + mutable RefPtr<OpenTypeMathData> m_mathData; +#if ENABLE(OPENTYPE_VERTICAL) + RefPtr<OpenTypeVerticalData> m_verticalData; +#endif - TypesettingFeatures computeTypesettingFeatures() const - { - TextRenderingMode textRenderingMode = m_fontDescription.textRenderingMode(); - TypesettingFeatures features = s_defaultTypesettingFeatures; + Glyph m_spaceGlyph { 0 }; + float m_spaceWidth { 0 }; + Glyph m_zeroGlyph { 0 }; + float m_adjustedSpaceWidth { 0 }; - switch (textRenderingMode) { - case AutoTextRendering: - break; - case OptimizeSpeed: - features &= ~(Kerning | Ligatures); - break; - case GeometricPrecision: - case OptimizeLegibility: - features |= Kerning | Ligatures; - break; - } + Glyph m_zeroWidthSpaceGlyph { 0 }; - switch (m_fontDescription.kerning()) { - case FontDescription::NoneKerning: - features &= ~Kerning; - break; - case FontDescription::NormalKerning: - features |= Kerning; - break; - case FontDescription::AutoKerning: - break; + struct DerivedFonts { +#if !COMPILER(MSVC) + WTF_MAKE_FAST_ALLOCATED; +#endif + public: + explicit DerivedFonts(bool custom) + : forCustomFont(custom) + { } + ~DerivedFonts(); + + bool forCustomFont; + RefPtr<Font> smallCaps; + RefPtr<Font> noSynthesizableFeatures; + RefPtr<Font> emphasisMark; + RefPtr<Font> brokenIdeograph; + RefPtr<Font> verticalRightOrientation; + RefPtr<Font> uprightOrientation; + RefPtr<Font> nonSyntheticItalic; + }; + + mutable std::unique_ptr<DerivedFonts> m_derivedFontData; + +#if USE(CG) || USE(DIRECT2D) || USE(CAIRO) + float m_syntheticBoldOffset; +#endif - switch (m_fontDescription.commonLigaturesState()) { - case FontDescription::DisabledLigaturesState: - features &= ~Ligatures; - break; - case FontDescription::EnabledLigaturesState: - features |= Ligatures; - break; - case FontDescription::NormalLigaturesState: - break; - } +#if PLATFORM(COCOA) + mutable RetainPtr<CFDictionaryRef> m_nonKernedCFStringAttributes; + mutable RetainPtr<CFDictionaryRef> m_kernedCFStringAttributes; + mutable std::optional<BitVector> m_glyphsSupportedBySmallCaps; + mutable std::optional<BitVector> m_glyphsSupportedByAllSmallCaps; + mutable std::optional<BitVector> m_glyphsSupportedByPetiteCaps; + mutable std::optional<BitVector> m_glyphsSupportedByAllPetiteCaps; +#endif - return features; - } +#if PLATFORM(COCOA) || USE(HARFBUZZ) + mutable std::unique_ptr<HashMap<String, bool>> m_combiningCharacterSequenceSupport; +#endif - static TypesettingFeatures s_defaultTypesettingFeatures; +#if PLATFORM(WIN) + mutable SCRIPT_CACHE m_scriptCache; + mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; +#endif - FontDescription m_fontDescription; - mutable RefPtr<FontGlyphs> m_glyphs; - float m_letterSpacing; - float m_wordSpacing; - mutable bool m_useBackslashAsYenSymbol; - mutable unsigned m_typesettingFeatures : 2; // (TypesettingFeatures) Caches values computed from m_fontDescription. -}; + unsigned m_treatAsFixedPitch : 1; + unsigned m_isCustomFont : 1; // Whether or not we are custom font loaded via @font-face + unsigned m_isLoading : 1; // Whether or not this custom font is still in the act of loading. -void invalidateFontGlyphsCache(); -void pruneUnreferencedEntriesFromFontGlyphsCache(); -void clearWidthCaches(); + unsigned m_isTextOrientationFallback : 1; + unsigned m_isBrokenIdeographFallback : 1; + unsigned m_hasVerticalGlyphs : 1; -inline Font::~Font() -{ -} + unsigned m_isUsedInSystemFallbackCache : 1; -inline const SimpleFontData* Font::primaryFont() const -{ - ASSERT(m_glyphs); - return m_glyphs->primarySimpleFontData(m_fontDescription); -} +#if PLATFORM(IOS) + unsigned m_shouldNotBeUsedForArabic : 1; +#endif +}; -inline const FontData* Font::fontDataAt(unsigned index) const -{ - ASSERT(m_glyphs); - return m_glyphs->realizeFontDataAt(m_fontDescription, index); -} +#if PLATFORM(IOS) +bool fontFamilyShouldNotBeUsedForArabic(CFStringRef); +#endif -inline bool Font::isFixedPitch() const +ALWAYS_INLINE FloatRect Font::boundsForGlyph(Glyph glyph) const { - ASSERT(m_glyphs); - return m_glyphs->isFixedPitch(m_fontDescription); -} + if (isZeroWidthSpaceGlyph(glyph)) + return FloatRect(); + + FloatRect bounds; + if (m_glyphToBoundsMap) { + bounds = m_glyphToBoundsMap->metricsForGlyph(glyph); + if (bounds.width() != cGlyphSizeUnknown) + return bounds; + } -inline FontSelector* Font::fontSelector() const -{ - return m_glyphs ? m_glyphs->fontSelector() : 0; + bounds = platformBoundsForGlyph(glyph); + if (!m_glyphToBoundsMap) + m_glyphToBoundsMap = std::make_unique<GlyphMetricsMap<FloatRect>>(); + m_glyphToBoundsMap->setMetricsForGlyph(glyph, bounds); + return bounds; } -inline float Font::tabWidth(const SimpleFontData& fontData, unsigned tabSize, float position) const +ALWAYS_INLINE float Font::widthForGlyph(Glyph glyph) const { - if (!tabSize) - return letterSpacing(); - float tabWidth = tabSize * fontData.spaceWidth() + letterSpacing(); - return tabWidth - fmodf(position, tabWidth); -} + // The optimization of returning 0 for the zero-width-space glyph is incorrect for the LastResort font, + // used in place of the actual font when isLoading() is true on both macOS and iOS. + // The zero-width-space glyph in that font does not have a width of zero and, further, that glyph is used + // for many other characters and must not be zero width when used for them. + if (isZeroWidthSpaceGlyph(glyph) && !isLoading()) + return 0; + + float width = m_glyphToWidthMap.metricsForGlyph(glyph); + if (width != cGlyphSizeUnknown) + return width; + +#if ENABLE(OPENTYPE_VERTICAL) + if (m_verticalData) { +#if USE(CG) || USE(DIRECT2D) || USE(CAIRO) + width = m_verticalData->advanceHeight(this, glyph) + m_syntheticBoldOffset; +#else + width = m_verticalData->advanceHeight(this, glyph); +#endif + } else +#endif + width = platformWidthForGlyph(glyph); + m_glyphToWidthMap.setMetricsForGlyph(glyph, width); + return width; } -namespace WTF { - -template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout*); +} // namespace WebCore -} - -#endif +#endif // Font_h diff --git a/Source/WebCore/platform/graphics/FontBaseline.h b/Source/WebCore/platform/graphics/FontBaseline.h index f7d256d58..586baafb2 100644 --- a/Source/WebCore/platform/graphics/FontBaseline.h +++ b/Source/WebCore/platform/graphics/FontBaseline.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/platform/graphics/FontCache.cpp b/Source/WebCore/platform/graphics/FontCache.cpp index df26e68db..c1c6069f0 100644 --- a/Source/WebCore/platform/graphics/FontCache.cpp +++ b/Source/WebCore/platform/graphics/FontCache.cpp @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,13 +30,13 @@ #include "config.h" #include "FontCache.h" -#include "Font.h" -#include "FontGlyphs.h" +#include "FontCascade.h" #include "FontPlatformData.h" #include "FontSelector.h" +#include "MemoryPressureHandler.h" #include "WebKitFontFamilyNames.h" #include <wtf/HashMap.h> -#include <wtf/ListHashSet.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/text/AtomicStringHash.h> #include <wtf/text/StringHash.h> @@ -45,19 +45,22 @@ #include "OpenTypeVerticalData.h" #endif +#if USE(DIRECT2D) +#include <dwrite.h> +#endif + #if PLATFORM(IOS) -#include "MemoryPressureHandler.h" #include <wtf/Noncopyable.h> // FIXME: We may be able to simplify this code using C++11 threading primitives, including std::call_once(). -static pthread_mutex_t fontDataLock; +static pthread_mutex_t fontLock; static void initFontCacheLockOnce() { pthread_mutexattr_t mutexAttribute; pthread_mutexattr_init(&mutexAttribute); pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&fontDataLock, &mutexAttribute); + pthread_mutex_init(&fontLock, &mutexAttribute); pthread_mutexattr_destroy(&mutexAttribute); } @@ -69,13 +72,13 @@ public: FontLocker() { pthread_once(&initFontLockControl, initFontCacheLockOnce); - int lockcode = pthread_mutex_lock(&fontDataLock); - ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontDataLock lock failed with code:%d", lockcode); + int lockcode = pthread_mutex_lock(&fontLock); + ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock lock failed with code:%d", lockcode); } ~FontLocker() { - int lockcode = pthread_mutex_unlock(&fontDataLock); - ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontDataLock unlock failed with code:%d", lockcode); + int lockcode = pthread_mutex_unlock(&fontLock); + ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock unlock failed with code:%d", lockcode); } }; #endif // PLATFORM(IOS) @@ -84,15 +87,14 @@ using namespace WTF; namespace WebCore { -// FIXME: We should return a reference instead of a pointer since we never return a nullptr. -FontCache* fontCache() +FontCache& FontCache::singleton() { - DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); - return &globalFontCache; + static NeverDestroyed<FontCache> globalFontCache; + return globalFontCache; } FontCache::FontCache() - : m_purgePreventCount(0) + : m_purgeTimer(*this, &FontCache::purgeInactiveFontDataIfNeeded) { } @@ -100,35 +102,47 @@ struct FontPlatformDataCacheKey { WTF_MAKE_FAST_ALLOCATED; public: FontPlatformDataCacheKey() { } - FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description) + FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings) : m_fontDescriptionKey(description) , m_family(family) + , m_fontFaceFeatures(fontFaceFeatures ? *fontFaceFeatures : FontFeatureSettings()) + , m_fontFaceVariantSettings(fontFaceVariantSettings ? *fontFaceVariantSettings : FontVariantSettings()) { } - FontPlatformDataCacheKey(HashTableDeletedValueType) : m_fontDescriptionKey(hashTableDeletedSize()) { } - bool isHashTableDeletedValue() const { return m_fontDescriptionKey.size == hashTableDeletedSize(); } + explicit FontPlatformDataCacheKey(HashTableDeletedValueType t) + : m_fontDescriptionKey(t) + { } + + bool isHashTableDeletedValue() const { return m_fontDescriptionKey.isHashTableDeletedValue(); } bool operator==(const FontPlatformDataCacheKey& other) const { - return equalIgnoringCase(m_family, other.m_family) && m_fontDescriptionKey == other.m_fontDescriptionKey; + if (m_fontDescriptionKey != other.m_fontDescriptionKey + || m_fontFaceFeatures != other.m_fontFaceFeatures + || m_fontFaceVariantSettings != other.m_fontFaceVariantSettings) + return false; + if (m_family.impl() == other.m_family.impl()) + return true; + if (m_family.isNull() || other.m_family.isNull()) + return false; + return ASCIICaseInsensitiveHash::equal(m_family, other.m_family); } - FontDescriptionFontDataCacheKey m_fontDescriptionKey; + FontDescriptionKey m_fontDescriptionKey; AtomicString m_family; - -private: - static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } + FontFeatureSettings m_fontFaceFeatures; + FontVariantSettings m_fontFaceVariantSettings; }; -inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) -{ - return pairIntHash(CaseFoldingHash::hash(fontKey.m_family), fontKey.m_fontDescriptionKey.computeHash()); -} - struct FontPlatformDataCacheKeyHash { - static unsigned hash(const FontPlatformDataCacheKey& font) + static unsigned hash(const FontPlatformDataCacheKey& fontKey) { - return computeHash(font); + IntegerHasher hasher; + hasher.add(ASCIICaseInsensitiveHash::hash(fontKey.m_family)); + hasher.add(fontKey.m_fontDescriptionKey.computeHash()); + hasher.add(fontKey.m_fontFaceFeatures.hash()); + hasher.add(fontKey.m_fontFaceVariantSettings.uniqueValue()); + return hasher.hash(); } static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b) @@ -139,86 +153,64 @@ struct FontPlatformDataCacheKeyHash { static const bool safeToCompareToEmptyOrDeleted = true; }; -struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { }; - -typedef HashMap<FontPlatformDataCacheKey, OwnPtr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache; - -static FontPlatformDataCache* gFontPlatformDataCache = 0; +typedef HashMap<FontPlatformDataCacheKey, std::unique_ptr<FontPlatformData>, FontPlatformDataCacheKeyHash, WTF::SimpleClassHashTraits<FontPlatformDataCacheKey>> FontPlatformDataCache; -static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length) +static FontPlatformDataCache& fontPlatformDataCache() { - ASSERT(length > 0); - ASSERT(familyName.length() == length); - ASSERT(strlen(reference) == length); - const AtomicStringImpl* familyNameImpl = familyName.impl(); - if (familyNameImpl->is8Bit()) - return equalIgnoringCase(familyNameImpl->characters8(), reinterpret_cast<const LChar*>(reference), length); - return equalIgnoringCase(familyNameImpl->characters16(), reinterpret_cast<const LChar*>(reference), length); + static NeverDestroyed<FontPlatformDataCache> cache; + return cache; } -template<size_t length> -static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length]) +const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) { - return familyNameEqualIgnoringCase(familyName, reference, length - 1); -} + static NeverDestroyed<AtomicString> arial("Arial", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> courier("Courier", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> courierNew("Courier New", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> helvetica("Helvetica", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> times("Times", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> timesNewRoman("Times New Roman", AtomicString::ConstructFromLiteral); + + const AtomicString& platformSpecificAlternate = platformAlternateFamilyName(familyName); + if (!platformSpecificAlternate.isNull()) + return platformSpecificAlternate; -static const AtomicString alternateFamilyName(const AtomicString& familyName) -{ - // Alias Courier and Courier New. - // Alias Times and Times New Roman. - // Alias Arial and Helvetica. switch (familyName.length()) { case 5: - if (familyNameEqualIgnoringCase(familyName, "Arial")) - return AtomicString("Helvetica", AtomicString::ConstructFromLiteral); - if (familyNameEqualIgnoringCase(familyName, "Times")) - return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral); + if (equalLettersIgnoringASCIICase(familyName, "arial")) + return helvetica; + if (equalLettersIgnoringASCIICase(familyName, "times")) + return timesNewRoman; break; case 7: - if (familyNameEqualIgnoringCase(familyName, "Courier")) - return AtomicString("Courier New", AtomicString::ConstructFromLiteral); + if (equalLettersIgnoringASCIICase(familyName, "courier")) + return courierNew; break; case 9: - if (familyNameEqualIgnoringCase(familyName, "Helvetica")) - return AtomicString("Arial", AtomicString::ConstructFromLiteral); + if (equalLettersIgnoringASCIICase(familyName, "helvetica")) + return arial; break; #if !OS(WINDOWS) - // On Windows, Courier New (truetype font) is always present and - // Courier is a bitmap font. So, we don't want to map Courier New to - // Courier. + // On Windows, Courier New is a TrueType font that is always present and + // Courier is a bitmap font that we do not support. So, we don't want to map + // Courier New to Courier. + // FIXME: Not sure why this is harmful on Windows, since the alternative will + // only be tried if Courier New is not found. case 11: - if (familyNameEqualIgnoringCase(familyName, "Courier New")) - return AtomicString("Courier", AtomicString::ConstructFromLiteral); + if (equalLettersIgnoringASCIICase(familyName, "courier new")) + return courier; break; -#endif // !OS(WINDOWS) +#endif case 15: - if (familyNameEqualIgnoringCase(familyName, "Times New Roman")) - return AtomicString("Times", AtomicString::ConstructFromLiteral); - break; -#if OS(WINDOWS) - // On Windows, bitmap fonts are blocked altogether so that we have to - // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) - case 13: - if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif")) - return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral); - break; - - // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no - // 'Microsoft Sans Serif-equivalent' for Serif. - case 8: - if (familyNameEqualIgnoringCase(familyName, "MS Serif")) - return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral); + if (equalLettersIgnoringASCIICase(familyName, "times new roman")) + return times; break; -#endif // OS(WINDOWS) - } return nullAtom; } -FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, - const AtomicString& passedFamilyName, - bool checkingAlternateName) +FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, const AtomicString& passedFamilyName, + const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, bool checkingAlternateName) { #if PLATFORM(IOS) FontLocker fontLocker; @@ -234,30 +226,31 @@ FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo const AtomicString& familyName = passedFamilyName; #endif - if (!gFontPlatformDataCache) { - gFontPlatformDataCache = new FontPlatformDataCache; + static bool initialized; + if (!initialized) { platformInit(); + initialized = true; } - FontPlatformDataCacheKey key(familyName, fontDescription); + FontPlatformDataCacheKey key(familyName, fontDescription, fontFaceFeatures, fontFaceVariantSettings); - FontPlatformDataCache::AddResult result = gFontPlatformDataCache->add(key, nullptr); - FontPlatformDataCache::iterator it = result.iterator; - if (result.isNewEntry) { - it->value = createFontPlatformData(fontDescription, familyName); + auto addResult = fontPlatformDataCache().add(key, nullptr); + FontPlatformDataCache::iterator it = addResult.iterator; + if (addResult.isNewEntry) { + it->value = createFontPlatformData(fontDescription, familyName, fontFaceFeatures, fontFaceVariantSettings); if (!it->value && !checkingAlternateName) { // We were unable to find a font. We have a small set of fonts that we alias to other names, // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. - const AtomicString alternateName = alternateFamilyName(familyName); + const AtomicString& alternateName = alternateFamilyName(familyName); if (!alternateName.isNull()) { - FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, true); + FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, fontFaceFeatures, fontFaceVariantSettings, true); // Lookup the key in the hash table again as the previous iterator may have // been invalidated by the recursive call to getCachedFontPlatformData(). - it = gFontPlatformDataCache->find(key); - ASSERT(it != gFontPlatformDataCache->end()); + it = fontPlatformDataCache().find(key); + ASSERT(it != fontPlatformDataCache().end()); if (fontPlatformDataForAlternateName) - it->value = adoptPtr(new FontPlatformData(*fontPlatformDataForAlternateName)); + it->value = std::make_unique<FontPlatformData>(*fontPlatformDataForAlternateName); } } } @@ -265,62 +258,6 @@ FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo return it->value.get(); } -#if ENABLE(OPENTYPE_VERTICAL) -struct FontVerticalDataCacheKeyHash { - static unsigned hash(const FontCache::FontFileKey& fontFileKey) - { - return PtrHash<const FontCache::FontFileKey*>::hash(&fontFileKey); - } - - static bool equal(const FontCache::FontFileKey& a, const FontCache::FontFileKey& b) - { - return a == b; - } - - static const bool safeToCompareToEmptyOrDeleted = true; -}; - -struct FontVerticalDataCacheKeyTraits : WTF::GenericHashTraits<FontCache::FontFileKey> { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = true; - static const FontCache::FontFileKey& emptyValue() - { - DEFINE_STATIC_LOCAL(FontCache::FontFileKey, key, (nullAtom)); - return key; - } - static void constructDeletedValue(FontCache::FontFileKey& slot) - { - new (NotNull, &slot) FontCache::FontFileKey(HashTableDeletedValue); - } - static bool isDeletedValue(const FontCache::FontFileKey& value) - { - return value.isHashTableDeletedValue(); - } -}; - -typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, FontVerticalDataCacheKeyHash, FontVerticalDataCacheKeyTraits> FontVerticalDataCache; - -FontVerticalDataCache& fontVerticalDataCacheInstance() -{ - DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ()); - return fontVerticalDataCache; -} - -PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData) -{ - FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); - FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key); - if (result != fontVerticalDataCache.end()) - return result.get()->value; - - RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData); - if (!verticalData->isOpenType()) - verticalData.clear(); - fontVerticalDataCache.set(key, verticalData); - return verticalData; -} -#endif - struct FontDataCacheKeyHash { static unsigned hash(const FontPlatformData& platformData) { @@ -337,10 +274,9 @@ struct FontDataCacheKeyHash { struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { static const bool emptyValueIsZero = true; - static const bool needsDestruction = true; static const FontPlatformData& emptyValue() { - DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); + static NeverDestroyed<FontPlatformData> key(0.f, false, false); return key; } static void constructDeletedValue(FontPlatformData& slot) @@ -353,266 +289,162 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { } }; -typedef HashMap<FontPlatformData, std::pair<RefPtr<SimpleFontData>, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; +typedef HashMap<FontPlatformData, RefPtr<Font>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; -static FontDataCache* gFontDataCache = 0; +static FontDataCache& cachedFonts() +{ + static NeverDestroyed<FontDataCache> cache; + return cache; +} -#if PLATFORM(IOS) -const int cMaxInactiveFontData = 120; -const int cTargetInactiveFontData = 100; -const int cMaxUnderMemoryPressureInactiveFontData = 50; -const int cTargetUnderMemoryPressureInactiveFontData = 30; -#else -const int cMaxInactiveFontData = 225; -const int cTargetInactiveFontData = 200; -#endif -static ListHashSet<RefPtr<SimpleFontData>>* gInactiveFontData = 0; +#if ENABLE(OPENTYPE_VERTICAL) +typedef HashMap<FontPlatformData, RefPtr<OpenTypeVerticalData>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontVerticalDataCache; -PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain) +FontVerticalDataCache& fontVerticalDataCache() { - FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName); - if (!platformData) - return 0; - - return getCachedFontData(platformData, shouldRetain); + static NeverDestroyed<FontVerticalDataCache> fontVerticalDataCache; + return fontVerticalDataCache; } -PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain) +RefPtr<OpenTypeVerticalData> FontCache::verticalData(const FontPlatformData& platformData) { - if (!platformData) - return 0; - -#if !ASSERT_DISABLED - if (shouldRetain == DoNotRetain) - ASSERT(m_purgePreventCount); + auto addResult = fontVerticalDataCache().ensure(platformData, [&platformData] { + return OpenTypeVerticalData::create(platformData); + }); + return addResult.iterator->value; +} #endif #if PLATFORM(IOS) - FontLocker fontLocker; +const unsigned cMaxInactiveFontData = 120; +const unsigned cTargetInactiveFontData = 100; +#else +const unsigned cMaxInactiveFontData = 225; +const unsigned cTargetInactiveFontData = 200; #endif - - if (!gFontDataCache) { - gFontDataCache = new FontDataCache; - gInactiveFontData = new ListHashSet<RefPtr<SimpleFontData>>; - } - - FontDataCache::iterator result = gFontDataCache->find(*platformData); - if (result == gFontDataCache->end()) { - std::pair<RefPtr<SimpleFontData>, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0); - gFontDataCache->set(*platformData, newValue); - if (shouldRetain == DoNotRetain) - gInactiveFontData->add(newValue.first); - return newValue.first.release(); - } - if (!result.get()->value.second) { - ASSERT(gInactiveFontData->contains(result.get()->value.first)); - gInactiveFontData->remove(result.get()->value.first); - } +const unsigned cMaxUnderMemoryPressureInactiveFontData = 50; +const unsigned cTargetUnderMemoryPressureInactiveFontData = 30; - if (shouldRetain == Retain) - result.get()->value.second++; - else if (!result.get()->value.second) { - // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from - // gInactiveFontData (above) and re-add here to update LRU position. - gInactiveFontData->add(result.get()->value.first); - } +RefPtr<Font> FontCache::fontForFamily(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, bool checkingAlternateName) +{ + if (!m_purgeTimer.isActive()) + m_purgeTimer.startOneShot(0ms); - return result.get()->value.first; -} + FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, fontFaceFeatures, fontFaceVariantSettings, checkingAlternateName); + if (!platformData) + return nullptr; -SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription) -{ - return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef(); + return fontForPlatformData(*platformData); } -void FontCache::releaseFontData(const SimpleFontData* fontData) +Ref<Font> FontCache::fontForPlatformData(const FontPlatformData& platformData) { - ASSERT(gFontDataCache); - ASSERT(!fontData->isCustomFont()); - #if PLATFORM(IOS) FontLocker fontLocker; #endif - FontDataCache::iterator it = gFontDataCache->find(fontData->platformData()); - ASSERT(it != gFontDataCache->end()); - if (it == gFontDataCache->end()) - return; + auto addResult = cachedFonts().add(platformData, nullptr); + if (addResult.isNewEntry) + addResult.iterator->value = Font::create(platformData); - ASSERT(it->value.second); - if (!--it->value.second) - gInactiveFontData->add(it->value.first); -} - -void FontCache::purgeInactiveFontDataIfNeeded() -{ -#if PLATFORM(IOS) - bool underMemoryPressure = memoryPressureHandler().hasReceivedMemoryPressure(); - int inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData; - int targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData; + ASSERT(addResult.iterator->value->platformData() == platformData); - if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > inactiveFontDataLimit) - purgeInactiveFontData(gInactiveFontData->size() - targetFontDataLimit); -#else - if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData) - purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData); -#endif + return *addResult.iterator->value; } -void FontCache::purgeInactiveFontData(int count) +void FontCache::purgeInactiveFontDataIfNeeded() { - pruneUnreferencedEntriesFromFontGlyphsCache(); + bool underMemoryPressure = MemoryPressureHandler::singleton().isUnderMemoryPressure(); + unsigned inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData; - if (!gInactiveFontData || m_purgePreventCount) + if (cachedFonts().size() < inactiveFontDataLimit) return; - - static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. - if (isPurging) + unsigned inactiveCount = inactiveFontCount(); + if (inactiveCount <= inactiveFontDataLimit) return; - isPurging = true; + unsigned targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData; + purgeInactiveFontData(inactiveCount - targetFontDataLimit); +} + +void FontCache::purgeInactiveFontData(unsigned purgeCount) +{ + pruneUnreferencedEntriesFromFontCascadeCache(); + pruneSystemFallbackFonts(); #if PLATFORM(IOS) FontLocker fontLocker; #endif - Vector<RefPtr<SimpleFontData>, 20> fontDataToDelete; - ListHashSet<RefPtr<SimpleFontData>>::iterator end = gInactiveFontData->end(); - ListHashSet<RefPtr<SimpleFontData>>::iterator it = gInactiveFontData->begin(); - for (int i = 0; i < count && it != end; ++it, ++i) { - RefPtr<SimpleFontData>& fontData = *it.get(); - gFontDataCache->remove(fontData->platformData()); - // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011 - fontDataToDelete.append(fontData); - } - - if (it == end) { - // Removed everything - gInactiveFontData->clear(); - } else { - for (int i = 0; i < count; ++i) - gInactiveFontData->remove(gInactiveFontData->begin()); - } - - fontDataToDelete.clear(); - - if (gFontPlatformDataCache) { - Vector<FontPlatformDataCacheKey> keysToRemove; - keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); - FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); - for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { - if (platformData->value && !gFontDataCache->contains(*platformData->value)) - keysToRemove.append(platformData->key); + while (purgeCount) { + Vector<RefPtr<Font>, 20> fontsToDelete; + for (auto& font : cachedFonts().values()) { + if (!font->hasOneRef()) + continue; + fontsToDelete.append(WTFMove(font)); + if (!--purgeCount) + break; } - - size_t keysToRemoveCount = keysToRemove.size(); - for (size_t i = 0; i < keysToRemoveCount; ++i) - gFontPlatformDataCache->remove(keysToRemove[i]); - } - + // Fonts may ref other fonts so we loop until there are no changes. + if (fontsToDelete.isEmpty()) + break; + for (auto& font : fontsToDelete) { + bool success = cachedFonts().remove(font->platformData()); + ASSERT_UNUSED(success, success); #if ENABLE(OPENTYPE_VERTICAL) - FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); - if (!fontVerticalDataCache.isEmpty()) { - // Mark & sweep unused verticalData - FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end(); - for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { - if (verticalData->value) - verticalData->value->m_inFontCache = false; - } - FontDataCache::iterator fontDataEnd = gFontDataCache->end(); - for (FontDataCache::iterator fontData = gFontDataCache->begin(); fontData != fontDataEnd; ++fontData) { - OpenTypeVerticalData* verticalData = const_cast<OpenTypeVerticalData*>(fontData->value.first->verticalData()); - if (verticalData) - verticalData->m_inFontCache = true; - } - Vector<FontFileKey> keysToRemove; - keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size()); - for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { - if (!verticalData->value || !verticalData->value->m_inFontCache) - keysToRemove.append(verticalData->key); - } - for (size_t i = 0, count = keysToRemove.size(); i < count; ++i) - fontVerticalDataCache.take(keysToRemove[i]); - } + fontVerticalDataCache().remove(font->platformData()); #endif + } + }; - isPurging = false; -} + Vector<FontPlatformDataCacheKey> keysToRemove; + keysToRemove.reserveInitialCapacity(fontPlatformDataCache().size()); + for (auto& entry : fontPlatformDataCache()) { + if (entry.value && !cachedFonts().contains(*entry.value)) + keysToRemove.uncheckedAppend(entry.key); + } + for (auto& key : keysToRemove) + fontPlatformDataCache().remove(key); -size_t FontCache::fontDataCount() -{ - if (gFontDataCache) - return gFontDataCache->size(); - return 0; + platformPurgeInactiveFontData(); } -size_t FontCache::inactiveFontDataCount() +size_t FontCache::fontCount() { - if (gInactiveFontData) - return gInactiveFontData->size(); - return 0; + return cachedFonts().size(); } -PassRefPtr<FontData> FontCache::getFontData(const FontDescription& description, int& familyIndex, FontSelector* fontSelector) +size_t FontCache::inactiveFontCount() { - ASSERT(familyIndex != cAllFamiliesScanned); - RefPtr<FontData> result; - - bool isFirst = !familyIndex; - int familyCount = description.familyCount(); - for (;familyIndex < familyCount && !result; ++familyIndex) { - const AtomicString& family = description.familyAt(familyIndex); - if (family.isEmpty()) - continue; - if (fontSelector) - result = fontSelector->getFontData(description, family); - if (!result) - result = getCachedFontData(description, family); - } - if (familyIndex == familyCount) - familyIndex = cAllFamiliesScanned; - -#if PLATFORM(MAC) - if (!result) { - // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform. - // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the - // Geeza Pro font. - result = similarFontPlatformData(description); - } +#if PLATFORM(IOS) + FontLocker fontLocker; #endif - - if (!result && isFirst) { - // If it's the primary font that we couldn't find, we try the following. In all other cases, we will - // just use per-character system fallback. - if (fontSelector) { - // Try the user's preferred standard font. - if (RefPtr<FontData> data = fontSelector->getFontData(description, standardFamily)) - return data.release(); - } - // Still no result. Hand back our last resort fallback font. - result = getLastResortFallbackFont(description); + unsigned count = 0; + for (auto& font : cachedFonts().values()) { + if (font->hasOneRef()) + ++count; } - return result.release(); + return count; } static HashSet<FontSelector*>* gClients; -void FontCache::addClient(FontSelector* client) +void FontCache::addClient(FontSelector& client) { if (!gClients) gClients = new HashSet<FontSelector*>; - ASSERT(!gClients->contains(client)); - gClients->add(client); + ASSERT(!gClients->contains(&client)); + gClients->add(&client); } -void FontCache::removeClient(FontSelector* client) +void FontCache::removeClient(FontSelector& client) { ASSERT(gClients); - ASSERT(gClients->contains(client)); + ASSERT(gClients->contains(&client)); - gClients->remove(client); + gClients->remove(&client); } static unsigned short gGeneration = 0; @@ -625,13 +457,15 @@ unsigned short FontCache::generation() void FontCache::invalidate() { if (!gClients) { - ASSERT(!gFontPlatformDataCache); + ASSERT(fontPlatformDataCache().isEmpty()); return; } - if (gFontPlatformDataCache) - gFontPlatformDataCache->clear(); - invalidateFontGlyphsCache(); + fontPlatformDataCache().clear(); +#if ENABLE(OPENTYPE_VERTICAL) + fontVerticalDataCache().clear(); +#endif + invalidateFontCascadeCache(); gGeneration++; @@ -646,4 +480,11 @@ void FontCache::invalidate() purgeInactiveFontData(); } +#if !PLATFORM(COCOA) +RefPtr<Font> FontCache::similarFont(const FontDescription&, const AtomicString&) +{ + return nullptr; +} +#endif + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FontCache.h b/Source/WebCore/platform/graphics/FontCache.h index 83ec1246d..e5a3f19cc 100644 --- a/Source/WebCore/platform/graphics/FontCache.h +++ b/Source/WebCore/platform/graphics/FontCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007-2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,19 +27,19 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FontCache_h -#define FontCache_h +#pragma once #include "FontDescription.h" +#include "Timer.h" +#include <array> #include <limits.h> #include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> +#include <wtf/text/AtomicStringHash.h> #include <wtf/text/WTFString.h> -#include <wtf/unicode/Unicode.h> -#if PLATFORM(IOS) +#if PLATFORM(COCOA) #include <CoreText/CTFont.h> #endif @@ -51,12 +51,11 @@ namespace WebCore { -class Font; +class FontCascade; class FontPlatformData; -class FontData; class FontSelector; class OpenTypeVerticalData; -class SimpleFontData; +class Font; #if PLATFORM(WIN) #if USE(IMLANG_FONT_LINK2) @@ -67,63 +66,129 @@ typedef IMLangFontLink IMLangFontLinkType; #endif // This key contains the FontDescription fields other than family that matter when fetching FontDatas (platform fonts). -struct FontDescriptionFontDataCacheKey { - explicit FontDescriptionFontDataCacheKey(unsigned size = 0) - : size(size) - , weight(0) - , flags(0) +struct FontDescriptionKey { + FontDescriptionKey() = default; + + FontDescriptionKey(const FontDescription& description) + : m_size(description.computedPixelSize()) + , m_weight(description.weight()) + , m_flags(makeFlagsKey(description)) + , m_featureSettings(description.featureSettings()) +#if ENABLE(VARIATION_FONTS) + , m_variationSettings(description.variationSettings()) +#endif { } - FontDescriptionFontDataCacheKey(const FontDescription& description) - : size(description.computedPixelSize()) - , weight(description.weight()) - , flags(makeFlagKey(description)) + + explicit FontDescriptionKey(WTF::HashTableDeletedValueType) + : m_size(cHashTableDeletedSize) { } - static unsigned makeFlagKey(const FontDescription& description) - { - return static_cast<unsigned>(description.widthVariant()) << 4 - | static_cast<unsigned>(description.orientation()) << 3 - | static_cast<unsigned>(description.italic()) << 2 - | static_cast<unsigned>(description.usePrinterFont()) << 1 - | static_cast<unsigned>(description.renderingMode()); - } - bool operator==(const FontDescriptionFontDataCacheKey& other) const + + bool operator==(const FontDescriptionKey& other) const { - return size == other.size && weight == other.weight && flags == other.flags; + return m_size == other.m_size + && m_weight == other.m_weight + && m_flags == other.m_flags +#if ENABLE(VARIATION_FONTS) + && m_variationSettings == other.m_variationSettings +#endif + && m_featureSettings == other.m_featureSettings; } - bool operator!=(const FontDescriptionFontDataCacheKey& other) const + + bool operator!=(const FontDescriptionKey& other) const { return !(*this == other); } + + bool isHashTableDeletedValue() const { return m_size == cHashTableDeletedSize; } + inline unsigned computeHash() const { - return StringHasher::hashMemory<sizeof(FontDescriptionFontDataCacheKey)>(this); + IntegerHasher hasher; + hasher.add(m_size); + hasher.add(m_weight); + for (unsigned flagItem : m_flags) + hasher.add(flagItem); + hasher.add(m_featureSettings.hash()); +#if ENABLE(VARIATION_FONTS) + hasher.add(m_variationSettings.hash()); +#endif + return hasher.hash(); + } + +private: + static std::array<unsigned, 2> makeFlagsKey(const FontDescription& description) + { + static_assert(USCRIPT_CODE_LIMIT < 0x1000, "Script code must fit in an unsigned along with the other flags"); + unsigned first = static_cast<unsigned>(description.script()) << 11 + | static_cast<unsigned>(description.textRenderingMode()) << 9 + | static_cast<unsigned>(description.fontSynthesis()) << 6 + | static_cast<unsigned>(description.widthVariant()) << 4 + | static_cast<unsigned>(description.nonCJKGlyphOrientation()) << 3 + | static_cast<unsigned>(description.orientation()) << 2 + | static_cast<unsigned>(description.italic()) << 1 + | static_cast<unsigned>(description.renderingMode()); + unsigned second = static_cast<unsigned>(description.variantEastAsianRuby()) << 27 + | static_cast<unsigned>(description.variantEastAsianWidth()) << 25 + | static_cast<unsigned>(description.variantEastAsianVariant()) << 22 + | static_cast<unsigned>(description.variantAlternates()) << 21 + | static_cast<unsigned>(description.variantNumericSlashedZero()) << 20 + | static_cast<unsigned>(description.variantNumericOrdinal()) << 19 + | static_cast<unsigned>(description.variantNumericFraction()) << 17 + | static_cast<unsigned>(description.variantNumericSpacing()) << 15 + | static_cast<unsigned>(description.variantNumericFigure()) << 13 + | static_cast<unsigned>(description.variantCaps()) << 10 + | static_cast<unsigned>(description.variantPosition()) << 8 + | static_cast<unsigned>(description.variantContextualAlternates()) << 6 + | static_cast<unsigned>(description.variantHistoricalLigatures()) << 4 + | static_cast<unsigned>(description.variantDiscretionaryLigatures()) << 2 + | static_cast<unsigned>(description.variantCommonLigatures()); + return {{ first, second }}; } - unsigned size; - unsigned weight; - unsigned flags; + + static const unsigned cHashTableDeletedSize = 0xFFFFFFFFU; + + // FontCascade::locale() is explicitly not included in this struct. + unsigned m_size { 0 }; + unsigned m_weight { 0 }; + std::array<unsigned, 2> m_flags {{ 0, 0 }}; + FontFeatureSettings m_featureSettings; +#if ENABLE(VARIATION_FONTS) + FontVariationSettings m_variationSettings; +#endif }; -class FontCache { - friend class FontCachePurgePreventer; +struct FontDescriptionKeyHash { + static unsigned hash(const FontDescriptionKey& key) + { + return key.computeHash(); + } - WTF_MAKE_NONCOPYABLE(FontCache); WTF_MAKE_FAST_ALLOCATED; -public: - friend FontCache* fontCache(); + static bool equal(const FontDescriptionKey& a, const FontDescriptionKey& b) + { + return a == b; + } - enum ShouldRetain { Retain, DoNotRetain }; + static const bool safeToCompareToEmptyOrDeleted = true; +}; - PassRefPtr<FontData> getFontData(const FontDescription&, int& familyIndex, FontSelector*); - void releaseFontData(const SimpleFontData*); +class FontCache { + friend class WTF::NeverDestroyed<FontCache>; - // This method is implemented by the platform. - PassRefPtr<SimpleFontData> systemFallbackForCharacters(const FontDescription&, const SimpleFontData* originalFontData, bool isPlatformFont, const UChar* characters, int length); + WTF_MAKE_NONCOPYABLE(FontCache); WTF_MAKE_FAST_ALLOCATED; +public: + WEBCORE_EXPORT static FontCache& singleton(); - // Also implemented by the platform. + // These methods are implemented by the platform. + RefPtr<Font> systemFallbackForCharacters(const FontDescription&, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length); + Vector<String> systemFontFamilies(); void platformInit(); #if PLATFORM(IOS) static float weightOfCTFont(CTFontRef); #endif +#if PLATFORM(COCOA) + WEBCORE_EXPORT static void setFontWhitelist(const Vector<String>&); +#endif #if PLATFORM(WIN) IMLangFontLinkType* getFontLinkInterface(); static void comInitialize(); @@ -131,89 +196,95 @@ public: static IMultiLanguage* getMultiLanguageInterface(); #endif - void getTraitsInFamily(const AtomicString&, Vector<unsigned>&); + // This function exists so CSSFontSelector can have a unified notion of preinstalled fonts and @font-face. + // It comes into play when you create an @font-face which shares a family name as a preinstalled font. + Vector<FontTraitsMask> getTraitsInFamily(const AtomicString&); - PassRefPtr<SimpleFontData> getCachedFontData(const FontDescription&, const AtomicString&, bool checkingAlternateName = false, ShouldRetain = Retain); - PassRefPtr<SimpleFontData> getLastResortFallbackFont(const FontDescription&, ShouldRetain = Retain); - SimpleFontData* getNonRetainedLastResortFallbackFont(const FontDescription&); + WEBCORE_EXPORT RefPtr<Font> fontForFamily(const FontDescription&, const AtomicString&, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false); + WEBCORE_EXPORT Ref<Font> lastResortFallbackFont(const FontDescription&); + Ref<Font> lastResortFallbackFontForEveryCharacter(const FontDescription&); + WEBCORE_EXPORT Ref<Font> fontForPlatformData(const FontPlatformData&); + RefPtr<Font> similarFont(const FontDescription&, const AtomicString& family); - void addClient(FontSelector*); - void removeClient(FontSelector*); + void addClient(FontSelector&); + void removeClient(FontSelector&); unsigned short generation(); - void invalidate(); + WEBCORE_EXPORT void invalidate(); - size_t fontDataCount(); - size_t inactiveFontDataCount(); - void purgeInactiveFontData(int count = INT_MAX); + WEBCORE_EXPORT size_t fontCount(); + WEBCORE_EXPORT size_t inactiveFontCount(); + WEBCORE_EXPORT void purgeInactiveFontData(unsigned count = UINT_MAX); + void platformPurgeInactiveFontData(); #if PLATFORM(WIN) - PassRefPtr<SimpleFontData> fontDataFromDescriptionAndLogFont(const FontDescription&, ShouldRetain, const LOGFONT&, AtomicString& outFontFamilyName); + RefPtr<Font> fontFromDescriptionAndLogFont(const FontDescription&, const LOGFONT&, AtomicString& outFontFamilyName); #endif #if ENABLE(OPENTYPE_VERTICAL) - typedef AtomicString FontFileKey; - PassRefPtr<OpenTypeVerticalData> getVerticalData(const FontFileKey&, const FontPlatformData&); + RefPtr<OpenTypeVerticalData> verticalData(const FontPlatformData&); #endif - struct SimpleFontFamily { - String name; - bool isBold; - bool isItalic; - }; - static void getFontFamilyForCharacters(const UChar* characters, size_t numCharacters, const char* preferredLocale, SimpleFontFamily*); - private: FontCache(); - ~FontCache(); + ~FontCache() = delete; - void disablePurging() { m_purgePreventCount++; } - void enablePurging() - { - ASSERT(m_purgePreventCount); - if (!--m_purgePreventCount) - purgeInactiveFontDataIfNeeded(); - } - - void purgeInactiveFontDataIfNeeded(); + WEBCORE_EXPORT void purgeInactiveFontDataIfNeeded(); // FIXME: This method should eventually be removed. - FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName = false); + FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false); // These methods are implemented by each platform. -#if PLATFORM(IOS) +#if PLATFORM(COCOA) FontPlatformData* getCustomFallbackFont(const UInt32, const FontDescription&); - PassRefPtr<SimpleFontData> getSystemFontFallbackForCharacters(const FontDescription&, const SimpleFontData*, const UChar* characters, int length); #endif - PassOwnPtr<FontPlatformData> createFontPlatformData(const FontDescription&, const AtomicString& family); -#if PLATFORM(MAC) - PassRefPtr<SimpleFontData> similarFontPlatformData(const FontDescription&); -#endif - - PassRefPtr<SimpleFontData> getCachedFontData(const FontPlatformData*, ShouldRetain = Retain); + std::unique_ptr<FontPlatformData> createFontPlatformData(const FontDescription&, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings); + + static const AtomicString& alternateFamilyName(const AtomicString&); + static const AtomicString& platformAlternateFamilyName(const AtomicString&); - // Don't purge if this count is > 0; - int m_purgePreventCount; + Timer m_purgeTimer; -#if PLATFORM(MAC) - friend class ComplexTextController; -#endif - friend class SimpleFontData; // For getCachedFontData(const FontPlatformData*) - friend class FontGlyphs; -#if PLATFORM(IOS) +#if PLATFORM(COCOA) friend class ComplexTextController; #endif + friend class Font; }; -// Get the global fontCache. -FontCache* fontCache(); +#if PLATFORM(COCOA) -class FontCachePurgePreventer { -public: - FontCachePurgePreventer() { fontCache()->disablePurging(); } - ~FontCachePurgePreventer() { fontCache()->enablePurging(); } +struct SynthesisPair { + SynthesisPair(bool needsSyntheticBold, bool needsSyntheticOblique) + : needsSyntheticBold(needsSyntheticBold) + , needsSyntheticOblique(needsSyntheticOblique) + { + } + + std::pair<bool, bool> boldObliquePair() const + { + return std::make_pair(needsSyntheticBold, needsSyntheticOblique); + } + + bool needsSyntheticBold; + bool needsSyntheticOblique; }; +RetainPtr<CTFontRef> preparePlatformFont(CTFontRef, TextRenderingMode, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, const FontFeatureSettings& features, const FontVariantSettings&, const FontVariationSettings&); +FontWeight fontWeightFromCoreText(CGFloat weight); +uint16_t toCoreTextFontWeight(FontWeight); +bool isFontWeightBold(FontWeight); +void platformInvalidateFontCache(); +SynthesisPair computeNecessarySynthesis(CTFontRef, const FontDescription&, bool isPlatformFont = false); +RetainPtr<CTFontRef> platformFontWithFamilySpecialCase(const AtomicString& family, FontWeight, CTFontSymbolicTraits, float size); +RetainPtr<CTFontRef> platformFontWithFamily(const AtomicString& family, CTFontSymbolicTraits, FontWeight, TextRenderingMode, float size); +bool requiresCustomFallbackFont(UChar32 character); + +#else + +inline void FontCache::platformPurgeInactiveFontData() +{ } #endif + +} diff --git a/Source/WebCore/platform/graphics/FontCascade.cpp b/Source/WebCore/platform/graphics/FontCascade.cpp new file mode 100644 index 000000000..0fac64331 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontCascade.cpp @@ -0,0 +1,1491 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "FontCascade.h" + +#include "CharacterProperties.h" +#include "FloatRect.h" +#include "FontCache.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "LayoutRect.h" +#include "SurrogatePairAwareTextIterator.h" +#include "TextRun.h" +#include "WidthIterator.h" +#if USE(CAIRO) +#include <cairo.h> +#endif +#include <wtf/MainThread.h> +#include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/AtomicStringHash.h> +#include <wtf/text/StringBuilder.h> + +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +static Ref<FontCascadeFonts> retrieveOrAddCachedFonts(const FontCascadeDescription&, RefPtr<FontSelector>&&); + +static bool useBackslashAsYenSignForFamily(const AtomicString& family) +{ + if (family.isEmpty()) + return false; + static HashSet<AtomicString>* set; + if (!set) { + set = new HashSet<AtomicString>; + set->add("MS PGothic"); + UChar unicodeNameMSPGothic[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x30B4, 0x30B7, 0x30C3, 0x30AF}; + set->add(AtomicString(unicodeNameMSPGothic, WTF_ARRAY_LENGTH(unicodeNameMSPGothic))); + + set->add("MS PMincho"); + UChar unicodeNameMSPMincho[] = {0xFF2D, 0xFF33, 0x0020, 0xFF30, 0x660E, 0x671D}; + set->add(AtomicString(unicodeNameMSPMincho, WTF_ARRAY_LENGTH(unicodeNameMSPMincho))); + + set->add("MS Gothic"); + UChar unicodeNameMSGothic[] = {0xFF2D, 0xFF33, 0x0020, 0x30B4, 0x30B7, 0x30C3, 0x30AF}; + set->add(AtomicString(unicodeNameMSGothic, WTF_ARRAY_LENGTH(unicodeNameMSGothic))); + + set->add("MS Mincho"); + UChar unicodeNameMSMincho[] = {0xFF2D, 0xFF33, 0x0020, 0x660E, 0x671D}; + set->add(AtomicString(unicodeNameMSMincho, WTF_ARRAY_LENGTH(unicodeNameMSMincho))); + + set->add("Meiryo"); + UChar unicodeNameMeiryo[] = {0x30E1, 0x30A4, 0x30EA, 0x30AA}; + set->add(AtomicString(unicodeNameMeiryo, WTF_ARRAY_LENGTH(unicodeNameMeiryo))); + } + return set->contains(family); +} + +FontCascade::CodePath FontCascade::s_codePath = Auto; + +// ============================================================================================ +// FontCascade Implementation (Cross-Platform Portion) +// ============================================================================================ + +FontCascade::FontCascade() + : m_weakPtrFactory(this) + , m_letterSpacing(0) + , m_wordSpacing(0) + , m_useBackslashAsYenSymbol(false) + , m_enableKerning(false) + , m_requiresShaping(false) +{ +} + +FontCascade::FontCascade(const FontCascadeDescription& fd, float letterSpacing, float wordSpacing) + : m_fontDescription(fd) + , m_weakPtrFactory(this) + , m_letterSpacing(letterSpacing) + , m_wordSpacing(wordSpacing) + , m_useBackslashAsYenSymbol(useBackslashAsYenSignForFamily(fd.firstFamily())) + , m_enableKerning(computeEnableKerning()) + , m_requiresShaping(computeRequiresShaping()) +{ +} + +// FIXME: We should make this constructor platform-independent. +FontCascade::FontCascade(const FontPlatformData& fontData, FontSmoothingMode fontSmoothingMode) + : m_fonts(FontCascadeFonts::createForPlatformFont(fontData)) + , m_weakPtrFactory(this) + , m_letterSpacing(0) + , m_wordSpacing(0) + , m_useBackslashAsYenSymbol(false) + , m_enableKerning(computeEnableKerning()) + , m_requiresShaping(computeRequiresShaping()) +{ + m_fontDescription.setFontSmoothing(fontSmoothingMode); +#if PLATFORM(IOS) + m_fontDescription.setSpecifiedSize(CTFontGetSize(fontData.font())); + m_fontDescription.setComputedSize(CTFontGetSize(fontData.font())); + m_fontDescription.setIsItalic(CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitItalic); + m_fontDescription.setWeight((CTFontGetSymbolicTraits(fontData.font()) & kCTFontTraitBold) ? FontWeightBold : FontWeightNormal); +#endif +} + +FontCascade::FontCascade(const FontCascade& other) + : m_fontDescription(other.m_fontDescription) + , m_fonts(other.m_fonts) + , m_weakPtrFactory(this) + , m_letterSpacing(other.m_letterSpacing) + , m_wordSpacing(other.m_wordSpacing) + , m_useBackslashAsYenSymbol(other.m_useBackslashAsYenSymbol) + , m_enableKerning(computeEnableKerning()) + , m_requiresShaping(computeRequiresShaping()) +{ +} + +FontCascade& FontCascade::operator=(const FontCascade& other) +{ + m_fontDescription = other.m_fontDescription; + m_fonts = other.m_fonts; + m_letterSpacing = other.m_letterSpacing; + m_wordSpacing = other.m_wordSpacing; + m_useBackslashAsYenSymbol = other.m_useBackslashAsYenSymbol; + m_enableKerning = other.m_enableKerning; + m_requiresShaping = other.m_requiresShaping; + return *this; +} + +bool FontCascade::operator==(const FontCascade& other) const +{ + if (isLoadingCustomFonts() || other.isLoadingCustomFonts()) + return false; + + if (m_fontDescription != other.m_fontDescription || m_letterSpacing != other.m_letterSpacing || m_wordSpacing != other.m_wordSpacing) + return false; + if (m_fonts == other.m_fonts) + return true; + if (!m_fonts || !other.m_fonts) + return false; + if (m_fonts->fontSelector() != other.m_fonts->fontSelector()) + return false; + // Can these cases actually somehow occur? All fonts should get wiped out by full style recalc. + if (m_fonts->fontSelectorVersion() != other.m_fonts->fontSelectorVersion()) + return false; + if (m_fonts->generation() != other.m_fonts->generation()) + return false; + return true; +} + +struct FontCascadeCacheKey { + FontDescriptionKey fontDescriptionKey; // Shared with the lower level FontCache (caching Font objects) + Vector<AtomicString, 3> families; + unsigned fontSelectorId; + unsigned fontSelectorVersion; +}; + +struct FontCascadeCacheEntry { + WTF_MAKE_FAST_ALLOCATED; +public: + FontCascadeCacheEntry(FontCascadeCacheKey&& key, Ref<FontCascadeFonts>&& fonts) + : key(WTFMove(key)) + , fonts(WTFMove(fonts)) + { } + FontCascadeCacheKey key; + Ref<FontCascadeFonts> fonts; +}; + +// FIXME: Should make hash traits for FontCascadeCacheKey instead of using a hash as the key (so we hash a hash). +typedef HashMap<unsigned, std::unique_ptr<FontCascadeCacheEntry>, AlreadyHashed> FontCascadeCache; + +static bool keysMatch(const FontCascadeCacheKey& a, const FontCascadeCacheKey& b) +{ + if (a.fontDescriptionKey != b.fontDescriptionKey) + return false; + if (a.fontSelectorId != b.fontSelectorId || a.fontSelectorVersion != b.fontSelectorVersion) + return false; + unsigned size = a.families.size(); + if (size != b.families.size()) + return false; + for (unsigned i = 0; i < size; ++i) { + if (!equalIgnoringASCIICase(a.families[i], b.families[i])) + return false; + } + return true; +} + +static FontCascadeCache& fontCascadeCache() +{ + static NeverDestroyed<FontCascadeCache> cache; + return cache.get(); +} + +void invalidateFontCascadeCache() +{ + fontCascadeCache().clear(); +} + +void clearWidthCaches() +{ + for (auto& value : fontCascadeCache().values()) + value->fonts.get().widthCache().clear(); +} + +static FontCascadeCacheKey makeFontCascadeCacheKey(const FontCascadeDescription& description, FontSelector* fontSelector) +{ + FontCascadeCacheKey key; + key.fontDescriptionKey = FontDescriptionKey(description); + unsigned familyCount = description.familyCount(); + key.families.reserveInitialCapacity(familyCount); + for (unsigned i = 0; i < familyCount; ++i) + key.families.uncheckedAppend(description.familyAt(i)); + key.fontSelectorId = fontSelector ? fontSelector->uniqueId() : 0; + key.fontSelectorVersion = fontSelector ? fontSelector->version() : 0; + return key; +} + +static unsigned computeFontCascadeCacheHash(const FontCascadeCacheKey& key) +{ + // FIXME: Should hash the key and the family name characters rather than making a hash out of other hashes. + IntegerHasher hasher; + hasher.add(key.fontDescriptionKey.computeHash()); + hasher.add(key.fontSelectorId); + hasher.add(key.fontSelectorVersion); + for (unsigned i = 0; i < key.families.size(); ++i) { + StringImpl* family = key.families[i].impl(); + hasher.add(family ? ASCIICaseInsensitiveHash::hash(family) : 0); + } + return hasher.hash(); +} + +void pruneUnreferencedEntriesFromFontCascadeCache() +{ + fontCascadeCache().removeIf([](auto& entry) { + return entry.value->fonts.get().hasOneRef(); + }); +} + +void pruneSystemFallbackFonts() +{ + for (auto& entry : fontCascadeCache().values()) + entry->fonts->pruneSystemFallbacks(); +} + +static Ref<FontCascadeFonts> retrieveOrAddCachedFonts(const FontCascadeDescription& fontDescription, RefPtr<FontSelector>&& fontSelector) +{ + auto key = makeFontCascadeCacheKey(fontDescription, fontSelector.get()); + + unsigned hash = computeFontCascadeCacheHash(key); + auto addResult = fontCascadeCache().add(hash, nullptr); + if (!addResult.isNewEntry && keysMatch(addResult.iterator->value->key, key)) + return addResult.iterator->value->fonts.get(); + + auto& newEntry = addResult.iterator->value; + newEntry = std::make_unique<FontCascadeCacheEntry>(WTFMove(key), FontCascadeFonts::create(WTFMove(fontSelector))); + Ref<FontCascadeFonts> glyphs = newEntry->fonts.get(); + + static const unsigned unreferencedPruneInterval = 50; + static const int maximumEntries = 400; + static unsigned pruneCounter; + // Referenced FontCascadeFonts would exist anyway so pruning them saves little memory. + if (!(++pruneCounter % unreferencedPruneInterval)) + pruneUnreferencedEntriesFromFontCascadeCache(); + // Prevent pathological growth. + if (fontCascadeCache().size() > maximumEntries) + fontCascadeCache().remove(fontCascadeCache().begin()); + return glyphs; +} + +void FontCascade::update(RefPtr<FontSelector>&& fontSelector) const +{ + m_fonts = retrieveOrAddCachedFonts(m_fontDescription, WTFMove(fontSelector)); + m_useBackslashAsYenSymbol = useBackslashAsYenSignForFamily(firstFamily()); + m_enableKerning = computeEnableKerning(); + m_requiresShaping = computeRequiresShaping(); +} + +float FontCascade::glyphBufferForTextRun(CodePath codePathToUse, const TextRun& run, unsigned from, unsigned to, GlyphBuffer& glyphBuffer) const +{ + if (codePathToUse != Complex) + return getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer); + return getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer); +} + +float FontCascade::drawText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, unsigned from, std::optional<unsigned> to, CustomFontNotReadyAction customFontNotReadyAction) const +{ + // Don't draw anything while we are using custom fonts that are in the process of loading, + // except if the 'force' argument is set to true (in which case it will use a fallback + // font). + if (isLoadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady) + return 0; + + unsigned destination = to.value_or(run.length()); + + CodePath codePathToUse = codePath(run); + // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 + if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length())) + codePathToUse = Complex; + + GlyphBuffer glyphBuffer; + float startX = point.x() + glyphBufferForTextRun(codePathToUse, run, from, destination, glyphBuffer); + // We couldn't generate any glyphs for the run. Give up. + if (glyphBuffer.isEmpty()) + return 0; + // Draw the glyph buffer now at the starting point returned in startX. + FloatPoint startPoint(startX, point.y()); + drawGlyphBuffer(context, glyphBuffer, startPoint); + return startPoint.x() - startX; +} + +void FontCascade::drawEmphasisMarks(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, std::optional<unsigned> to) const +{ + if (isLoadingCustomFonts()) + return; + + unsigned destination = to.value_or(run.length()); + + CodePath codePathToUse = codePath(run); + // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 + if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length())) + codePathToUse = Complex; + + if (codePathToUse != Complex) + drawEmphasisMarksForSimpleText(context, run, mark, point, from, destination); + else + drawEmphasisMarksForComplexText(context, run, mark, point, from, destination); +} + +float FontCascade::width(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +{ + CodePath codePathToUse = codePath(run); + if (codePathToUse != Complex) { + // The complex path is more restrictive about returning fallback fonts than the simple path, so we need an explicit test to make their behaviors match. + if (!canReturnFallbackFontsForComplexText()) + fallbackFonts = nullptr; + // The simple path can optimize the case where glyph overflow is not observable. + if (codePathToUse != SimpleWithGlyphOverflow && (glyphOverflow && !glyphOverflow->computeBounds)) + glyphOverflow = nullptr; + } + + bool hasWordSpacingOrLetterSpacing = wordSpacing() || letterSpacing(); + float* cacheEntry = m_fonts->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), enableKerning() || requiresShaping(), hasWordSpacingOrLetterSpacing, glyphOverflow); + if (cacheEntry && !std::isnan(*cacheEntry)) + return *cacheEntry; + + HashSet<const Font*> localFallbackFonts; + if (!fallbackFonts) + fallbackFonts = &localFallbackFonts; + + float result; + if (codePathToUse == Complex) + result = floatWidthForComplexText(run, fallbackFonts, glyphOverflow); + else + result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow); + + if (cacheEntry && fallbackFonts->isEmpty()) + *cacheEntry = result; + return result; +} + +float FontCascade::widthForSimpleText(StringView text) const +{ + if (text.isNull() || text.isEmpty()) + return 0; + ASSERT(codePath(TextRun(text)) != FontCascade::Complex); + float* cacheEntry = m_fonts->widthCache().add(text, std::numeric_limits<float>::quiet_NaN()); + if (cacheEntry && !std::isnan(*cacheEntry)) + return *cacheEntry; + + Vector<GlyphBufferGlyph, 16> glyphs; + Vector<GlyphBufferAdvance, 16> advances; + bool hasKerningOrLigatures = enableKerning() || requiresShaping(); + float runWidth = 0; + auto& font = primaryFont(); + for (unsigned i = 0; i < text.length(); ++i) { + auto glyph = glyphDataForCharacter(text[i], false).glyph; + auto glyphWidth = font.widthForGlyph(glyph); + runWidth += glyphWidth; + if (!hasKerningOrLigatures) + continue; +#if USE(CAIRO) + cairo_glyph_t cairoGlyph; + cairoGlyph.index = glyph; + glyphs.append(cairoGlyph); +#else + glyphs.append(glyph); +#endif + advances.append(FloatSize(glyphWidth, 0)); + } + if (hasKerningOrLigatures) { + font.applyTransforms(&glyphs[0], &advances[0], glyphs.size(), enableKerning(), requiresShaping()); + // This is needed only to match the result of the slow path. Same glyph widths but different floating point arithmentics can + // produce different run width. + float runWidthDifferenceWithTransformApplied = -runWidth; + for (auto& advance : advances) + runWidthDifferenceWithTransformApplied += advance.width(); + runWidth += runWidthDifferenceWithTransformApplied; + } + + if (cacheEntry) + *cacheEntry = runWidth; + return runWidth; +} + +GlyphData FontCascade::glyphDataForCharacter(UChar32 c, bool mirror, FontVariant variant) const +{ + if (variant == AutoVariant) { + if (m_fontDescription.variantCaps() == FontVariantCaps::Small) { + UChar32 upperC = u_toupper(c); + if (upperC != c) { + c = upperC; + variant = SmallCapsVariant; + } else + variant = NormalVariant; + } else + variant = NormalVariant; + } + + if (mirror) + c = u_charMirror(c); + + return m_fonts->glyphDataForCharacter(c, m_fontDescription, variant); +} + +static const char* fontFamiliesWithInvalidCharWidth[] = { + "American Typewriter", + "Arial Hebrew", + "Chalkboard", + "Cochin", + "Corsiva Hebrew", + "Courier", + "Euphemia UCAS", + "Geneva", + "Gill Sans", + "Hei", + "Helvetica", + "Hoefler Text", + "InaiMathi", + "Kai", + "Lucida Grande", + "Marker Felt", + "Monaco", + "Mshtakan", + "New Peninim MT", + "Osaka", + "Raanana", + "STHeiti", + "Symbol", + "Times", + "Apple Braille", + "Apple LiGothic", + "Apple LiSung", + "Apple Symbols", + "AppleGothic", + "AppleMyungjo", + "#GungSeo", + "#HeadLineA", + "#PCMyungjo", + "#PilGi", +}; + +// For font families where any of the fonts don't have a valid entry in the OS/2 table +// for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth +// from the width of a '0'. This only seems to apply to a fixed number of Mac fonts, +// but, in order to get similar rendering across platforms, we do this check for +// all platforms. +bool FontCascade::hasValidAverageCharWidth() const +{ + AtomicString family = firstFamily(); + if (family.isEmpty()) + return false; + +#if PLATFORM(MAC) || PLATFORM(IOS) + // Internal fonts on OS X and iOS also have an invalid entry in the table for avgCharWidth. + if (primaryFontIsSystemFont()) + return false; +#endif + + static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0; + + if (!fontFamiliesWithInvalidCharWidthMap) { + fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i) + fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i])); + } + + return !fontFamiliesWithInvalidCharWidthMap->contains(family); +} + +bool FontCascade::fastAverageCharWidthIfAvailable(float& width) const +{ + bool success = hasValidAverageCharWidth(); + if (success) + width = roundf(primaryFont().avgCharWidth()); // FIXME: primaryFont() might not correspond to firstFamily(). + return success; +} + +void FontCascade::adjustSelectionRectForText(const TextRun& run, LayoutRect& selectionRect, unsigned from, std::optional<unsigned> to) const +{ + unsigned destination = to.value_or(run.length()); + + CodePath codePathToUse = codePath(run); + // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 + if (codePathToUse != Complex && (enableKerning() || requiresShaping()) && (from || destination != run.length())) + codePathToUse = Complex; + + if (codePathToUse != Complex) + return adjustSelectionRectForSimpleText(run, selectionRect, from, destination); + + return adjustSelectionRectForComplexText(run, selectionRect, from, destination); +} + +int FontCascade::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const +{ + // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 + if (codePath(run) != Complex && (!(enableKerning() || requiresShaping()))) + return offsetForPositionForSimpleText(run, x, includePartialGlyphs); + + return offsetForPositionForComplexText(run, x, includePartialGlyphs); +} + +template <typename CharacterType> +static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length) +{ + StringBuilder normalized; + normalized.reserveCapacity(length); + + for (unsigned i = 0; i < length; ++i) + normalized.append(FontCascade::normalizeSpaces(characters[i])); + + return normalized.toString(); +} + +String FontCascade::normalizeSpaces(const LChar* characters, unsigned length) +{ + return normalizeSpacesInternal(characters, length); +} + +String FontCascade::normalizeSpaces(const UChar* characters, unsigned length) +{ + return normalizeSpacesInternal(characters, length); +} + +static bool shouldUseFontSmoothing = true; + +void FontCascade::setShouldUseSmoothing(bool shouldUseSmoothing) +{ + ASSERT(isMainThread()); + shouldUseFontSmoothing = shouldUseSmoothing; +} + +bool FontCascade::shouldUseSmoothing() +{ + return shouldUseFontSmoothing; +} + +void FontCascade::setCodePath(CodePath p) +{ + s_codePath = p; +} + +FontCascade::CodePath FontCascade::codePath() +{ + return s_codePath; +} + +FontCascade::CodePath FontCascade::codePath(const TextRun& run) const +{ + if (s_codePath != Auto) + return s_codePath; + +#if PLATFORM(COCOA) + // Because Font::applyTransforms() doesn't know which features to enable/disable in the simple code path, it can't properly handle feature or variant settings. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=150791: @font-face features should also cause this to be complex. + if (m_fontDescription.featureSettings().size() > 0 || !m_fontDescription.variantSettings().isAllNormal()) + return Complex; + +#else + + if (run.length() > 1 && (enableKerning() || requiresShaping())) + return Complex; +#endif + + if (!run.characterScanForCodePath()) + return Simple; + + if (run.is8Bit()) + return Simple; + + // Start from 0 since drawing and highlighting also measure the characters before run->from. + return characterRangeCodePath(run.characters16(), run.length()); +} + +FontCascade::CodePath FontCascade::characterRangeCodePath(const UChar* characters, unsigned len) +{ + // FIXME: Should use a UnicodeSet in ports where ICU is used. Note that we + // can't simply use UnicodeCharacter Property/class because some characters + // are not 'combining', but still need to go to the complex path. + // Alternatively, we may as well consider binary search over a sorted + // list of ranges. + CodePath result = Simple; + bool previousCharacterIsEmojiGroupCandidate = false; + for (unsigned i = 0; i < len; i++) { + const UChar c = characters[i]; + if (c == zeroWidthJoiner && previousCharacterIsEmojiGroupCandidate) + return Complex; + + previousCharacterIsEmojiGroupCandidate = false; + if (c < 0x2E5) // U+02E5 through U+02E9 (Modifier Letters : Tone letters) + continue; + if (c <= 0x2E9) + return Complex; + + if (c < 0x300) // U+0300 through U+036F Combining diacritical marks + continue; + if (c <= 0x36F) + return Complex; + + if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha + continue; + if (c <= 0x05CF) + return Complex; + + // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic, + // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, + // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar + if (c < 0x0600) + continue; + if (c <= 0x109F) + return Complex; + + // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; + // Modern Korean will be precomposed as a result of step A) + if (c < 0x1100) + continue; + if (c <= 0x11FF) + return Complex; + + if (c < 0x135D) // U+135D through U+135F Ethiopic combining marks + continue; + if (c <= 0x135F) + return Complex; + + if (c < 0x1700) // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian + continue; + if (c <= 0x18AF) + return Complex; + + if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) + continue; + if (c <= 0x194F) + return Complex; + + if (c < 0x1980) // U+1980 through U+19DF New Tai Lue + continue; + if (c <= 0x19DF) + return Complex; + + if (c < 0x1A00) // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic + continue; + if (c <= 0x1CFF) + return Complex; + + if (c < 0x1DC0) // U+1DC0 through U+1DFF Comining diacritical mark supplement + continue; + if (c <= 0x1DFF) + return Complex; + + // U+1E00 through U+2000 characters with diacritics and stacked diacritics + if (c <= 0x2000) { + result = SimpleWithGlyphOverflow; + continue; + } + + if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols + continue; + if (c <= 0x20FF) + return Complex; + + if (c < 0x26F9) + continue; + if (c < 0x26FA) + return Complex; + + if (c < 0x2CEF) // U+2CEF through U+2CF1 Combining marks for Coptic + continue; + if (c <= 0x2CF1) + return Complex; + + if (c < 0x302A) // U+302A through U+302F Ideographic and Hangul Tone marks + continue; + if (c <= 0x302F) + return Complex; + + if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic + continue; + if (c <= 0xA67D) + return Complex; + + if (c < 0xA6F0) // U+A6F0 through U+A6F1 Combining mark for Bamum + continue; + if (c <= 0xA6F1) + return Complex; + + // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended, + // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek, + if (c < 0xA800) + continue; + if (c <= 0xABFF) + return Complex; + + if (c < 0xD7B0) // U+D7B0 through U+D7FF Hangul Jamo Ext. B + continue; + if (c <= 0xD7FF) + return Complex; + + if (c <= 0xDBFF) { + // High surrogate + + if (i == len - 1) + continue; + + UChar next = characters[++i]; + if (!U16_IS_TRAIL(next)) + continue; + + UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next); + + if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols + continue; + if (supplementaryCharacter <= 0x1F1FF) + return Complex; + + if (isEmojiGroupCandidate(supplementaryCharacter)) { + previousCharacterIsEmojiGroupCandidate = true; + continue; + } + if (isEmojiFitzpatrickModifier(supplementaryCharacter)) + return Complex; + if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors. + continue; + if (supplementaryCharacter <= 0xE01EF) + return Complex; + + // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts + // in plane 1 or higher. + + continue; + } + + if (c < 0xFE00) // U+FE00 through U+FE0F Unicode variation selectors + continue; + if (c <= 0xFE0F) + return Complex; + + if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks + continue; + if (c <= 0xFE2F) + return Complex; + } + return result; +} + +bool FontCascade::isCJKIdeograph(UChar32 c) +{ + // The basic CJK Unified Ideographs block. + if (c >= 0x4E00 && c <= 0x9FFF) + return true; + + // CJK Unified Ideographs Extension A. + if (c >= 0x3400 && c <= 0x4DBF) + return true; + + // CJK Radicals Supplement. + if (c >= 0x2E80 && c <= 0x2EFF) + return true; + + // Kangxi Radicals. + if (c >= 0x2F00 && c <= 0x2FDF) + return true; + + // CJK Strokes. + if (c >= 0x31C0 && c <= 0x31EF) + return true; + + // CJK Compatibility Ideographs. + if (c >= 0xF900 && c <= 0xFAFF) + return true; + + // CJK Unified Ideographs Extension B. + if (c >= 0x20000 && c <= 0x2A6DF) + return true; + + // CJK Unified Ideographs Extension C. + if (c >= 0x2A700 && c <= 0x2B73F) + return true; + + // CJK Unified Ideographs Extension D. + if (c >= 0x2B740 && c <= 0x2B81F) + return true; + + // CJK Compatibility Ideographs Supplement. + if (c >= 0x2F800 && c <= 0x2FA1F) + return true; + + return false; +} + +bool FontCascade::isCJKIdeographOrSymbol(UChar32 c) +{ + // 0x2C7 Caron, Mandarin Chinese 3rd Tone + // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone + // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone + // 0x2D9 Dot Above, Mandarin Chinese 5th Tone + if ((c == 0x2C7) || (c == 0x2CA) || (c == 0x2CB) || (c == 0x2D9)) + return true; + + if ((c == 0x2020) || (c == 0x2021) || (c == 0x2030) || (c == 0x203B) || (c == 0x203C) + || (c == 0x2042) || (c == 0x2047) || (c == 0x2048) || (c == 0x2049) || (c == 0x2051) + || (c == 0x20DD) || (c == 0x20DE) || (c == 0x2100) || (c == 0x2103) || (c == 0x2105) + || (c == 0x2109) || (c == 0x210A) || (c == 0x2113) || (c == 0x2116) || (c == 0x2121) + || (c == 0x212B) || (c == 0x213B) || (c == 0x2150) || (c == 0x2151) || (c == 0x2152)) + return true; + + if (c >= 0x2156 && c <= 0x215A) + return true; + + if (c >= 0x2160 && c <= 0x216B) + return true; + + if (c >= 0x2170 && c <= 0x217B) + return true; + + if ((c == 0x217F) || (c == 0x2189) || (c == 0x2307) || (c == 0x2312) || (c == 0x23BE) || (c == 0x23BF)) + return true; + + if (c >= 0x23C0 && c <= 0x23CC) + return true; + + if ((c == 0x23CE) || (c == 0x2423)) + return true; + + if (c >= 0x2460 && c <= 0x2492) + return true; + + if (c >= 0x249C && c <= 0x24FF) + return true; + + if ((c == 0x25A0) || (c == 0x25A1) || (c == 0x25A2) || (c == 0x25AA) || (c == 0x25AB)) + return true; + + if ((c == 0x25B1) || (c == 0x25B2) || (c == 0x25B3) || (c == 0x25B6) || (c == 0x25B7) || (c == 0x25BC) || (c == 0x25BD)) + return true; + + if ((c == 0x25C0) || (c == 0x25C1) || (c == 0x25C6) || (c == 0x25C7) || (c == 0x25C9) || (c == 0x25CB) || (c == 0x25CC)) + return true; + + if (c >= 0x25CE && c <= 0x25D3) + return true; + + if (c >= 0x25E2 && c <= 0x25E6) + return true; + + if (c == 0x25EF) + return true; + + if (c >= 0x2600 && c <= 0x2603) + return true; + + if ((c == 0x2605) || (c == 0x2606) || (c == 0x260E) || (c == 0x2616) || (c == 0x2617) || (c == 0x2640) || (c == 0x2642)) + return true; + + if (c >= 0x2660 && c <= 0x266F) + return true; + + if (c >= 0x2672 && c <= 0x267D) + return true; + + if ((c == 0x26A0) || (c == 0x26BD) || (c == 0x26BE) || (c == 0x2713) || (c == 0x271A) || (c == 0x273F) || (c == 0x2740) || (c == 0x2756)) + return true; + + if (c >= 0x2776 && c <= 0x277F) + return true; + + if (c == 0x2B1A) + return true; + + // Ideographic Description Characters. + if (c >= 0x2FF0 && c <= 0x2FFF) + return true; + + // CJK Symbols and Punctuation, excluding 0x3030. + if (c >= 0x3000 && c < 0x3030) + return true; + + if (c > 0x3030 && c <= 0x303F) + return true; + + // Hiragana + if (c >= 0x3040 && c <= 0x309F) + return true; + + // Katakana + if (c >= 0x30A0 && c <= 0x30FF) + return true; + + // Bopomofo + if (c >= 0x3100 && c <= 0x312F) + return true; + + if (c >= 0x3190 && c <= 0x319F) + return true; + + // Bopomofo Extended + if (c >= 0x31A0 && c <= 0x31BF) + return true; + + // Enclosed CJK Letters and Months. + if (c >= 0x3200 && c <= 0x32FF) + return true; + + // CJK Compatibility. + if (c >= 0x3300 && c <= 0x33FF) + return true; + + if (c >= 0xF860 && c <= 0xF862) + return true; + + // CJK Compatibility Forms. + if (c >= 0xFE30 && c <= 0xFE4F) + return true; + + if ((c == 0xFE10) || (c == 0xFE11) || (c == 0xFE12) || (c == 0xFE19)) + return true; + + if ((c == 0xFF0D) || (c == 0xFF1B) || (c == 0xFF1C) || (c == 0xFF1E)) + return false; + + // Halfwidth and Fullwidth Forms + // Usually only used in CJK + if (c >= 0xFF00 && c <= 0xFFEF) + return true; + + // Emoji. + if (c == 0x1F100) + return true; + + if (c >= 0x1F110 && c <= 0x1F129) + return true; + + if (c >= 0x1F130 && c <= 0x1F149) + return true; + + if (c >= 0x1F150 && c <= 0x1F169) + return true; + + if (c >= 0x1F170 && c <= 0x1F189) + return true; + + if (c >= 0x1F200 && c <= 0x1F6C5) + return true; + + return isCJKIdeograph(c); +} + +std::pair<unsigned, bool> FontCascade::expansionOpportunityCountInternal(const LChar* characters, unsigned length, TextDirection direction, ExpansionBehavior expansionBehavior) +{ + unsigned count = 0; + bool isAfterExpansion = (expansionBehavior & LeadingExpansionMask) == ForbidLeadingExpansion; + if ((expansionBehavior & LeadingExpansionMask) == ForceLeadingExpansion) { + ++count; + isAfterExpansion = true; + } + if (direction == LTR) { + for (unsigned i = 0; i < length; ++i) { + if (treatAsSpace(characters[i])) { + count++; + isAfterExpansion = true; + } else + isAfterExpansion = false; + } + } else { + for (unsigned i = length; i > 0; --i) { + if (treatAsSpace(characters[i - 1])) { + count++; + isAfterExpansion = true; + } else + isAfterExpansion = false; + } + } + if (!isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForceTrailingExpansion) { + ++count; + isAfterExpansion = true; + } else if (isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForbidTrailingExpansion) { + ASSERT(count); + --count; + isAfterExpansion = false; + } + return std::make_pair(count, isAfterExpansion); +} + +std::pair<unsigned, bool> FontCascade::expansionOpportunityCountInternal(const UChar* characters, unsigned length, TextDirection direction, ExpansionBehavior expansionBehavior) +{ + static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText(); + unsigned count = 0; + bool isAfterExpansion = (expansionBehavior & LeadingExpansionMask) == ForbidLeadingExpansion; + if ((expansionBehavior & LeadingExpansionMask) == ForceLeadingExpansion) { + ++count; + isAfterExpansion = true; + } + if (direction == LTR) { + for (unsigned i = 0; i < length; ++i) { + UChar32 character = characters[i]; + if (treatAsSpace(character)) { + count++; + isAfterExpansion = true; + continue; + } + if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) { + character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); + i++; + } + if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { + if (!isAfterExpansion) + count++; + count++; + isAfterExpansion = true; + continue; + } + isAfterExpansion = false; + } + } else { + for (unsigned i = length; i > 0; --i) { + UChar32 character = characters[i - 1]; + if (treatAsSpace(character)) { + count++; + isAfterExpansion = true; + continue; + } + if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) { + character = U16_GET_SUPPLEMENTARY(characters[i - 2], character); + i--; + } + if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { + if (!isAfterExpansion) + count++; + count++; + isAfterExpansion = true; + continue; + } + isAfterExpansion = false; + } + } + if (!isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForceTrailingExpansion) { + ++count; + isAfterExpansion = true; + } else if (isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForbidTrailingExpansion) { + ASSERT(count); + --count; + isAfterExpansion = false; + } + return std::make_pair(count, isAfterExpansion); +} + +std::pair<unsigned, bool> FontCascade::expansionOpportunityCount(const StringView& stringView, TextDirection direction, ExpansionBehavior expansionBehavior) +{ + // For each character, iterating from left to right: + // If it is recognized as a space, insert an opportunity after it + // If it is an ideograph, insert one opportunity before it and one opportunity after it + // Do this such a way so that there are not two opportunities next to each other. + if (stringView.is8Bit()) + return expansionOpportunityCountInternal(stringView.characters8(), stringView.length(), direction, expansionBehavior); + return expansionOpportunityCountInternal(stringView.characters16(), stringView.length(), direction, expansionBehavior); +} + +bool FontCascade::leadingExpansionOpportunity(const StringView& stringView, TextDirection direction) +{ + if (!stringView.length()) + return false; + + UChar32 initialCharacter; + if (direction == LTR) { + initialCharacter = stringView[0]; + if (U16_IS_LEAD(initialCharacter) && stringView.length() > 1 && U16_IS_TRAIL(stringView[1])) + initialCharacter = U16_GET_SUPPLEMENTARY(initialCharacter, stringView[1]); + } else { + initialCharacter = stringView[stringView.length() - 1]; + if (U16_IS_TRAIL(initialCharacter) && stringView.length() > 1 && U16_IS_LEAD(stringView[stringView.length() - 2])) + initialCharacter = U16_GET_SUPPLEMENTARY(stringView[stringView.length() - 2], initialCharacter); + } + + return canExpandAroundIdeographsInComplexText() && isCJKIdeographOrSymbol(initialCharacter); +} + +bool FontCascade::trailingExpansionOpportunity(const StringView& stringView, TextDirection direction) +{ + if (!stringView.length()) + return false; + + UChar32 finalCharacter; + if (direction == LTR) { + finalCharacter = stringView[stringView.length() - 1]; + if (U16_IS_TRAIL(finalCharacter) && stringView.length() > 1 && U16_IS_LEAD(stringView[stringView.length() - 2])) + finalCharacter = U16_GET_SUPPLEMENTARY(stringView[stringView.length() - 2], finalCharacter); + } else { + finalCharacter = stringView[0]; + if (U16_IS_LEAD(finalCharacter) && stringView.length() > 1 && U16_IS_TRAIL(stringView[1])) + finalCharacter = U16_GET_SUPPLEMENTARY(finalCharacter, stringView[1]); + } + + return treatAsSpace(finalCharacter) || (canExpandAroundIdeographsInComplexText() && isCJKIdeographOrSymbol(finalCharacter)); +} + +bool FontCascade::canReceiveTextEmphasis(UChar32 c) +{ + if (U_GET_GC_MASK(c) & (U_GC_Z_MASK | U_GC_CN_MASK | U_GC_CC_MASK | U_GC_CF_MASK)) + return false; + + // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010. + if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot + || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar) + return false; + + return true; +} + +bool FontCascade::isLoadingCustomFonts() const +{ + return m_fonts && m_fonts->isLoadingCustomFonts(); +} + +GlyphToPathTranslator::GlyphUnderlineType computeUnderlineType(const TextRun& textRun, const GlyphBuffer& glyphBuffer, unsigned index) +{ + // In general, we want to skip descenders. However, skipping descenders on CJK characters leads to undesirable renderings, + // so we want to draw through CJK characters (on a character-by-character basis). + UChar32 baseCharacter; + unsigned offsetInString = glyphBuffer.offsetInString(index); + + if (offsetInString == GlyphBuffer::noOffset || offsetInString >= textRun.length()) { + // We have no idea which character spawned this glyph. Bail. + ASSERT_WITH_SECURITY_IMPLICATION(offsetInString < textRun.length()); + return GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph; + } + + if (textRun.is8Bit()) + baseCharacter = textRun.characters8()[offsetInString]; + else + U16_NEXT(textRun.characters16(), offsetInString, textRun.length(), baseCharacter); + + // u_getIntPropertyValue with UCHAR_IDEOGRAPHIC doesn't return true for Japanese or Korean codepoints. + // Instead, we can use the "Unicode allocation block" for the character. + UBlockCode blockCode = ublock_getCode(baseCharacter); + switch (blockCode) { + case UBLOCK_CJK_RADICALS_SUPPLEMENT: + case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: + case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS: + case UBLOCK_CJK_COMPATIBILITY: + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A: + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS: + case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS: + case UBLOCK_CJK_COMPATIBILITY_FORMS: + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B: + case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT: + case UBLOCK_CJK_STROKES: + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C: + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D: + case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS: + case UBLOCK_LINEAR_B_IDEOGRAMS: + case UBLOCK_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT: + case UBLOCK_HIRAGANA: + case UBLOCK_KATAKANA: + case UBLOCK_BOPOMOFO: + case UBLOCK_BOPOMOFO_EXTENDED: + case UBLOCK_HANGUL_JAMO: + case UBLOCK_HANGUL_COMPATIBILITY_JAMO: + case UBLOCK_HANGUL_SYLLABLES: + case UBLOCK_HANGUL_JAMO_EXTENDED_A: + case UBLOCK_HANGUL_JAMO_EXTENDED_B: + return GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph; + default: + return GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders; + } +} + +// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the +// standard emphasis marks do so. +std::optional<GlyphData> FontCascade::getEmphasisMarkGlyphData(const AtomicString& mark) const +{ + if (mark.isEmpty()) + return std::nullopt; + + UChar32 character; + if (!mark.is8Bit()) { + SurrogatePairAwareTextIterator iterator(mark.characters16(), 0, mark.length(), mark.length()); + unsigned clusterLength; + if (!iterator.consume(character, clusterLength)) + return std::nullopt; + } else + character = mark[0]; + + std::optional<GlyphData> glyphData(glyphDataForCharacter(character, false, EmphasisMarkVariant)); + return glyphData.value().isValid() ? glyphData : std::nullopt; +} + +int FontCascade::emphasisMarkAscent(const AtomicString& mark) const +{ + std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark); + if (!markGlyphData) + return 0; + + const Font* markFontData = markGlyphData.value().font; + ASSERT(markFontData); + if (!markFontData) + return 0; + + return markFontData->fontMetrics().ascent(); +} + +int FontCascade::emphasisMarkDescent(const AtomicString& mark) const +{ + std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark); + if (!markGlyphData) + return 0; + + const Font* markFontData = markGlyphData.value().font; + ASSERT(markFontData); + if (!markFontData) + return 0; + + return markFontData->fontMetrics().descent(); +} + +int FontCascade::emphasisMarkHeight(const AtomicString& mark) const +{ + std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark); + if (!markGlyphData) + return 0; + + const Font* markFontData = markGlyphData.value().font; + ASSERT(markFontData); + if (!markFontData) + return 0; + + return markFontData->fontMetrics().height(); +} + +float FontCascade::getGlyphsAndAdvancesForSimpleText(const TextRun& run, unsigned from, unsigned to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const +{ + float initialAdvance; + + WidthIterator it(this, run, 0, false, forTextEmphasis); + // FIXME: Using separate glyph buffers for the prefix and the suffix is incorrect when kerning or + // ligatures are enabled. + GlyphBuffer localGlyphBuffer; + it.advance(from, &localGlyphBuffer); + float beforeWidth = it.m_runWidthSoFar; + it.advance(to, &glyphBuffer); + + if (glyphBuffer.isEmpty()) + return 0; + + float afterWidth = it.m_runWidthSoFar; + + if (run.rtl()) { + float finalRoundingWidth = it.m_finalRoundingWidth; + it.advance(run.length(), &localGlyphBuffer); + initialAdvance = finalRoundingWidth + it.m_runWidthSoFar - afterWidth; + } else + initialAdvance = beforeWidth; + + if (run.rtl()) + glyphBuffer.reverse(0, glyphBuffer.size()); + + return initialAdvance; +} + +void FontCascade::drawEmphasisMarksForSimpleText(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, unsigned to) const +{ + GlyphBuffer glyphBuffer; + float initialAdvance = getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer, ForTextEmphasis); + + if (glyphBuffer.isEmpty()) + return; + + drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y())); +} + +void FontCascade::drawGlyphBuffer(GraphicsContext& context, const GlyphBuffer& glyphBuffer, FloatPoint& point) const +{ + // Draw each contiguous run of glyphs that use the same font data. + const Font* fontData = glyphBuffer.fontAt(0); + FloatSize offset = glyphBuffer.offsetAt(0); + FloatPoint startPoint(point.x(), point.y() - glyphBuffer.initialAdvance().height()); + float nextX = startPoint.x() + glyphBuffer.advanceAt(0).width(); + float nextY = startPoint.y() + glyphBuffer.advanceAt(0).height(); + unsigned lastFrom = 0; + unsigned nextGlyph = 1; + while (nextGlyph < glyphBuffer.size()) { + const Font* nextFontData = glyphBuffer.fontAt(nextGlyph); + FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph); + + if (nextFontData != fontData || nextOffset != offset) { + context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); + + lastFrom = nextGlyph; + fontData = nextFontData; + offset = nextOffset; + startPoint.setX(nextX); + startPoint.setY(nextY); + } + nextX += glyphBuffer.advanceAt(nextGlyph).width(); + nextY += glyphBuffer.advanceAt(nextGlyph).height(); + nextGlyph++; + } + + context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); + point.setX(nextX); +} + +inline static float offsetToMiddleOfGlyph(const Font* fontData, Glyph glyph) +{ + if (fontData->platformData().orientation() == Horizontal) { + FloatRect bounds = fontData->boundsForGlyph(glyph); + return bounds.x() + bounds.width() / 2; + } + // FIXME: Use glyph bounds once they make sense for vertical fonts. + return fontData->widthForGlyph(glyph) / 2; +} + +inline static float offsetToMiddleOfGlyphAtIndex(const GlyphBuffer& glyphBuffer, unsigned i) +{ + return offsetToMiddleOfGlyph(glyphBuffer.fontAt(i), glyphBuffer.glyphAt(i)); +} + +void FontCascade::drawEmphasisMarks(GraphicsContext& context, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const +{ + std::optional<GlyphData> markGlyphData = getEmphasisMarkGlyphData(mark); + if (!markGlyphData) + return; + + const Font* markFontData = markGlyphData.value().font; + ASSERT(markFontData); + if (!markFontData) + return; + + Glyph markGlyph = markGlyphData.value().glyph; + Glyph spaceGlyph = markFontData->spaceGlyph(); + + float middleOfLastGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, 0); + FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y()); + + GlyphBuffer markBuffer; + for (unsigned i = 0; i + 1 < glyphBuffer.size(); ++i) { + float middleOfNextGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, i + 1); + float advance = glyphBuffer.advanceAt(i).width() - middleOfLastGlyph + middleOfNextGlyph; + markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance); + middleOfLastGlyph = middleOfNextGlyph; + } + markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0); + + drawGlyphBuffer(context, markBuffer, startPoint); +} + +float FontCascade::floatWidthForSimpleText(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +{ + WidthIterator it(this, run, fallbackFonts, glyphOverflow); + GlyphBuffer glyphBuffer; + it.advance(run.length(), (enableKerning() || requiresShaping()) ? &glyphBuffer : nullptr); + + if (glyphOverflow) { + glyphOverflow->top = std::max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent())); + glyphOverflow->bottom = std::max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent())); + glyphOverflow->left = ceilf(it.firstGlyphOverflow()); + glyphOverflow->right = ceilf(it.lastGlyphOverflow()); + } + + return it.m_runWidthSoFar; +} + +void FontCascade::adjustSelectionRectForSimpleText(const TextRun& run, LayoutRect& selectionRect, unsigned from, unsigned to) const +{ + GlyphBuffer glyphBuffer; + WidthIterator it(this, run); + it.advance(from, &glyphBuffer); + float beforeWidth = it.m_runWidthSoFar; + it.advance(to, &glyphBuffer); + float afterWidth = it.m_runWidthSoFar; + float totalWidth = -1; + + if (run.rtl()) { + it.advance(run.length(), &glyphBuffer); + totalWidth = it.m_runWidthSoFar; + selectionRect.move(totalWidth - afterWidth, 0); + } else + selectionRect.move(beforeWidth, 0); + selectionRect.setWidth(LayoutUnit::fromFloatCeil(afterWidth - beforeWidth)); +} + +int FontCascade::offsetForPositionForSimpleText(const TextRun& run, float x, bool includePartialGlyphs) const +{ + float delta = x; + + WidthIterator it(this, run); + GlyphBuffer localGlyphBuffer; + unsigned offset; + if (run.rtl()) { + delta -= floatWidthForSimpleText(run); + while (1) { + offset = it.m_currentCharacter; + float w; + if (!it.advanceOneCharacter(w, localGlyphBuffer)) + break; + delta += w; + if (includePartialGlyphs) { + if (delta - w / 2 >= 0) + break; + } else { + if (delta >= 0) + break; + } + } + } else { + while (1) { + offset = it.m_currentCharacter; + float w; + if (!it.advanceOneCharacter(w, localGlyphBuffer)) + break; + delta -= w; + if (includePartialGlyphs) { + if (delta + w / 2 <= 0) + break; + } else { + if (delta <= 0) + break; + } + } + } + + return offset; +} + +#if !PLATFORM(COCOA) +// FIXME: Unify this with the macOS and iOS implementation. +const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characters, size_t length) const +{ + UChar32 baseCharacter; + size_t baseCharacterLength = 0; + U16_NEXT(characters, baseCharacterLength, length, baseCharacter); + GlyphData baseCharacterGlyphData = glyphDataForCharacter(baseCharacter, false, NormalVariant); + + if (!baseCharacterGlyphData.glyph) + return nullptr; + return baseCharacterGlyphData.font; +} +#endif + +void FontCascade::drawEmphasisMarksForComplexText(GraphicsContext& context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, unsigned to) const +{ + GlyphBuffer glyphBuffer; + float initialAdvance = getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer, ForTextEmphasis); + + if (glyphBuffer.isEmpty()) + return; + + drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y())); +} + +} diff --git a/Source/WebCore/platform/graphics/FontCascade.h b/Source/WebCore/platform/graphics/FontCascade.h new file mode 100644 index 000000000..92d878620 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontCascade.h @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006, 2007, 2010, 2011-2016 Apple Inc. All rights reserved. + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef FontCascade_h +#define FontCascade_h + +#include "DashArray.h" +#include "Font.h" +#include "FontCascadeFonts.h" +#include "FontDescription.h" +#include "Path.h" +#include "TextFlags.h" +#include <wtf/HashSet.h> +#include <wtf/Optional.h> +#include <wtf/WeakPtr.h> +#include <wtf/unicode/CharacterNames.h> + +// "X11/X.h" defines Complex to 0 and conflicts +// with Complex value in CodePath enum. +#ifdef Complex +#undef Complex +#endif + +namespace WebCore { + +class FloatPoint; +class FloatRect; +class FontData; +class FontMetrics; +class FontPlatformData; +class FontSelector; +class GlyphBuffer; +class GraphicsContext; +class LayoutRect; +class RenderText; +class TextLayout; +class TextRun; + +struct GlyphData; + +struct GlyphOverflow { + GlyphOverflow() + : left(0) + , right(0) + , top(0) + , bottom(0) + , computeBounds(false) + { + } + + inline bool isEmpty() + { + return !left && !right && !top && !bottom; + } + + inline void extendTo(const GlyphOverflow& other) + { + left = std::max(left, other.left); + right = std::max(right, other.right); + top = std::max(top, other.top); + bottom = std::max(bottom, other.bottom); + } + + bool operator!=(const GlyphOverflow& other) + { + return left != other.left || right != other.right || top != other.top || bottom != other.bottom; + } + + int left; + int right; + int top; + int bottom; + bool computeBounds; +}; + +class GlyphToPathTranslator { +public: + enum class GlyphUnderlineType {SkipDescenders, SkipGlyph, DrawOverGlyph}; + virtual bool containsMorePaths() = 0; + virtual Path path() = 0; + virtual std::pair<float, float> extents() = 0; + virtual GlyphUnderlineType underlineType() = 0; + virtual void advance() = 0; + virtual ~GlyphToPathTranslator() { } +}; +GlyphToPathTranslator::GlyphUnderlineType computeUnderlineType(const TextRun&, const GlyphBuffer&, unsigned index); + +class TextLayoutDeleter { +public: + void operator()(TextLayout*) const; +}; + +class FontCascade { +public: + WEBCORE_EXPORT FontCascade(); + WEBCORE_EXPORT FontCascade(const FontCascadeDescription&, float letterSpacing = 0, float wordSpacing = 0); + // This constructor is only used if the platform wants to start with a native font. + WEBCORE_EXPORT FontCascade(const FontPlatformData&, FontSmoothingMode = AutoSmoothing); + + FontCascade(const FontCascade&); + WEBCORE_EXPORT FontCascade& operator=(const FontCascade&); + + WEBCORE_EXPORT bool operator==(const FontCascade& other) const; + bool operator!=(const FontCascade& other) const { return !(*this == other); } + + const FontCascadeDescription& fontDescription() const { return m_fontDescription; } + + int pixelSize() const { return fontDescription().computedPixelSize(); } + float size() const { return fontDescription().computedSize(); } + + WEBCORE_EXPORT void update(RefPtr<FontSelector>&& = nullptr) const; + + enum CustomFontNotReadyAction { DoNotPaintIfFontNotReady, UseFallbackIfFontNotReady }; + WEBCORE_EXPORT float drawText(GraphicsContext&, const TextRun&, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt, CustomFontNotReadyAction = DoNotPaintIfFontNotReady) const; + static void drawGlyphs(GraphicsContext&, const Font&, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint&, FontSmoothingMode); + void drawEmphasisMarks(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt) const; + + DashArray dashesForIntersectionsWithRect(const TextRun&, const FloatPoint& textOrigin, const FloatRect& lineExtents) const; + + WEBCORE_EXPORT float width(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const; + float widthForSimpleText(StringView text) const; + + std::unique_ptr<TextLayout, TextLayoutDeleter> createLayout(RenderText&, float xPos, bool collapseWhiteSpace) const; + static float width(TextLayout&, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts = 0); + + int offsetForPosition(const TextRun&, float position, bool includePartialGlyphs) const; + void adjustSelectionRectForText(const TextRun&, LayoutRect& selectionRect, unsigned from = 0, std::optional<unsigned> to = std::nullopt) const; + + bool isSmallCaps() const { return m_fontDescription.variantCaps() == FontVariantCaps::Small; } + + float wordSpacing() const { return m_wordSpacing; } + float letterSpacing() const { return m_letterSpacing; } + void setWordSpacing(float s) { m_wordSpacing = s; } + void setLetterSpacing(float s) { m_letterSpacing = s; } + bool isFixedPitch() const; + + FontRenderingMode renderingMode() const { return m_fontDescription.renderingMode(); } + + bool enableKerning() const { return m_enableKerning; } + bool requiresShaping() const { return m_requiresShaping; } + + const AtomicString& firstFamily() const { return m_fontDescription.firstFamily(); } + unsigned familyCount() const { return m_fontDescription.familyCount(); } + const AtomicString& familyAt(unsigned i) const { return m_fontDescription.familyAt(i); } + + FontItalic italic() const { return m_fontDescription.italic(); } + FontWeight weight() const { return m_fontDescription.weight(); } + FontWidthVariant widthVariant() const { return m_fontDescription.widthVariant(); } + + bool isPlatformFont() const { return m_fonts->isForPlatformFont(); } + + const FontMetrics& fontMetrics() const { return primaryFont().fontMetrics(); } + float spaceWidth() const { return primaryFont().spaceWidth() + m_letterSpacing; } + float tabWidth(const Font&, unsigned tabSize, float position) const; + float tabWidth(unsigned tabSize, float position) const { return tabWidth(primaryFont(), tabSize, position); } + bool hasValidAverageCharWidth() const; + bool fastAverageCharWidthIfAvailable(float &width) const; // returns true on success + + int emphasisMarkAscent(const AtomicString&) const; + int emphasisMarkDescent(const AtomicString&) const; + int emphasisMarkHeight(const AtomicString&) const; + + const Font& primaryFont() const; + const FontRanges& fallbackRangesAt(unsigned) const; + GlyphData glyphDataForCharacter(UChar32, bool mirror, FontVariant = AutoVariant) const; + + const Font* fontForCombiningCharacterSequence(const UChar*, size_t length) const; + + static bool isCJKIdeograph(UChar32); + static bool isCJKIdeographOrSymbol(UChar32); + + // Returns (the number of opportunities, whether the last expansion is a trailing expansion) + // If there are no opportunities, the bool will be true iff we are forbidding leading expansions. + static std::pair<unsigned, bool> expansionOpportunityCount(const StringView&, TextDirection, ExpansionBehavior); + + // Whether or not there is an expansion opportunity just before the first character + // Note that this does not take a isAfterExpansion flag; this assumes that isAfterExpansion is false + // Here, "Leading" and "Trailing" are relevant after the line has been rearranged for bidi. + // ("Leading" means "left" and "Trailing" means "right.") + static bool leadingExpansionOpportunity(const StringView&, TextDirection); + static bool trailingExpansionOpportunity(const StringView&, TextDirection); + + WEBCORE_EXPORT static void setShouldUseSmoothing(bool); + WEBCORE_EXPORT static bool shouldUseSmoothing(); + + enum CodePath { Auto, Simple, Complex, SimpleWithGlyphOverflow }; + CodePath codePath(const TextRun&) const; + static CodePath characterRangeCodePath(const LChar*, unsigned) { return Simple; } + static CodePath characterRangeCodePath(const UChar*, unsigned len); + + bool primaryFontIsSystemFont() const; + + WeakPtr<FontCascade> createWeakPtr() const { return m_weakPtrFactory.createWeakPtr(); } + +private: + enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis }; + + float glyphBufferForTextRun(CodePath, const TextRun&, unsigned from, unsigned to, GlyphBuffer&) const; + // Returns the initial in-stream advance. + float getGlyphsAndAdvancesForSimpleText(const TextRun&, unsigned from, unsigned to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const; + void drawEmphasisMarksForSimpleText(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from, unsigned to) const; + void drawGlyphBuffer(GraphicsContext&, const GlyphBuffer&, FloatPoint&) const; + void drawEmphasisMarks(GraphicsContext&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const; + float floatWidthForSimpleText(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const; + int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const; + void adjustSelectionRectForSimpleText(const TextRun&, LayoutRect& selectionRect, unsigned from, unsigned to) const; + + std::optional<GlyphData> getEmphasisMarkGlyphData(const AtomicString&) const; + + static bool canReturnFallbackFontsForComplexText(); + static bool canExpandAroundIdeographsInComplexText(); + + // Returns the initial in-stream advance. + float getGlyphsAndAdvancesForComplexText(const TextRun&, unsigned from, unsigned to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const; + void drawEmphasisMarksForComplexText(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from, unsigned to) const; + float floatWidthForComplexText(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const; + int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const; + void adjustSelectionRectForComplexText(const TextRun&, LayoutRect& selectionRect, unsigned from, unsigned to) const; + + static std::pair<unsigned, bool> expansionOpportunityCountInternal(const LChar*, unsigned length, TextDirection, ExpansionBehavior); + static std::pair<unsigned, bool> expansionOpportunityCountInternal(const UChar*, unsigned length, TextDirection, ExpansionBehavior); + + friend struct WidthIterator; + +public: +#if ENABLE(TEXT_AUTOSIZING) + bool equalForTextAutoSizing(const FontCascade& other) const + { + return m_fontDescription.equalForTextAutoSizing(other.m_fontDescription) + && m_letterSpacing == other.m_letterSpacing + && m_wordSpacing == other.m_wordSpacing; + } +#endif + + // Useful for debugging the different font rendering code paths. + WEBCORE_EXPORT static void setCodePath(CodePath); + static CodePath codePath(); + static CodePath s_codePath; + + FontSelector* fontSelector() const; + static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == noBreakSpace; } + static bool treatAsZeroWidthSpace(UChar c) { return treatAsZeroWidthSpaceInComplexScript(c) || c == 0x200c || c == 0x200d; } + static bool treatAsZeroWidthSpaceInComplexScript(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == softHyphen || c == zeroWidthSpace || (c >= 0x200e && c <= 0x200f) || (c >= 0x202a && c <= 0x202e) || c == zeroWidthNoBreakSpace || c == objectReplacementCharacter; } + static bool canReceiveTextEmphasis(UChar32); + + static inline UChar normalizeSpaces(UChar character) + { + if (treatAsSpace(character)) + return space; + + if (treatAsZeroWidthSpace(character)) + return zeroWidthSpace; + + return character; + } + + static String normalizeSpaces(const LChar*, unsigned length); + static String normalizeSpaces(const UChar*, unsigned length); + + bool useBackslashAsYenSymbol() const { return m_useBackslashAsYenSymbol; } + FontCascadeFonts* fonts() const { return m_fonts.get(); } + +private: + bool isLoadingCustomFonts() const; + + bool advancedTextRenderingMode() const + { + auto textRenderingMode = m_fontDescription.textRenderingMode(); + if (textRenderingMode == GeometricPrecision || textRenderingMode == OptimizeLegibility) + return true; + if (textRenderingMode == OptimizeSpeed) + return false; +#if PLATFORM(COCOA) + return true; +#else + return false; +#endif + } + + bool computeEnableKerning() const + { + auto kerning = m_fontDescription.kerning(); + if (kerning == Kerning::Normal) + return true; + if (kerning == Kerning::NoShift) + return false; + return advancedTextRenderingMode(); + } + + bool computeRequiresShaping() const + { +#if PLATFORM(COCOA) + if (!m_fontDescription.variantSettings().isAllNormal()) + return true; + if (m_fontDescription.featureSettings().size()) + return true; +#endif + return advancedTextRenderingMode(); + } + + static int syntheticObliqueAngle() { return 14; } + + FontCascadeDescription m_fontDescription; + mutable RefPtr<FontCascadeFonts> m_fonts; + WeakPtrFactory<FontCascade> m_weakPtrFactory; + float m_letterSpacing; + float m_wordSpacing; + mutable bool m_useBackslashAsYenSymbol; + mutable unsigned m_enableKerning : 1; // Computed from m_fontDescription. + mutable unsigned m_requiresShaping : 1; // Computed from m_fontDescription. +}; + +void invalidateFontCascadeCache(); +void pruneUnreferencedEntriesFromFontCascadeCache(); +void pruneSystemFallbackFonts(); +void clearWidthCaches(); + +inline const Font& FontCascade::primaryFont() const +{ + ASSERT(m_fonts); + return m_fonts->primaryFont(m_fontDescription); +} + +inline const FontRanges& FontCascade::fallbackRangesAt(unsigned index) const +{ + ASSERT(m_fonts); + return m_fonts->realizeFallbackRangesAt(m_fontDescription, index); +} + +inline bool FontCascade::isFixedPitch() const +{ + ASSERT(m_fonts); + return m_fonts->isFixedPitch(m_fontDescription); +} + +inline FontSelector* FontCascade::fontSelector() const +{ + return m_fonts ? m_fonts->fontSelector() : nullptr; +} + +inline float FontCascade::tabWidth(const Font& font, unsigned tabSize, float position) const +{ + if (!tabSize) + return letterSpacing(); + float tabWidth = tabSize * font.spaceWidth() + letterSpacing(); + float tabDeltaWidth = tabWidth - fmodf(position, tabWidth); + return (tabDeltaWidth < font.spaceWidth() / 2) ? tabWidth : tabDeltaWidth; +} + +} + +#endif diff --git a/Source/WebCore/platform/graphics/FontCascadeFonts.cpp b/Source/WebCore/platform/graphics/FontCascadeFonts.cpp new file mode 100644 index 000000000..3d5455b2f --- /dev/null +++ b/Source/WebCore/platform/graphics/FontCascadeFonts.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2006, 2013-2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FontCascadeFonts.h" + +#include "FontCache.h" +#include "FontCascade.h" +#include "GlyphPage.h" + +namespace WebCore { + +class MixedFontGlyphPage { + WTF_MAKE_FAST_ALLOCATED; +public: + MixedFontGlyphPage(const GlyphPage* initialPage) + { + if (initialPage) { + for (unsigned i = 0; i < GlyphPage::size; ++i) + setGlyphDataForIndex(i, initialPage->glyphDataForIndex(i)); + } + } + + GlyphData glyphDataForCharacter(UChar32 c) const + { + unsigned index = GlyphPage::indexForCodePoint(c); + ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size); + return { m_glyphs[index], m_fonts[index] }; + } + + void setGlyphDataForCharacter(UChar32 c, GlyphData glyphData) + { + setGlyphDataForIndex(GlyphPage::indexForCodePoint(c), glyphData); + } + +private: + void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData) + { + ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size); + m_glyphs[index] = glyphData.glyph; + m_fonts[index] = glyphData.font; + } + + Glyph m_glyphs[GlyphPage::size] { }; + const Font* m_fonts[GlyphPage::size] { }; +}; + +GlyphData FontCascadeFonts::GlyphPageCacheEntry::glyphDataForCharacter(UChar32 character) +{ + ASSERT(!(m_singleFont && m_mixedFont)); + if (m_singleFont) + return m_singleFont->glyphDataForCharacter(character); + if (m_mixedFont) + return m_mixedFont->glyphDataForCharacter(character); + return 0; +} + +void FontCascadeFonts::GlyphPageCacheEntry::setGlyphDataForCharacter(UChar32 character, GlyphData glyphData) +{ + ASSERT(!glyphDataForCharacter(character).glyph); + if (!m_mixedFont) { + m_mixedFont = std::make_unique<MixedFontGlyphPage>(m_singleFont.get()); + m_singleFont = nullptr; + } + m_mixedFont->setGlyphDataForCharacter(character, glyphData); +} + +void FontCascadeFonts::GlyphPageCacheEntry::setSingleFontPage(RefPtr<GlyphPage>&& page) +{ + ASSERT(isNull()); + m_singleFont = page; +} + +FontCascadeFonts::FontCascadeFonts(RefPtr<FontSelector>&& fontSelector) + : m_cachedPrimaryFont(nullptr) + , m_fontSelector(fontSelector) + , m_fontSelectorVersion(m_fontSelector ? m_fontSelector->version() : 0) + , m_generation(FontCache::singleton().generation()) +{ +} + +FontCascadeFonts::FontCascadeFonts(const FontPlatformData& platformData) + : m_cachedPrimaryFont(nullptr) + , m_fontSelectorVersion(0) + , m_generation(FontCache::singleton().generation()) + , m_isForPlatformFont(true) +{ + m_realizedFallbackRanges.append(FontRanges(FontCache::singleton().fontForPlatformData(platformData))); +} + +FontCascadeFonts::~FontCascadeFonts() +{ +} + +void FontCascadeFonts::determinePitch(const FontCascadeDescription& description) +{ + auto& primaryRanges = realizeFallbackRangesAt(description, 0); + unsigned numRanges = primaryRanges.size(); + if (numRanges == 1) + m_pitch = primaryRanges.fontForFirstRange().pitch(); + else + m_pitch = VariablePitch; +} + +bool FontCascadeFonts::isLoadingCustomFonts() const +{ + for (auto& fontRanges : m_realizedFallbackRanges) { + if (fontRanges.isLoading()) + return true; + } + return false; +} + +static FontRanges realizeNextFallback(const FontCascadeDescription& description, unsigned& index, FontSelector* fontSelector) +{ + ASSERT(index < description.familyCount()); + + auto& fontCache = FontCache::singleton(); + while (index < description.familyCount()) { + const AtomicString& family = description.familyAt(index++); + if (family.isEmpty()) + continue; + if (fontSelector) { + auto ranges = fontSelector->fontRangesForFamily(description, family); + if (!ranges.isNull()) + return ranges; + } + if (auto font = fontCache.fontForFamily(description, family)) + return FontRanges(WTFMove(font)); + } + // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform. + // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the + // Geeza Pro font. + for (auto& family : description.families()) { + if (auto font = fontCache.similarFont(description, family)) + return FontRanges(WTFMove(font)); + } + return { }; +} + +const FontRanges& FontCascadeFonts::realizeFallbackRangesAt(const FontCascadeDescription& description, unsigned index) +{ + if (index < m_realizedFallbackRanges.size()) + return m_realizedFallbackRanges[index]; + + ASSERT(index == m_realizedFallbackRanges.size()); + ASSERT(FontCache::singleton().generation() == m_generation); + + m_realizedFallbackRanges.append(FontRanges()); + auto& fontRanges = m_realizedFallbackRanges.last(); + + if (!index) { + fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get()); + if (fontRanges.isNull() && m_fontSelector) + fontRanges = m_fontSelector->fontRangesForFamily(description, standardFamily); + if (fontRanges.isNull()) + fontRanges = FontRanges(FontCache::singleton().lastResortFallbackFont(description)); + return fontRanges; + } + + if (m_lastRealizedFallbackIndex < description.familyCount()) + fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get()); + + if (fontRanges.isNull() && m_fontSelector) { + ASSERT(m_lastRealizedFallbackIndex >= description.familyCount()); + + unsigned fontSelectorFallbackIndex = m_lastRealizedFallbackIndex - description.familyCount(); + if (fontSelectorFallbackIndex == m_fontSelector->fallbackFontCount()) + return fontRanges; + ++m_lastRealizedFallbackIndex; + fontRanges = FontRanges(m_fontSelector->fallbackFontAt(description, fontSelectorFallbackIndex)); + } + + return fontRanges; +} + +static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound) +{ + return character >= lowerBound && character <= upperBound; +} + +static bool shouldIgnoreRotation(UChar32 character) +{ + if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE) + return true; + + if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE) + return true; + + if (isInRange(character, 0x002E5, 0x002EB)) + return true; + + if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF)) + return true; + + if (character == 0x02016 || character == 0x02020 || character == 0x02021 || character == 0x2030 || character == 0x02031) + return true; + + if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047 + || character == 0x02048 || character == 0x02049 || character == 0x2051) + return true; + + if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0) + || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117) + || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F)) + return true; + + if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D + || isInRange(character, 0x0214F, 0x0218F)) + return true; + + if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F) + || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A) + || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF) + || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF)) + return true; + + if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767) + || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F) + || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007)) + return true; + + if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F) + || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB) + || isInRange(character, 0x030FD, 0x0A4CF)) + return true; + + if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F) + || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF)) + return true; + + if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48) + || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62) + || isInRange(character, 0x0FE67, 0x0FE6F)) + return true; + + if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C) + || isInRange(character, 0x0FF0E, 0x0FF19) || character == 0x0FF1B || isInRange(character, 0x0FF1F, 0x0FF3A)) + return true; + + if (character == 0x0FF3C || character == 0x0FF3E) + return true; + + if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2) + || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8) + || character == 0x0FFFD) + return true; + + if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF) + || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F) + || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F)) + return true; + + if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD)) + return true; + + return false; +} + +#if PLATFORM(COCOA) || USE(CAIRO) +static GlyphData glyphDataForCJKCharacterWithoutSyntheticItalic(UChar32 character, GlyphData& data) +{ + GlyphData nonItalicData = data.font->nonSyntheticItalicFont().glyphDataForCharacter(character); + if (nonItalicData.font) + return nonItalicData; + return data; +} +#endif + +static GlyphData glyphDataForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, const GlyphData& data) +{ + if (orientation == NonCJKGlyphOrientation::Upright || shouldIgnoreRotation(character)) { + GlyphData uprightData = data.font->uprightOrientationFont().glyphDataForCharacter(character); + // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright. + if (data.glyph == uprightData.glyph) + return data; + // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that + // glyph, so we fall back to the upright data and use the horizontal glyph. + if (uprightData.font) + return uprightData; + } else if (orientation == NonCJKGlyphOrientation::Mixed) { + GlyphData verticalRightData = data.font->verticalRightOrientationFont().glyphDataForCharacter(character); + // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked + // into it. + if (data.glyph != verticalRightData.glyph) + return data; + // The glyphs are identical, meaning that we should just use the horizontal glyph. + if (verticalRightData.font) + return verticalRightData; + } + return data; +} + +GlyphData FontCascadeFonts::glyphDataForSystemFallback(UChar32 c, const FontCascadeDescription& description, FontVariant variant) +{ + // System fallback is character-dependent. + auto& primaryRanges = realizeFallbackRangesAt(description, 0); + auto* originalFont = primaryRanges.fontForCharacter(c); + if (!originalFont) + originalFont = &primaryRanges.fontForFirstRange(); + + auto systemFallbackFont = originalFont->systemFallbackFontForCharacter(c, description, m_isForPlatformFont); + if (!systemFallbackFont) + return GlyphData(); + + if (systemFallbackFont->platformData().orientation() == Vertical && !systemFallbackFont->hasVerticalGlyphs() && FontCascade::isCJKIdeographOrSymbol(c)) + variant = BrokenIdeographVariant; + + GlyphData fallbackGlyphData; + if (variant == NormalVariant) + fallbackGlyphData = systemFallbackFont->glyphDataForCharacter(c); + else + fallbackGlyphData = systemFallbackFont->variantFont(description, variant)->glyphDataForCharacter(c); + + if (fallbackGlyphData.font && fallbackGlyphData.font->platformData().orientation() == Vertical && !fallbackGlyphData.font->isTextOrientationFallback()) { + if (variant == NormalVariant && !FontCascade::isCJKIdeographOrSymbol(c)) + fallbackGlyphData = glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), fallbackGlyphData); +#if PLATFORM(COCOA) || USE(CAIRO) + if (fallbackGlyphData.font->platformData().syntheticOblique() && FontCascade::isCJKIdeographOrSymbol(c)) + fallbackGlyphData = glyphDataForCJKCharacterWithoutSyntheticItalic(c, fallbackGlyphData); +#endif + } + + // Keep the system fallback fonts we use alive. + if (fallbackGlyphData.glyph) + m_systemFallbackFontSet.add(WTFMove(systemFallbackFont)); + + return fallbackGlyphData; +} + +GlyphData FontCascadeFonts::glyphDataForVariant(UChar32 c, const FontCascadeDescription& description, FontVariant variant, unsigned fallbackIndex) +{ + while (true) { + auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex++); + if (fontRanges.isNull()) + break; + GlyphData data = fontRanges.glyphDataForCharacter(c); + if (!data.font) + continue; + // The variantFont function should not normally return 0. + // But if it does, we will just render the capital letter big. + if (const Font* variantFont = data.font->variantFont(description, variant)) + return variantFont->glyphDataForCharacter(c); + return data; + } + + return glyphDataForSystemFallback(c, description, variant); +} + +GlyphData FontCascadeFonts::glyphDataForNormalVariant(UChar32 c, const FontCascadeDescription& description) +{ + for (unsigned fallbackIndex = 0; ; ++fallbackIndex) { + auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex); + if (fontRanges.isNull()) + break; + GlyphData data = fontRanges.glyphDataForCharacter(c); + if (!data.font) + continue; + if (data.font->platformData().orientation() == Vertical && !data.font->isTextOrientationFallback()) { + if (!FontCascade::isCJKIdeographOrSymbol(c)) + return glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data); + + if (!data.font->hasVerticalGlyphs()) { + // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs + // to make sure you get a square (even for broken glyphs like symbols used for punctuation). + return glyphDataForVariant(c, description, BrokenIdeographVariant, fallbackIndex); + } +#if PLATFORM(COCOA) || USE(CAIRO) + if (data.font->platformData().syntheticOblique()) + return glyphDataForCJKCharacterWithoutSyntheticItalic(c, data); +#endif + } + return data; + } + + return glyphDataForSystemFallback(c, description, NormalVariant); +} + +static RefPtr<GlyphPage> glyphPageFromFontRanges(unsigned pageNumber, const FontRanges& fontRanges) +{ + const Font* font = nullptr; + UChar32 pageRangeFrom = pageNumber * GlyphPage::size; + UChar32 pageRangeTo = pageRangeFrom + GlyphPage::size - 1; + for (unsigned i = 0; i < fontRanges.size(); ++i) { + auto& range = fontRanges.rangeAt(i); + if (range.to()) { + if (range.from() <= pageRangeFrom && pageRangeTo <= range.to()) + font = range.font(); + break; + } + } + if (!font || font->platformData().orientation() == Vertical) + return nullptr; + + return const_cast<GlyphPage*>(font->glyphPage(pageNumber)); +} + +GlyphData FontCascadeFonts::glyphDataForCharacter(UChar32 c, const FontCascadeDescription& description, FontVariant variant) +{ + ASSERT(isMainThread()); + ASSERT(variant != AutoVariant); + + if (variant != NormalVariant) + return glyphDataForVariant(c, description, variant, 0); + + const unsigned pageNumber = GlyphPage::pageNumberForCodePoint(c); + + auto& cacheEntry = pageNumber ? m_cachedPages.add(pageNumber, GlyphPageCacheEntry()).iterator->value : m_cachedPageZero; + + // Initialize cache with a full page of glyph mappings from a single font. + if (cacheEntry.isNull()) + cacheEntry.setSingleFontPage(glyphPageFromFontRanges(pageNumber, realizeFallbackRangesAt(description, 0))); + + GlyphData glyphData = cacheEntry.glyphDataForCharacter(c); + if (!glyphData.glyph) { + // No glyph, resolve per-character. + glyphData = glyphDataForNormalVariant(c, description); + // Cache the results. + cacheEntry.setGlyphDataForCharacter(c, glyphData); + } + + return glyphData; +} + +void FontCascadeFonts::pruneSystemFallbacks() +{ + if (m_systemFallbackFontSet.isEmpty()) + return; + // Mutable glyph pages may reference fallback fonts. + if (m_cachedPageZero.isMixedFont()) + m_cachedPageZero = { }; + m_cachedPages.removeIf([](auto& keyAndValue) { + return keyAndValue.value.isMixedFont(); + }); + m_systemFallbackFontSet.clear(); +} + +} diff --git a/Source/WebCore/platform/graphics/FontCascadeFonts.h b/Source/WebCore/platform/graphics/FontCascadeFonts.h new file mode 100644 index 000000000..fcc1c37a9 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontCascadeFonts.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2006, 2010, 2013-2015 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef FontCascadeFonts_h +#define FontCascadeFonts_h + +#include "Font.h" +#include "FontRanges.h" +#include "FontSelector.h" +#include "GlyphPage.h" +#include "WidthCache.h" +#include <wtf/Forward.h> +#include <wtf/MainThread.h> + +#if PLATFORM(IOS) +#include "WebCoreThread.h" +#endif + +namespace WebCore { + +class FontCascadeDescription; +class FontPlatformData; +class FontSelector; +class GraphicsContext; +class IntRect; +class MixedFontGlyphPage; + +class FontCascadeFonts : public RefCounted<FontCascadeFonts> { + WTF_MAKE_NONCOPYABLE(FontCascadeFonts); +public: + static Ref<FontCascadeFonts> create(RefPtr<FontSelector>&& fontSelector) { return adoptRef(*new FontCascadeFonts(WTFMove(fontSelector))); } + static Ref<FontCascadeFonts> createForPlatformFont(const FontPlatformData& platformData) { return adoptRef(*new FontCascadeFonts(platformData)); } + + WEBCORE_EXPORT ~FontCascadeFonts(); + + bool isForPlatformFont() const { return m_isForPlatformFont; } + + GlyphData glyphDataForCharacter(UChar32, const FontCascadeDescription&, FontVariant); + + bool isFixedPitch(const FontCascadeDescription&); + void determinePitch(const FontCascadeDescription&); + + bool isLoadingCustomFonts() const; + + FontSelector* fontSelector() { return m_fontSelector.get(); } + // FIXME: It should be possible to combine fontSelectorVersion and generation. + unsigned fontSelectorVersion() const { return m_fontSelectorVersion; } + unsigned generation() const { return m_generation; } + + WidthCache& widthCache() { return m_widthCache; } + const WidthCache& widthCache() const { return m_widthCache; } + + const Font& primaryFont(const FontCascadeDescription&); + WEBCORE_EXPORT const FontRanges& realizeFallbackRangesAt(const FontCascadeDescription&, unsigned fallbackIndex); + + void pruneSystemFallbacks(); + +private: + FontCascadeFonts(RefPtr<FontSelector>&&); + FontCascadeFonts(const FontPlatformData&); + + GlyphData glyphDataForSystemFallback(UChar32, const FontCascadeDescription&, FontVariant); + GlyphData glyphDataForNormalVariant(UChar32, const FontCascadeDescription&); + GlyphData glyphDataForVariant(UChar32, const FontCascadeDescription&, FontVariant, unsigned fallbackIndex); + + Vector<FontRanges, 1> m_realizedFallbackRanges; + unsigned m_lastRealizedFallbackIndex { 0 }; + + class GlyphPageCacheEntry { + public: + GlyphData glyphDataForCharacter(UChar32); + + void setSingleFontPage(RefPtr<GlyphPage>&&); + void setGlyphDataForCharacter(UChar32, GlyphData); + + bool isNull() const { return !m_singleFont && !m_mixedFont; } + bool isMixedFont() const { return !!m_mixedFont; } + + private: + // Only one of these is non-null. + RefPtr<GlyphPage> m_singleFont; + std::unique_ptr<MixedFontGlyphPage> m_mixedFont; + }; + + GlyphPageCacheEntry m_cachedPageZero; + HashMap<int, GlyphPageCacheEntry> m_cachedPages; + + HashSet<RefPtr<Font>> m_systemFallbackFontSet; + + const Font* m_cachedPrimaryFont; + RefPtr<FontSelector> m_fontSelector; + + WidthCache m_widthCache; + + unsigned m_fontSelectorVersion; + unsigned short m_generation; + Pitch m_pitch { UnknownPitch }; + bool m_isForPlatformFont { false }; +}; + +inline bool FontCascadeFonts::isFixedPitch(const FontCascadeDescription& description) +{ + if (m_pitch == UnknownPitch) + determinePitch(description); + return m_pitch == FixedPitch; +}; + +inline const Font& FontCascadeFonts::primaryFont(const FontCascadeDescription& description) +{ + ASSERT(isMainThread()); + if (!m_cachedPrimaryFont) { + auto& primaryRanges = realizeFallbackRangesAt(description, 0); + m_cachedPrimaryFont = primaryRanges.fontForCharacter(' '); + if (!m_cachedPrimaryFont) + m_cachedPrimaryFont = &primaryRanges.fontForFirstRange(); + } + return *m_cachedPrimaryFont; +} + +} + +#endif diff --git a/Source/WebCore/platform/graphics/FontData.cpp b/Source/WebCore/platform/graphics/FontData.cpp deleted file mode 100644 index c40e13cbd..000000000 --- a/Source/WebCore/platform/graphics/FontData.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "FontData.h" - -namespace WebCore { - -FontData::~FontData() -{ -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FontDescription.cpp b/Source/WebCore/platform/graphics/FontDescription.cpp index 84bb5af16..0c03e9a3f 100644 --- a/Source/WebCore/platform/graphics/FontDescription.cpp +++ b/Source/WebCore/platform/graphics/FontDescription.cpp @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,93 +30,125 @@ #include "config.h" #include "FontDescription.h" +#include "LocaleToScriptMapping.h" + namespace WebCore { -struct SameSizeAsFontDescription { - void* pointers[2]; - float sizes[2]; - // FXIME: Make them fit into one word. - uint32_t bitfields; - uint32_t bitfields2 : 8; +struct SameSizeAsFontCascadeDescription { + Vector<void*> vector; +#if ENABLE(VARIATION_FONTS) + Vector<void*> vector2; +#else + char c; +#endif + AtomicString string; + float size; + unsigned bitfields1; + unsigned bitfields2 : 22; + void* array; + float size2; + unsigned bitfields3 : 10; }; -COMPILE_ASSERT(sizeof(FontDescription) == sizeof(SameSizeAsFontDescription), FontDescription_should_stay_small); - -FontWeight FontDescription::lighterWeight(void) const +COMPILE_ASSERT(sizeof(FontCascadeDescription) == sizeof(SameSizeAsFontCascadeDescription), FontCascadeDescription_should_stay_small); + +FontDescription::FontDescription() + : m_orientation(Horizontal) + , m_nonCJKGlyphOrientation(static_cast<unsigned>(NonCJKGlyphOrientation::Mixed)) + , m_widthVariant(RegularWidth) + , m_italic(FontItalicOff) + , m_weight(FontWeightNormal) + , m_renderingMode(static_cast<unsigned>(FontRenderingMode::Normal)) + , m_textRendering(AutoTextRendering) + , m_script(USCRIPT_COMMON) + , m_fontSynthesis(FontSynthesisWeight | FontSynthesisStyle | FontSynthesisSmallCaps) + , m_variantCommonLigatures(static_cast<unsigned>(FontVariantLigatures::Normal)) + , m_variantDiscretionaryLigatures(static_cast<unsigned>(FontVariantLigatures::Normal)) + , m_variantHistoricalLigatures(static_cast<unsigned>(FontVariantLigatures::Normal)) + , m_variantContextualAlternates(static_cast<unsigned>(FontVariantLigatures::Normal)) + , m_variantPosition(static_cast<unsigned>(FontVariantPosition::Normal)) + , m_variantCaps(static_cast<unsigned>(FontVariantCaps::Normal)) + , m_variantNumericFigure(static_cast<unsigned>(FontVariantNumericFigure::Normal)) + , m_variantNumericSpacing(static_cast<unsigned>(FontVariantNumericSpacing::Normal)) + , m_variantNumericFraction(static_cast<unsigned>(FontVariantNumericFraction::Normal)) + , m_variantNumericOrdinal(static_cast<unsigned>(FontVariantNumericOrdinal::Normal)) + , m_variantNumericSlashedZero(static_cast<unsigned>(FontVariantNumericSlashedZero::Normal)) + , m_variantAlternates(static_cast<unsigned>(FontVariantAlternates::Normal)) + , m_variantEastAsianVariant(static_cast<unsigned>(FontVariantEastAsianVariant::Normal)) + , m_variantEastAsianWidth(static_cast<unsigned>(FontVariantEastAsianWidth::Normal)) + , m_variantEastAsianRuby(static_cast<unsigned>(FontVariantEastAsianRuby::Normal)) { - // FIXME: Should actually return the CSS weight corresponding to next lightest - // weight of the currently used font family. - switch (m_weight) { - case FontWeight100: - case FontWeight200: - return FontWeight100; - - case FontWeight300: - return FontWeight200; +} - case FontWeight400: - case FontWeight500: - return FontWeight300; +FontTraitsMask FontDescription::traitsMask() const +{ + return static_cast<FontTraitsMask>((m_italic ? FontStyleItalicMask : FontStyleNormalMask) + | (FontWeight100Mask << (m_weight - FontWeight100))); + +} - case FontWeight600: - case FontWeight700: - return FontWeight400; +void FontDescription::setLocale(const AtomicString& locale) +{ + m_locale = locale; + m_script = localeToScriptCodeForFontSelection(m_locale); +} - case FontWeight800: - return FontWeight500; +FontCascadeDescription::FontCascadeDescription() + : m_isAbsoluteSize(false) + , m_kerning(static_cast<unsigned>(Kerning::Auto)) + , m_keywordSize(0) + , m_fontSmoothing(AutoSmoothing) + , m_isSpecifiedFont(false) +{ +} - case FontWeight900: - return FontWeight700; +FontWeight FontCascadeDescription::lighterWeight(void) const +{ + switch (weight()) { + case FontWeight100: + case FontWeight200: + case FontWeight300: + case FontWeight400: + case FontWeight500: + return FontWeight100; + + case FontWeight600: + case FontWeight700: + return FontWeight400; + + case FontWeight800: + case FontWeight900: + return FontWeight700; } ASSERT_NOT_REACHED(); return FontWeightNormal; } -FontWeight FontDescription::bolderWeight(void) const +FontWeight FontCascadeDescription::bolderWeight(void) const { - // FIXME: Should actually return the CSS weight corresponding to next heaviest - // weight of the currently used font family. - switch (m_weight) { - case FontWeight100: - case FontWeight200: - return FontWeight300; - - case FontWeight300: - return FontWeight400; - - case FontWeight400: - case FontWeight500: - return FontWeight700; - - case FontWeight600: - case FontWeight700: - return FontWeight800; - - case FontWeight800: - case FontWeight900: - return FontWeight900; + switch (weight()) { + case FontWeight100: + case FontWeight200: + case FontWeight300: + return FontWeight400; + + case FontWeight400: + case FontWeight500: + return FontWeight700; + + case FontWeight600: + case FontWeight700: + case FontWeight800: + case FontWeight900: + return FontWeight900; } ASSERT_NOT_REACHED(); return FontWeightNormal; } -FontTraitsMask FontDescription::traitsMask() const -{ - return static_cast<FontTraitsMask>((m_italic ? FontStyleItalicMask : FontStyleNormalMask) - | (m_smallCaps ? FontVariantSmallCapsMask : FontVariantNormalMask) - | (FontWeight100Mask << (m_weight - FontWeight100))); - -} +#if ENABLE(TEXT_AUTOSIZING) -FontDescription FontDescription::makeNormalFeatureSettings() const -{ - FontDescription normalDescription(*this); - normalDescription.setFeatureSettings(0); - return normalDescription; -} - -#if ENABLE(IOS_TEXT_AUTOSIZING) -bool FontDescription::familiesEqualForTextAutoSizing(const FontDescription& other) const +bool FontCascadeDescription::familiesEqualForTextAutoSizing(const FontCascadeDescription& other) const { unsigned thisFamilyCount = familyCount(); unsigned otherFamilyCount = other.familyCount(); @@ -125,12 +157,13 @@ bool FontDescription::familiesEqualForTextAutoSizing(const FontDescription& othe return false; for (unsigned i = 0; i < thisFamilyCount; ++i) { - if (!equalIgnoringCase(familyAt(i), other.familyAt(i))) + if (!equalIgnoringASCIICase(familyAt(i), other.familyAt(i))) return false; } return true; } -#endif // ENABLE(IOS_TEXT_AUTOSIZING) + +#endif // ENABLE(TEXT_AUTOSIZING) } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FontDescription.h b/Source/WebCore/platform/graphics/FontDescription.h index 3ff17909c..a7fda4012 100644 --- a/Source/WebCore/platform/graphics/FontDescription.h +++ b/Source/WebCore/platform/graphics/FontDescription.h @@ -25,84 +25,182 @@ #ifndef FontDescription_h #define FontDescription_h -#include "FontFeatureSettings.h" -#include "FontOrientation.h" -#include "FontRenderingMode.h" -#include "FontSmoothingMode.h" -#include "FontTraitsMask.h" -#include "FontWidthVariant.h" -#include "NonCJKGlyphOrientation.h" -#include "TextRenderingMode.h" +#include "CSSValueKeywords.h" +#include "FontTaggedSettings.h" +#include "TextFlags.h" #include "WebKitFontFamilyNames.h" +#include <unicode/uscript.h> #include <wtf/MathExtras.h> #include <wtf/RefCountedArray.h> -#include <wtf/RefPtr.h> namespace WebCore { using namespace WebKitFontFamilyNames; -enum FontWeight { - FontWeight100, - FontWeight200, - FontWeight300, - FontWeight400, - FontWeight500, - FontWeight600, - FontWeight700, - FontWeight800, - FontWeight900, - FontWeightNormal = FontWeight400, - FontWeightBold = FontWeight700 -}; +class FontDescription { +public: + WEBCORE_EXPORT FontDescription(); -enum FontItalic { - FontItalicOff = 0, - FontItalicOn = 1 -}; + bool operator==(const FontDescription&) const; + bool operator!=(const FontDescription& other) const { return !(*this == other); } -enum FontSmallCaps { - FontSmallCapsOff = 0, - FontSmallCapsOn = 1 -}; + float computedSize() const { return m_computedSize; } + FontItalic italic() const { return static_cast<FontItalic>(m_italic); } + int computedPixelSize() const { return int(m_computedSize + 0.5f); } + FontWeight weight() const { return static_cast<FontWeight>(m_weight); } + FontWeight lighterWeight() const; + FontWeight bolderWeight() const; + FontRenderingMode renderingMode() const { return static_cast<FontRenderingMode>(m_renderingMode); } + TextRenderingMode textRenderingMode() const { return static_cast<TextRenderingMode>(m_textRendering); } + UScriptCode script() const { return static_cast<UScriptCode>(m_script); } + const AtomicString& locale() const { return m_locale; } -class FontDescription { -public: - enum GenericFamilyType { NoFamily, StandardFamily, SerifFamily, SansSerifFamily, - MonospaceFamily, CursiveFamily, FantasyFamily, PictographFamily }; - - enum Kerning { AutoKerning, NormalKerning, NoneKerning }; - - enum LigaturesState { NormalLigaturesState, DisabledLigaturesState, EnabledLigaturesState }; - - FontDescription() - : m_families(1) - , m_specifiedSize(0) - , m_computedSize(0) - , m_orientation(Horizontal) - , m_nonCJKGlyphOrientation(NonCJKGlyphOrientationVerticalRight) - , m_widthVariant(RegularWidth) - , m_italic(FontItalicOff) - , m_smallCaps(FontSmallCapsOff) - , m_isAbsoluteSize(false) - , m_weight(FontWeightNormal) - , m_genericFamily(NoFamily) - , m_usePrinterFont(false) - , m_renderingMode(NormalRenderingMode) - , m_kerning(AutoKerning) - , m_commonLigaturesState(NormalLigaturesState) - , m_discretionaryLigaturesState(NormalLigaturesState) - , m_historicalLigaturesState(NormalLigaturesState) - , m_keywordSize(0) - , m_fontSmoothing(AutoSmoothing) - , m_textRendering(AutoTextRendering) - , m_isSpecifiedFont(false) - , m_script(USCRIPT_COMMON) + FontOrientation orientation() const { return static_cast<FontOrientation>(m_orientation); } + NonCJKGlyphOrientation nonCJKGlyphOrientation() const { return static_cast<NonCJKGlyphOrientation>(m_nonCJKGlyphOrientation); } + FontWidthVariant widthVariant() const { return static_cast<FontWidthVariant>(m_widthVariant); } + const FontFeatureSettings& featureSettings() const { return m_featureSettings; } + const FontVariationSettings& variationSettings() const { return m_variationSettings; } + FontSynthesis fontSynthesis() const { return static_cast<FontSynthesis>(m_fontSynthesis); } + FontVariantLigatures variantCommonLigatures() const { return static_cast<FontVariantLigatures>(m_variantCommonLigatures); } + FontVariantLigatures variantDiscretionaryLigatures() const { return static_cast<FontVariantLigatures>(m_variantDiscretionaryLigatures); } + FontVariantLigatures variantHistoricalLigatures() const { return static_cast<FontVariantLigatures>(m_variantHistoricalLigatures); } + FontVariantLigatures variantContextualAlternates() const { return static_cast<FontVariantLigatures>(m_variantContextualAlternates); } + FontVariantPosition variantPosition() const { return static_cast<FontVariantPosition>(m_variantPosition); } + FontVariantCaps variantCaps() const { return static_cast<FontVariantCaps>(m_variantCaps); } + FontVariantNumericFigure variantNumericFigure() const { return static_cast<FontVariantNumericFigure>(m_variantNumericFigure); } + FontVariantNumericSpacing variantNumericSpacing() const { return static_cast<FontVariantNumericSpacing>(m_variantNumericSpacing); } + FontVariantNumericFraction variantNumericFraction() const { return static_cast<FontVariantNumericFraction>(m_variantNumericFraction); } + FontVariantNumericOrdinal variantNumericOrdinal() const { return static_cast<FontVariantNumericOrdinal>(m_variantNumericOrdinal); } + FontVariantNumericSlashedZero variantNumericSlashedZero() const { return static_cast<FontVariantNumericSlashedZero>(m_variantNumericSlashedZero); } + FontVariantAlternates variantAlternates() const { return static_cast<FontVariantAlternates>(m_variantAlternates); } + FontVariantEastAsianVariant variantEastAsianVariant() const { return static_cast<FontVariantEastAsianVariant>(m_variantEastAsianVariant); } + FontVariantEastAsianWidth variantEastAsianWidth() const { return static_cast<FontVariantEastAsianWidth>(m_variantEastAsianWidth); } + FontVariantEastAsianRuby variantEastAsianRuby() const { return static_cast<FontVariantEastAsianRuby>(m_variantEastAsianRuby); } + FontVariantSettings variantSettings() const { + return { variantCommonLigatures(), + variantDiscretionaryLigatures(), + variantHistoricalLigatures(), + variantContextualAlternates(), + variantPosition(), + variantCaps(), + variantNumericFigure(), + variantNumericSpacing(), + variantNumericFraction(), + variantNumericOrdinal(), + variantNumericSlashedZero(), + variantAlternates(), + variantEastAsianVariant(), + variantEastAsianWidth(), + variantEastAsianRuby() }; } - bool operator==(const FontDescription&) const; - bool operator!=(const FontDescription& other) const { return !(*this == other); } + void setComputedSize(float s) { m_computedSize = clampToFloat(s); } + void setItalic(FontItalic i) { m_italic = i; } + void setIsItalic(bool i) { setItalic(i ? FontItalicOn : FontItalicOff); } + void setWeight(FontWeight w) { m_weight = w; } + void setRenderingMode(FontRenderingMode mode) { m_renderingMode = static_cast<unsigned>(mode); } + void setTextRenderingMode(TextRenderingMode rendering) { m_textRendering = rendering; } + void setOrientation(FontOrientation orientation) { m_orientation = orientation; } + void setNonCJKGlyphOrientation(NonCJKGlyphOrientation orientation) { m_nonCJKGlyphOrientation = static_cast<unsigned>(orientation); } + void setWidthVariant(FontWidthVariant widthVariant) { m_widthVariant = widthVariant; } // Make sure new callers of this sync with FontPlatformData::isForTextCombine()! + void setLocale(const AtomicString&); + void setFeatureSettings(FontFeatureSettings&& settings) { m_featureSettings = WTFMove(settings); } +#if ENABLE(VARIATION_FONTS) + void setVariationSettings(FontVariationSettings&& settings) { m_variationSettings = WTFMove(settings); } +#endif + void setFontSynthesis(FontSynthesis fontSynthesis) { m_fontSynthesis = fontSynthesis; } + void setVariantCommonLigatures(FontVariantLigatures variant) { m_variantCommonLigatures = static_cast<unsigned>(variant); } + void setVariantDiscretionaryLigatures(FontVariantLigatures variant) { m_variantDiscretionaryLigatures = static_cast<unsigned>(variant); } + void setVariantHistoricalLigatures(FontVariantLigatures variant) { m_variantHistoricalLigatures = static_cast<unsigned>(variant); } + void setVariantContextualAlternates(FontVariantLigatures variant) { m_variantContextualAlternates = static_cast<unsigned>(variant); } + void setVariantPosition(FontVariantPosition variant) { m_variantPosition = static_cast<unsigned>(variant); } + void setVariantCaps(FontVariantCaps variant) { m_variantCaps = static_cast<unsigned>(variant); } + void setVariantNumericFigure(FontVariantNumericFigure variant) { m_variantNumericFigure = static_cast<unsigned>(variant); } + void setVariantNumericSpacing(FontVariantNumericSpacing variant) { m_variantNumericSpacing = static_cast<unsigned>(variant); } + void setVariantNumericFraction(FontVariantNumericFraction variant) { m_variantNumericFraction = static_cast<unsigned>(variant); } + void setVariantNumericOrdinal(FontVariantNumericOrdinal variant) { m_variantNumericOrdinal = static_cast<unsigned>(variant); } + void setVariantNumericSlashedZero(FontVariantNumericSlashedZero variant) { m_variantNumericSlashedZero = static_cast<unsigned>(variant); } + void setVariantAlternates(FontVariantAlternates variant) { m_variantAlternates = static_cast<unsigned>(variant); } + void setVariantEastAsianVariant(FontVariantEastAsianVariant variant) { m_variantEastAsianVariant = static_cast<unsigned>(variant); } + void setVariantEastAsianWidth(FontVariantEastAsianWidth variant) { m_variantEastAsianWidth = static_cast<unsigned>(variant); } + void setVariantEastAsianRuby(FontVariantEastAsianRuby variant) { m_variantEastAsianRuby = static_cast<unsigned>(variant); } + + FontTraitsMask traitsMask() const; + +private: + // FIXME: Investigate moving these into their own object on the heap (to save memory). + FontFeatureSettings m_featureSettings; + FontVariationSettings m_variationSettings; + AtomicString m_locale; + + float m_computedSize { 0 }; // Computed size adjusted for the minimum font size and the zoom factor. + unsigned m_orientation : 1; // FontOrientation - Whether the font is rendering on a horizontal line or a vertical line. + unsigned m_nonCJKGlyphOrientation : 1; // NonCJKGlyphOrientation - Only used by vertical text. Determines the default orientation for non-ideograph glyphs. + unsigned m_widthVariant : 2; // FontWidthVariant + unsigned m_italic : 1; // FontItalic + unsigned m_weight : 8; // FontWeight + unsigned m_renderingMode : 1; // Used to switch between CG and GDI text on Windows. + unsigned m_textRendering : 2; // TextRenderingMode + unsigned m_script : 7; // Used to help choose an appropriate font for generic font families. + unsigned m_fontSynthesis : 3; // FontSynthesis type + unsigned m_variantCommonLigatures : 2; // FontVariantLigatures + unsigned m_variantDiscretionaryLigatures : 2; // FontVariantLigatures + unsigned m_variantHistoricalLigatures : 2; // FontVariantLigatures + unsigned m_variantContextualAlternates : 2; // FontVariantLigatures + unsigned m_variantPosition : 2; // FontVariantPosition + unsigned m_variantCaps : 3; // FontVariantCaps + unsigned m_variantNumericFigure : 2; // FontVariantNumericFigure + unsigned m_variantNumericSpacing : 2; // FontVariantNumericSpacing + unsigned m_variantNumericFraction : 2; // FontVariantNumericFraction + unsigned m_variantNumericOrdinal : 1; // FontVariantNumericOrdinal + unsigned m_variantNumericSlashedZero : 1; // FontVariantNumericSlashedZero + unsigned m_variantAlternates : 1; // FontVariantAlternates + unsigned m_variantEastAsianVariant : 3; // FontVariantEastAsianVariant + unsigned m_variantEastAsianWidth : 2; // FontVariantEastAsianWidth + unsigned m_variantEastAsianRuby : 1; // FontVariantEastAsianRuby +}; + +inline bool FontDescription::operator==(const FontDescription& other) const +{ + return m_computedSize == other.m_computedSize + && m_italic == other.m_italic + && m_weight == other.m_weight + && m_renderingMode == other.m_renderingMode + && m_textRendering == other.m_textRendering + && m_orientation == other.m_orientation + && m_nonCJKGlyphOrientation == other.m_nonCJKGlyphOrientation + && m_widthVariant == other.m_widthVariant + && m_locale == other.m_locale + && m_featureSettings == other.m_featureSettings +#if ENABLE(VARIATION_FONTS) + && m_variationSettings == other.m_variationSettings +#endif + && m_fontSynthesis == other.m_fontSynthesis + && m_variantCommonLigatures == other.m_variantCommonLigatures + && m_variantDiscretionaryLigatures == other.m_variantDiscretionaryLigatures + && m_variantHistoricalLigatures == other.m_variantHistoricalLigatures + && m_variantContextualAlternates == other.m_variantContextualAlternates + && m_variantPosition == other.m_variantPosition + && m_variantCaps == other.m_variantCaps + && m_variantNumericFigure == other.m_variantNumericFigure + && m_variantNumericSpacing == other.m_variantNumericSpacing + && m_variantNumericFraction == other.m_variantNumericFraction + && m_variantNumericOrdinal == other.m_variantNumericOrdinal + && m_variantNumericSlashedZero == other.m_variantNumericSlashedZero + && m_variantAlternates == other.m_variantAlternates + && m_variantEastAsianVariant == other.m_variantEastAsianVariant + && m_variantEastAsianWidth == other.m_variantEastAsianWidth + && m_variantEastAsianRuby == other.m_variantEastAsianRuby; +} + +// FIXME: Move to a file of its own. +class FontCascadeDescription : public FontDescription { +public: + WEBCORE_EXPORT FontCascadeDescription(); + + bool operator==(const FontCascadeDescription&) const; + bool operator!=(const FontCascadeDescription& other) const { return !(*this == other); } unsigned familyCount() const { return m_families.size(); } const AtomicString& firstFamily() const { return familyAt(0); } @@ -110,141 +208,96 @@ public: const RefCountedArray<AtomicString>& families() const { return m_families; } float specifiedSize() const { return m_specifiedSize; } - float computedSize() const { return m_computedSize; } - FontItalic italic() const { return static_cast<FontItalic>(m_italic); } - int computedPixelSize() const { return int(m_computedSize + 0.5f); } - FontSmallCaps smallCaps() const { return static_cast<FontSmallCaps>(m_smallCaps); } bool isAbsoluteSize() const { return m_isAbsoluteSize; } - FontWeight weight() const { return static_cast<FontWeight>(m_weight); } FontWeight lighterWeight() const; FontWeight bolderWeight() const; - GenericFamilyType genericFamily() const { return static_cast<GenericFamilyType>(m_genericFamily); } - bool usePrinterFont() const { return m_usePrinterFont; } + // only use fixed default size when there is only one font family, and that family is "monospace" - bool useFixedDefaultSize() const { return genericFamily() == MonospaceFamily && familyCount() == 1 && firstFamily() == monospaceFamily; } - FontRenderingMode renderingMode() const { return static_cast<FontRenderingMode>(m_renderingMode); } + bool useFixedDefaultSize() const { return familyCount() == 1 && firstFamily() == monospaceFamily; } + Kerning kerning() const { return static_cast<Kerning>(m_kerning); } - LigaturesState commonLigaturesState() const { return static_cast<LigaturesState>(m_commonLigaturesState); } - LigaturesState discretionaryLigaturesState() const { return static_cast<LigaturesState>(m_discretionaryLigaturesState); } - LigaturesState historicalLigaturesState() const { return static_cast<LigaturesState>(m_historicalLigaturesState); } unsigned keywordSize() const { return m_keywordSize; } + CSSValueID keywordSizeAsIdentifier() const + { + CSSValueID identifier = m_keywordSize ? static_cast<CSSValueID>(CSSValueXxSmall + m_keywordSize - 1) : CSSValueInvalid; + ASSERT(identifier == CSSValueInvalid || (identifier >= CSSValueXxSmall && identifier <= CSSValueWebkitXxxLarge)); + return identifier; + } FontSmoothingMode fontSmoothing() const { return static_cast<FontSmoothingMode>(m_fontSmoothing); } - TextRenderingMode textRenderingMode() const { return static_cast<TextRenderingMode>(m_textRendering); } - UScriptCode script() const { return static_cast<UScriptCode>(m_script); } - - FontTraitsMask traitsMask() const; bool isSpecifiedFont() const { return m_isSpecifiedFont; } - FontOrientation orientation() const { return static_cast<FontOrientation>(m_orientation); } - NonCJKGlyphOrientation nonCJKGlyphOrientation() const { return static_cast<NonCJKGlyphOrientation>(m_nonCJKGlyphOrientation); } - FontWidthVariant widthVariant() const { return static_cast<FontWidthVariant>(m_widthVariant); } - FontFeatureSettings* featureSettings() const { return m_featureSettings.get(); } - FontDescription makeNormalFeatureSettings() const; void setOneFamily(const AtomicString& family) { ASSERT(m_families.size() == 1); m_families[0] = family; } void setFamilies(const Vector<AtomicString>& families) { m_families = RefCountedArray<AtomicString>(families); } void setFamilies(const RefCountedArray<AtomicString>& families) { m_families = families; } - void setComputedSize(float s) { m_computedSize = clampToFloat(s); } void setSpecifiedSize(float s) { m_specifiedSize = clampToFloat(s); } - void setItalic(FontItalic i) { m_italic = i; } - void setItalic(bool i) { setItalic(i ? FontItalicOn : FontItalicOff); } - void setSmallCaps(FontSmallCaps c) { m_smallCaps = c; } - void setSmallCaps(bool c) { setSmallCaps(c ? FontSmallCapsOn : FontSmallCapsOff); } void setIsAbsoluteSize(bool s) { m_isAbsoluteSize = s; } - void setWeight(FontWeight w) { m_weight = w; } - void setGenericFamily(GenericFamilyType genericFamily) { m_genericFamily = genericFamily; } - void setUsePrinterFont(bool p) { m_usePrinterFont = p; } - void setRenderingMode(FontRenderingMode mode) { m_renderingMode = mode; } - void setKerning(Kerning kerning) { m_kerning = kerning; } - void setCommonLigaturesState(LigaturesState commonLigaturesState) { m_commonLigaturesState = commonLigaturesState; } - void setDiscretionaryLigaturesState(LigaturesState discretionaryLigaturesState) { m_discretionaryLigaturesState = discretionaryLigaturesState; } - void setHistoricalLigaturesState(LigaturesState historicalLigaturesState) { m_historicalLigaturesState = historicalLigaturesState; } - void setKeywordSize(unsigned s) { m_keywordSize = s; } + void setKerning(Kerning kerning) { m_kerning = static_cast<unsigned>(kerning); } + void setKeywordSize(unsigned size) + { + ASSERT(size <= 8); + m_keywordSize = size; + ASSERT(m_keywordSize == size); // Make sure it fits in the bitfield. + } + void setKeywordSizeFromIdentifier(CSSValueID identifier) + { + ASSERT(!identifier || (identifier >= CSSValueXxSmall && identifier <= CSSValueWebkitXxxLarge)); + static_assert(CSSValueWebkitXxxLarge - CSSValueXxSmall + 1 == 8, "Maximum keyword size should be 8."); + setKeywordSize(identifier ? identifier - CSSValueXxSmall + 1 : 0); + } void setFontSmoothing(FontSmoothingMode smoothing) { m_fontSmoothing = smoothing; } - void setTextRenderingMode(TextRenderingMode rendering) { m_textRendering = rendering; } void setIsSpecifiedFont(bool isSpecifiedFont) { m_isSpecifiedFont = isSpecifiedFont; } - void setOrientation(FontOrientation orientation) { m_orientation = orientation; } - void setNonCJKGlyphOrientation(NonCJKGlyphOrientation orientation) { m_nonCJKGlyphOrientation = orientation; } - void setWidthVariant(FontWidthVariant widthVariant) { m_widthVariant = widthVariant; } - void setScript(UScriptCode s) { m_script = s; } - void setFeatureSettings(PassRefPtr<FontFeatureSettings> settings) { m_featureSettings = settings; } -#if ENABLE(IOS_TEXT_AUTOSIZING) - bool familiesEqualForTextAutoSizing(const FontDescription& other) const; +#if ENABLE(TEXT_AUTOSIZING) + bool familiesEqualForTextAutoSizing(const FontCascadeDescription& other) const; - bool equalForTextAutoSizing(const FontDescription& other) const + bool equalForTextAutoSizing(const FontCascadeDescription& other) const { return familiesEqualForTextAutoSizing(other) && m_specifiedSize == other.m_specifiedSize - && m_smallCaps == other.m_smallCaps - && m_isAbsoluteSize == other.m_isAbsoluteSize - && m_genericFamily == other.m_genericFamily - && m_usePrinterFont == other.m_usePrinterFont; + && variantSettings() == other.variantSettings() + && m_isAbsoluteSize == other.m_isAbsoluteSize; } #endif + // Initial values for font properties. + static FontItalic initialItalic() { return FontItalicOff; } + static FontSmallCaps initialSmallCaps() { return FontSmallCapsOff; } + static Kerning initialKerning() { return Kerning::Auto; } + static FontSmoothingMode initialFontSmoothing() { return AutoSmoothing; } + static TextRenderingMode initialTextRenderingMode() { return AutoTextRendering; } + static FontSynthesis initialFontSynthesis() { return FontSynthesisWeight | FontSynthesisStyle | FontSynthesisSmallCaps; } + static FontVariantPosition initialVariantPosition() { return FontVariantPosition::Normal; } + static FontVariantCaps initialVariantCaps() { return FontVariantCaps::Normal; } + static FontVariantAlternates initialVariantAlternates() { return FontVariantAlternates::Normal; } + static const AtomicString& initialLocale() { return nullAtom; } + private: - RefCountedArray<AtomicString> m_families; - RefPtr<FontFeatureSettings> m_featureSettings; + RefCountedArray<AtomicString> m_families { 1 }; - float m_specifiedSize; // Specified CSS value. Independent of rendering issues such as integer + float m_specifiedSize { 0 }; // Specified CSS value. Independent of rendering issues such as integer // rounding, minimum font sizes, and zooming. - float m_computedSize; // Computed size adjusted for the minimum font size and the zoom factor. - - unsigned m_orientation : 1; // FontOrientation - Whether the font is rendering on a horizontal line or a vertical line. - unsigned m_nonCJKGlyphOrientation : 1; // NonCJKGlyphOrientation - Only used by vertical text. Determines the default orientation for non-ideograph glyphs. - - unsigned m_widthVariant : 2; // FontWidthVariant - - unsigned m_italic : 1; // FontItalic - unsigned m_smallCaps : 1; // FontSmallCaps unsigned m_isAbsoluteSize : 1; // Whether or not CSS specified an explicit size // (logical sizes like "medium" don't count). - unsigned m_weight : 8; // FontWeight - unsigned m_genericFamily : 3; // GenericFamilyType - unsigned m_usePrinterFont : 1; - - unsigned m_renderingMode : 1; // Used to switch between CG and GDI text on Windows. unsigned m_kerning : 2; // Kerning - unsigned m_commonLigaturesState : 2; - unsigned m_discretionaryLigaturesState : 2; - unsigned m_historicalLigaturesState : 2; - unsigned m_keywordSize : 4; // We cache whether or not a font is currently represented by a CSS keyword (e.g., medium). If so, // then we can accurately translate across different generic families to adjust for different preference settings // (e.g., 13px monospace vs. 16px everything else). Sizes are 1-8 (like the HTML size values for <font>). unsigned m_fontSmoothing : 2; // FontSmoothingMode - unsigned m_textRendering : 2; // TextRenderingMode unsigned m_isSpecifiedFont : 1; // True if a web page specifies a non-generic font family as the first font family. - unsigned m_script : 7; // Used to help choose an appropriate font for generic font families. }; -inline bool FontDescription::operator==(const FontDescription& other) const +inline bool FontCascadeDescription::operator==(const FontCascadeDescription& other) const { - return m_families == other.m_families + return FontDescription::operator==(other) + && m_families == other.m_families && m_specifiedSize == other.m_specifiedSize - && m_computedSize == other.m_computedSize - && m_italic == other.m_italic - && m_smallCaps == other.m_smallCaps && m_isAbsoluteSize == other.m_isAbsoluteSize - && m_weight == other.m_weight - && m_genericFamily == other.m_genericFamily - && m_usePrinterFont == other.m_usePrinterFont - && m_renderingMode == other.m_renderingMode && m_kerning == other.m_kerning - && m_commonLigaturesState == other.m_commonLigaturesState - && m_discretionaryLigaturesState == other.m_discretionaryLigaturesState - && m_historicalLigaturesState == other.m_historicalLigaturesState && m_keywordSize == other.m_keywordSize && m_fontSmoothing == other.m_fontSmoothing - && m_textRendering == other.m_textRendering - && m_isSpecifiedFont == other.m_isSpecifiedFont - && m_orientation == other.m_orientation - && m_nonCJKGlyphOrientation == other.m_nonCJKGlyphOrientation - && m_widthVariant == other.m_widthVariant - && m_script == other.m_script - && m_featureSettings == other.m_featureSettings; + && m_isSpecifiedFont == other.m_isSpecifiedFont; } } diff --git a/Source/WebCore/platform/graphics/FontFastPath.cpp b/Source/WebCore/platform/graphics/FontFastPath.cpp deleted file mode 100644 index 6602c3b66..000000000 --- a/Source/WebCore/platform/graphics/FontFastPath.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/** - * Copyright (C) 2003, 2006, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2008 Holger Hans Peter Freyther - * Copyright (C) 2009 Torch Mobile, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "Font.h" - -#include "FloatRect.h" -#include "FontCache.h" -#include "FontGlyphs.h" -#include "GlyphBuffer.h" -#include "GlyphPageTreeNode.h" -#include "SimpleFontData.h" -#include "TextRun.h" -#include "WidthIterator.h" -#include <wtf/MainThread.h> -#include <wtf/MathExtras.h> -#include <wtf/unicode/CharacterNames.h> -#include <wtf/unicode/Unicode.h> - -using namespace WTF; -using namespace Unicode; - -namespace WebCore { - -bool Font::primaryFontHasGlyphForCharacter(UChar32 character) const -{ - unsigned pageNumber = (character / GlyphPage::size); - - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber); - GlyphPage* page = node->page(); - - return page && page->fontDataForCharacter(character); -} - -// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the -// standard emphasis marks do so. -bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, GlyphData& glyphData) const -{ - if (mark.isEmpty()) - return false; - - UChar32 character = mark[0]; - - if (U16_IS_SURROGATE(character)) { - if (!U16_IS_SURROGATE_LEAD(character)) - return false; - - if (mark.length() < 2) - return false; - - UChar low = mark[1]; - if (!U16_IS_TRAIL(low)) - return false; - - character = U16_GET_SUPPLEMENTARY(character, low); - } - - glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant); - return true; -} - -int Font::emphasisMarkAscent(const AtomicString& mark) const -{ - FontCachePurgePreventer purgePreventer; - - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return 0; - - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return 0; - - return markFontData->fontMetrics().ascent(); -} - -int Font::emphasisMarkDescent(const AtomicString& mark) const -{ - FontCachePurgePreventer purgePreventer; - - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return 0; - - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return 0; - - return markFontData->fontMetrics().descent(); -} - -int Font::emphasisMarkHeight(const AtomicString& mark) const -{ - FontCachePurgePreventer purgePreventer; - - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return 0; - - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return 0; - - return markFontData->fontMetrics().height(); -} - -float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const -{ - WidthIterator it(this, run, 0, false, forTextEmphasis); - GlyphBuffer localGlyphBuffer; - it.advance(run.length(), &localGlyphBuffer); - - if (localGlyphBuffer.isEmpty()) - return 0; - - float totalWidth = it.m_runWidthSoFar; - float beforeWidth = 0; - int glyphPos = 0; - for (; glyphPos < localGlyphBuffer.size() && it.m_characterIndexOfGlyph[glyphPos] < from; ++glyphPos) - beforeWidth += localGlyphBuffer.advanceAt(glyphPos).width(); - int glyphFrom = glyphPos; - - float afterWidth = totalWidth; - glyphPos = localGlyphBuffer.size() - 1; - for (; glyphPos >= glyphFrom && it.m_characterIndexOfGlyph[glyphPos] >= to; --glyphPos) - afterWidth -= localGlyphBuffer.advanceAt(glyphPos).width(); - int glyphTo = glyphPos + 1; - - glyphBuffer.add(&localGlyphBuffer, glyphFrom, glyphTo - glyphFrom); - - if (run.rtl()) { - glyphBuffer.reverse(0, glyphBuffer.size()); - return totalWidth - afterWidth; - } - - return beforeWidth; -} - -float Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const -{ - // This glyph buffer holds our glyphs+advances+font data for each glyph. - GlyphBuffer glyphBuffer; - - float startX = point.x() + getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer); - - if (glyphBuffer.isEmpty()) - return 0; - - FloatPoint startPoint(startX, point.y()); - drawGlyphBuffer(context, run, glyphBuffer, startPoint); - - return startPoint.x() - startX; -} - -void Font::drawEmphasisMarksForSimpleText(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const -{ - GlyphBuffer glyphBuffer; - float initialAdvance = getGlyphsAndAdvancesForSimpleText(run, from, to, glyphBuffer, ForTextEmphasis); - - if (glyphBuffer.isEmpty()) - return; - - drawEmphasisMarks(context, run, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y())); -} - -void Font::drawGlyphBuffer(GraphicsContext* context, const TextRun& run, const GlyphBuffer& glyphBuffer, FloatPoint& point) const -{ -#if !ENABLE(SVG_FONTS) - UNUSED_PARAM(run); -#endif - - // Draw each contiguous run of glyphs that use the same font data. - const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); - FloatSize offset = glyphBuffer.offsetAt(0); - FloatPoint startPoint(point.x(), point.y() - glyphBuffer.initialAdvance().height()); - float nextX = startPoint.x() + glyphBuffer.advanceAt(0).width(); - float nextY = startPoint.y() + glyphBuffer.advanceAt(0).height(); - int lastFrom = 0; - int nextGlyph = 1; -#if ENABLE(SVG_FONTS) - TextRun::RenderingContext* renderingContext = run.renderingContext(); -#endif - while (nextGlyph < glyphBuffer.size()) { - const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph); - FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph); - - if (nextFontData != fontData || nextOffset != offset) { -#if ENABLE(SVG_FONTS) - if (renderingContext && fontData->isSVGFont()) - renderingContext->drawSVGGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); - else -#endif - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); - - lastFrom = nextGlyph; - fontData = nextFontData; - offset = nextOffset; - startPoint.setX(nextX); - startPoint.setY(nextY); - } - nextX += glyphBuffer.advanceAt(nextGlyph).width(); - nextY += glyphBuffer.advanceAt(nextGlyph).height(); - nextGlyph++; - } - -#if ENABLE(SVG_FONTS) - if (renderingContext && fontData->isSVGFont()) - renderingContext->drawSVGGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); - else -#endif - { - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint); - point.setX(nextX); - } -} - -inline static float offsetToMiddleOfGlyph(const SimpleFontData* fontData, Glyph glyph) -{ - if (fontData->platformData().orientation() == Horizontal) { - FloatRect bounds = fontData->boundsForGlyph(glyph); - return bounds.x() + bounds.width() / 2; - } - // FIXME: Use glyph bounds once they make sense for vertical fonts. - return fontData->widthForGlyph(glyph) / 2; -} - -inline static float offsetToMiddleOfGlyphAtIndex(const GlyphBuffer& glyphBuffer, size_t i) -{ - return offsetToMiddleOfGlyph(glyphBuffer.fontDataAt(i), glyphBuffer.glyphAt(i)); -} - -void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const -{ - FontCachePurgePreventer purgePreventer; - - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return; - - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return; - - Glyph markGlyph = markGlyphData.glyph; - Glyph spaceGlyph = markFontData->spaceGlyph(); - - float middleOfLastGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, 0); - FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y()); - - GlyphBuffer markBuffer; - for (int i = 0; i + 1 < glyphBuffer.size(); ++i) { - float middleOfNextGlyph = offsetToMiddleOfGlyphAtIndex(glyphBuffer, i + 1); - float advance = glyphBuffer.advanceAt(i).width() - middleOfLastGlyph + middleOfNextGlyph; - markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance); - middleOfLastGlyph = middleOfNextGlyph; - } - markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0); - - drawGlyphBuffer(context, run, markBuffer, startPoint); -} - -float Font::floatWidthForSimpleText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const -{ - WidthIterator it(this, run, fallbackFonts, glyphOverflow); - GlyphBuffer glyphBuffer; - it.advance(run.length(), (typesettingFeatures() & (Kerning | Ligatures)) ? &glyphBuffer : 0); - - if (glyphOverflow) { - glyphOverflow->top = std::max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent())); - glyphOverflow->bottom = std::max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent())); - glyphOverflow->left = ceilf(it.firstGlyphOverflow()); - glyphOverflow->right = ceilf(it.lastGlyphOverflow()); - } - - return it.m_runWidthSoFar; -} - -FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const -{ - GlyphBuffer glyphBuffer; - WidthIterator it(this, run); - it.advance(run.length(), &glyphBuffer); - - float totalWidth = it.m_runWidthSoFar; - float beforeWidth = 0; - int glyphPos = 0; - for (; glyphPos < glyphBuffer.size() && it.m_characterIndexOfGlyph[glyphPos] < from; ++glyphPos) - beforeWidth += glyphBuffer.advanceAt(glyphPos).width(); - int glyphFrom = glyphPos; - - float afterWidth = totalWidth; - glyphPos = glyphBuffer.size() - 1; - for (; glyphPos >= glyphFrom && it.m_characterIndexOfGlyph[glyphPos] >= to; --glyphPos) - afterWidth -= glyphBuffer.advanceAt(glyphPos).width(); - - // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning. - if (run.rtl()) { - return FloatRect(floorf(point.x() + totalWidth - afterWidth), point.y(), roundf(point.x() + totalWidth - beforeWidth) - floorf(point.x() + totalWidth - afterWidth), h); - } - - return FloatRect(floorf(point.x() + beforeWidth), point.y(), roundf(point.x() + afterWidth) - floorf(point.x() + beforeWidth), h); -} - -int Font::offsetForPositionForSimpleText(const TextRun& run, float x, bool includePartialGlyphs) const -{ - GlyphBuffer glyphBuffer; - WidthIterator it(this, run); - it.advance(run.length(), &glyphBuffer); - - int characterOffset = 0; - if (run.rtl()) { - float currentX = it.m_runWidthSoFar; - for (int glyphPosition = 0; glyphPosition <= glyphBuffer.size(); ++glyphPosition) { - if (glyphPosition == glyphBuffer.size()) { - characterOffset = run.length(); - break; - } - characterOffset = it.m_characterIndexOfGlyph[glyphPosition]; - float glyphWidth = glyphBuffer.advanceAt(glyphPosition).width(); - if (includePartialGlyphs) { - if (currentX - glyphWidth / 2.0f <= x) - break; - } else { - if (currentX - glyphWidth <= x) - break; - } - currentX -= glyphWidth; - } - } else { - float currentX = 0; - for (int glyphPosition = 0; glyphPosition <= glyphBuffer.size(); ++glyphPosition) { - if (glyphPosition == glyphBuffer.size()) { - characterOffset = run.length(); - break; - } - characterOffset = it.m_characterIndexOfGlyph[glyphPosition]; - float glyphWidth = glyphBuffer.advanceAt(glyphPosition).width(); - if (includePartialGlyphs) { - if (currentX + glyphWidth / 2.0f >= x) - break; - } else { - if (currentX + glyphWidth >= x) - break; - } - currentX += glyphWidth; - } - } - - return characterOffset; -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FontFeatureSettings.cpp b/Source/WebCore/platform/graphics/FontFeatureSettings.cpp deleted file mode 100644 index 3713322f4..000000000 --- a/Source/WebCore/platform/graphics/FontFeatureSettings.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "FontFeatureSettings.h" - -namespace WebCore { - -FontFeature::FontFeature(const AtomicString& tag, int value) - : m_tag(tag) - , m_value(value) -{ -} - -bool FontFeature::operator==(const FontFeature& other) -{ - return m_tag == other.m_tag && m_value == other.m_value; -} - -FontFeatureSettings::FontFeatureSettings() -{ -} - -} diff --git a/Source/WebCore/platform/graphics/FontFeatureSettings.h b/Source/WebCore/platform/graphics/FontFeatureSettings.h deleted file mode 100644 index 1bd9b339e..000000000 --- a/Source/WebCore/platform/graphics/FontFeatureSettings.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FontFeatureSettings_h -#define FontFeatureSettings_h - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> -#include <wtf/text/AtomicString.h> - -namespace WebCore { - -class FontFeature { -public: - FontFeature(const AtomicString& tag, int value); - bool operator==(const FontFeature&); - - const AtomicString& tag() const { return m_tag; } - int value() const { return m_value; } - -private: - AtomicString m_tag; - const int m_value; -}; - -class FontFeatureSettings : public RefCounted<FontFeatureSettings> { -public: - static PassRefPtr<FontFeatureSettings> create() - { - return adoptRef(new FontFeatureSettings()); - } - void append(const FontFeature& feature) { m_list.append(feature); } - size_t size() const { return m_list.size(); } - const FontFeature& operator[](int index) const { return m_list[index]; } - const FontFeature& at(size_t index) const { return m_list.at(index); } - -private: - FontFeatureSettings(); - Vector<FontFeature> m_list; -}; - -} - -#endif // FontFeatureSettings_h diff --git a/Source/WebCore/platform/graphics/FontGenericFamilies.cpp b/Source/WebCore/platform/graphics/FontGenericFamilies.cpp index e08255347..655eee3a4 100644 --- a/Source/WebCore/platform/graphics/FontGenericFamilies.cpp +++ b/Source/WebCore/platform/graphics/FontGenericFamilies.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "FontGenericFamilies.h" +#include "Language.h" namespace WebCore { @@ -46,11 +47,41 @@ static bool setGenericFontFamilyForScript(ScriptFontFamilyMap& fontMap, const At return true; } +static inline bool computeUserPrefersSimplified() +{ + const Vector<String>& preferredLanguages = userPreferredLanguages(); + for (auto& language : preferredLanguages) { + if (equalIgnoringASCIICase(language, "zh-tw")) + return false; + if (equalIgnoringASCIICase(language, "zh-cn")) + return true; + } + return true; +} + +static bool& cachedUserPrefersSimplified() +{ + static bool cached = true; + return cached; +} + +static void languageChanged(void*) +{ + cachedUserPrefersSimplified() = computeUserPrefersSimplified(); +} + static const AtomicString& genericFontFamilyForScript(const ScriptFontFamilyMap& fontMap, UScriptCode script) { ScriptFontFamilyMap::const_iterator it = fontMap.find(static_cast<int>(script)); if (it != fontMap.end()) return it->value; + // Content using USCRIPT_HAN doesn't tell us if we should be using Simplified Chinese or Traditional Chinese. In the + // absence of all other signals, we consult with the user's system preferences. + if (script == USCRIPT_HAN) { + it = fontMap.find(static_cast<int>(cachedUserPrefersSimplified() ? USCRIPT_SIMPLIFIED_HAN : USCRIPT_TRADITIONAL_HAN)); + if (it != fontMap.end()) + return it->value; + } if (script != USCRIPT_COMMON) return genericFontFamilyForScript(fontMap, USCRIPT_COMMON); return emptyAtom; @@ -58,6 +89,8 @@ static const AtomicString& genericFontFamilyForScript(const ScriptFontFamilyMap& FontGenericFamilies::FontGenericFamilies() { + addLanguageChangeObserver(this, &languageChanged); + languageChanged(nullptr); } const AtomicString& FontGenericFamilies::standardFontFamily(UScriptCode script) const diff --git a/Source/WebCore/platform/graphics/FontGenericFamilies.h b/Source/WebCore/platform/graphics/FontGenericFamilies.h index d0d1dabab..f762bae2f 100644 --- a/Source/WebCore/platform/graphics/FontGenericFamilies.h +++ b/Source/WebCore/platform/graphics/FontGenericFamilies.h @@ -26,6 +26,7 @@ #ifndef FontGenericFamilies_h #define FontGenericFamilies_h +#include <unicode/uscript.h> #include <wtf/HashMap.h> #include <wtf/text/AtomicString.h> @@ -43,6 +44,7 @@ struct UScriptCodeHashTraits : WTF::GenericHashTraits<int> { typedef HashMap<int, AtomicString, DefaultHash<int>::Hash, UScriptCodeHashTraits> ScriptFontFamilyMap; class FontGenericFamilies { + WTF_MAKE_FAST_ALLOCATED; public: FontGenericFamilies(); diff --git a/Source/WebCore/platform/graphics/FontGlyphs.cpp b/Source/WebCore/platform/graphics/FontGlyphs.cpp deleted file mode 100644 index 819e371ce..000000000 --- a/Source/WebCore/platform/graphics/FontGlyphs.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright (C) 2006, 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "FontGlyphs.h" - -#include "Font.h" -#include "FontCache.h" -#include "SegmentedFontData.h" -#include <wtf/unicode/Unicode.h> - -namespace WebCore { - - -FontGlyphs::FontGlyphs(PassRefPtr<FontSelector> fontSelector) - : m_pageZero(0) - , m_cachedPrimarySimpleFontData(0) - , m_fontSelector(fontSelector) - , m_fontSelectorVersion(m_fontSelector ? m_fontSelector->version() : 0) - , m_familyIndex(0) - , m_generation(fontCache()->generation()) - , m_pitch(UnknownPitch) - , m_loadingCustomFonts(false) - , m_isForPlatformFont(false) -{ -} - -FontGlyphs::FontGlyphs(const FontPlatformData& platformData) - : m_pageZero(0) - , m_cachedPrimarySimpleFontData(0) - , m_fontSelector(0) - , m_fontSelectorVersion(0) - , m_familyIndex(cAllFamiliesScanned) - , m_generation(fontCache()->generation()) - , m_pitch(UnknownPitch) - , m_loadingCustomFonts(false) - , m_isForPlatformFont(true) -{ - RefPtr<FontData> fontData = fontCache()->getCachedFontData(&platformData); - m_realizedFontData.append(fontData.release()); -} - -void FontGlyphs::releaseFontData() -{ - unsigned numFonts = m_realizedFontData.size(); - for (unsigned i = 0; i < numFonts; ++i) { - if (m_realizedFontData[i]->isCustomFont()) - continue; - ASSERT(!m_realizedFontData[i]->isSegmented()); - fontCache()->releaseFontData(static_cast<const SimpleFontData*>(m_realizedFontData[i].get())); - } -} - -void FontGlyphs::determinePitch(const FontDescription& description) const -{ - const FontData* fontData = primaryFontData(description); - if (!fontData->isSegmented()) - m_pitch = static_cast<const SimpleFontData*>(fontData)->pitch(); - else { - const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData); - unsigned numRanges = segmentedFontData->numRanges(); - if (numRanges == 1) - m_pitch = segmentedFontData->rangeAt(0).fontData()->pitch(); - else - m_pitch = VariablePitch; - } -} - -const FontData* FontGlyphs::realizeFontDataAt(const FontDescription& description, unsigned realizedFontIndex) const -{ - if (realizedFontIndex < m_realizedFontData.size()) - return m_realizedFontData[realizedFontIndex].get(); // This fallback font is already in our list. - - // Make sure we're not passing in some crazy value here. - ASSERT(realizedFontIndex == m_realizedFontData.size()); - - if (m_familyIndex <= cAllFamiliesScanned) { - if (!m_fontSelector) - return 0; - - size_t index = cAllFamiliesScanned - m_familyIndex; - if (index == m_fontSelector->fallbackFontDataCount()) - return 0; - - m_familyIndex--; - RefPtr<FontData> fallback = m_fontSelector->getFallbackFontData(description, index); - if (fallback) - m_realizedFontData.append(fallback); - return fallback.get(); - } - - // Ask the font cache for the font data. - // We are obtaining this font for the first time. We keep track of the families we've looked at before - // in |m_familyIndex|, so that we never scan the same spot in the list twice. getFontData will adjust our - // |m_familyIndex| as it scans for the right font to make. - ASSERT(fontCache()->generation() == m_generation); - RefPtr<FontData> result = fontCache()->getFontData(description, m_familyIndex, m_fontSelector.get()); - if (result) { - m_realizedFontData.append(result); - if (result->isLoading()) - m_loadingCustomFonts = true; - } - return result.get(); -} - -static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound) -{ - return character >= lowerBound && character <= upperBound; -} - -static bool shouldIgnoreRotation(UChar32 character) -{ - if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE) - return true; - - if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE) - return true; - - if (isInRange(character, 0x002E5, 0x002EB)) - return true; - - if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF)) - return true; - - if (character == 0x02016 || character == 0x02018 || character == 0x02019 || character == 0x02020 || character == 0x02021 - || character == 0x2030 || character == 0x02031) - return true; - - if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047 - || character == 0x02048 || character == 0x02049 || character == 0x2051) - return true; - - if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0) - || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117) - || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F)) - return true; - - if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D - || isInRange(character, 0x0214F, 0x0218F)) - return true; - - if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F) - || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A) - || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF) - || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF)) - return true; - - if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767) - || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F) - || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007)) - return true; - - if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F) - || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB) - || isInRange(character, 0x030FD, 0x0A4CF)) - return true; - - if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F) - || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF)) - return true; - - if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48) - || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62) - || isInRange(character, 0x0FE67, 0x0FE6F)) - return true; - - if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C) - || isInRange(character, 0x0FF0E, 0x0FF19) || character == 0x0FF1B || isInRange(character, 0x0FF1F, 0x0FF3A)) - return true; - - if (character == 0x0FF3C || character == 0x0FF3E) - return true; - - if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2) - || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8) - || character == 0x0FFFD) - return true; - - if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF) - || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F) - || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F)) - return true; - - if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD)) - return true; - - return false; -} - -static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCJKCharacterWithoutSyntheticItalic(UChar32 character, GlyphData& data, GlyphPage* page, unsigned pageNumber) -{ - RefPtr<SimpleFontData> nonItalicFontData = data.fontData->nonSyntheticItalicFontData(); - GlyphPageTreeNode* nonItalicNode = GlyphPageTreeNode::getRootChild(nonItalicFontData.get(), pageNumber); - GlyphPage* nonItalicPage = nonItalicNode->page(); - if (nonItalicPage) { - GlyphData nonItalicData = nonItalicPage->glyphDataForCharacter(character); - if (nonItalicData.fontData) - return std::make_pair(nonItalicData, nonItalicPage); - } - return std::make_pair(data, page); -} - -static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, GlyphData& data, GlyphPage* page, unsigned pageNumber) -{ - if (orientation == NonCJKGlyphOrientationUpright || shouldIgnoreRotation(character)) { - RefPtr<SimpleFontData> uprightFontData = data.fontData->uprightOrientationFontData(); - GlyphPageTreeNode* uprightNode = GlyphPageTreeNode::getRootChild(uprightFontData.get(), pageNumber); - GlyphPage* uprightPage = uprightNode->page(); - if (uprightPage) { - GlyphData uprightData = uprightPage->glyphDataForCharacter(character); - // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright. - if (data.glyph == uprightData.glyph) - return std::make_pair(data, page); - // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that - // glyph, so we fall back to the upright data and use the horizontal glyph. - if (uprightData.fontData) - return std::make_pair(uprightData, uprightPage); - } - } else if (orientation == NonCJKGlyphOrientationVerticalRight) { - RefPtr<SimpleFontData> verticalRightFontData = data.fontData->verticalRightOrientationFontData(); - GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild(verticalRightFontData.get(), pageNumber); - GlyphPage* verticalRightPage = verticalRightNode->page(); - if (verticalRightPage) { - GlyphData verticalRightData = verticalRightPage->glyphDataForCharacter(character); - // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked - // into it. - if (data.glyph != verticalRightData.glyph) - return std::make_pair(data, page); - // The glyphs are identical, meaning that we should just use the horizontal glyph. - if (verticalRightData.fontData) - return std::make_pair(verticalRightData, verticalRightPage); - } - } - return std::make_pair(data, page); -} - -std::pair<GlyphData, GlyphPage*> FontGlyphs::glyphDataAndPageForCharacter(const FontDescription& description, UChar32 c, bool mirror, FontDataVariant variant) const -{ - ASSERT(isMainThread()); - - if (variant == AutoVariant) { - if (description.smallCaps() && !primarySimpleFontData(description)->isSVGFont()) { - UChar32 upperC = u_toupper(c); - if (upperC != c) { - c = upperC; - variant = SmallCapsVariant; - } else - variant = NormalVariant; - } else - variant = NormalVariant; - } - - if (mirror) - c = u_charMirror(c); - - unsigned pageNumber = (c / GlyphPage::size); - - GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; - if (!node) { - node = GlyphPageTreeNode::getRootChild(realizeFontDataAt(description, 0), pageNumber); - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; - } - - GlyphPage* page = 0; - if (variant == NormalVariant) { - // Fastest loop, for the common case (normal variant). - while (true) { - page = node->page(); - if (page) { - GlyphData data = page->glyphDataForCharacter(c); - if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback())) - return std::make_pair(data, page); - - if (data.fontData) { - if (Font::isCJKIdeographOrSymbol(c)) { - if (!data.fontData->hasVerticalGlyphs()) { - // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs - // to make sure you get a square (even for broken glyphs like symbols used for punctuation). - variant = BrokenIdeographVariant; - break; - } -#if PLATFORM(MAC) - else if (data.fontData->platformData().syntheticOblique()) - return glyphDataAndPageForCJKCharacterWithoutSyntheticItalic(c, data, page, pageNumber); -#endif - } else - return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data, page, pageNumber); - - return std::make_pair(data, page); - } - - if (node->isSystemFallback()) - break; - } - - node = node->getChild(realizeFontDataAt(description, node->level()), pageNumber); - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; - } - } - if (variant != NormalVariant) { - while (true) { - page = node->page(); - if (page) { - GlyphData data = page->glyphDataForCharacter(c); - if (data.fontData) { - // The variantFontData function should not normally return 0. - // But if it does, we will just render the capital letter big. - RefPtr<SimpleFontData> variantFontData = data.fontData->variantFontData(description, variant); - if (!variantFontData) - return std::make_pair(data, page); - - GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData.get(), pageNumber); - GlyphPage* variantPage = variantNode->page(); - if (variantPage) { - GlyphData data = variantPage->glyphDataForCharacter(c); - if (data.fontData) - return std::make_pair(data, variantPage); - } - - // Do not attempt system fallback off the variantFontData. This is the very unlikely case that - // a font has the lowercase character but the small caps font does not have its uppercase version. - return std::make_pair(variantFontData->missingGlyphData(), page); - } - - if (node->isSystemFallback()) - break; - } - - node = node->getChild(realizeFontDataAt(description, node->level()), pageNumber); - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; - } - } - - ASSERT(page); - ASSERT(node->isSystemFallback()); - - // System fallback is character-dependent. When we get here, we - // know that the character in question isn't in the system fallback - // font's glyph page. Try to lazily create it here. - UChar codeUnits[2]; - int codeUnitsLength; - if (c <= 0xFFFF) { - codeUnits[0] = Font::normalizeSpaces(c); - codeUnitsLength = 1; - } else { - codeUnits[0] = U16_LEAD(c); - codeUnits[1] = U16_TRAIL(c); - codeUnitsLength = 2; - } - const SimpleFontData* originalFontData = primaryFontData(description)->fontDataForCharacter(c); - RefPtr<SimpleFontData> characterFontData = fontCache()->systemFallbackForCharacters(description, originalFontData, m_isForPlatformFont, codeUnits, codeUnitsLength); - if (characterFontData) { - if (characterFontData->platformData().orientation() == Vertical && !characterFontData->hasVerticalGlyphs() && Font::isCJKIdeographOrSymbol(c)) - variant = BrokenIdeographVariant; - if (variant != NormalVariant) - characterFontData = characterFontData->variantFontData(description, variant); - } - if (characterFontData) { - // Got the fallback glyph and font. - GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber)->page(); - GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); - // Cache it so we don't have to do system fallback again next time. - if (variant == NormalVariant) { -#if OS(WINCE) - // missingGlyphData returns a null character, which is not suitable for GDI to display. - // Also, sometimes we cannot map a font for the character on WINCE, but GDI can still - // display the character, probably because the font package is not installed correctly. - // So we just always set the glyph to be same as the character, and let GDI solve it. - page->setGlyphDataForCharacter(c, c, characterFontData.get()); - characterFontData->setMaxGlyphPageTreeLevel(std::max(characterFontData->maxGlyphPageTreeLevel(), node->level())); - return std::make_pair(page->glyphDataForCharacter(c), page); -#else - page->setGlyphDataForCharacter(c, data.glyph, data.fontData); - data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); - if (!Font::isCJKIdeographOrSymbol(c) && data.fontData->platformData().orientation() != Horizontal && !data.fontData->isTextOrientationFallback()) - return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data, fallbackPage, pageNumber); -#endif - } - return std::make_pair(data, page); - } - - // Even system fallback can fail; use the missing glyph in that case. - // FIXME: It would be nicer to use the missing glyph from the last resort font instead. - GlyphData data = primarySimpleFontData(description)->missingGlyphData(); - if (variant == NormalVariant) { -#if OS(WINCE) - // See comment about WINCE GDI handling near setGlyphDataForCharacter above. - page->setGlyphDataForCharacter(c, c, data.fontData); - data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); - return std::make_pair(page->glyphDataForCharacter(c), page); -#else - page->setGlyphDataForCharacter(c, data.glyph, data.fontData); - data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); -#endif - } - return std::make_pair(data, page); -} - - -} diff --git a/Source/WebCore/platform/graphics/FontGlyphs.h b/Source/WebCore/platform/graphics/FontGlyphs.h deleted file mode 100644 index 4c70d7df2..000000000 --- a/Source/WebCore/platform/graphics/FontGlyphs.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2006, 2010, 2013 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef FontGlyphs_h -#define FontGlyphs_h - -#include "FontSelector.h" -#include "SimpleFontData.h" -#include "WidthCache.h" -#include <wtf/Forward.h> -#include <wtf/MainThread.h> - -#if PLATFORM(IOS) -#include "WebCoreThread.h" -#endif - -namespace WebCore { - -class GlyphPageTreeNode; -class GraphicsContext; -class IntRect; -class FontDescription; -class FontPlatformData; -class FontSelector; - -const int cAllFamiliesScanned = -1; - -class FontGlyphs : public RefCounted<FontGlyphs> { - WTF_MAKE_NONCOPYABLE(FontGlyphs); -public: - typedef HashMap<int, GlyphPageTreeNode*, DefaultHash<int>::Hash> GlyphPages; - - class GlyphPagesStateSaver { - public: - GlyphPagesStateSaver(FontGlyphs& glyphs) - : m_glyphs(glyphs) - , m_pages(glyphs.m_pages) - , m_pageZero(glyphs.m_pageZero) - { - } - - ~GlyphPagesStateSaver() - { - m_glyphs.m_pages = m_pages; - m_glyphs.m_pageZero = m_pageZero; - } - - private: - FontGlyphs& m_glyphs; - GlyphPages& m_pages; - GlyphPageTreeNode* m_pageZero; - }; - - static PassRef<FontGlyphs> create(PassRefPtr<FontSelector> fontSelector) { return adoptRef(*new FontGlyphs(fontSelector)); } - static PassRef<FontGlyphs> createForPlatformFont(const FontPlatformData& platformData) { return adoptRef(*new FontGlyphs(platformData)); } - - ~FontGlyphs() { releaseFontData(); } - - bool isForPlatformFont() const { return m_isForPlatformFont; } - - std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCharacter(const FontDescription&, UChar32, bool mirror, FontDataVariant) const; - - bool isFixedPitch(const FontDescription&) const; - void determinePitch(const FontDescription&) const; - - bool loadingCustomFonts() const { return m_loadingCustomFonts; } - - FontSelector* fontSelector() const { return m_fontSelector.get(); } - // FIXME: It should be possible to combine fontSelectorVersion and generation. - unsigned fontSelectorVersion() const { return m_fontSelectorVersion; } - unsigned generation() const { return m_generation; } - - WidthCache& widthCache() const { return m_widthCache; } - - const SimpleFontData* primarySimpleFontData(const FontDescription&) const; - const FontData* primaryFontData(const FontDescription& description) const { return realizeFontDataAt(description, 0); } - const FontData* realizeFontDataAt(const FontDescription&, unsigned index) const; - -private: - FontGlyphs(PassRefPtr<FontSelector>); - FontGlyphs(const FontPlatformData&); - - void releaseFontData(); - - mutable Vector<RefPtr<FontData>, 1> m_realizedFontData; - mutable GlyphPages m_pages; - mutable GlyphPageTreeNode* m_pageZero; - mutable const SimpleFontData* m_cachedPrimarySimpleFontData; - RefPtr<FontSelector> m_fontSelector; - mutable WidthCache m_widthCache; - unsigned m_fontSelectorVersion; - mutable int m_familyIndex; - unsigned short m_generation; - mutable unsigned m_pitch : 3; // Pitch - mutable bool m_loadingCustomFonts : 1; - bool m_isForPlatformFont : 1; -}; - -inline bool FontGlyphs::isFixedPitch(const FontDescription& description) const -{ - if (m_pitch == UnknownPitch) - determinePitch(description); - return m_pitch == FixedPitch; -}; - -inline const SimpleFontData* FontGlyphs::primarySimpleFontData(const FontDescription& description) const -{ - ASSERT(isMainThread()); - if (!m_cachedPrimarySimpleFontData) - m_cachedPrimarySimpleFontData = primaryFontData(description)->fontDataForCharacter(' '); - return m_cachedPrimarySimpleFontData; -} - -} - -#endif diff --git a/Source/WebCore/platform/graphics/FontMetrics.h b/Source/WebCore/platform/graphics/FontMetrics.h index 5142ffe25..090d5ca9c 100644 --- a/Source/WebCore/platform/graphics/FontMetrics.h +++ b/Source/WebCore/platform/graphics/FontMetrics.h @@ -22,25 +22,13 @@ #include "FontBaseline.h" #include <wtf/MathExtras.h> +#include <wtf/Optional.h> namespace WebCore { -const unsigned gDefaultUnitsPerEm = 1000; - class FontMetrics { public: - FontMetrics() - : m_unitsPerEm(gDefaultUnitsPerEm) - , m_ascent(0) - , m_descent(0) - , m_lineGap(0) - , m_lineSpacing(0) - , m_xHeight(0) - , m_zeroWidth(0) - , m_hasXHeight(false) - , m_hasZeroWidth(false) - { - } + static const unsigned defaultUnitsPerEm = 1000; unsigned unitsPerEm() const { return m_unitsPerEm; } void setUnitsPerEm(unsigned unitsPerEm) { m_unitsPerEm = unitsPerEm; } @@ -75,15 +63,13 @@ public: void setLineSpacing(float lineSpacing) { m_lineSpacing = lineSpacing; } float xHeight() const { return m_xHeight; } - void setXHeight(float xHeight) - { - m_xHeight = xHeight; - m_hasXHeight = true; - } - - bool hasXHeight() const { return m_hasXHeight && m_xHeight > 0; } - void setHasXHeight(bool hasXHeight) { m_hasXHeight = hasXHeight; } - + void setXHeight(float xHeight) { m_xHeight = xHeight; } + bool hasXHeight() const { return m_xHeight > 0; } + + bool hasCapHeight() const { return m_capHeight > 0; } + float floatCapHeight() const { return m_capHeight; } + void setCapHeight(float capHeight) { m_capHeight = capHeight; } + // Integer variants of certain metrics, used for HTML rendering. int ascent(FontBaseline baselineType = AlphabeticBaseline) const { @@ -91,7 +77,7 @@ public: return lroundf(m_ascent); return height() - height() / 2; } - + int descent(FontBaseline baselineType = AlphabeticBaseline) const { if (baselineType == AlphabeticBaseline) @@ -106,45 +92,40 @@ public: int lineGap() const { return lroundf(m_lineGap); } int lineSpacing() const { return lroundf(m_lineSpacing); } - + + int capHeight() const { return lroundf(m_capHeight); } + bool hasIdenticalAscentDescentAndLineGap(const FontMetrics& other) const { return ascent() == other.ascent() && descent() == other.descent() && lineGap() == other.lineGap(); } float zeroWidth() const { return m_zeroWidth; } - void setZeroWidth(float zeroWidth) - { - m_zeroWidth = zeroWidth; - m_hasZeroWidth = true; - } - - bool hasZeroWidth() const { return m_hasZeroWidth; } - void setHasZeroWidth(bool hasZeroWidth) { m_hasZeroWidth = hasZeroWidth; } + void setZeroWidth(float zeroWidth) { m_zeroWidth = zeroWidth; } private: - friend class SimpleFontData; + friend class Font; void reset() { - m_unitsPerEm = gDefaultUnitsPerEm; + m_unitsPerEm = defaultUnitsPerEm; m_ascent = 0; m_descent = 0; m_lineGap = 0; m_lineSpacing = 0; m_xHeight = 0; - m_hasXHeight = false; + m_capHeight = 0; + m_zeroWidth = 0; } - unsigned m_unitsPerEm; - float m_ascent; - float m_descent; - float m_lineGap; - float m_lineSpacing; - float m_xHeight; - float m_zeroWidth; - bool m_hasXHeight; - bool m_hasZeroWidth; + unsigned m_unitsPerEm { defaultUnitsPerEm }; + float m_ascent { 0 }; + float m_descent { 0 }; + float m_lineGap { 0 }; + float m_lineSpacing { 0 }; + float m_zeroWidth { 0 }; + float m_xHeight { 0 }; + float m_capHeight { 0 }; }; static inline float scaleEmToUnits(float x, unsigned unitsPerEm) diff --git a/Source/WebCore/platform/graphics/FontOrientation.h b/Source/WebCore/platform/graphics/FontOrientation.h deleted file mode 100644 index 12cf5c1b6..000000000 --- a/Source/WebCore/platform/graphics/FontOrientation.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FontOrientation_h -#define FontOrientation_h - -namespace WebCore { - -enum FontOrientation { Horizontal, Vertical }; - -} // namespace WebCore - -#endif // FontOrientation_h diff --git a/Source/WebCore/platform/graphics/FontPlatformData.cpp b/Source/WebCore/platform/graphics/FontPlatformData.cpp new file mode 100644 index 000000000..e41902cf6 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontPlatformData.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2011 Brent Fulgham + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "FontPlatformData.h" + +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +#if OS(DARWIN) && USE(CG) +#include "SharedBuffer.h" +#include <CoreGraphics/CGFont.h> +#endif + +#if USE(DIRECT2D) +#include <dwrite.h> +#endif + +namespace WebCore { + +FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType) + : m_isHashTableDeletedValue(true) +{ +} + +FontPlatformData::FontPlatformData() +{ +} + +FontPlatformData::FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant, TextRenderingMode textRenderingMode) + : m_size(size) + , m_orientation(orientation) + , m_widthVariant(widthVariant) + , m_textRenderingMode(textRenderingMode) + , m_syntheticBold(syntheticBold) + , m_syntheticOblique(syntheticOblique) +{ +} + +#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000)) +FontPlatformData::FontPlatformData(CGFontRef cgFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant, TextRenderingMode textRenderingMode) + : FontPlatformData(size, syntheticBold, syntheticOblique, orientation, widthVariant, textRenderingMode) +{ + m_cgFont = cgFont; + ASSERT(m_cgFont); +} +#endif + +#if !USE(FREETYPE) +FontPlatformData FontPlatformData::cloneWithOrientation(const FontPlatformData& source, FontOrientation orientation) +{ + FontPlatformData copy(source); + copy.m_orientation = orientation; + return copy; +} + +FontPlatformData FontPlatformData::cloneWithSyntheticOblique(const FontPlatformData& source, bool syntheticOblique) +{ + FontPlatformData copy(source); + copy.m_syntheticOblique = syntheticOblique; + return copy; +} + +FontPlatformData FontPlatformData::cloneWithSize(const FontPlatformData& source, float size) +{ + FontPlatformData copy(source); + copy.m_size = size; + return copy; +} +#endif + +} diff --git a/Source/WebCore/platform/graphics/FontPlatformData.h b/Source/WebCore/platform/graphics/FontPlatformData.h index bfa57095e..b154b65d4 100644 --- a/Source/WebCore/platform/graphics/FontPlatformData.h +++ b/Source/WebCore/platform/graphics/FontPlatformData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2010, 2013 Apple Inc. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007 Holger Hans Peter Freyther * Copyright (C) 2007 Pioneer Research Center USA, Inc. @@ -22,58 +22,50 @@ * */ -// FIXME: This is temporary until all ports switch to using this file. -#if USE(WINGDI) -#include "wince/FontPlatformData.h" -#elif PLATFORM(EFL) || PLATFORM(GTK) -#include "freetype/FontPlatformData.h" -#else +#pragma once -#ifndef FontPlatformData_h -#define FontPlatformData_h +#include "TextFlags.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/StringImpl.h> -#include "FontOrientation.h" -#include "FontWidthVariant.h" #if PLATFORM(WIN) +#include "COMPtr.h" #include "SharedGDIObject.h" #endif #if USE(CAIRO) +#include "RefPtrCairo.h" #include <wtf/HashFunctions.h> #include <cairo.h> #endif -#if OS(DARWIN) -OBJC_CLASS NSFont; +#if USE(FREETYPE) +#include "FcUniquePtr.h" +#include "HarfBuzzFace.h" +#include "OpenTypeVerticalData.h" +#endif -#if PLATFORM(IOS) -#import <CoreGraphics/CoreGraphics.h> +#if USE(APPKIT) +OBJC_CLASS NSFont; #endif -typedef struct CGFont* CGFontRef; +#if PLATFORM(COCOA) typedef const struct __CTFont* CTFontRef; #endif -#include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RetainPtr.h> -#include <wtf/text/StringImpl.h> +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif #if PLATFORM(WIN) #include <wtf/win/GDIObject.h> typedef struct HFONT__* HFONT; -#endif - -#if USE(CG) -typedef struct CGFont* CGFontRef; -#if OS(DARWIN) -typedef const struct __CTFont* CTFontRef; -typedef UInt32 FMFont; -typedef FMFont ATSUFontID; -typedef UInt32 ATSFontRef; -#endif +interface IDWriteFont; +interface IDWriteFontFace; #endif namespace WebCore { @@ -81,143 +73,136 @@ namespace WebCore { class FontDescription; class SharedBuffer; -#if OS(DARWIN) && USE(APPKIT) -inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef>(nsFont); } -#endif - +// This class is conceptually immutable. Once created, no instances should ever change (in an observable way). class FontPlatformData { + WTF_MAKE_FAST_ALLOCATED; public: FontPlatformData(WTF::HashTableDeletedValueType); FontPlatformData(); - FontPlatformData(const FontPlatformData&); + FontPlatformData(const FontDescription&, const AtomicString& family); - FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation = Horizontal, FontWidthVariant = RegularWidth); + FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, TextRenderingMode = AutoTextRendering); -#if OS(DARWIN) -#if USE(APPKIT) - FontPlatformData(NSFont*, float size, bool isPrinterFont = false, bool syntheticBold = false, bool syntheticOblique = false, - FontOrientation = Horizontal, FontWidthVariant = RegularWidth); -#else - FontPlatformData(CTFontRef, float size, bool isPrinterFont = false, bool syntheticBold = false, bool syntheticOblique = false, - FontOrientation = Horizontal, FontWidthVariant = RegularWidth); +#if PLATFORM(COCOA) + WEBCORE_EXPORT FontPlatformData(CTFontRef, float size, bool syntheticBold = false, bool syntheticOblique = false, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, TextRenderingMode = AutoTextRendering); #endif -#if USE(CG) - FontPlatformData(CGFontRef, float size, bool syntheticBold, bool syntheticOblique, FontOrientation, FontWidthVariant); -#endif + static FontPlatformData cloneWithOrientation(const FontPlatformData&, FontOrientation); + static FontPlatformData cloneWithSyntheticOblique(const FontPlatformData&, bool); + static FontPlatformData cloneWithSize(const FontPlatformData&, float); + +#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000)) + FontPlatformData(CGFontRef, float size, bool syntheticBold, bool syntheticOblique, FontOrientation, FontWidthVariant, TextRenderingMode); #endif + #if PLATFORM(WIN) FontPlatformData(GDIObject<HFONT>, float size, bool syntheticBold, bool syntheticOblique, bool useGDI); -#if USE(CG) +#endif + +#if PLATFORM(WIN) && USE(CG) FontPlatformData(GDIObject<HFONT>, CGFontRef, float size, bool syntheticBold, bool syntheticOblique, bool useGDI); -#elif USE(CAIRO) - FontPlatformData(GDIObject<HFONT>, cairo_font_face_t*, float size, bool bold, bool italic); #endif + +#if PLATFORM(WIN) && USE(DIRECT2D) + FontPlatformData(GDIObject<HFONT>, IDWriteFont*, float size, bool syntheticBold, bool syntheticOblique, bool useGDI); #endif -#if PLATFORM(IOS) - FontPlatformData(CTFontRef, float size, bool syntheticBold = false, bool syntheticOblique = false, FontOrientation = Horizontal, FontWidthVariant = RegularWidth); +#if PLATFORM(WIN) && USE(CAIRO) + FontPlatformData(GDIObject<HFONT>, cairo_font_face_t*, float size, bool bold, bool italic); #endif +#if USE(FREETYPE) + FontPlatformData(FcPattern*, const FontDescription&); + FontPlatformData(cairo_font_face_t*, const FontDescription&, bool syntheticBold, bool syntheticOblique); + FontPlatformData(const FontPlatformData&); + FontPlatformData(FontPlatformData&&) = default; + FontPlatformData& operator=(const FontPlatformData&); + FontPlatformData& operator=(FontPlatformData&&) = default; ~FontPlatformData(); +#endif #if PLATFORM(WIN) HFONT hfont() const { return m_font ? m_font->get() : 0; } bool useGDI() const { return m_useGDI; } -#elif PLATFORM(IOS) - CTFontRef font() const { return m_font; } - void setFont(CTFontRef); -#elif OS(DARWIN) - NSFont* font() const { return m_font; } - void setFont(NSFont*); #endif -#if USE(CG) -#if OS(DARWIN) - CGFontRef cgFont() const { return m_cgFont.get(); } +#if PLATFORM(COCOA) + CTFontRef font() const { return m_font.get(); } + WEBCORE_EXPORT CTFontRef registeredFont() const; // Returns nullptr iff the font is not registered, such as web fonts (otherwise returns font()). + CTFontRef ctFont() const; + static RetainPtr<CFTypeRef> objectForEqualityCheck(CTFontRef); + RetainPtr<CFTypeRef> objectForEqualityCheck() const; -#if !PLATFORM(IOS) - bool roundsGlyphAdvances() const; -#else - bool roundsGlyphAdvances() const { return false; } -#endif // !PLATFORM(IOS) + bool hasCustomTracking() const { return isSystemFont(); } +#endif - bool allowsLigatures() const; -#else +#if PLATFORM(WIN) || PLATFORM(COCOA) + bool isSystemFont() const { return m_isSystemFont; } +#endif + + bool hasVariations() const { return m_hasVariations; } + +#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000)) CGFontRef cgFont() const { return m_cgFont.get(); } #endif + +#if USE(DIRECT2D) + IDWriteFont* dwFont() const { return m_dwFont.get(); } + IDWriteFontFace* dwFontFace() const { return m_dwFontFace.get(); } #endif bool isFixedPitch() const; float size() const { return m_size; } - void setSize(float size) { m_size = size; } bool syntheticBold() const { return m_syntheticBold; } bool syntheticOblique() const { return m_syntheticOblique; } bool isColorBitmapFont() const { return m_isColorBitmapFont; } - bool isCompositeFontReference() const { return m_isCompositeFontReference; } -#if OS(DARWIN) - bool isPrinterFont() const { return m_isPrinterFont; } -#endif FontOrientation orientation() const { return m_orientation; } FontWidthVariant widthVariant() const { return m_widthVariant; } - - void setOrientation(FontOrientation orientation) { m_orientation = orientation; } + TextRenderingMode textRenderingMode() const { return m_textRenderingMode; } + bool isForTextCombine() const { return widthVariant() != RegularWidth; } // Keep in sync with callers of FontDescription::setWidthVariant(). #if USE(CAIRO) - cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } + cairo_scaled_font_t* scaledFont() const { return m_scaledFont.get(); } #endif - unsigned hash() const - { -#if PLATFORM(WIN) && !USE(CAIRO) - return m_font ? m_font->hash() : 0; -#elif OS(DARWIN) -#if !PLATFORM(IOS) -#if USE(CG) - ASSERT(m_font || !m_cgFont); +#if USE(FREETYPE) + HarfBuzzFace* harfBuzzFace() const; + bool hasCompatibleCharmap() const; + FcFontSet* fallbacks() const; #endif - uintptr_t hashCodes[3] = { (uintptr_t)m_font, m_widthVariant, static_cast<uintptr_t>(m_isPrinterFont << 3 | m_orientation << 2 | m_syntheticBold << 1 | m_syntheticOblique) }; -#else - ASSERT(m_font || !m_cgFont || m_isEmoji); - uintptr_t hashCodes[3] = { static_cast<uintptr_t>(CFHash(m_font)), m_widthVariant, static_cast<uintptr_t>(m_isEmoji << 4 | m_isPrinterFont << 3 | m_orientation << 2 | m_syntheticBold << 1 | m_syntheticOblique) }; -#endif // !PLATFORM(IOS) - return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes); -#elif USE(CAIRO) - return PtrHash<cairo_scaled_font_t*>::hash(m_scaledFont); -#endif - } - const FontPlatformData& operator=(const FontPlatformData&); + unsigned hash() const; bool operator==(const FontPlatformData& other) const { return platformIsEqual(other) + && m_isHashTableDeletedValue == other.m_isHashTableDeletedValue && m_size == other.m_size && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique && m_isColorBitmapFont == other.m_isColorBitmapFont - && m_isCompositeFontReference == other.m_isCompositeFontReference -#if OS(DARWIN) - && m_isPrinterFont == other.m_isPrinterFont -#endif && m_orientation == other.m_orientation - && m_widthVariant == other.m_widthVariant; + && m_widthVariant == other.m_widthVariant + && m_textRenderingMode == other.m_textRenderingMode; } bool isHashTableDeletedValue() const { -#if PLATFORM(WIN) && !USE(CAIRO) - return m_font.isHashTableDeletedValue(); -#elif OS(DARWIN) - return m_font == hashTableDeletedFontValue(); -#elif USE(CAIRO) - return m_scaledFont == hashTableDeletedFontValue(); + return m_isHashTableDeletedValue; + } + + bool isEmoji() const + { +#if PLATFORM(IOS) + return m_isEmoji; +#else + return false; #endif } -#if PLATFORM(WIN) && (USE(CG) || USE(CAIRO)) - PassRefPtr<SharedBuffer> openTypeTable(uint32_t table) const; +#if PLATFORM(COCOA) || PLATFORM(WIN) || USE(FREETYPE) + RefPtr<SharedBuffer> openTypeTable(uint32_t table) const; #endif #ifndef NDEBUG @@ -226,67 +211,116 @@ public: private: bool platformIsEqual(const FontPlatformData&) const; - void platformDataInit(const FontPlatformData&); - const FontPlatformData& platformDataAssign(const FontPlatformData&); -#if PLATFORM(IOS) - static CTFontRef hashTableDeletedFontValue() { return reinterpret_cast<CTFontRef>(-1); } -#elif OS(DARWIN) - // Load various data about the font specified by |nsFont| with the size fontSize into the following output paramters: - void loadFont(NSFont*, float fontSize, NSFont*& outNSFont, CGFontRef&); - static NSFont* hashTableDeletedFontValue() { return reinterpret_cast<NSFont *>(-1); } -#elif PLATFORM(WIN) - void platformDataInit(HFONT, float size, HDC, WCHAR* faceName); + +#if PLATFORM(COCOA) + CGFloat ctFontSize() const; #endif -#if USE(CAIRO) - static cairo_scaled_font_t* hashTableDeletedFontValue() { return reinterpret_cast<cairo_scaled_font_t*>(-1); } +#if PLATFORM(WIN) + void platformDataInit(HFONT, float size, HDC, WCHAR* faceName); #endif -public: - bool m_syntheticBold; - bool m_syntheticOblique; - FontOrientation m_orientation; -#if PLATFORM(IOS) - bool m_isEmoji; +#if USE(FREETYPE) + void buildScaledFont(cairo_font_face_t*); #endif - float m_size; - FontWidthVariant m_widthVariant; -private: -#if PLATFORM(IOS) - CTFontRef m_font; -#elif OS(DARWIN) - NSFont* m_font; +#if PLATFORM(COCOA) + // FIXME: Get rid of one of these. These two fonts are subtly different, and it is not obvious which one to use where. + RetainPtr<CTFontRef> m_font; + mutable RetainPtr<CTFontRef> m_ctFont; #elif PLATFORM(WIN) RefPtr<SharedGDIObject<HFONT>> m_font; #endif -#if USE(CG) -#if PLATFORM(WIN) - RetainPtr<CGFontRef> m_cgFont; -#else +#if USE(CG) && (PLATFORM(WIN) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000)) RetainPtr<CGFontRef> m_cgFont; - mutable RetainPtr<CTFontRef> m_CTFont; #endif + +#if USE(DIRECT2D) + COMPtr<IDWriteFont> m_dwFont; + COMPtr<IDWriteFontFace> m_dwFontFace; #endif #if USE(CAIRO) - cairo_scaled_font_t* m_scaledFont; + RefPtr<cairo_scaled_font_t> m_scaledFont; #endif - bool m_isColorBitmapFont; - bool m_isCompositeFontReference; -#if OS(DARWIN) - bool m_isPrinterFont; +#if USE(FREETYPE) + RefPtr<FcPattern> m_pattern; + mutable FcUniquePtr<FcFontSet> m_fallbacks; + mutable RefPtr<HarfBuzzFace> m_harfBuzzFace; +#endif + + // The values below are common to all ports + // FIXME: If they're common to all ports, they should move to Font + float m_size { 0 }; + + FontOrientation m_orientation { Horizontal }; + FontWidthVariant m_widthVariant { RegularWidth }; + TextRenderingMode m_textRenderingMode { AutoTextRendering }; + + bool m_syntheticBold { false }; + bool m_syntheticOblique { false }; + bool m_isColorBitmapFont { false }; + bool m_isHashTableDeletedValue { false }; + bool m_isSystemFont { false }; + bool m_hasVariations { false }; + // The values above are common to all ports + +#if PLATFORM(IOS) + bool m_isEmoji { false }; #endif #if PLATFORM(WIN) - bool m_useGDI; + bool m_useGDI { false }; +#endif + +#if USE(FREETYPE) + bool m_fixedWidth { false }; #endif }; -} // namespace WebCore +#if USE(APPKIT) -#endif // FontPlatformData_h +// NSFonts and CTFontRefs are toll-free-bridged. +inline CTFontRef toCTFont(NSFont *font) +{ + return (CTFontRef)font; +} + +inline NSFont *toNSFont(CTFontRef font) +{ + return (NSFont *)font; +} #endif + +#if USE(CG) + +class ScopedTextMatrix { +public: + ScopedTextMatrix(CGAffineTransform newMatrix, CGContextRef context) + : m_context(context) + , m_textMatrix(CGContextGetTextMatrix(context)) + { + CGContextSetTextMatrix(m_context, newMatrix); + } + + ~ScopedTextMatrix() + { + CGContextSetTextMatrix(m_context, m_textMatrix); + } + + CGAffineTransform savedMatrix() const + { + return m_textMatrix; + } + +private: + CGContextRef m_context; + CGAffineTransform m_textMatrix; +}; + +#endif + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FontRanges.cpp b/Source/WebCore/platform/graphics/FontRanges.cpp new file mode 100644 index 000000000..d1b791761 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontRanges.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008, 2009, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FontRanges.h" + +#include "Font.h" +#include "FontSelector.h" +#include <wtf/Assertions.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +const Font* FontRanges::Range::font() const +{ + return m_fontAccessor->font(); +} + +FontRanges::FontRanges() +{ +} + +class TrivialFontAccessor final : public FontAccessor { +public: + static Ref<TrivialFontAccessor> create(Ref<Font>&& font) + { + return adoptRef(*new TrivialFontAccessor(WTFMove(font))); + } + +private: + TrivialFontAccessor(RefPtr<Font>&& font) + : m_font(WTFMove(font)) + { + } + + const Font* font() const final + { + return m_font.get(); + } + + bool isLoading() const final + { + return m_font->isLoading(); + } + + RefPtr<Font> m_font; +}; + +FontRanges::FontRanges(RefPtr<Font>&& font) +{ + if (font) + m_ranges.append(Range { 0, 0x7FFFFFFF, TrivialFontAccessor::create(font.releaseNonNull()) }); +} + +FontRanges::~FontRanges() +{ +} + +GlyphData FontRanges::glyphDataForCharacter(UChar32 character) const +{ + for (auto& range : m_ranges) { + if (range.from() <= character && character <= range.to()) { + if (auto* font = range.font()) { + auto glyphData = font->glyphDataForCharacter(character); + if (glyphData.glyph) + return glyphData; + } + } + } + return GlyphData(); +} + +const Font* FontRanges::fontForCharacter(UChar32 character) const +{ + return glyphDataForCharacter(character).font; +} + +const Font& FontRanges::fontForFirstRange() const +{ + auto* font = m_ranges[0].font(); + ASSERT(font); + return *font; +} + +bool FontRanges::isLoading() const +{ + for (auto& range : m_ranges) { + if (range.fontAccessor().isLoading()) + return true; + } + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/FontRanges.h b/Source/WebCore/platform/graphics/FontRanges.h new file mode 100644 index 000000000..4fe0bec47 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontRanges.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008, 2009, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FontRanges_h +#define FontRanges_h + +#include "Font.h" +#include <wtf/TypeCasts.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class FontAccessor; + +class FontRanges { +public: + struct Range { + Range(UChar32 from, UChar32 to, Ref<FontAccessor>&& fontAccessor) + : m_from(from) + , m_to(to) + , m_fontAccessor(WTFMove(fontAccessor)) + { + } + + Range(const Range& range) + : m_from(range.m_from) + , m_to(range.m_to) + , m_fontAccessor(range.m_fontAccessor.copyRef()) + { + } + + Range(Range&&) = default; + Range& operator=(const Range&) = delete; + Range& operator=(Range&&) = default; + + UChar32 from() const { return m_from; } + UChar32 to() const { return m_to; } + const Font* font() const; + const FontAccessor& fontAccessor() const { return m_fontAccessor; } + + private: + UChar32 m_from; + UChar32 m_to; + Ref<FontAccessor> m_fontAccessor; + }; + + FontRanges(); + explicit FontRanges(RefPtr<Font>&&); + ~FontRanges(); + + FontRanges(const FontRanges&) = default; + FontRanges& operator=(FontRanges&&) = default; + + bool isNull() const { return m_ranges.isEmpty(); } + + void appendRange(Range&& range) { m_ranges.append(WTFMove(range)); } + unsigned size() const { return m_ranges.size(); } + const Range& rangeAt(unsigned i) const { return m_ranges[i]; } + + GlyphData glyphDataForCharacter(UChar32) const; + WEBCORE_EXPORT const Font* fontForCharacter(UChar32) const; + WEBCORE_EXPORT const Font& fontForFirstRange() const; + bool isLoading() const; + +private: + Vector<Range, 1> m_ranges; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/FontSelector.h b/Source/WebCore/platform/graphics/FontSelector.h index 68a22de1a..7f5f3230f 100644 --- a/Source/WebCore/platform/graphics/FontSelector.h +++ b/Source/WebCore/platform/graphics/FontSelector.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,46 +23,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FontSelector_h -#define FontSelector_h +#pragma once +#include "FontRanges.h" #include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { -class FontData; +class FontCascadeDescription; class FontDescription; class FontSelectorClient; +class FontAccessor : public RefCounted<FontAccessor> { +public: + virtual ~FontAccessor() { } + + virtual const Font* font() const = 0; + virtual bool isLoading() const = 0; +}; + class FontSelector : public RefCounted<FontSelector> { public: virtual ~FontSelector() { } - // FIXME: Remove the "get" prefix from these two member functions - virtual PassRefPtr<FontData> getFontData(const FontDescription&, const AtomicString& familyName) = 0; - virtual PassRefPtr<FontData> getFallbackFontData(const FontDescription&, size_t) = 0; + virtual FontRanges fontRangesForFamily(const FontDescription&, const AtomicString&) = 0; + virtual RefPtr<Font> fallbackFontAt(const FontDescription&, size_t) = 0; - virtual size_t fallbackFontDataCount() = 0; - virtual bool resolvesFamilyFor(const FontDescription&) const = 0; + virtual size_t fallbackFontCount() = 0; virtual void fontCacheInvalidated() { } - virtual void registerForInvalidationCallbacks(FontSelectorClient*) = 0; - virtual void unregisterForInvalidationCallbacks(FontSelectorClient*) = 0; + virtual void registerForInvalidationCallbacks(FontSelectorClient&) = 0; + virtual void unregisterForInvalidationCallbacks(FontSelectorClient&) = 0; virtual unsigned uniqueId() const = 0; virtual unsigned version() const = 0; }; -class FontSelectorClient { -public: - virtual ~FontSelectorClient() { } - - virtual void fontsNeedUpdate(FontSelector*) = 0; -}; - -} // namespace WebCore - -#endif // FontSelector_h +} diff --git a/Source/WebCore/platform/graphics/FontSmoothingMode.h b/Source/WebCore/platform/graphics/FontSelectorClient.h index 7c23394f1..a83468580 100644 --- a/Source/WebCore/platform/graphics/FontSmoothingMode.h +++ b/Source/WebCore/platform/graphics/FontSelectorClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,16 +20,20 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FontSmoothingMode_h -#define FontSmoothingMode_h +#pragma once namespace WebCore { - enum FontSmoothingMode { AutoSmoothing, NoSmoothing, Antialiased, SubpixelAntialiased }; - -} // namespace WebCore +class FontSelector; -#endif // FontSmoothingMode_h +class FontSelectorClient { +public: + virtual ~FontSelectorClient() { } + + virtual void fontsNeedUpdate(FontSelector&) = 0; +}; + +} diff --git a/Source/WebCore/platform/graphics/FontTaggedSettings.cpp b/Source/WebCore/platform/graphics/FontTaggedSettings.cpp new file mode 100644 index 000000000..3aaad5af4 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontTaggedSettings.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FontTaggedSettings.h" + +#include "TextStream.h" + +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + +template <> +unsigned FontFeatureSettings::hash() const +{ + IntegerHasher hasher; + for (auto& feature : m_list) { + hasher.add(FourCharacterTagHash::hash(feature.tag())); + hasher.add(feature.value()); + } + return hasher.hash(); +} + +#if ENABLE(VARIATION_FONTS) +template <> +unsigned FontVariationSettings::hash() const +{ + static_assert(sizeof(float) == sizeof(int), "IntegerHasher needs to accept floats too"); + union { + float f; + int i; + } floatToInt; + + IntegerHasher hasher; + for (auto& variation : m_list) { + hasher.add(FourCharacterTagHash::hash(variation.tag())); + floatToInt.f = variation.value(); + hasher.add(floatToInt.i); + } + return hasher.hash(); +} + +TextStream& operator<<(TextStream& ts, const FontVariationSettings& item) +{ + for (unsigned i = 0; i < item.size(); ++i) { + auto& variation = item.at(i); + StringBuilder s; + s.append(variation.tag()[0]); + s.append(variation.tag()[1]); + s.append(variation.tag()[2]); + s.append(variation.tag()[3]); + ts.dumpProperty(s.toString(), item.at(i).value()); + } + return ts; +} +#endif + +} diff --git a/Source/WebCore/platform/graphics/FontTaggedSettings.h b/Source/WebCore/platform/graphics/FontTaggedSettings.h new file mode 100644 index 000000000..7553c4f91 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontTaggedSettings.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <array> +#include <wtf/Vector.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class TextStream; + +typedef std::array<char, 4> FontTag; + +inline FontTag fontFeatureTag(const char arr[4]) { return {{ arr[0], arr[1], arr[2], arr[3] }}; } + +struct FourCharacterTagHash { + static unsigned hash(const FontTag& characters) { return (characters[0] << 24) | (characters[1] << 16) | (characters[2] << 8) | characters[3]; } + static bool equal(const FontTag& a, const FontTag& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +struct FourCharacterTagHashTraits : WTF::GenericHashTraits<FontTag> { + static const bool emptyValueIsZero = true; + static void constructDeletedValue(FontTag& slot) { new (NotNull, std::addressof(slot)) FontTag({{ ff, ff, ff, ff }}); } + static bool isDeletedValue(const FontTag& value) { return value == FontTag({{ ff, ff, ff, ff }}); } + +private: + const static char ff = static_cast<char>(0xFF); +}; + +template <typename T> +class FontTaggedSetting { +public: + FontTaggedSetting() = delete; + FontTaggedSetting(const FontTag&, T value); + FontTaggedSetting(FontTag&&, T value); + + bool operator==(const FontTaggedSetting<T>& other) const; + bool operator!=(const FontTaggedSetting<T>& other) const { return !(*this == other); } + bool operator<(const FontTaggedSetting<T>& other) const; + + const FontTag& tag() const { return m_tag; } + T value() const { return m_value; } + bool enabled() const { return value(); } + +private: + FontTag m_tag; + T m_value; +}; + +template <typename T> +FontTaggedSetting<T>::FontTaggedSetting(const FontTag& tag, T value) + : m_tag(tag) + , m_value(value) +{ +} + +template <typename T> +FontTaggedSetting<T>::FontTaggedSetting(FontTag&& tag, T value) + : m_tag(WTFMove(tag)) + , m_value(value) +{ +} + +template <typename T> +bool FontTaggedSetting<T>::operator==(const FontTaggedSetting<T>& other) const +{ + return m_tag == other.m_tag && m_value == other.m_value; +} + +template <typename T> +bool FontTaggedSetting<T>::operator<(const FontTaggedSetting<T>& other) const +{ + return (m_tag < other.m_tag) || (m_tag == other.m_tag && m_value < other.m_value); +} + +template <typename T> +class FontTaggedSettings { +public: + void insert(FontTaggedSetting<T>&&); + bool operator==(const FontTaggedSettings<T>& other) const { return m_list == other.m_list; } + bool operator!=(const FontTaggedSettings<T>& other) const { return !(*this == other); } + + bool isEmpty() const { return !size(); } + size_t size() const { return m_list.size(); } + const FontTaggedSetting<T>& operator[](int index) const { return m_list[index]; } + const FontTaggedSetting<T>& at(size_t index) const { return m_list.at(index); } + + typename Vector<FontTaggedSetting<T>>::const_iterator begin() const { return m_list.begin(); } + typename Vector<FontTaggedSetting<T>>::const_iterator end() const { return m_list.end(); } + + unsigned hash() const; + +private: + Vector<FontTaggedSetting<T>> m_list; +}; + +template <typename T> +void FontTaggedSettings<T>::insert(FontTaggedSetting<T>&& feature) +{ + // This vector will almost always have 0 or 1 items in it. Don't bother with the overhead of a binary search or a hash set. + size_t i; + for (i = 0; i < m_list.size(); ++i) { + if (!(feature < m_list[i])) + break; + } + if (i < m_list.size() && feature.tag() == m_list[i].tag()) + m_list.remove(i); + m_list.insert(i, WTFMove(feature)); +} + +typedef FontTaggedSetting<int> FontFeature; +typedef FontTaggedSettings<int> FontFeatureSettings; + +#if ENABLE(VARIATION_FONTS) + +typedef FontTaggedSettings<float> FontVariationSettings; +TextStream& operator<<(TextStream&, const FontVariationSettings&); + +#else + +struct FontVariationSettings { + bool isEmpty() const { return true; } +}; + +#endif + +} diff --git a/Source/WebCore/platform/graphics/FontTraitsMask.h b/Source/WebCore/platform/graphics/FontTraitsMask.h deleted file mode 100644 index 686c30cda..000000000 --- a/Source/WebCore/platform/graphics/FontTraitsMask.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FontTraitsMask_h -#define FontTraitsMask_h - -namespace WebCore { - - enum { - FontStyleNormalBit = 0, - FontStyleItalicBit, - FontVariantNormalBit, - FontVariantSmallCapsBit, - FontWeight100Bit, - FontWeight200Bit, - FontWeight300Bit, - FontWeight400Bit, - FontWeight500Bit, - FontWeight600Bit, - FontWeight700Bit, - FontWeight800Bit, - FontWeight900Bit, - FontTraitsMaskWidth - }; - - enum FontTraitsMask { - FontStyleNormalMask = 1 << FontStyleNormalBit, - FontStyleItalicMask = 1 << FontStyleItalicBit, - FontStyleMask = FontStyleNormalMask | FontStyleItalicMask, - - FontVariantNormalMask = 1 << FontVariantNormalBit, - FontVariantSmallCapsMask = 1 << FontVariantSmallCapsBit, - FontVariantMask = FontVariantNormalMask | FontVariantSmallCapsMask, - - FontWeight100Mask = 1 << FontWeight100Bit, - FontWeight200Mask = 1 << FontWeight200Bit, - FontWeight300Mask = 1 << FontWeight300Bit, - FontWeight400Mask = 1 << FontWeight400Bit, - FontWeight500Mask = 1 << FontWeight500Bit, - FontWeight600Mask = 1 << FontWeight600Bit, - FontWeight700Mask = 1 << FontWeight700Bit, - FontWeight800Mask = 1 << FontWeight800Bit, - FontWeight900Mask = 1 << FontWeight900Bit, - FontWeightMask = FontWeight100Mask | FontWeight200Mask | FontWeight300Mask | FontWeight400Mask | FontWeight500Mask | FontWeight600Mask | FontWeight700Mask | FontWeight800Mask | FontWeight900Mask - }; - -} // namespace WebCore -#endif // FontTraitsMask_h diff --git a/Source/WebCore/platform/graphics/FormatConverter.cpp b/Source/WebCore/platform/graphics/FormatConverter.cpp index c4a2f11cd..7f0263740 100644 --- a/Source/WebCore/platform/graphics/FormatConverter.cpp +++ b/Source/WebCore/platform/graphics/FormatConverter.cpp @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "FormatConverter.h" @@ -91,7 +91,7 @@ void generatetables(){ } */ -unsigned short baseTable[512] = { +static const unsigned short baseTable[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -126,7 +126,7 @@ unsigned short baseTable[512] = { 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512 }; -unsigned char shiftTable[512] = { +static const unsigned char shiftTable[512] = { 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, @@ -1273,4 +1273,4 @@ ALWAYS_INLINE_EXCEPT_MSVC void FormatConverter::convert() } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/FormatConverter.h b/Source/WebCore/platform/graphics/FormatConverter.h index b7b0b1428..dd97b37c7 100644 --- a/Source/WebCore/platform/graphics/FormatConverter.h +++ b/Source/WebCore/platform/graphics/FormatConverter.h @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,9 +25,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" - -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" #include <wtf/StdLibExtras.h> @@ -75,4 +73,4 @@ private: } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/GLContext.cpp b/Source/WebCore/platform/graphics/GLContext.cpp index 82752ae9d..67d60ce83 100644 --- a/Source/WebCore/platform/graphics/GLContext.cpp +++ b/Source/WebCore/platform/graphics/GLContext.cpp @@ -17,29 +17,22 @@ */ #include "config.h" -#include "GLContext.h" -#if USE(OPENGL) +#if ENABLE(GRAPHICS_CONTEXT_3D) +#include "GLContext.h" +#include <wtf/ThreadSpecific.h> #if USE(EGL) #include "GLContextEGL.h" #endif -#if USE(GLX) -#include "GLContextGLX.h" +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> +#include <GLES3/gl3.h> #endif -#include <wtf/ThreadSpecific.h> - -#if PLATFORM(X11) -#include <X11/Xlib.h> -#endif - -#if PLATFORM(GTK) -#include <gdk/gdk.h> -#if PLATFORM(WAYLAND) && !defined(GTK_API_VERSION_2) && defined(GDK_WINDOWING_WAYLAND) -#include <gdk/gdkwayland.h> -#endif +#if USE(GLX) +#include "GLContextGLX.h" #endif using WTF::ThreadSpecific; @@ -66,124 +59,83 @@ inline ThreadGlobalGLContext* currentContext() return *ThreadGlobalGLContext::staticGLContext; } -GLContext* GLContext::sharingContext() -{ - DEFINE_STATIC_LOCAL(OwnPtr<GLContext>, sharing, (createOffscreenContext())); - return sharing.get(); -} - -#if PLATFORM(X11) -// We do not want to call glXMakeContextCurrent using different Display pointers, -// because it might lead to crashes in some drivers (fglrx). We use a shared display -// pointer here. -static Display* gSharedX11Display = 0; -Display* GLContext::sharedX11Display() +static bool initializeOpenGLShimsIfNeeded() { - if (!gSharedX11Display) - gSharedX11Display = XOpenDisplay(0); - return gSharedX11Display; -} - -void GLContext::cleanupSharedX11Display() -{ - if (!gSharedX11Display) - return; - XCloseDisplay(gSharedX11Display); - gSharedX11Display = 0; +#if USE(OPENGL_ES_2) + return true; +#else + static bool initialized = false; + static bool success = true; + if (!initialized) { + success = initializeOpenGLShims(); + initialized = true; + } + return success; +#endif } -// Because of driver bugs, exiting the program when there are active pbuffers -// can crash the X server (this has been observed with the official Nvidia drivers). -// We need to ensure that we clean everything up on exit. There are several reasons -// that GraphicsContext3Ds will still be alive at exit, including user error (memory -// leaks) and the page cache. In any case, we don't want the X server to crash. -typedef Vector<GLContext*> ActiveContextList; -static ActiveContextList& activeContextList() +std::unique_ptr<GLContext> GLContext::createContextForWindow(GLNativeWindowType windowHandle, PlatformDisplay* platformDisplay) { - DEFINE_STATIC_LOCAL(ActiveContextList, activeContexts, ()); - return activeContexts; -} + if (!initializeOpenGLShimsIfNeeded()) + return nullptr; -void GLContext::addActiveContext(GLContext* context) -{ - static bool addedAtExitHandler = false; - if (!addedAtExitHandler) { - atexit(&GLContext::cleanupActiveContextsAtExit); - addedAtExitHandler = true; + PlatformDisplay& display = platformDisplay ? *platformDisplay : PlatformDisplay::sharedDisplay(); +#if PLATFORM(WAYLAND) + if (display.type() == PlatformDisplay::Type::Wayland) { + if (auto eglContext = GLContextEGL::createContext(windowHandle, display)) + return WTFMove(eglContext); + return nullptr; } - activeContextList().append(context); -} - -static bool gCleaningUpAtExit = false; +#endif -void GLContext::removeActiveContext(GLContext* context) -{ - // If we are cleaning up the context list at exit, don't bother removing the context - // from the list, since we don't want to modify the list while it's being iterated. - if (gCleaningUpAtExit) - return; - - ActiveContextList& contextList = activeContextList(); - size_t i = contextList.find(context); - if (i != notFound) - contextList.remove(i); +#if USE(GLX) + if (auto glxContext = GLContextGLX::createContext(windowHandle, display)) + return WTFMove(glxContext); +#endif +#if USE(EGL) + if (auto eglContext = GLContextEGL::createContext(windowHandle, display)) + return WTFMove(eglContext); +#endif + return nullptr; } -void GLContext::cleanupActiveContextsAtExit() +std::unique_ptr<GLContext> GLContext::createOffscreenContext(PlatformDisplay* platformDisplay) { - gCleaningUpAtExit = true; - - ActiveContextList& contextList = activeContextList(); - for (size_t i = 0; i < contextList.size(); ++i) - delete contextList[i]; + if (!initializeOpenGLShimsIfNeeded()) + return nullptr; - cleanupSharedX11Display(); + return createContextForWindow(0, platformDisplay ? platformDisplay : &PlatformDisplay::sharedDisplay()); } -#endif // PLATFORM(X11) - -PassOwnPtr<GLContext> GLContext::createContextForWindow(GLNativeWindowType windowHandle, GLContext* sharingContext) +std::unique_ptr<GLContext> GLContext::createSharingContext(PlatformDisplay& display) { -#if PLATFORM(GTK) && PLATFORM(WAYLAND) && !defined(GTK_API_VERSION_2) && defined(GDK_WINDOWING_WAYLAND) && USE(EGL) - GdkDisplay* display = gdk_display_manager_get_default_display(gdk_display_manager_get()); - - if (GDK_IS_WAYLAND_DISPLAY(display)) { - if (OwnPtr<GLContext> eglContext = GLContextEGL::createContext(windowHandle, sharingContext)) - return eglContext.release(); + if (!initializeOpenGLShimsIfNeeded()) return nullptr; - } -#endif #if USE(GLX) - if (OwnPtr<GLContext> glxContext = GLContextGLX::createContext(windowHandle, sharingContext)) - return glxContext.release(); -#endif -#if USE(EGL) - if (OwnPtr<GLContext> eglContext = GLContextEGL::createContext(windowHandle, sharingContext)) - return eglContext.release(); + if (display.type() == PlatformDisplay::Type::X11) { + if (auto glxContext = GLContextGLX::createSharingContext(display)) + return WTFMove(glxContext); + } #endif - return nullptr; -} -GLContext::GLContext() -{ -#if PLATFORM(X11) - addActiveContext(this); +#if USE(EGL) || PLATFORM(WAYLAND) + if (auto eglContext = GLContextEGL::createSharingContext(display)) + return WTFMove(eglContext); #endif + + return nullptr; } -PassOwnPtr<GLContext> GLContext::createOffscreenContext(GLContext* sharingContext) +GLContext::GLContext(PlatformDisplay& display) + : m_display(display) { - return createContextForWindow(0, sharingContext); } GLContext::~GLContext() { if (this == currentContext()->context()) - currentContext()->setContext(0); -#if PLATFORM(X11) - removeActiveContext(this); -#endif + currentContext()->setContext(nullptr); } bool GLContext::makeContextCurrent() @@ -192,12 +144,53 @@ bool GLContext::makeContextCurrent() return true; } -GLContext* GLContext::getCurrent() +GLContext* GLContext::current() { return currentContext()->context(); } -} // namespace WebCore +bool GLContext::isExtensionSupported(const char* extensionList, const char* extension) +{ + if (!extensionList) + return false; + + ASSERT(extension); + int extensionLen = strlen(extension); + const char* extensionListPtr = extensionList; + while ((extensionListPtr = strstr(extensionListPtr, extension))) { + if (extensionListPtr[extensionLen] == ' ' || extensionListPtr[extensionLen] == '\0') + return true; + extensionListPtr += extensionLen; + } + return false; +} -#endif // USE(OPENGL) +unsigned GLContext::version() +{ + if (!m_version) { + // Version string can start with the version number (all versions except GLES 1 and 2) or with + // "OpenGL". Different fields inside the version string are separated by spaces. + String versionString = String(reinterpret_cast<const char*>(::glGetString(GL_VERSION))); + Vector<String> versionStringComponents; + versionString.split(' ', versionStringComponents); + + Vector<String> versionDigits; + if (versionStringComponents[0] == "OpenGL") { + // If the version string starts with "OpenGL" it can be GLES 1 or 2. In GLES1 version string starts + // with "OpenGL ES-<profile> major.minor" and in GLES2 with "OpenGL ES major.minor". Version is the + // third component in both cases. + versionStringComponents[2].split('.', versionDigits); + } else { + // Version is the first component. The version number is always "major.minor" or + // "major.minor.release". Ignore the release number. + versionStringComponents[0].split('.', versionDigits); + } + + m_version = versionDigits[0].toUInt() * 100 + versionDigits[1].toUInt() * 10; + } + return m_version; +} +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/GLContext.h b/Source/WebCore/platform/graphics/GLContext.h index 03b57b5e8..2c0ea27e1 100644 --- a/Source/WebCore/platform/graphics/GLContext.h +++ b/Source/WebCore/platform/graphics/GLContext.h @@ -21,9 +21,8 @@ #define GLContext_h #include "GraphicsContext3D.h" -#include "Widget.h" +#include "PlatformDisplay.h" #include <wtf/Noncopyable.h> -#include <wtf/PassOwnPtr.h> #if USE(EGL) && !PLATFORM(GTK) #include "eglplatform.h" @@ -36,44 +35,50 @@ typedef uint64_t GLNativeWindowType; typedef struct _cairo_device cairo_device_t; #endif -#if PLATFORM(X11) -typedef struct _XDisplay Display; -#endif - namespace WebCore { class GLContext { - WTF_MAKE_NONCOPYABLE(GLContext); + WTF_MAKE_NONCOPYABLE(GLContext); WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<GLContext> createContextForWindow(GLNativeWindowType windowHandle, GLContext* sharingContext); - static PassOwnPtr<GLContext> createOffscreenContext(GLContext* sharing = 0); - static GLContext* getCurrent(); - static GLContext* sharingContext(); + static std::unique_ptr<GLContext> createContextForWindow(GLNativeWindowType windowHandle, PlatformDisplay* = nullptr); + static std::unique_ptr<GLContext> createOffscreenContext(PlatformDisplay* = nullptr); + static std::unique_ptr<GLContext> createSharingContext(PlatformDisplay&); + static GLContext* current(); + static bool isExtensionSupported(const char* extensionList, const char* extension); + + PlatformDisplay& display() const { return m_display; } + unsigned version(); - GLContext(); virtual ~GLContext(); virtual bool makeContextCurrent(); virtual void swapBuffers() = 0; virtual void waitNative() = 0; virtual bool canRenderToDefaultFramebuffer() = 0; virtual IntSize defaultFrameBufferSize() = 0; + virtual void swapInterval(int) = 0; + + virtual bool isEGLContext() const = 0; #if USE(CAIRO) virtual cairo_device_t* cairoDevice() = 0; #endif -#if PLATFORM(X11) - static Display* sharedX11Display(); - static void cleanupSharedX11Display(); +#if ENABLE(GRAPHICS_CONTEXT_3D) + virtual PlatformGraphicsContext3D platformContext() = 0; #endif +#if PLATFORM(X11) +private: static void addActiveContext(GLContext*); static void removeActiveContext(GLContext*); static void cleanupActiveContextsAtExit(); - -#if USE(3D_GRAPHICS) - virtual PlatformGraphicsContext3D platformContext() = 0; #endif + +protected: + GLContext(PlatformDisplay&); + + PlatformDisplay& m_display; + unsigned m_version { 0 }; }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/GeneratedImage.cpp b/Source/WebCore/platform/graphics/GeneratedImage.cpp index b250cb1c2..08921c957 100644 --- a/Source/WebCore/platform/graphics/GeneratedImage.cpp +++ b/Source/WebCore/platform/graphics/GeneratedImage.cpp @@ -33,7 +33,6 @@ #include "FloatSize.h" - namespace WebCore { void GeneratedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) diff --git a/Source/WebCore/platform/graphics/GeneratedImage.h b/Source/WebCore/platform/graphics/GeneratedImage.h index 446388beb..7d9fe358d 100644 --- a/Source/WebCore/platform/graphics/GeneratedImage.h +++ b/Source/WebCore/platform/graphics/GeneratedImage.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,40 +26,40 @@ #ifndef GeneratedImage_h #define GeneratedImage_h +#include "FloatSize.h" #include "Image.h" -#include "IntSize.h" -#include <wtf/RefPtr.h> - namespace WebCore { class GeneratedImage : public Image { public: - virtual bool hasSingleSecurityOrigin() const override { return true; } + bool hasSingleSecurityOrigin() const override { return true; } - virtual void setContainerSize(const IntSize& size) override { m_size = size; } - virtual bool usesContainerSize() const override { return true; } - virtual bool hasRelativeWidth() const override { return true; } - virtual bool hasRelativeHeight() const override { return true; } - virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) override; + void setContainerSize(const FloatSize& size) override { m_size = size; } + bool usesContainerSize() const override { return true; } + bool hasRelativeWidth() const override { return true; } + bool hasRelativeHeight() const override { return true; } + void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) override; - virtual IntSize size() const override { return m_size; } + FloatSize size() const override { return m_size; } // Assume that generated content has no decoded data we need to worry about - virtual void destroyDecodedData(bool /*destroyAll*/ = true) override { } + void destroyDecodedData(bool /*destroyAll*/ = true) override { } protected: - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override = 0; - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode) override = 0; + void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override = 0; + void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override = 0; // FIXME: Implement this to be less conservative. - virtual bool currentFrameKnownToBeOpaque() override { return false; } + bool currentFrameKnownToBeOpaque() const override { return false; } GeneratedImage() { } private: - IntSize m_size; + bool isGeneratedImage() const override { return true; } + + FloatSize m_size; }; } diff --git a/Source/WebCore/platform/graphics/GeometryUtilities.cpp b/Source/WebCore/platform/graphics/GeometryUtilities.cpp new file mode 100644 index 000000000..5f296ac74 --- /dev/null +++ b/Source/WebCore/platform/graphics/GeometryUtilities.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GeometryUtilities.h" + +namespace WebCore { + +float euclidianDistance(const FloatPoint& p1, const FloatPoint& p2) +{ + FloatSize delta = p1 - p2; + return sqrt(delta.width() * delta.width() + delta.height() * delta.height()); +} + +float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c) +{ + if (p2.x() == p1.x()) + return std::numeric_limits<float>::infinity(); + + // y = mx + c + float slope = (p2.y() - p1.y()) / (p2.x() - p1.x()); + c = p1.y() - slope * p1.x(); + return slope; +} + +bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection) +{ + float pOffset = 0; + float pSlope = findSlope(p1, p2, pOffset); + + float dOffset = 0; + float dSlope = findSlope(d1, d2, dOffset); + + if (dSlope == pSlope) + return false; + + if (pSlope == std::numeric_limits<float>::infinity()) { + intersection.setX(p1.x()); + intersection.setY(dSlope * intersection.x() + dOffset); + return true; + } + if (dSlope == std::numeric_limits<float>::infinity()) { + intersection.setX(d1.x()); + intersection.setY(pSlope * intersection.x() + pOffset); + return true; + } + + // Find x at intersection, where ys overlap; x = (c' - c) / (m - m') + intersection.setX((dOffset - pOffset) / (pSlope - dSlope)); + intersection.setY(pSlope * intersection.x() + pOffset); + return true; +} + +IntRect unionRect(const Vector<IntRect>& rects) +{ + IntRect result; + + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) + result.unite(rects[i]); + + return result; +} + +FloatRect unionRect(const Vector<FloatRect>& rects) +{ + FloatRect result; + + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) + result.unite(rects[i]); + + return result; +} + +FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect) +{ + if (!srcRect.width() || !srcRect.height()) + return FloatRect(); + + float widthScale = destRect.width() / srcRect.width(); + float heightScale = destRect.height() / srcRect.height(); + return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale, + destRect.y() + (r.y() - srcRect.y()) * heightScale, + r.width() * widthScale, r.height() * heightScale); +} + +FloatRect largestRectWithAspectRatioInsideRect(float aspectRatio, const FloatRect& srcRect) +{ + FloatRect destRect = srcRect; + + if (aspectRatio > srcRect.size().aspectRatio()) { + float dy = destRect.width() / aspectRatio - destRect.height(); + destRect.inflateY(dy / 2); + } else { + float dx = destRect.height() * aspectRatio - destRect.width(); + destRect.inflateX(dx / 2); + } + return destRect; +} + +FloatRect boundsOfRotatingRect(const FloatRect& r) +{ + // Compute the furthest corner from the origin. + float maxCornerDistance = euclidianDistance(FloatPoint(), r.minXMinYCorner()); + maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.maxXMinYCorner())); + maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.minXMaxYCorner())); + maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.maxXMaxYCorner())); + + return FloatRect(-maxCornerDistance, -maxCornerDistance, 2 * maxCornerDistance, 2 * maxCornerDistance); +} + +FloatRect smallestRectWithAspectRatioAroundRect(float aspectRatio, const FloatRect& srcRect) +{ + FloatRect destRect = srcRect; + + if (aspectRatio < srcRect.size().aspectRatio()) { + float dy = destRect.width() / aspectRatio - destRect.height(); + destRect.inflateY(dy / 2); + } else { + float dx = destRect.height() * aspectRatio - destRect.width(); + destRect.inflateX(dx / 2); + } + return destRect; +} + +bool ellipseContainsPoint(const FloatPoint& center, const FloatSize& radii, const FloatPoint& point) +{ + FloatPoint transformedPoint(point); + transformedPoint.move(-center.x(), -center.y()); + transformedPoint.scale(radii.height(), radii.width()); + float radius = radii.width() * radii.height(); + + if (transformedPoint.x() > radius || transformedPoint.y() > radius) + return false; + if (transformedPoint.x() + transformedPoint.y() <= radius) + return true; + return (transformedPoint.lengthSquared() <= radius * radius); +} + +} diff --git a/Source/WebCore/platform/graphics/GeometryUtilities.h b/Source/WebCore/platform/graphics/GeometryUtilities.h new file mode 100644 index 000000000..86ca19720 --- /dev/null +++ b/Source/WebCore/platform/graphics/GeometryUtilities.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "FloatRect.h" +#include "IntRect.h" +#include <wtf/Vector.h> + +namespace WebCore { + +float euclidianDistance(const FloatPoint&, const FloatPoint&); + +float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c); + +// Find point where lines through the two pairs of points intersect. Returns false if the lines don't intersect. +WEBCORE_EXPORT bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection); + +IntRect unionRect(const Vector<IntRect>&); +WEBCORE_EXPORT FloatRect unionRect(const Vector<FloatRect>&); + +// Map rect r from srcRect to an equivalent rect in destRect. +FloatRect mapRect(const FloatRect&, const FloatRect& srcRect, const FloatRect& destRect); + +WEBCORE_EXPORT FloatRect largestRectWithAspectRatioInsideRect(float aspectRatio, const FloatRect&); +WEBCORE_EXPORT FloatRect smallestRectWithAspectRatioAroundRect(float aspectRatio, const FloatRect&); + +// Compute a rect that encloses all points covered by the given rect if it were rotated a full turn around (0,0). +FloatRect boundsOfRotatingRect(const FloatRect&); + +bool ellipseContainsPoint(const FloatPoint& center, const FloatSize& radii, const FloatPoint&); +} diff --git a/Source/WebCore/platform/graphics/Glyph.h b/Source/WebCore/platform/graphics/Glyph.h index 309463f49..2af8b33d6 100644 --- a/Source/WebCore/platform/graphics/Glyph.h +++ b/Source/WebCore/platform/graphics/Glyph.h @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * diff --git a/Source/WebCore/platform/graphics/GlyphBuffer.h b/Source/WebCore/platform/graphics/GlyphBuffer.h index d72634cfd..61f3f050b 100644 --- a/Source/WebCore/platform/graphics/GlyphBuffer.h +++ b/Source/WebCore/platform/graphics/GlyphBuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2009, 2011, 2016 Apple Inc. All rights reserved. * Copyright (C) 2007-2008 Torch Mobile Inc. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -32,6 +32,7 @@ #include "FloatSize.h" #include "Glyph.h" +#include <climits> #include <wtf/Vector.h> #if USE(CG) @@ -44,7 +45,7 @@ namespace WebCore { -class SimpleFontData; +class Font; #if USE(CAIRO) // FIXME: Why does Cairo use such a huge struct instead of just an offset into an array? @@ -61,11 +62,17 @@ typedef Glyph GlyphBufferGlyph; struct GlyphBufferAdvance : CGSize { public: GlyphBufferAdvance() : CGSize(CGSizeZero) { } - GlyphBufferAdvance(CGSize size) : CGSize(size) + GlyphBufferAdvance(CGSize size) + : CGSize(size) + { + } + GlyphBufferAdvance(float width, float height) + : CGSize(CGSizeMake(width, height)) { } void setWidth(CGFloat width) { this->CGSize::width = width; } + void setHeight(CGFloat height) { this->CGSize::height = height; } CGFloat width() const { return this->CGSize::width; } CGFloat height() const { return this->CGSize::height; } }; @@ -75,30 +82,33 @@ typedef FloatSize GlyphBufferAdvance; class GlyphBuffer { public: - bool isEmpty() const { return m_fontData.isEmpty(); } - int size() const { return m_fontData.size(); } + bool isEmpty() const { return m_font.isEmpty(); } + unsigned size() const { return m_font.size(); } void clear() { - m_fontData.clear(); + m_font.clear(); m_glyphs.clear(); m_advances.clear(); + if (m_offsetsInString) + m_offsetsInString->clear(); #if PLATFORM(WIN) m_offsets.clear(); #endif } - GlyphBufferGlyph* glyphs(int from) { return m_glyphs.data() + from; } - GlyphBufferAdvance* advances(int from) { return m_advances.data() + from; } - const GlyphBufferGlyph* glyphs(int from) const { return m_glyphs.data() + from; } - const GlyphBufferAdvance* advances(int from) const { return m_advances.data() + from; } + GlyphBufferGlyph* glyphs(unsigned from) { return m_glyphs.data() + from; } + GlyphBufferAdvance* advances(unsigned from) { return m_advances.data() + from; } + const GlyphBufferGlyph* glyphs(unsigned from) const { return m_glyphs.data() + from; } + const GlyphBufferAdvance* advances(unsigned from) const { return m_advances.data() + from; } + size_t advancesCount() const { return m_advances.size(); } - const SimpleFontData* fontDataAt(int index) const { return m_fontData[index]; } + const Font* fontAt(unsigned index) const { return m_font[index]; } void setInitialAdvance(GlyphBufferAdvance initialAdvance) { m_initialAdvance = initialAdvance; } const GlyphBufferAdvance& initialAdvance() const { return m_initialAdvance; } - Glyph glyphAt(int index) const + Glyph glyphAt(unsigned index) const { #if USE(CAIRO) return m_glyphs[index].index; @@ -107,12 +117,12 @@ public: #endif } - GlyphBufferAdvance advanceAt(int index) const + GlyphBufferAdvance advanceAt(unsigned index) const { return m_advances[index]; } - FloatSize offsetAt(int index) const + FloatSize offsetAt(unsigned index) const { #if PLATFORM(WIN) return m_offsets[index]; @@ -121,20 +131,11 @@ public: return FloatSize(); #endif } - - void add(const GlyphBuffer* glyphBuffer, int from, int len) - { - m_glyphs.append(glyphBuffer->glyphs(from), len); - m_advances.append(glyphBuffer->advances(from), len); - m_fontData.append(glyphBuffer->m_fontData.data() + from, len); -#if PLATFORM(WIN) - m_offsets.append(glyphBuffer->m_offsets.data() + from, len); -#endif - } - - void add(Glyph glyph, const SimpleFontData* font, float width, const FloatSize* offset = 0) + + static const unsigned noOffset = UINT_MAX; + void add(Glyph glyph, const Font* font, float width, unsigned offsetInString = noOffset, const FloatSize* offset = 0) { - m_fontData.append(font); + m_font.append(font); #if USE(CAIRO) cairo_glyph_t cairoGlyph; @@ -159,12 +160,15 @@ public: #else UNUSED_PARAM(offset); #endif + + if (offsetInString != noOffset && m_offsetsInString) + m_offsetsInString->append(offsetInString); } #if !USE(WINGDI) - void add(Glyph glyph, const SimpleFontData* font, GlyphBufferAdvance advance) + void add(Glyph glyph, const Font* font, GlyphBufferAdvance advance, unsigned offsetInString = noOffset) { - m_fontData.append(font); + m_font.append(font); #if USE(CAIRO) cairo_glyph_t cairoGlyph; cairoGlyph.index = glyph; @@ -174,12 +178,15 @@ public: #endif m_advances.append(advance); + + if (offsetInString != noOffset && m_offsetsInString) + m_offsetsInString->append(offsetInString); } #endif - void reverse(int from, int length) + void reverse(unsigned from, unsigned length) { - for (int i = from, end = from + length - 1; i < end; ++i, --end) + for (unsigned i = from, end = from + length - 1; i < end; ++i, --end) swap(i, end); } @@ -189,13 +196,36 @@ public: GlyphBufferAdvance& lastAdvance = m_advances.last(); lastAdvance.setWidth(lastAdvance.width() + width); } + + void saveOffsetsInString() + { + m_offsetsInString.reset(new Vector<unsigned, 2048>()); + } + + int offsetInString(unsigned index) const + { + ASSERT(m_offsetsInString); + return (*m_offsetsInString)[index]; + } + + void shrink(unsigned truncationPoint) + { + m_font.shrink(truncationPoint); + m_glyphs.shrink(truncationPoint); + m_advances.shrink(truncationPoint); + if (m_offsetsInString) + m_offsetsInString->shrink(truncationPoint); +#if PLATFORM(WIN) + m_offsets.shrink(truncationPoint); +#endif + } private: - void swap(int index1, int index2) + void swap(unsigned index1, unsigned index2) { - const SimpleFontData* f = m_fontData[index1]; - m_fontData[index1] = m_fontData[index2]; - m_fontData[index2] = f; + const Font* f = m_font[index1]; + m_font[index1] = m_font[index2]; + m_font[index2] = f; GlyphBufferGlyph g = m_glyphs[index1]; m_glyphs[index1] = m_glyphs[index2]; @@ -212,10 +242,11 @@ private: #endif } - Vector<const SimpleFontData*, 2048> m_fontData; + Vector<const Font*, 2048> m_font; Vector<GlyphBufferGlyph, 2048> m_glyphs; Vector<GlyphBufferAdvance, 2048> m_advances; GlyphBufferAdvance m_initialAdvance; + std::unique_ptr<Vector<unsigned, 2048>> m_offsetsInString; #if PLATFORM(WIN) Vector<FloatSize, 2048> m_offsets; #endif diff --git a/Source/WebCore/platform/graphics/GlyphMetricsMap.h b/Source/WebCore/platform/graphics/GlyphMetricsMap.h index 61c5fb09c..4f0f58a58 100644 --- a/Source/WebCore/platform/graphics/GlyphMetricsMap.h +++ b/Source/WebCore/platform/graphics/GlyphMetricsMap.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -32,58 +32,70 @@ #include "Glyph.h" #include <array> #include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/unicode/Unicode.h> namespace WebCore { const float cGlyphSizeUnknown = -1; template<class T> class GlyphMetricsMap { - WTF_MAKE_NONCOPYABLE(GlyphMetricsMap); + WTF_MAKE_FAST_ALLOCATED; public: - GlyphMetricsMap() : m_filledPrimaryPage(false) { } T metricsForGlyph(Glyph glyph) { - return locatePage(glyph / GlyphMetricsPage::size)->metricsForGlyph(glyph); + return locatePage(glyph / GlyphMetricsPage::size).metricsForGlyph(glyph); } void setMetricsForGlyph(Glyph glyph, const T& metrics) { - locatePage(glyph / GlyphMetricsPage::size)->setMetricsForGlyph(glyph, metrics); + locatePage(glyph / GlyphMetricsPage::size).setMetricsForGlyph(glyph, metrics); } private: - struct GlyphMetricsPage { - static const size_t size = 256; // Usually covers Latin-1 in a single page. - std::array<T, size> m_metrics; + class GlyphMetricsPage { + WTF_MAKE_FAST_ALLOCATED; + public: + static const size_t size = 16; + + GlyphMetricsPage() = default; + explicit GlyphMetricsPage(const T& initialValue) + { + fill(initialValue); + } + + void fill(const T& value) + { + m_metrics.fill(value); + } T metricsForGlyph(Glyph glyph) const { return m_metrics[glyph % size]; } void setMetricsForGlyph(Glyph glyph, const T& metrics) { setMetricsForIndex(glyph % size, metrics); } + + private: void setMetricsForIndex(unsigned index, const T& metrics) { m_metrics[index] = metrics; } + + std::array<T, size> m_metrics; }; - GlyphMetricsPage* locatePage(unsigned pageNumber) + GlyphMetricsPage& locatePage(unsigned pageNumber) { if (!pageNumber && m_filledPrimaryPage) - return &m_primaryPage; + return m_primaryPage; return locatePageSlowCase(pageNumber); } - GlyphMetricsPage* locatePageSlowCase(unsigned pageNumber); + GlyphMetricsPage& locatePageSlowCase(unsigned pageNumber); static T unknownMetrics(); - bool m_filledPrimaryPage; + bool m_filledPrimaryPage { false }; GlyphMetricsPage m_primaryPage; // We optimize for the page that contains glyph indices 0-255. - OwnPtr<HashMap<int, OwnPtr<GlyphMetricsPage>>> m_pages; + std::unique_ptr<HashMap<int, std::unique_ptr<GlyphMetricsPage>>> m_pages; }; template<> inline float GlyphMetricsMap<float>::unknownMetrics() @@ -96,28 +108,22 @@ template<> inline FloatRect GlyphMetricsMap<FloatRect>::unknownMetrics() return FloatRect(0, 0, cGlyphSizeUnknown, cGlyphSizeUnknown); } -template<class T> typename GlyphMetricsMap<T>::GlyphMetricsPage* GlyphMetricsMap<T>::locatePageSlowCase(unsigned pageNumber) +template<class T> typename GlyphMetricsMap<T>::GlyphMetricsPage& GlyphMetricsMap<T>::locatePageSlowCase(unsigned pageNumber) { - GlyphMetricsPage* page; if (!pageNumber) { ASSERT(!m_filledPrimaryPage); - page = &m_primaryPage; + m_primaryPage.fill(unknownMetrics()); m_filledPrimaryPage = true; - } else { - if (m_pages) { - if ((page = m_pages->get(pageNumber))) - return page; - } else - m_pages = adoptPtr(new HashMap<int, OwnPtr<GlyphMetricsPage>>); - page = new GlyphMetricsPage; - m_pages->set(pageNumber, adoptPtr(page)); + return m_primaryPage; } - // Fill in the whole page with the unknown glyph information. - for (unsigned i = 0; i < GlyphMetricsPage::size; i++) - page->setMetricsForIndex(i, unknownMetrics()); + if (!m_pages) + m_pages = std::make_unique<HashMap<int, std::unique_ptr<GlyphMetricsPage>>>(); - return page; + auto& page = m_pages->ensure(pageNumber, [] { + return std::make_unique<GlyphMetricsPage>(unknownMetrics()); + }).iterator->value; + return *page; } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/GlyphPage.h b/Source/WebCore/platform/graphics/GlyphPage.h index 53c200955..cd0dcd46f 100644 --- a/Source/WebCore/platform/graphics/GlyphPage.h +++ b/Source/WebCore/platform/graphics/GlyphPage.h @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,174 +31,104 @@ #define GlyphPage_h #include "Glyph.h" -#include <string.h> -#include <wtf/PassRefPtr.h> +#include <unicode/utypes.h> #include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/unicode/Unicode.h> +#include <wtf/Ref.h> namespace WebCore { -class SimpleFontData; -class GlyphPageTreeNode; +class Font; -// Holds the glyph index and the corresponding SimpleFontData information for a given +// Holds the glyph index and the corresponding Font information for a given // character. struct GlyphData { - GlyphData(Glyph g = 0, const SimpleFontData* f = 0) - : glyph(g) - , fontData(f) + GlyphData(Glyph glyph = 0, const Font* font = nullptr) + : glyph(glyph) + , font(font) { } + + bool isValid() const { return glyph || font; } + Glyph glyph; - const SimpleFontData* fontData; + const Font* font; }; -#if COMPILER(MSVC) -#pragma warning(push) -#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning -#endif - // A GlyphPage contains a fixed-size set of GlyphData mappings for a contiguous // range of characters in the Unicode code space. GlyphPages are indexed -// starting from 0 and incrementing for each 256 glyphs. -// -// One page may actually include glyphs from other fonts if the characters are -// missing in the primary font. It is owned by exactly one GlyphPageTreeNode, -// although multiple nodes may reference it as their "page" if they are supposed -// to be overriding the parent's node, but provide no additional information. +// starting from 0 and incrementing for each "size" number of glyphs. class GlyphPage : public RefCounted<GlyphPage> { public: - static PassRefPtr<GlyphPage> createForMixedFontData(GlyphPageTreeNode* owner) + static Ref<GlyphPage> create(const Font& font) { - void* slot = fastMalloc(sizeof(GlyphPage) + sizeof(SimpleFontData*) * GlyphPage::size); - return adoptRef(new (NotNull, slot) GlyphPage(owner)); + return adoptRef(*new GlyphPage(font)); } - static PassRefPtr<GlyphPage> createForSingleFontData(GlyphPageTreeNode* owner, const SimpleFontData* fontData) + ~GlyphPage() { - ASSERT(fontData); - return adoptRef(new GlyphPage(owner, fontData)); + --s_count; } - PassRefPtr<GlyphPage> createCopiedSystemFallbackPage(GlyphPageTreeNode* owner) const - { - RefPtr<GlyphPage> page = GlyphPage::createForMixedFontData(owner); - memcpy(page->m_glyphs, m_glyphs, sizeof(m_glyphs)); - if (hasPerGlyphFontData()) - memcpy(page->m_perGlyphFontData, m_perGlyphFontData, sizeof(SimpleFontData*) * GlyphPage::size); - else { - for (size_t i = 0; i < GlyphPage::size; ++i) { - page->m_perGlyphFontData[i] = m_glyphs[i] ? m_fontDataForAllGlyphs : 0; - } - } - return page.release(); - } + static unsigned count() { return s_count; } - ~GlyphPage() { } + static const unsigned size = 16; - static const size_t size = 256; // Covers Latin-1 in a single page. - static unsigned indexForCharacter(UChar32 c) { return c % GlyphPage::size; } + static unsigned sizeForPageNumber(unsigned) { return 16; } + static unsigned indexForCodePoint(UChar32 c) { return c % size; } + static unsigned pageNumberForCodePoint(UChar32 c) { return c / size; } + static UChar32 startingCodePointInPageNumber(unsigned pageNumber) { return pageNumber * size; } + static bool pageNumberIsUsedForArabic(unsigned pageNumber) { return startingCodePointInPageNumber(pageNumber) >= 0x600 && startingCodePointInPageNumber(pageNumber) + sizeForPageNumber(pageNumber) < 0x700; } - ALWAYS_INLINE GlyphData glyphDataForCharacter(UChar32 c) const + GlyphData glyphDataForCharacter(UChar32 c) const { - return glyphDataForIndex(indexForCharacter(c)); + return glyphDataForIndex(indexForCodePoint(c)); } - ALWAYS_INLINE GlyphData glyphDataForIndex(unsigned index) const + Glyph glyphForCharacter(UChar32 c) const { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - Glyph glyph = m_glyphs[index]; - if (hasPerGlyphFontData()) - return GlyphData(glyph, m_perGlyphFontData[index]); - return GlyphData(glyph, glyph ? m_fontDataForAllGlyphs : 0); - } - - ALWAYS_INLINE Glyph glyphAt(unsigned index) const - { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - return m_glyphs[index]; + return glyphForIndex(indexForCodePoint(c)); } - ALWAYS_INLINE const SimpleFontData* fontDataForCharacter(UChar32 c) const + GlyphData glyphDataForIndex(unsigned index) const { - unsigned index = indexForCharacter(c); - if (hasPerGlyphFontData()) - return m_perGlyphFontData[index]; - return m_glyphs[index] ? m_fontDataForAllGlyphs : 0; + Glyph glyph = glyphForIndex(index); + return GlyphData(glyph, glyph ? &m_font : nullptr); } - void setGlyphDataForCharacter(UChar32 c, Glyph g, const SimpleFontData* f) + Glyph glyphForIndex(unsigned index) const { - setGlyphDataForIndex(indexForCharacter(c), g, f); + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + return m_glyphs[index]; } - void setGlyphDataForIndex(unsigned index, Glyph glyph, const SimpleFontData* fontData) + // FIXME: Pages are immutable after initialization. This should be private. + void setGlyphForIndex(unsigned index, Glyph glyph) { ASSERT_WITH_SECURITY_IMPLICATION(index < size); m_glyphs[index] = glyph; - - // GlyphPage getters will always return a null SimpleFontData* for glyph #0 if there's no per-glyph font array. - if (hasPerGlyphFontData()) { - m_perGlyphFontData[index] = glyph ? fontData : 0; - return; - } - - // A single-font GlyphPage already assigned m_fontDataForAllGlyphs in the constructor. - ASSERT(!glyph || fontData == m_fontDataForAllGlyphs); - } - - void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData) - { - setGlyphDataForIndex(index, glyphData.glyph, glyphData.fontData); } - void removeFontDataFromSystemFallbackPage(const SimpleFontData* fontData) + const Font& font() const { - // This method should only be called on the system fallback page, which is never single-font. - ASSERT(hasPerGlyphFontData()); - for (size_t i = 0; i < size; ++i) { - if (m_perGlyphFontData[i] == fontData) { - m_glyphs[i] = 0; - m_perGlyphFontData[i] = 0; - } - } + return m_font; } - GlyphPageTreeNode* owner() const { return m_owner; } - // Implemented by the platform. - bool fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData*); -#if PLATFORM(MAC) - static bool mayUseMixedFontDataWhenFilling(const UChar* characterBuffer, unsigned bufferLength, const SimpleFontData*); -#else - static bool mayUseMixedFontDataWhenFilling(const UChar*, unsigned, const SimpleFontData*) { return false; } -#endif + bool fill(UChar* characterBuffer, unsigned bufferLength); private: - explicit GlyphPage(GlyphPageTreeNode* owner, const SimpleFontData* fontDataForAllGlyphs = 0) - : m_fontDataForAllGlyphs(fontDataForAllGlyphs) - , m_owner(owner) + explicit GlyphPage(const Font& font) + : m_font(font) { - memset(m_glyphs, 0, sizeof(m_glyphs)); - if (hasPerGlyphFontData()) - memset(m_perGlyphFontData, 0, sizeof(SimpleFontData*) * GlyphPage::size); + ++s_count; } - bool hasPerGlyphFontData() const { return !m_fontDataForAllGlyphs; } + const Font& m_font; + Glyph m_glyphs[size] { }; - const SimpleFontData* m_fontDataForAllGlyphs; - GlyphPageTreeNode* m_owner; - Glyph m_glyphs[size]; - - // NOTE: This array has (GlyphPage::size) elements if m_fontDataForAllGlyphs is null. - const SimpleFontData* m_perGlyphFontData[0]; + WEBCORE_EXPORT static unsigned s_count; }; -#if COMPILER(MSVC) -#pragma warning(pop) -#endif - } // namespace WebCore #endif // GlyphPage_h diff --git a/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp deleted file mode 100644 index caef5e9d6..000000000 --- a/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "GlyphPageTreeNode.h" - -#include "SegmentedFontData.h" -#include "SimpleFontData.h" -#include <stdio.h> -#include <wtf/text/CString.h> -#include <wtf/text/WTFString.h> -#include <wtf/unicode/CharacterNames.h> -#include <wtf/unicode/Unicode.h> - -#if ENABLE(OPENTYPE_VERTICAL) -#include "OpenTypeVerticalData.h" -#endif - -namespace WebCore { - -HashMap<int, GlyphPageTreeNode*>* GlyphPageTreeNode::roots = 0; -GlyphPageTreeNode* GlyphPageTreeNode::pageZeroRoot = 0; - -GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber) -{ - static bool initialized; - if (!initialized) { - initialized = true; - roots = new HashMap<int, GlyphPageTreeNode*>; - pageZeroRoot = new GlyphPageTreeNode; - } - - if (!pageNumber) - return pageZeroRoot; - - if (GlyphPageTreeNode* foundNode = roots->get(pageNumber)) - return foundNode; - - GlyphPageTreeNode* node = new GlyphPageTreeNode; -#ifndef NDEBUG - node->m_pageNumber = pageNumber; -#endif - roots->set(pageNumber, node); - return node; -} - -size_t GlyphPageTreeNode::treeGlyphPageCount() -{ - size_t count = 0; - if (roots) { - HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); - for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it) - count += it->value->pageCount(); - } - - if (pageZeroRoot) - count += pageZeroRoot->pageCount(); - - return count; -} - -size_t GlyphPageTreeNode::pageCount() const -{ - size_t count = m_page && m_page->owner() == this ? 1 : 0; - GlyphPageTreeNodeMap::const_iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::const_iterator it = m_children.begin(); it != end; ++it) - count += it->value->pageCount(); - - return count; -} - -void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) -{ - // Enumerate all the roots and prune any tree that contains our custom font data. - if (roots) { - HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); - for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it) - it->value->pruneCustomFontData(fontData); - } - - if (pageZeroRoot) - pageZeroRoot->pruneCustomFontData(fontData); -} - -void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData) -{ - if (roots) { - HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end(); - for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it) - it->value->pruneFontData(fontData); - } - - if (pageZeroRoot) - pageZeroRoot->pruneFontData(fontData); -} - -static bool fill(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) -{ -#if ENABLE(SVG_FONTS) - if (SimpleFontData::AdditionalFontData* additionalFontData = fontData->fontData()) - return additionalFontData->fillSVGGlyphPage(pageToFill, offset, length, buffer, bufferLength, fontData); -#endif - bool hasGlyphs = pageToFill->fill(offset, length, buffer, bufferLength, fontData); -#if ENABLE(OPENTYPE_VERTICAL) - if (hasGlyphs && fontData->verticalData()) - fontData->verticalData()->substituteWithVerticalGlyphs(fontData, pageToFill, offset, length); -#endif - return hasGlyphs; -} - -void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNumber) -{ - ASSERT(!m_page); - - // This function must not be called for the root of the tree, because that - // level does not contain any glyphs. - ASSERT(m_level > 0 && m_parent); - - // The parent's page will be 0 if we are level one or the parent's font data - // did not contain any glyphs for that page. - GlyphPage* parentPage = m_parent->page(); - - // NULL FontData means we're being asked for the system fallback font. - if (fontData) { - if (m_level == 1) { - // Children of the root hold pure pages. These will cover only one - // font data's glyphs, and will have glyph index 0 if the font data does not - // contain the glyph. - unsigned start = pageNumber * GlyphPage::size; - UChar buffer[GlyphPage::size * 2 + 2]; - unsigned bufferLength; - unsigned i; - - // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for. - if (start < 0x10000) { - bufferLength = GlyphPage::size; - for (i = 0; i < GlyphPage::size; i++) - buffer[i] = start + i; - - if (start == 0) { - // Control characters must not render at all. - for (i = 0; i < 0x20; ++i) - buffer[i] = zeroWidthSpace; - for (i = 0x7F; i < 0xA0; i++) - buffer[i] = zeroWidthSpace; - buffer[softHyphen] = zeroWidthSpace; - - // \n, \t, and nonbreaking space must render as a space. - buffer[(int)'\n'] = ' '; - buffer[(int)'\t'] = ' '; - buffer[noBreakSpace] = ' '; - } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) { - // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all. - buffer[leftToRightMark - start] = zeroWidthSpace; - buffer[rightToLeftMark - start] = zeroWidthSpace; - buffer[leftToRightEmbed - start] = zeroWidthSpace; - buffer[rightToLeftEmbed - start] = zeroWidthSpace; - buffer[leftToRightOverride - start] = zeroWidthSpace; - buffer[rightToLeftOverride - start] = zeroWidthSpace; - buffer[zeroWidthNonJoiner - start] = zeroWidthSpace; - buffer[zeroWidthJoiner - start] = zeroWidthSpace; - buffer[popDirectionalFormatting - start] = zeroWidthSpace; - } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) { - // Object replacement character must not render at all. - buffer[objectReplacementCharacter - start] = zeroWidthSpace; - } else if (start == (zeroWidthNoBreakSpace & ~(GlyphPage::size - 1))) { - // ZWNBS/BOM must not render at all. - buffer[zeroWidthNoBreakSpace - start] = zeroWidthSpace; - } - } else { - bufferLength = GlyphPage::size * 2; - for (i = 0; i < GlyphPage::size; i++) { - int c = i + start; - buffer[i * 2] = U16_LEAD(c); - buffer[i * 2 + 1] = U16_TRAIL(c); - } - } - - // Now that we have a buffer full of characters, we want to get back an array - // of glyph indices. This part involves calling into the platform-specific - // routine of our glyph map for actually filling in the page with the glyphs. - // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data - // for only 128 out of 256 characters. - bool haveGlyphs; - if (!fontData->isSegmented()) { - if (GlyphPage::mayUseMixedFontDataWhenFilling(buffer, bufferLength, static_cast<const SimpleFontData*>(fontData))) - m_page = GlyphPage::createForMixedFontData(this); - else - m_page = GlyphPage::createForSingleFontData(this, static_cast<const SimpleFontData*>(fontData)); -#if PLATFORM(IOS) - // FIXME: Times New Roman contains Arabic glyphs, but Core Text doesn't know how to shape them. See <rdar://problem/9823975>. - // Once we have the fix for <rdar://problem/9823975> then remove this code together with SimpleFontData::shouldNotBeUsedForArabic() - // in <rdar://problem/12096835>. - if (pageNumber == 6 && static_cast<const SimpleFontData*>(fontData)->shouldNotBeUsedForArabic()) - haveGlyphs = false; - else -#endif - haveGlyphs = fill(m_page.get(), 0, GlyphPage::size, buffer, bufferLength, static_cast<const SimpleFontData*>(fontData)); - } else { - m_page = GlyphPage::createForMixedFontData(this); - haveGlyphs = false; - - const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData); - unsigned numRanges = segmentedFontData->numRanges(); - bool zeroFilled = false; - RefPtr<GlyphPage> scratchPage; - GlyphPage* pageToFill = m_page.get(); - for (unsigned i = 0; i < numRanges; i++) { - const FontDataRange& range = segmentedFontData->rangeAt(i); - // all this casting is to ensure all the parameters to min and max have the same type, - // to avoid ambiguous template parameter errors on Windows - int from = std::max(0, static_cast<int>(range.from()) - static_cast<int>(start)); - int to = 1 + std::min(static_cast<int>(range.to()) - static_cast<int>(start), static_cast<int>(GlyphPage::size) - 1); - if (from < static_cast<int>(GlyphPage::size) && to > 0) { - if (haveGlyphs && !scratchPage) { - scratchPage = GlyphPage::createForMixedFontData(this); - pageToFill = scratchPage.get(); - } - - if (!zeroFilled) { - if (from > 0 || to < static_cast<int>(GlyphPage::size)) { - for (unsigned i = 0; i < GlyphPage::size; i++) - pageToFill->setGlyphDataForIndex(i, 0, 0); - } - zeroFilled = true; - } - haveGlyphs |= fill(pageToFill, from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData().get()); - if (scratchPage) { - ASSERT_WITH_SECURITY_IMPLICATION(to <= static_cast<int>(GlyphPage::size)); - for (int j = from; j < to; j++) { - if (!m_page->glyphAt(j) && pageToFill->glyphAt(j)) - m_page->setGlyphDataForIndex(j, pageToFill->glyphDataForIndex(j)); - } - } - } - } - } - - if (!haveGlyphs) - m_page = 0; - } else if (parentPage && parentPage->owner() != m_parent) { - // The page we're overriding may not be owned by our parent node. - // This happens when our parent node provides no useful overrides - // and just copies the pointer to an already-existing page (see - // below). - // - // We want our override to be shared by all nodes that reference - // that page to avoid duplication, and so standardize on having the - // page's owner collect all the overrides. Call getChild on the - // page owner with the desired font data (this will populate - // the page) and then reference it. - m_page = parentPage->owner()->getChild(fontData, pageNumber)->page(); - } else { - // Get the pure page for the fallback font (at level 1 with no - // overrides). getRootChild will always create a page if one - // doesn't exist, but the page doesn't necessarily have glyphs - // (this pointer may be 0). - GlyphPage* fallbackPage = getRootChild(fontData, pageNumber)->page(); - if (!parentPage) { - // When the parent has no glyphs for this page, we can easily - // override it just by supplying the glyphs from our font. - m_page = fallbackPage; - } else if (!fallbackPage) { - // When our font has no glyphs for this page, we can just reference the - // parent page. - m_page = parentPage; - } else { - // Combine the parent's glyphs and ours to form a new more complete page. - m_page = GlyphPage::createForMixedFontData(this); - - // Overlay the parent page on the fallback page. Check if the fallback font - // has added anything. - bool newGlyphs = false; - for (unsigned i = 0; i < GlyphPage::size; i++) { - if (parentPage->glyphAt(i)) - m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i)); - else if (fallbackPage->glyphAt(i)) { - m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i)); - newGlyphs = true; - } else - m_page->setGlyphDataForIndex(i, 0, 0); - } - - if (!newGlyphs) - // We didn't override anything, so our override is just the parent page. - m_page = parentPage; - } - } - } else { - // System fallback. Initialized with the parent's page here, as individual - // entries may use different fonts depending on character. If the Font - // ever finds it needs a glyph out of the system fallback page, it will - // ask the system for the best font to use and fill that glyph in for us. - if (parentPage) - m_page = parentPage->createCopiedSystemFallbackPage(this); - else - m_page = GlyphPage::createForMixedFontData(this); - } -} - -GlyphPageTreeNode* GlyphPageTreeNode::getChild(const FontData* fontData, unsigned pageNumber) -{ - ASSERT(fontData || !m_isSystemFallback); - ASSERT(pageNumber == m_pageNumber); - - if (GlyphPageTreeNode* foundChild = fontData ? m_children.get(fontData) : m_systemFallbackChild.get()) - return foundChild; - - GlyphPageTreeNode* child = new GlyphPageTreeNode; - child->m_parent = this; - child->m_level = m_level + 1; - if (fontData && fontData->isCustomFont()) { - for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) - curr->m_customFontCount++; - } - -#ifndef NDEBUG - child->m_pageNumber = m_pageNumber; -#endif - if (fontData) { - m_children.set(fontData, adoptPtr(child)); - fontData->setMaxGlyphPageTreeLevel(std::max(fontData->maxGlyphPageTreeLevel(), child->m_level)); - } else { - m_systemFallbackChild = adoptPtr(child); - child->m_isSystemFallback = true; - } - child->initializePage(fontData, pageNumber); - return child; -} - -void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData) -{ - if (!fontData || !m_customFontCount) - return; - - // Prune any branch that contains this FontData. - if (OwnPtr<GlyphPageTreeNode> node = m_children.take(fontData)) { - if (unsigned customFontCount = node->m_customFontCount + 1) { - for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) - curr->m_customFontCount -= customFontCount; - } - } - - // Check any branches that remain that still have custom fonts underneath them. - if (!m_customFontCount) - return; - - GlyphPageTreeNodeMap::iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) - it->value->pruneCustomFontData(fontData); -} - -void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, unsigned level) -{ - ASSERT(fontData); - - // Prune fall back child (if any) of this font. - if (m_systemFallbackChild && m_systemFallbackChild->m_page) - m_systemFallbackChild->m_page->removeFontDataFromSystemFallbackPage(fontData); - - // Prune any branch that contains this FontData. - if (OwnPtr<GlyphPageTreeNode> node = m_children.take(fontData)) { - if (unsigned customFontCount = node->m_customFontCount) { - for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) - curr->m_customFontCount -= customFontCount; - } - } - - level++; - if (level > fontData->maxGlyphPageTreeLevel()) - return; - - GlyphPageTreeNodeMap::iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) - it->value->pruneFontData(fontData, level); -} - -#ifndef NDEBUG - void GlyphPageTreeNode::showSubtree() - { - Vector<char> indent(level()); - indent.fill('\t', level()); - indent.append(0); - - GlyphPageTreeNodeMap::iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) { - printf("%s\t%p %s\n", indent.data(), it->key, it->key->description().utf8().data()); - it->value->showSubtree(); - } - if (m_systemFallbackChild) { - printf("%s\t* fallback\n", indent.data()); - m_systemFallbackChild->showSubtree(); - } - } -#endif - -} - -#ifndef NDEBUG -void showGlyphPageTrees() -{ - printf("Page 0:\n"); - showGlyphPageTree(0); - HashMap<int, WebCore::GlyphPageTreeNode*>::iterator end = WebCore::GlyphPageTreeNode::roots->end(); - for (HashMap<int, WebCore::GlyphPageTreeNode*>::iterator it = WebCore::GlyphPageTreeNode::roots->begin(); it != end; ++it) { - printf("\nPage %d:\n", it->key); - showGlyphPageTree(it->key); - } -} - -void showGlyphPageTree(unsigned pageNumber) -{ - WebCore::GlyphPageTreeNode::getRoot(pageNumber)->showSubtree(); -} -#endif diff --git a/Source/WebCore/platform/graphics/GlyphPageTreeNode.h b/Source/WebCore/platform/graphics/GlyphPageTreeNode.h deleted file mode 100644 index b68772922..000000000 --- a/Source/WebCore/platform/graphics/GlyphPageTreeNode.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef GlyphPageTreeNode_h -#define GlyphPageTreeNode_h - -#include "GlyphPage.h" -#include <string.h> -#include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/unicode/Unicode.h> - -#ifndef NDEBUG -void showGlyphPageTrees(); -void showGlyphPageTree(unsigned pageNumber); -#endif - -namespace WebCore { - -class FontData; -class SimpleFontData; - -// The glyph page tree is a data structure that maps (FontData, glyph page number) -// to a GlyphPage. Level 0 (the "root") is special. There is one root -// GlyphPageTreeNode for each glyph page number. The roots do not have a -// GlyphPage associated with them, and their initializePage() function is never -// called to fill the glyphs. -// -// Each root node maps a FontData pointer to another GlyphPageTreeNode at -// level 1 (the "root child") that stores the actual glyphs for a specific font data. -// These nodes will only have a GlyphPage if they have glyphs for that range. -// -// Levels greater than one correspond to subsequent levels of the fallback list -// for that font. These levels override their parent's page of glyphs by -// filling in holes with the new font (thus making a more complete page). -// -// A NULL FontData pointer corresponds to the system fallback -// font. It is tracked separately from the regular pages and overrides so that -// the glyph pages do not get polluted with these last-resort glyphs. The -// system fallback page is not populated at construction like the other pages, -// but on demand for each glyph, because the system may need to use different -// fallback fonts for each. This lazy population is done by the Font. -class GlyphPageTreeNode { - WTF_MAKE_FAST_ALLOCATED; -public: - static GlyphPageTreeNode* getRootChild(const FontData* fontData, unsigned pageNumber) - { - return getRoot(pageNumber)->getChild(fontData, pageNumber); - } - - static void pruneTreeCustomFontData(const FontData*); - static void pruneTreeFontData(const SimpleFontData*); - - void pruneCustomFontData(const FontData*); - void pruneFontData(const SimpleFontData*, unsigned level = 0); - - GlyphPageTreeNode* parent() const { return m_parent; } - GlyphPageTreeNode* getChild(const FontData*, unsigned pageNumber); - - // Returns a page of glyphs (or NULL if there are no glyphs in this page's character range). - GlyphPage* page() const { return m_page.get(); } - - // Returns the level of this node. See class-level comment. - unsigned level() const { return m_level; } - - // The system fallback font has special rules (see above). - bool isSystemFallback() const { return m_isSystemFallback; } - - static size_t treeGlyphPageCount(); - size_t pageCount() const; - -private: - GlyphPageTreeNode() - : m_parent(0) - , m_level(0) - , m_isSystemFallback(false) - , m_customFontCount(0) -#ifndef NDEBUG - , m_pageNumber(0) -#endif - { - } - - static GlyphPageTreeNode* getRoot(unsigned pageNumber); - void initializePage(const FontData*, unsigned pageNumber); - -#ifndef NDEBUG - void showSubtree(); -#endif - - static HashMap<int, GlyphPageTreeNode*>* roots; - static GlyphPageTreeNode* pageZeroRoot; - - typedef HashMap<const FontData*, OwnPtr<GlyphPageTreeNode>> GlyphPageTreeNodeMap; - - GlyphPageTreeNodeMap m_children; - GlyphPageTreeNode* m_parent; - RefPtr<GlyphPage> m_page; - unsigned m_level : 31; - bool m_isSystemFallback : 1; - unsigned m_customFontCount; - OwnPtr<GlyphPageTreeNode> m_systemFallbackChild; - -#ifndef NDEBUG - unsigned m_pageNumber; - - friend void ::showGlyphPageTrees(); - friend void ::showGlyphPageTree(unsigned pageNumber); -#endif -}; - -} // namespace WebCore - -#endif // GlyphPageTreeNode_h diff --git a/Source/WebCore/platform/graphics/Gradient.cpp b/Source/WebCore/platform/graphics/Gradient.cpp index 67cd6075c..b0d1f6e97 100644 --- a/Source/WebCore/platform/graphics/Gradient.cpp +++ b/Source/WebCore/platform/graphics/Gradient.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,7 +30,7 @@ #include "Color.h" #include "FloatRect.h" #include <wtf/HashFunctions.h> -#include <wtf/StringHasher.h> +#include <wtf/Hasher.h> using WTF::pairIntHash; @@ -69,7 +69,7 @@ Gradient::~Gradient() platformDestroy(); } -void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect) +void Gradient::adjustParametersForTiledDrawing(FloatSize& size, FloatRect& srcRect, const FloatSize& spacing) { if (m_radial) return; @@ -77,6 +77,9 @@ void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect if (srcRect.isEmpty()) return; + if (!spacing.isZero()) + return; + if (m_p0.x() == m_p1.x()) { size.setWidth(1); srcRect.setWidth(1); @@ -93,6 +96,7 @@ void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect void Gradient::addColorStop(float value, const Color& color) { + // FIXME: ExtendedColor - update this to support colors with color spaces. float r; float g; float b; diff --git a/Source/WebCore/platform/graphics/Gradient.h b/Source/WebCore/platform/graphics/Gradient.h index 8d19e3325..ccb018819 100644 --- a/Source/WebCore/platform/graphics/Gradient.h +++ b/Source/WebCore/platform/graphics/Gradient.h @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,7 +31,6 @@ #include "AffineTransform.h" #include "FloatPoint.h" #include "GraphicsTypes.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> @@ -42,6 +41,10 @@ typedef struct CGContext* CGContextRef; typedef struct CGGradient* CGGradientRef; typedef CGGradientRef PlatformGradient; +#elif USE(DIRECT2D) +interface ID2D1Brush; +interface ID2D1RenderTarget; +typedef ID2D1Brush* PlatformGradient; #elif USE(CAIRO) typedef struct _cairo_pattern cairo_pattern_t; typedef cairo_pattern_t* PlatformGradient; @@ -57,19 +60,19 @@ namespace WebCore { class Gradient : public RefCounted<Gradient> { public: - static PassRefPtr<Gradient> create(const FloatPoint& p0, const FloatPoint& p1) + static Ref<Gradient> create(const FloatPoint& p0, const FloatPoint& p1) { - return adoptRef(new Gradient(p0, p1)); + return adoptRef(*new Gradient(p0, p1)); } - static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1) + static Ref<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1) { - return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio)); + return adoptRef(*new Gradient(p0, r0, p1, r1, aspectRatio)); } - ~Gradient(); + WEBCORE_EXPORT ~Gradient(); struct ColorStop; - void addColorStop(const ColorStop&); - void addColorStop(float, const Color&); + WEBCORE_EXPORT void addColorStop(const ColorStop&); + WEBCORE_EXPORT void addColorStop(float, const Color&); bool hasAlpha() const; @@ -130,6 +133,7 @@ namespace WebCore { PlatformGradient platformGradient(); #endif + // FIXME: ExtendedColor - A color stop needs a notion of color space. struct ColorStop { float stop; float red; @@ -150,7 +154,7 @@ namespace WebCore { AffineTransform gradientSpaceTransform() { return m_gradientSpaceTransformation; } void fill(GraphicsContext*, const FloatRect&); - void adjustParametersForTiledDrawing(IntSize&, FloatRect&); + void adjustParametersForTiledDrawing(FloatSize&, FloatRect&, const FloatSize& spacing); void setPlatformGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation); @@ -160,19 +164,25 @@ namespace WebCore { #if USE(CG) void paint(CGContextRef); void paint(GraphicsContext*); +#elif USE(DIRECT2D) + PlatformGradient createPlatformGradientIfNecessary(ID2D1RenderTarget*); #elif USE(CAIRO) PlatformGradient platformGradient(float globalAlpha); #endif private: - Gradient(const FloatPoint& p0, const FloatPoint& p1); + WEBCORE_EXPORT Gradient(const FloatPoint& p0, const FloatPoint& p1); Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio); - void platformInit() { m_gradient = 0; } + void platformInit() { m_gradient = nullptr; } void platformDestroy(); void sortStopsIfNecessary(); +#if USE(DIRECT2D) + void generateGradient(ID2D1RenderTarget*); +#endif + // Keep any parameters relevant to rendering in sync with the structure in Gradient::hash(). bool m_radial; FloatPoint m_p0; diff --git a/Source/WebCore/platform/graphics/GradientImage.cpp b/Source/WebCore/platform/graphics/GradientImage.cpp index e8c420365..50b9d77cf 100644 --- a/Source/WebCore/platform/graphics/GradientImage.cpp +++ b/Source/WebCore/platform/graphics/GradientImage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,34 +26,43 @@ #include "config.h" #include "GradientImage.h" -#include "FloatRect.h" #include "GraphicsContext.h" -#include "Length.h" +#include "ImageBuffer.h" namespace WebCore { -void GradientImage::draw(GraphicsContext* destContext, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) +GradientImage::GradientImage(Gradient& generator, const FloatSize& size) + : m_gradient(generator) { - GraphicsContextStateSaver stateSaver(*destContext); - destContext->setCompositeOperation(compositeOp, blendMode); - destContext->clip(destRect); - destContext->translate(destRect.x(), destRect.y()); + setContainerSize(size); +} + +GradientImage::~GradientImage() +{ +} + +void GradientImage::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) +{ + GraphicsContextStateSaver stateSaver(destContext); + destContext.setCompositeOperation(compositeOp, blendMode); + destContext.clip(destRect); + destContext.translate(destRect.x(), destRect.y()); if (destRect.size() != srcRect.size()) - destContext->scale(FloatSize(destRect.width() / srcRect.width(), destRect.height() / srcRect.height())); - destContext->translate(-srcRect.x(), -srcRect.y()); - destContext->fillRect(FloatRect(FloatPoint(), size()), *m_gradient.get()); + destContext.scale(FloatSize(destRect.width() / srcRect.width(), destRect.height() / srcRect.height())); + destContext.translate(-srcRect.x(), -srcRect.y()); + destContext.fillRect(FloatRect(FloatPoint(), size()), m_gradient.get()); } -void GradientImage::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect, BlendMode blendMode) +void GradientImage::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, BlendMode blendMode) { // Allow the generator to provide visually-equivalent tiling parameters for better performance. - IntSize adjustedSize = size(); + FloatSize adjustedSize = size(); FloatRect adjustedSrcRect = srcRect; - m_gradient->adjustParametersForTiledDrawing(adjustedSize, adjustedSrcRect); + m_gradient->adjustParametersForTiledDrawing(adjustedSize, adjustedSrcRect, spacing); // Factor in the destination context's scale to generate at the best resolution - AffineTransform destContextCTM = destContext->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); + AffineTransform destContextCTM = destContext.getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); double xScale = fabs(destContextCTM.xScale()); double yScale = fabs(destContextCTM.yScale()); AffineTransform adjustedPatternCTM = patternTransform; @@ -62,26 +71,31 @@ void GradientImage::drawPattern(GraphicsContext* destContext, const FloatRect& s unsigned generatorHash = m_gradient->hash(); - if (!m_cachedImageBuffer || m_cachedGeneratorHash != generatorHash || m_cachedAdjustedSize != adjustedSize || !destContext->isCompatibleWithBuffer(m_cachedImageBuffer.get())) { - m_cachedImageBuffer = destContext->createCompatibleBuffer(adjustedSize, m_gradient->hasAlpha()); + if (!m_cachedImageBuffer || m_cachedGeneratorHash != generatorHash || m_cachedAdjustedSize != adjustedSize || !m_cachedImageBuffer->isCompatibleWithContext(destContext)) { + m_cachedImageBuffer = ImageBuffer::createCompatibleBuffer(adjustedSize, ColorSpaceSRGB, destContext); if (!m_cachedImageBuffer) return; // Fill with the generated image. - m_cachedImageBuffer->context()->fillRect(FloatRect(FloatPoint(), adjustedSize), *m_gradient); + m_cachedImageBuffer->context().fillRect(FloatRect(FloatPoint(), adjustedSize), m_gradient.get()); m_cachedGeneratorHash = generatorHash; m_cachedAdjustedSize = adjustedSize; - if (destContext->drawLuminanceMask()) + if (destContext.drawLuminanceMask()) m_cachedImageBuffer->convertToLuminanceMask(); } - m_cachedImageBuffer->setSpaceSize(spaceSize()); - destContext->setDrawLuminanceMask(false); + destContext.setDrawLuminanceMask(false); // Tile the image buffer into the context. - m_cachedImageBuffer->drawPattern(destContext, adjustedSrcRect, adjustedPatternCTM, phase, styleColorSpace, compositeOp, destRect, blendMode); + m_cachedImageBuffer->drawPattern(destContext, destRect, adjustedSrcRect, adjustedPatternCTM, phase, spacing, compositeOp, blendMode); +} + +void GradientImage::dump(TextStream& ts) const +{ + GeneratedImage::dump(ts); + // FIXME: dump the gradient. } } diff --git a/Source/WebCore/platform/graphics/GradientImage.h b/Source/WebCore/platform/graphics/GradientImage.h index 0182dc1d7..c49463951 100644 --- a/Source/WebCore/platform/graphics/GradientImage.h +++ b/Source/WebCore/platform/graphics/GradientImage.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2012, 2013 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,45 +23,36 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GradientImage_h -#define GradientImage_h +#pragma once #include "GeneratedImage.h" -#include "Gradient.h" -#include "Image.h" -#include "ImageBuffer.h" -#include "IntSize.h" -#include <wtf/RefPtr.h> namespace WebCore { +class Gradient; +class ImageBuffer; + class GradientImage final : public GeneratedImage { public: - static PassRefPtr<GradientImage> create(PassRefPtr<Gradient> generator, const IntSize& size) + static Ref<GradientImage> create(Gradient& generator, const FloatSize& size) { - return adoptRef(new GradientImage(generator, size)); + return adoptRef(*new GradientImage(generator, size)); } - virtual ~GradientImage() { } - -protected: - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription) override; - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode) override; - - GradientImage(PassRefPtr<Gradient> generator, const IntSize& size) - : m_gradient(generator) - { - setContainerSize(size); - } + virtual ~GradientImage(); private: - RefPtr<Gradient> m_gradient; + GradientImage(Gradient&, const FloatSize&); + + void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) final; + void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) final; + bool isGradientImage() const final { return true; } + void dump(TextStream&) const final; + + Ref<Gradient> m_gradient; std::unique_ptr<ImageBuffer> m_cachedImageBuffer; - IntSize m_cachedAdjustedSize; + FloatSize m_cachedAdjustedSize; unsigned m_cachedGeneratorHash; }; } - -#endif diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp index f12f02a6f..1bf59c30e 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp @@ -28,13 +28,14 @@ #include "BidiResolver.h" #include "BitmapImage.h" +#include "DisplayListRecorder.h" +#include "FloatRoundedRect.h" #include "Gradient.h" #include "ImageBuffer.h" #include "IntRect.h" #include "RoundedRect.h" #include "TextRun.h" - -#include "stdio.h" +#include "TextStream.h" namespace WebCore { @@ -73,24 +74,267 @@ public: private: const TextRun* m_textRun; - int m_offset; + unsigned m_offset; }; -#if !PLATFORM(IOS) -GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext) - : m_updatingControlTints(false) - , m_transparencyCount(0) +#define CHECK_FOR_CHANGED_PROPERTY(flag, property) \ + if ((m_changeFlags & GraphicsContextState::flag) && (m_state.property != state.property)) \ + changeFlags |= GraphicsContextState::flag; + +GraphicsContextState::StateChangeFlags GraphicsContextStateChange::changesFromState(const GraphicsContextState& state) const { - platformInit(platformGraphicsContext); + GraphicsContextState::StateChangeFlags changeFlags = GraphicsContextState::NoChange; + + CHECK_FOR_CHANGED_PROPERTY(StrokeGradientChange, strokeGradient); + CHECK_FOR_CHANGED_PROPERTY(StrokePatternChange, strokePattern); + CHECK_FOR_CHANGED_PROPERTY(FillGradientChange, fillGradient); + CHECK_FOR_CHANGED_PROPERTY(FillPatternChange, fillPattern); + + if ((m_changeFlags & GraphicsContextState::ShadowChange) + && (m_state.shadowOffset != state.shadowOffset + || m_state.shadowBlur != state.shadowBlur + || m_state.shadowColor != state.shadowColor)) + changeFlags |= GraphicsContextState::ShadowChange; + + CHECK_FOR_CHANGED_PROPERTY(StrokeThicknessChange, strokeThickness); + CHECK_FOR_CHANGED_PROPERTY(TextDrawingModeChange, textDrawingMode); + CHECK_FOR_CHANGED_PROPERTY(StrokeColorChange, strokeColor); + CHECK_FOR_CHANGED_PROPERTY(FillColorChange, fillColor); + CHECK_FOR_CHANGED_PROPERTY(StrokeStyleChange, strokeStyle); + CHECK_FOR_CHANGED_PROPERTY(FillRuleChange, fillRule); + CHECK_FOR_CHANGED_PROPERTY(AlphaChange, alpha); + + if ((m_changeFlags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange)) + && (m_state.compositeOperator != state.compositeOperator || m_state.blendMode != state.blendMode)) + changeFlags |= (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange); + + CHECK_FOR_CHANGED_PROPERTY(ShouldAntialiasChange, shouldAntialias); + CHECK_FOR_CHANGED_PROPERTY(ShouldSmoothFontsChange, shouldSmoothFonts); + CHECK_FOR_CHANGED_PROPERTY(ShouldSubpixelQuantizeFontsChange, shouldSubpixelQuantizeFonts); + CHECK_FOR_CHANGED_PROPERTY(ShadowsIgnoreTransformsChange, shadowsIgnoreTransforms); + CHECK_FOR_CHANGED_PROPERTY(DrawLuminanceMaskChange, drawLuminanceMask); + CHECK_FOR_CHANGED_PROPERTY(ImageInterpolationQualityChange, imageInterpolationQuality); + + return changeFlags; } -#else -GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, bool shouldUseContextColors) - : m_updatingControlTints(false) - , m_transparencyCount(0) + +void GraphicsContextStateChange::accumulate(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) { - platformInit(platformGraphicsContext, shouldUseContextColors); + // FIXME: This code should move to GraphicsContextState. + if (flags & GraphicsContextState::StrokeGradientChange) + m_state.strokeGradient = state.strokeGradient; + + if (flags & GraphicsContextState::StrokePatternChange) + m_state.strokePattern = state.strokePattern; + + if (flags & GraphicsContextState::FillGradientChange) + m_state.fillGradient = state.fillGradient; + + if (flags & GraphicsContextState::FillPatternChange) + m_state.fillPattern = state.fillPattern; + + if (flags & GraphicsContextState::ShadowChange) { + // FIXME: Deal with state.shadowsUseLegacyRadius. + m_state.shadowOffset = state.shadowOffset; + m_state.shadowBlur = state.shadowBlur; + m_state.shadowColor = state.shadowColor; + } + + if (flags & GraphicsContextState::StrokeThicknessChange) + m_state.strokeThickness = state.strokeThickness; + + if (flags & GraphicsContextState::TextDrawingModeChange) + m_state.textDrawingMode = state.textDrawingMode; + + if (flags & GraphicsContextState::StrokeColorChange) + m_state.strokeColor = state.strokeColor; + + if (flags & GraphicsContextState::FillColorChange) + m_state.fillColor = state.fillColor; + + if (flags & GraphicsContextState::StrokeStyleChange) + m_state.strokeStyle = state.strokeStyle; + + if (flags & GraphicsContextState::FillRuleChange) + m_state.fillRule = state.fillRule; + + if (flags & GraphicsContextState::AlphaChange) + m_state.alpha = state.alpha; + + if (flags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange)) { + m_state.compositeOperator = state.compositeOperator; + m_state.blendMode = state.blendMode; + } + + if (flags & GraphicsContextState::ShouldAntialiasChange) + m_state.shouldAntialias = state.shouldAntialias; + + if (flags & GraphicsContextState::ShouldSmoothFontsChange) + m_state.shouldSmoothFonts = state.shouldSmoothFonts; + + if (flags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange) + m_state.shouldSubpixelQuantizeFonts = state.shouldSubpixelQuantizeFonts; + + if (flags & GraphicsContextState::ShadowsIgnoreTransformsChange) + m_state.shadowsIgnoreTransforms = state.shadowsIgnoreTransforms; + + if (flags & GraphicsContextState::DrawLuminanceMaskChange) + m_state.drawLuminanceMask = state.drawLuminanceMask; + + if (flags & GraphicsContextState::ImageInterpolationQualityChange) + m_state.imageInterpolationQuality = state.imageInterpolationQuality; + + m_changeFlags |= flags; } + +void GraphicsContextStateChange::apply(GraphicsContext& context) const +{ + if (m_changeFlags & GraphicsContextState::StrokeGradientChange) + context.setStrokeGradient(*m_state.strokeGradient); + + if (m_changeFlags & GraphicsContextState::StrokePatternChange) + context.setStrokePattern(*m_state.strokePattern); + + if (m_changeFlags & GraphicsContextState::FillGradientChange) + context.setFillGradient(*m_state.fillGradient); + + if (m_changeFlags & GraphicsContextState::FillPatternChange) + context.setFillPattern(*m_state.fillPattern); + + if (m_changeFlags & GraphicsContextState::ShadowChange) { +#if USE(CG) + if (m_state.shadowsUseLegacyRadius) + context.setLegacyShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor); + else #endif + context.setShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor); + } + + if (m_changeFlags & GraphicsContextState::StrokeThicknessChange) + context.setStrokeThickness(m_state.strokeThickness); + + if (m_changeFlags & GraphicsContextState::TextDrawingModeChange) + context.setTextDrawingMode(m_state.textDrawingMode); + + if (m_changeFlags & GraphicsContextState::StrokeColorChange) + context.setStrokeColor(m_state.strokeColor); + + if (m_changeFlags & GraphicsContextState::FillColorChange) + context.setFillColor(m_state.fillColor); + + if (m_changeFlags & GraphicsContextState::StrokeStyleChange) + context.setStrokeStyle(m_state.strokeStyle); + + if (m_changeFlags & GraphicsContextState::FillRuleChange) + context.setFillRule(m_state.fillRule); + + if (m_changeFlags & GraphicsContextState::AlphaChange) + context.setAlpha(m_state.alpha); + + if (m_changeFlags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange)) + context.setCompositeOperation(m_state.compositeOperator, m_state.blendMode); + + if (m_changeFlags & GraphicsContextState::ShouldAntialiasChange) + context.setShouldAntialias(m_state.shouldAntialias); + + if (m_changeFlags & GraphicsContextState::ShouldSmoothFontsChange) + context.setShouldSmoothFonts(m_state.shouldSmoothFonts); + + if (m_changeFlags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange) + context.setShouldSubpixelQuantizeFonts(m_state.shouldSubpixelQuantizeFonts); + + if (m_changeFlags & GraphicsContextState::ShadowsIgnoreTransformsChange) + context.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms); + + if (m_changeFlags & GraphicsContextState::DrawLuminanceMaskChange) + context.setDrawLuminanceMask(m_state.drawLuminanceMask); + + if (m_changeFlags & GraphicsContextState::ImageInterpolationQualityChange) + context.setImageInterpolationQuality(m_state.imageInterpolationQuality); +} + +void GraphicsContextStateChange::dump(TextStream& ts) const +{ + ts.dumpProperty("change-flags", m_changeFlags); + + if (m_changeFlags & GraphicsContextState::StrokeGradientChange) + ts.dumpProperty("stroke-gradient", m_state.strokeGradient.get()); + + if (m_changeFlags & GraphicsContextState::StrokePatternChange) + ts.dumpProperty("stroke-pattern", m_state.strokePattern.get()); + + if (m_changeFlags & GraphicsContextState::FillGradientChange) + ts.dumpProperty("fill-gradient", m_state.fillGradient.get()); + + if (m_changeFlags & GraphicsContextState::FillPatternChange) + ts.dumpProperty("fill-pattern", m_state.fillPattern.get()); + + if (m_changeFlags & GraphicsContextState::ShadowChange) { + ts.dumpProperty("shadow-blur", m_state.shadowBlur); + ts.dumpProperty("shadow-offset", m_state.shadowOffset); +#if USE(CG) + ts.dumpProperty("shadows-use-legacy-radius", m_state.shadowsUseLegacyRadius); +#endif + } + + if (m_changeFlags & GraphicsContextState::StrokeThicknessChange) + ts.dumpProperty("stroke-thickness", m_state.strokeThickness); + + if (m_changeFlags & GraphicsContextState::TextDrawingModeChange) + ts.dumpProperty("text-drawing-mode", m_state.textDrawingMode); + + if (m_changeFlags & GraphicsContextState::StrokeColorChange) + ts.dumpProperty("stroke-color", m_state.strokeColor); + + if (m_changeFlags & GraphicsContextState::FillColorChange) + ts.dumpProperty("fill-color", m_state.fillColor); + + if (m_changeFlags & GraphicsContextState::StrokeStyleChange) + ts.dumpProperty("stroke-style", m_state.strokeStyle); + + if (m_changeFlags & GraphicsContextState::FillRuleChange) + ts.dumpProperty("fill-rule", m_state.fillRule); + + if (m_changeFlags & GraphicsContextState::AlphaChange) + ts.dumpProperty("alpha", m_state.alpha); + + if (m_changeFlags & GraphicsContextState::CompositeOperationChange) + ts.dumpProperty("composite-operator", m_state.compositeOperator); + + if (m_changeFlags & GraphicsContextState::BlendModeChange) + ts.dumpProperty("blend-mode", m_state.blendMode); + + if (m_changeFlags & GraphicsContextState::ShouldAntialiasChange) + ts.dumpProperty("should-antialias", m_state.shouldAntialias); + + if (m_changeFlags & GraphicsContextState::ShouldSmoothFontsChange) + ts.dumpProperty("should-smooth-fonts", m_state.shouldSmoothFonts); + + if (m_changeFlags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange) + ts.dumpProperty("should-subpixel-quantize-fonts", m_state.shouldSubpixelQuantizeFonts); + + if (m_changeFlags & GraphicsContextState::ShadowsIgnoreTransformsChange) + ts.dumpProperty("shadows-ignore-transforms", m_state.shadowsIgnoreTransforms); + + if (m_changeFlags & GraphicsContextState::DrawLuminanceMaskChange) + ts.dumpProperty("draw-luminance-mask", m_state.drawLuminanceMask); +} + +TextStream& operator<<(TextStream& ts, const GraphicsContextStateChange& stateChange) +{ + stateChange.dump(ts); + return ts; +} + +GraphicsContext::GraphicsContext(NonPaintingReasons nonPaintingReasons) + : m_nonPaintingReasons(nonPaintingReasons) +{ +} + +GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext) +{ + platformInit(platformGraphicsContext); +} GraphicsContext::~GraphicsContext() { @@ -106,6 +350,11 @@ void GraphicsContext::save() m_stack.append(m_state); + if (isRecording()) { + m_displayListRecorder->save(); + return; + } + savePlatformState(); } @@ -118,74 +367,104 @@ void GraphicsContext::restore() LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); return; } + m_state = m_stack.last(); m_stack.removeLast(); + // Make sure we deallocate the state stack buffer when it goes empty. + // Canvas elements will immediately save() again, but that goes into inline capacity. + if (m_stack.isEmpty()) + m_stack.clear(); + + if (isRecording()) { + m_displayListRecorder->restore(); + return; + } + restorePlatformState(); } -#if PLATFORM(IOS) -void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace) +void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, const Color& shadowColor) { if (paintingDisabled()) return; save(); - setStrokeColor(shadowColor, shadowColorSpace); - setFillColor(shadowColor, shadowColorSpace); + setStrokeColor(shadowColor); + setFillColor(shadowColor); drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height())); - setStrokeColor(ellipseColor, ellipseColorSpace); - setFillColor(ellipseColor, ellipseColorSpace); + setStrokeColor(ellipseColor); + setFillColor(ellipseColor); drawEllipse(rect); restore(); } -#endif void GraphicsContext::setStrokeThickness(float thickness) { m_state.strokeThickness = thickness; + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeThicknessChange); + return; + } + setPlatformStrokeThickness(thickness); } void GraphicsContext::setStrokeStyle(StrokeStyle style) { m_state.strokeStyle = style; + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeStyleChange); + return; + } setPlatformStrokeStyle(style); } -void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace) +void GraphicsContext::setStrokeColor(const Color& color) { m_state.strokeColor = color; - m_state.strokeColorSpace = colorSpace; - m_state.strokeGradient.clear(); - m_state.strokePattern.clear(); - setPlatformStrokeColor(color, colorSpace); + m_state.strokeGradient = nullptr; + m_state.strokePattern = nullptr; + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeColorChange); + return; + } + setPlatformStrokeColor(color); } -void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace) +void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color) { m_state.shadowOffset = offset; m_state.shadowBlur = blur; m_state.shadowColor = color; - m_state.shadowColorSpace = colorSpace; - setPlatformShadow(offset, blur, color, colorSpace); +#if USE(CG) + m_state.shadowsUseLegacyRadius = false; +#endif + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::ShadowChange); + return; + } + setPlatformShadow(offset, blur, color); } -void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace) +void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color) { m_state.shadowOffset = offset; m_state.shadowBlur = blur; m_state.shadowColor = color; - m_state.shadowColorSpace = colorSpace; #if USE(CG) m_state.shadowsUseLegacyRadius = true; #endif - setPlatformShadow(offset, blur, color, colorSpace); + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::ShadowChange); + return; + } + setPlatformShadow(offset, blur, color); } void GraphicsContext::clearShadow() @@ -193,31 +472,26 @@ void GraphicsContext::clearShadow() m_state.shadowOffset = FloatSize(); m_state.shadowBlur = 0; m_state.shadowColor = Color(); - m_state.shadowColorSpace = ColorSpaceDeviceRGB; - clearPlatformShadow(); -} +#if USE(CG) + m_state.shadowsUseLegacyRadius = false; +#endif -bool GraphicsContext::hasShadow() const -{ - return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() - && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height()); + if (isRecording()) { + m_displayListRecorder->clearShadow(); + return; + } + clearPlatformShadow(); } -bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const +bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color) const { offset = m_state.shadowOffset; blur = m_state.shadowBlur; color = m_state.shadowColor; - colorSpace = m_state.shadowColorSpace; return hasShadow(); } -bool GraphicsContext::hasBlurredShadow() const -{ - return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur; -} - #if USE(CAIRO) bool GraphicsContext::mustUseShadowBlur() const { @@ -236,273 +510,179 @@ bool GraphicsContext::mustUseShadowBlur() const } #endif -float GraphicsContext::strokeThickness() const -{ - return m_state.strokeThickness; -} - -StrokeStyle GraphicsContext::strokeStyle() const -{ - return m_state.strokeStyle; -} - -Color GraphicsContext::strokeColor() const -{ - return m_state.strokeColor; -} - -ColorSpace GraphicsContext::strokeColorSpace() const -{ - return m_state.strokeColorSpace; -} - -WindRule GraphicsContext::fillRule() const -{ - return m_state.fillRule; -} - -void GraphicsContext::setFillRule(WindRule fillRule) -{ - m_state.fillRule = fillRule; -} - -void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace) +void GraphicsContext::setFillColor(const Color& color) { m_state.fillColor = color; - m_state.fillColorSpace = colorSpace; - m_state.fillGradient.clear(); - m_state.fillPattern.clear(); - setPlatformFillColor(color, colorSpace); -} + m_state.fillGradient = nullptr; + m_state.fillPattern = nullptr; -Color GraphicsContext::fillColor() const -{ - return m_state.fillColor; -} - -ColorSpace GraphicsContext::fillColorSpace() const -{ - return m_state.fillColorSpace; -} - -void GraphicsContext::setShouldAntialias(bool b) -{ - m_state.shouldAntialias = b; - setPlatformShouldAntialias(b); -} - -bool GraphicsContext::shouldAntialias() const -{ - return m_state.shouldAntialias; -} + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::FillColorChange); + return; + } -void GraphicsContext::setShouldSmoothFonts(bool b) -{ - m_state.shouldSmoothFonts = b; - setPlatformShouldSmoothFonts(b); + setPlatformFillColor(color); } -bool GraphicsContext::shouldSmoothFonts() const +void GraphicsContext::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms) { - return m_state.shouldSmoothFonts; + m_state.shadowsIgnoreTransforms = shadowsIgnoreTransforms; + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::ShadowsIgnoreTransformsChange); } -void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b) +void GraphicsContext::setShouldAntialias(bool shouldAntialias) { - m_state.shouldSubpixelQuantizeFonts = b; -} + m_state.shouldAntialias = shouldAntialias; -bool GraphicsContext::shouldSubpixelQuantizeFonts() const -{ - return m_state.shouldSubpixelQuantizeFonts; -} + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::ShouldAntialiasChange); + return; + } -const GraphicsContextState& GraphicsContext::state() const -{ - return m_state; + setPlatformShouldAntialias(shouldAntialias); } -void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) +void GraphicsContext::setShouldSmoothFonts(bool shouldSmoothFonts) { - ASSERT(pattern); - if (!pattern) { - setStrokeColor(Color::black, ColorSpaceDeviceRGB); + m_state.shouldSmoothFonts = shouldSmoothFonts; + + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::ShouldSmoothFontsChange); return; } - m_state.strokeGradient.clear(); - m_state.strokePattern = pattern; + + setPlatformShouldSmoothFonts(shouldSmoothFonts); } -void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) +void GraphicsContext::setShouldSubpixelQuantizeFonts(bool shouldSubpixelQuantizeFonts) { - ASSERT(pattern); - if (!pattern) { - setFillColor(Color::black, ColorSpaceDeviceRGB); - return; - } - m_state.fillGradient.clear(); - m_state.fillPattern = pattern; + m_state.shouldSubpixelQuantizeFonts = shouldSubpixelQuantizeFonts; + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::ShouldSubpixelQuantizeFontsChange); } -void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) +void GraphicsContext::setImageInterpolationQuality(InterpolationQuality imageInterpolationQuality) { - ASSERT(gradient); - if (!gradient) { - setStrokeColor(Color::black, ColorSpaceDeviceRGB); + m_state.imageInterpolationQuality = imageInterpolationQuality; + + if (paintingDisabled()) return; - } - m_state.strokeGradient = gradient; - m_state.strokePattern.clear(); -} -void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) -{ - ASSERT(gradient); - if (!gradient) { - setFillColor(Color::black, ColorSpaceDeviceRGB); + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::ImageInterpolationQualityChange); return; } - m_state.fillGradient = gradient; - m_state.fillPattern.clear(); -} -Gradient* GraphicsContext::fillGradient() const -{ - return m_state.fillGradient.get(); + setPlatformImageInterpolationQuality(imageInterpolationQuality); } -Gradient* GraphicsContext::strokeGradient() const +void GraphicsContext::setStrokePattern(Ref<Pattern>&& pattern) { - return m_state.strokeGradient.get(); + m_state.strokeGradient = nullptr; + m_state.strokePattern = WTFMove(pattern); + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokePatternChange); } -Pattern* GraphicsContext::fillPattern() const +void GraphicsContext::setFillPattern(Ref<Pattern>&& pattern) { - return m_state.fillPattern.get(); + m_state.fillGradient = nullptr; + m_state.fillPattern = WTFMove(pattern); + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::FillPatternChange); } -Pattern* GraphicsContext::strokePattern() const +void GraphicsContext::setStrokeGradient(Ref<Gradient>&& gradient) { - return m_state.strokePattern.get(); + m_state.strokeGradient = WTFMove(gradient); + m_state.strokePattern = nullptr; + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::StrokeGradientChange); } -void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms) +void GraphicsContext::setFillRule(WindRule fillRule) { - m_state.shadowsIgnoreTransforms = ignoreTransforms; + m_state.fillRule = fillRule; + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::FillRuleChange); } -bool GraphicsContext::shadowsIgnoreTransforms() const +void GraphicsContext::setFillGradient(Ref<Gradient>&& gradient) { - return m_state.shadowsIgnoreTransforms; + m_state.fillGradient = WTFMove(gradient); + m_state.fillPattern = nullptr; + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::FillGradientChange); // FIXME: also fill pattern? } void GraphicsContext::beginTransparencyLayer(float opacity) { + if (isRecording()) { + m_displayListRecorder->beginTransparencyLayer(opacity); + return; + } beginPlatformTransparencyLayer(opacity); ++m_transparencyCount; } void GraphicsContext::endTransparencyLayer() { + if (isRecording()) { + m_displayListRecorder->endTransparencyLayer(); + return; + } endPlatformTransparencyLayer(); ASSERT(m_transparencyCount > 0); --m_transparencyCount; } -bool GraphicsContext::isInTransparencyLayer() const -{ - return (m_transparencyCount > 0) && supportsTransparencyLayers(); -} - -bool GraphicsContext::updatingControlTints() const -{ - return m_updatingControlTints; -} - -void GraphicsContext::setUpdatingControlTints(bool b) -{ - setPaintingDisabled(b); - m_updatingControlTints = b; -} - -void GraphicsContext::setPaintingDisabled(bool f) +float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, const FloatPoint& point, unsigned from, std::optional<unsigned> to) { - m_state.paintingDisabled = f; -} + if (paintingDisabled()) + return 0; -bool GraphicsContext::paintingDisabled() const -{ - return m_state.paintingDisabled; + // Display list recording for text content is done at glyphs level. See GraphicsContext::drawGlyphs. + return font.drawText(*this, run, point, from, to); } -// FIXME: Replace the non-iOS implementation with the iOS implementation since the latter computes returns -// the width of the drawn text. Ensure that there aren't noticeable differences in layout. -#if !PLATFORM(IOS) -#if !USE(WINGDI) -void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to) +void GraphicsContext::drawGlyphs(const FontCascade& fontCascade, const Font& font, const GlyphBuffer& buffer, unsigned from, unsigned numGlyphs, const FloatPoint& point) { if (paintingDisabled()) return; - font.drawText(this, run, point, from, to); -} -#endif -#else -float GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to) -{ - if (paintingDisabled()) - return 0; + if (isRecording()) { + m_displayListRecorder->drawGlyphs(font, buffer, from, numGlyphs, point, fontCascade.fontDescription().fontSmoothing()); + return; + } - return font.drawText(this, run, point, from, to); + fontCascade.drawGlyphs(*this, font, buffer, from, numGlyphs, point, fontCascade.fontDescription().fontSmoothing()); } -#endif // !PLATFORM(IOS) -void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) +void GraphicsContext::drawEmphasisMarks(const FontCascade& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, std::optional<unsigned> to) { if (paintingDisabled()) return; - font.drawEmphasisMarks(this, run, mark, point, from, to); + font.drawEmphasisMarks(*this, run, mark, point, from, to); } -// FIXME: Better merge the iOS and non-iOS differences. In particular, make this method use the -// returned width of the drawn text, Font::drawText(), instead of computing it. Ensure that there -// aren't noticeable differences in layout with such a change. -#if !PLATFORM(IOS) -void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction) -#else -float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length) -#endif +void GraphicsContext::drawBidiText(const FontCascade& font, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction customFontNotReadyAction) { if (paintingDisabled()) -#if !PLATFORM(IOS) return; -#else - return 0; -#endif BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; -#if !PLATFORM(IOS) bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); -#else - bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride())); -#endif bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); -#if !PLATFORM(IOS) bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); -#else - bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length)); -#endif + if (!bidiRuns.runCount()) -#if !PLATFORM(IOS) return; -#else - return 0; -#endif FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); @@ -512,203 +692,135 @@ float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const subrun.setDirection(isRTL ? RTL : LTR); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); -#if !PLATFORM(IOS) - font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); - - bidiRun = bidiRun->next(); - // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. - if (bidiRun) - currPoint.move(font.width(subrun), 0); -#else - float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); + float width = font.drawText(*this, subrun, currPoint, 0, std::nullopt, customFontNotReadyAction); currPoint.move(width, 0); bidiRun = bidiRun->next(); -#endif } -#if PLATFORM(IOS) - if (status) - *status = bidiResolver.status(); -#endif - bidiRuns.deleteRuns(); - -#if PLATFORM(IOS) - return currPoint.x() - static_cast<float>(point.x()); -#endif + bidiRuns.clear(); } -void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to) +void GraphicsContext::drawImage(Image& image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions) { - if (paintingDisabled()) - return; - - fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace); + drawImage(image, FloatRect(destination, image.size()), FloatRect(FloatPoint(), image.size()), imagePaintingOptions); } -void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op, ImageOrientationDescription description) +void GraphicsContext::drawImage(Image& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions) { - if (!image) - return; - drawImage(image, styleColorSpace, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, description); +#if PLATFORM(IOS) + FloatRect srcRect(FloatPoint(), image.originalSize()); +#else + FloatRect srcRect(FloatPoint(), image.size()); +#endif + + drawImage(image, destination, srcRect, imagePaintingOptions); } -void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, ImageOrientationDescription description, bool useLowQualityScale) +void GraphicsContext::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) { - if (!image) + if (paintingDisabled()) return; - drawImage(image, styleColorSpace, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, description, useLowQualityScale); -} -void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op, ImageOrientationDescription description) -{ - drawImage(image, styleColorSpace, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect), op, description); -} + if (isRecording()) { + m_displayListRecorder->drawImage(image, destination, source, imagePaintingOptions); + return; + } -void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, ImageOrientationDescription description, bool useLowQualityScale) -{ - drawImage(image, styleColorSpace, dest, src, op, BlendModeNormal, description, useLowQualityScale); + InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality); + image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription); } -void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest) +void GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) { - if (!image) + if (paintingDisabled()) return; - drawImage(image, styleColorSpace, dest, FloatRect(IntRect(IntPoint(), image->size()))); -} -void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, BlendMode blendMode, ImageOrientationDescription description, bool useLowQualityScale) -{ if (paintingDisabled() || !image) + if (isRecording()) { + m_displayListRecorder->drawTiledImage(image, destination, source, tileSize, spacing, imagePaintingOptions); return; - - InterpolationQuality previousInterpolationQuality = InterpolationDefault; - - if (useLowQualityScale) { - previousInterpolationQuality = imageInterpolationQuality(); - // FIXME (49002): Should be InterpolationLow - setImageInterpolationQuality(InterpolationNone); } - image->draw(this, dest, src, styleColorSpace, op, blendMode, description); - - if (useLowQualityScale) - setImageInterpolationQuality(previousInterpolationQuality); + InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality); + image.drawTiled(*this, destination, source, tileSize, spacing, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode); } -void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale, BlendMode blendMode) +void GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, + Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) { - if (paintingDisabled() || !image) + if (paintingDisabled()) return; - if (useLowQualityScale) { - InterpolationQuality previousInterpolationQuality = imageInterpolationQuality(); - setImageInterpolationQuality(InterpolationLow); - image->drawTiled(this, destRect, srcPoint, tileSize, styleColorSpace, op, blendMode); - setImageInterpolationQuality(previousInterpolationQuality); - } else - image->drawTiled(this, destRect, srcPoint, tileSize, styleColorSpace, op, blendMode); -} - -void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, - const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale) -{ - if (paintingDisabled() || !image) + if (isRecording()) { + m_displayListRecorder->drawTiledImage(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions); return; + } if (hRule == Image::StretchTile && vRule == Image::StretchTile) { // Just do a scale. - drawImage(image, styleColorSpace, dest, srcRect, op); + drawImage(image, destination, source, imagePaintingOptions); return; } - if (useLowQualityScale) { - InterpolationQuality previousInterpolationQuality = imageInterpolationQuality(); - setImageInterpolationQuality(InterpolationLow); - image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, styleColorSpace, op); - setImageInterpolationQuality(previousInterpolationQuality); - } else - image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, styleColorSpace, op); + InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality); + image.drawTiled(*this, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions.m_compositeOperator); } -void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op, BlendMode blendMode) +void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions) { - if (!image) - return; - drawImageBuffer(image, styleColorSpace, FloatRect(IntRect(p, image->logicalSize())), FloatRect(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode); + drawImageBuffer(image, FloatRect(destination, image.logicalSize()), FloatRect(FloatPoint(), image.logicalSize()), imagePaintingOptions); } -void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale) +void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions) { - if (!image) - return; - drawImageBuffer(image, styleColorSpace, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode, useLowQualityScale); + drawImageBuffer(image, destination, FloatRect(FloatPoint(), FloatSize(image.logicalSize())), imagePaintingOptions); } -void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op, BlendMode blendMode) +void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) { - drawImageBuffer(image, styleColorSpace, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect), op, blendMode); -} + if (paintingDisabled()) + return; -void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale) -{ - drawImageBuffer(image, styleColorSpace, FloatRect(dest), FloatRect(srcRect), op, blendMode, useLowQualityScale); + InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality); + image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode); } -void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest) +void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions) { if (!image) return; - drawImageBuffer(image, styleColorSpace, dest, FloatRect(IntRect(IntPoint(), image->logicalSize()))); + IntSize imageLogicalSize = image->logicalSize(); + drawConsumingImageBuffer(WTFMove(image), FloatRect(destination, imageLogicalSize), FloatRect(FloatPoint(), imageLogicalSize), imagePaintingOptions); } -void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale) +void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions) { - if (paintingDisabled() || !image) + if (!image) return; - - if (useLowQualityScale) { - InterpolationQuality previousInterpolationQuality = imageInterpolationQuality(); - // FIXME (49002): Should be InterpolationLow - setImageInterpolationQuality(InterpolationNone); - image->draw(this, styleColorSpace, dest, src, op, blendMode, useLowQualityScale); - setImageInterpolationQuality(previousInterpolationQuality); - } else - image->draw(this, styleColorSpace, dest, src, op, blendMode, useLowQualityScale); -} - -void GraphicsContext::clip(const IntRect& rect) -{ - clip(FloatRect(rect)); + IntSize imageLogicalSize = image->logicalSize(); + drawConsumingImageBuffer(WTFMove(image), destination, FloatRect(FloatPoint(), FloatSize(imageLogicalSize)), imagePaintingOptions); } -void GraphicsContext::clipRoundedRect(const RoundedRect& rect) +void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) { - if (paintingDisabled()) - return; - - if (!rect.isRounded()) { - clip(rect.rect()); + if (paintingDisabled() || !image) return; - } - - Path path; - path.addRoundedRect(rect); - clip(path); + + InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality); + ImageBuffer::drawConsuming(WTFMove(image), *this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode); } -// FIXME: Consider writing this in terms of a specialized RoundedRect that uses FloatRect and FloatSize radii. -void GraphicsContext::clipRoundedRect(const FloatRect& rect, const FloatSize& topLeft, const FloatSize& topRight, - const FloatSize& bottomLeft, const FloatSize& bottomRight) +void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect) { if (paintingDisabled()) return; Path path; - path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); - clip(path); + path.addRoundedRect(rect); + clipPath(path); } -void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) +void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect) { if (paintingDisabled()) return; @@ -723,14 +835,7 @@ void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) clipOut(path); } -void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect) -{ - if (paintingDisabled()) - return; - buffer->clip(this, rect); -} - -#if !USE(CG) && !USE(CAIRO) +#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO) IntRect GraphicsContext::clipBounds() const { ASSERT_NOT_REACHED(); @@ -738,16 +843,16 @@ IntRect GraphicsContext::clipBounds() const } #endif -TextDrawingModeFlags GraphicsContext::textDrawingMode() const -{ - return m_state.textDrawingMode; -} - void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode) { m_state.textDrawingMode = mode; if (paintingDisabled()) return; + + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::TextDrawingModeChange); + return; + } setPlatformTextDrawingMode(mode); } @@ -755,33 +860,51 @@ void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient) { if (paintingDisabled()) return; + + if (isRecording()) { + m_displayListRecorder->fillRect(rect, gradient); + return; + } + gradient.fill(this, rect); } -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode) +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode) { if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->fillRect(rect, color, op, blendMode); + return; + } + CompositeOperator previousOperator = compositeOperation(); setCompositeOperation(op, blendMode); - fillRect(rect, color, styleColorSpace); + fillRect(rect, color); setCompositeOperation(previousOperator); } -void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode) +void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode) { + if (paintingDisabled()) + return; + + if (isRecording()) { + m_displayListRecorder->fillRoundedRect(rect, color, blendMode); + return; + } if (rect.isRounded()) { setCompositeOperation(compositeOperation(), blendMode); - fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color, colorSpace); + platformFillRoundedRect(rect, color); setCompositeOperation(compositeOperation()); } else - fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode); + fillRect(rect.rect(), color, compositeOperation(), blendMode); } -#if !USE(CG) && !USE(CAIRO) -void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace) +#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO) +void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color) { if (paintingDisabled()) return; @@ -796,78 +919,65 @@ void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const Rounded WindRule oldFillRule = fillRule(); Color oldFillColor = fillColor(); - ColorSpace oldFillColorSpace = fillColorSpace(); setFillRule(RULE_EVENODD); - setFillColor(color, colorSpace); + setFillColor(color); fillPath(path); setFillRule(oldFillRule); - setFillColor(oldFillColor, oldFillColorSpace); + setFillColor(oldFillColor); } #endif +void GraphicsContext::setAlpha(float alpha) +{ + m_state.alpha = alpha; + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::AlphaChange); + return; + } + setPlatformAlpha(alpha); +} + void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode) { m_state.compositeOperator = compositeOperation; m_state.blendMode = blendMode; + if (isRecording()) { + m_displayListRecorder->updateState(m_state, GraphicsContextState::CompositeOperationChange); + return; + } setPlatformCompositeOperation(compositeOperation, blendMode); } -CompositeOperator GraphicsContext::compositeOperation() const -{ - return m_state.compositeOperator; -} - -BlendMode GraphicsContext::blendModeOperation() const -{ - return m_state.blendMode; -} - -#if PLATFORM(IOS) -bool GraphicsContext::emojiDrawingEnabled() -{ - return m_state.emojiDrawingEnabled; -} - -void GraphicsContext::setEmojiDrawingEnabled(bool emojiDrawingEnabled) -{ - m_state.emojiDrawingEnabled = emojiDrawingEnabled; -} -#endif - void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask) { m_state.drawLuminanceMask = drawLuminanceMask; + if (isRecording()) + m_displayListRecorder->updateState(m_state, GraphicsContextState::DrawLuminanceMaskChange); } -bool GraphicsContext::drawLuminanceMask() const -{ - return m_state.drawLuminanceMask; -} - -#if !USE(CG) -// Implement this if you want to go ahead and push the drawing mode into your native context -// immediately. +#if !USE(CG) && !USE(DIRECT2D) +// Implement this if you want to go push the drawing mode into your native context immediately. void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags) { } #endif -#if !USE(CAIRO) +#if !USE(CAIRO) && !USE(DIRECT2D) void GraphicsContext::setPlatformStrokeStyle(StrokeStyle) { } #endif -#if !USE(CG) +#if !USE(CG) && !USE(DIRECT2D) void GraphicsContext::setPlatformShouldSmoothFonts(bool) { } #endif -#if !USE(CG) && !USE(CAIRO) +#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO) bool GraphicsContext::isAcceleratedContext() const { return false; @@ -903,38 +1013,7 @@ void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2 } } -static bool scalesMatch(AffineTransform a, AffineTransform b) -{ - return a.xScale() == b.xScale() && a.yScale() == b.yScale(); -} - -std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, bool hasAlpha) const -{ - // Make the buffer larger if the context's transform is scaling it so we need a higher - // resolution than one pixel per unit. Also set up a corresponding scale factor on the - // graphics context. - - AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale); - IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale()))); - - std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, this, hasAlpha); - if (!buffer) - return nullptr; - - buffer->context()->scale(FloatSize(static_cast<float>(scaledSize.width()) / size.width(), - static_cast<float>(scaledSize.height()) / size.height())); - - return buffer; -} - -bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer* buffer) const -{ - GraphicsContext* bufferContext = buffer->context(); - - return scalesMatch(getCTM(), bufferContext->getCTM()) && isAcceleratedContext() == bufferContext->isAcceleratedContext(); -} - -#if !USE(CG) +#if !USE(CG) && !USE(DIRECT2D) void GraphicsContext::platformApplyDeviceScaleFactor(float) { } @@ -942,9 +1021,21 @@ void GraphicsContext::platformApplyDeviceScaleFactor(float) void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor) { - scale(FloatSize(deviceScaleFactor, deviceScaleFactor)); + scale(deviceScaleFactor); + + if (isRecording()) { + m_displayListRecorder->applyDeviceScaleFactor(deviceScaleFactor); + return; + } + platformApplyDeviceScaleFactor(deviceScaleFactor); } + +FloatSize GraphicsContext::scaleFactor() const +{ + AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); + return FloatSize(transform.xScale(), transform.yScale()); +} void GraphicsContext::fillEllipse(const FloatRect& ellipse) { @@ -970,7 +1061,7 @@ void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse) strokePath(path); } -#if !USE(CG) +#if !USE(CG) && !USE(DIRECT2D) void GraphicsContext::platformFillEllipse(const FloatRect& ellipse) { if (paintingDisabled()) @@ -988,4 +1079,117 @@ void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse) } #endif +FloatRect GraphicsContext::computeUnderlineBoundsForText(const FloatPoint& point, float width, bool printing) +{ + Color dummyColor; + return computeLineBoundsAndAntialiasingModeForText(point, width, printing, dummyColor); +} + +FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, Color& color) +{ + FloatPoint origin = point; + float thickness = std::max(strokeThickness(), 0.5f); + if (printing) + return FloatRect(origin, FloatSize(width, thickness)); + + AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); + // Just compute scale in x dimension, assuming x and y scales are equal. + float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a(); + if (scale < 1.0) { + // This code always draws a line that is at least one-pixel line high, + // which tends to visually overwhelm text at small scales. To counter this + // effect, an alpha is applied to the underline color when text is at small scales. + static const float minimumUnderlineAlpha = 0.4f; + float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha; + color = color.colorWithAlphaMultipliedBy(shade); + } + + FloatPoint devicePoint = transform.mapPoint(point); + // Visual overflow might occur here due to integral roundf/ceilf. visualOverflowForDecorations adjusts the overflow value for underline decoration. + FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y())); + if (auto inverse = transform.inverse()) + origin = inverse.value().mapPoint(deviceOrigin); + return FloatRect(origin, FloatSize(width, thickness)); +} + +void GraphicsContext::applyState(const GraphicsContextState& state) +{ + setPlatformShadow(state.shadowOffset, state.shadowBlur, state.shadowColor); + setPlatformStrokeThickness(state.strokeThickness); + setPlatformTextDrawingMode(state.textDrawingMode); + setPlatformStrokeColor(state.strokeColor); + setPlatformFillColor(state.fillColor); + setPlatformStrokeStyle(state.strokeStyle); + setPlatformAlpha(state.alpha); + setPlatformCompositeOperation(state.compositeOperator, state.blendMode); + setPlatformShouldAntialias(state.shouldAntialias); + setPlatformShouldSmoothFonts(state.shouldSmoothFonts); +} + +float GraphicsContext::dashedLineCornerWidthForStrokeWidth(float strokeWidth) const +{ + float thickness = strokeThickness(); + return strokeStyle() == DottedStroke ? thickness : std::min(2.0f * thickness, std::max(thickness, strokeWidth / 3.0f)); +} + +float GraphicsContext::dashedLinePatternWidthForStrokeWidth(float strokeWidth) const +{ + float thickness = strokeThickness(); + return strokeStyle() == DottedStroke ? thickness : std::min(3.0f * thickness, std::max(thickness, strokeWidth / 3.0f)); +} + +float GraphicsContext::dashedLinePatternOffsetForPatternAndStrokeWidth(float patternWidth, float strokeWidth) const +{ + // Pattern starts with full fill and ends with the empty fill. + // 1. Let's start with the empty phase after the corner. + // 2. Check if we've got odd or even number of patterns and whether they fully cover the line. + // 3. In case of even number of patterns and/or remainder, move the pattern start position + // so that the pattern is balanced between the corners. + float patternOffset = patternWidth; + int numberOfSegments = std::floor(strokeWidth / patternWidth); + bool oddNumberOfSegments = numberOfSegments % 2; + float remainder = strokeWidth - (numberOfSegments * patternWidth); + if (oddNumberOfSegments && remainder) + patternOffset -= remainder / 2.0f; + else if (!oddNumberOfSegments) { + if (remainder) + patternOffset += patternOffset - (patternWidth + remainder) / 2.0f; + else + patternOffset += patternWidth / 2.0f; + } + + return patternOffset; +} + +Vector<FloatPoint> GraphicsContext::centerLineAndCutOffCorners(bool isVerticalLine, float cornerWidth, FloatPoint point1, FloatPoint point2) const +{ + // Center line and cut off corners for pattern painting. + if (isVerticalLine) { + float centerOffset = (point2.x() - point1.x()) / 2.0f; + point1.move(centerOffset, cornerWidth); + point2.move(-centerOffset, -cornerWidth); + } else { + float centerOffset = (point2.y() - point1.y()) / 2.0f; + point1.move(cornerWidth, centerOffset); + point2.move(-cornerWidth, -centerOffset); + } + + return { point1, point2 }; +} + +#if !USE(CG) +bool GraphicsContext::supportsInternalLinks() const +{ + return false; +} + +void GraphicsContext::setDestinationForRect(const String&, const FloatRect&) +{ +} + +void GraphicsContext::addDestinationAtPoint(const String&, const FloatPoint&) +{ +} +#endif + } diff --git a/Source/WebCore/platform/graphics/GraphicsContext.h b/Source/WebCore/platform/graphics/GraphicsContext.h index a437827ac..82b96126c 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.h +++ b/Source/WebCore/platform/graphics/GraphicsContext.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * Copyright (C) 2008-2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,23 +24,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GraphicsContext_h -#define GraphicsContext_h +#pragma once -#include "ColorSpace.h" #include "DashArray.h" #include "FloatRect.h" -#include "Font.h" +#include "FontCascade.h" #include "Gradient.h" +#include "GraphicsTypes.h" #include "Image.h" #include "ImageOrientation.h" -#include "Path.h" #include "Pattern.h" #include <wtf/Noncopyable.h> -#include <wtf/PassOwnPtr.h> #if USE(CG) typedef struct CGContext PlatformGraphicsContext; +#elif USE(DIRECT2D) +interface ID2D1DCRenderTarget; +interface ID2D1RenderTarget; +interface ID2D1Factory; +interface ID2D1SolidColorBrush; +typedef ID2D1RenderTarget PlatformGraphicsContext; #elif USE(CAIRO) namespace WebCore { class PlatformContextCairo; @@ -64,563 +67,652 @@ typedef unsigned char UInt8; namespace WebCore { #if USE(WINGDI) - class SharedBitmap; - class SimpleFontData; - class GlyphBuffer; +class SharedBitmap; +class Font; +class GlyphBuffer; #endif - const int cMisspellingLineThickness = 3; - const int cMisspellingLinePatternWidth = 4; - const int cMisspellingLinePatternGapWidth = 1; - - class AffineTransform; - class DrawingBuffer; - class Gradient; - class GraphicsContextPlatformPrivate; - class ImageBuffer; - class IntRect; - class RoundedRect; - class URL; - class GraphicsContext3D; - class TextRun; - class TransformationMatrix; -#if PLATFORM(IOS) - struct BidiStatus; +const int cMisspellingLineThickness = 3; +const int cMisspellingLinePatternWidth = 4; +const int cMisspellingLinePatternGapWidth = 1; + +class AffineTransform; +class FloatRoundedRect; +class Gradient; +class GraphicsContextPlatformPrivate; +class ImageBuffer; +class IntRect; +class RoundedRect; +class URL; +class GraphicsContext3D; +class Path; +class TextRun; +class TransformationMatrix; + +enum TextDrawingMode { + TextModeFill = 1 << 0, + TextModeStroke = 1 << 1, +#if ENABLE(LETTERPRESS) + TextModeLetterpress = 1 << 2, #endif +}; +typedef unsigned TextDrawingModeFlags; + +enum StrokeStyle { + NoStroke, + SolidStroke, + DottedStroke, + DashedStroke, + DoubleStroke, + WavyStroke, +}; + +namespace DisplayList { +class Recorder; +} - enum TextDrawingMode { - TextModeFill = 1 << 0, - TextModeStroke = 1 << 1, -#if ENABLE(LETTERPRESS) - TextModeLetterpress = 1 << 2, +struct GraphicsContextState { + GraphicsContextState() + : shouldAntialias(true) + , shouldSmoothFonts(true) + , shouldSubpixelQuantizeFonts(true) + , shadowsIgnoreTransforms(false) +#if USE(CG) + // Core Graphics incorrectly renders shadows with radius > 8px (<rdar://problem/8103442>), + // but we need to preserve this buggy behavior for canvas and -webkit-box-shadow. + , shadowsUseLegacyRadius(false) #endif + , drawLuminanceMask(false) + { + } + + enum Change : uint32_t { + NoChange = 0, + StrokeGradientChange = 1 << 1, + StrokePatternChange = 1 << 2, + FillGradientChange = 1 << 3, + FillPatternChange = 1 << 4, + StrokeThicknessChange = 1 << 5, + StrokeColorChange = 1 << 6, + StrokeStyleChange = 1 << 7, + FillColorChange = 1 << 8, + FillRuleChange = 1 << 9, + ShadowChange = 1 << 10, + ShadowColorChange = 1 << 11, + ShadowsIgnoreTransformsChange = 1 << 12, + AlphaChange = 1 << 13, + CompositeOperationChange = 1 << 14, + BlendModeChange = 1 << 15, + TextDrawingModeChange = 1 << 16, + ShouldAntialiasChange = 1 << 17, + ShouldSmoothFontsChange = 1 << 18, + ShouldSubpixelQuantizeFontsChange = 1 << 19, + DrawLuminanceMaskChange = 1 << 20, + ImageInterpolationQualityChange = 1 << 21, }; - typedef unsigned TextDrawingModeFlags; - - enum StrokeStyle { - NoStroke, - SolidStroke, - DottedStroke, - DashedStroke, - DoubleStroke, - WavyStroke, - }; + typedef uint32_t StateChangeFlags; - enum InterpolationQuality { - InterpolationDefault, - InterpolationNone, - InterpolationLow, - InterpolationMedium, - InterpolationHigh - }; + RefPtr<Gradient> strokeGradient; + RefPtr<Pattern> strokePattern; + + RefPtr<Gradient> fillGradient; + RefPtr<Pattern> fillPattern; - struct GraphicsContextState { - GraphicsContextState() - : strokeThickness(0) - , shadowBlur(0) - , textDrawingMode(TextModeFill) - , strokeColor(Color::black) - , fillColor(Color::black) - , strokeStyle(SolidStroke) - , fillRule(RULE_NONZERO) - , strokeColorSpace(ColorSpaceDeviceRGB) - , fillColorSpace(ColorSpaceDeviceRGB) - , shadowColorSpace(ColorSpaceDeviceRGB) - , compositeOperator(CompositeSourceOver) - , blendMode(BlendModeNormal) -#if PLATFORM(IOS) - , emojiDrawingEnabled(true) - , shouldUseContextColors(true) -#endif - , shouldAntialias(true) - , shouldSmoothFonts(true) - , shouldSubpixelQuantizeFonts(true) - , paintingDisabled(false) - , shadowsIgnoreTransforms(false) + FloatSize shadowOffset; + + float strokeThickness { 0 }; + float shadowBlur { 0 }; + + TextDrawingModeFlags textDrawingMode { TextModeFill }; + + Color strokeColor { Color::black }; + Color fillColor { Color::black }; + Color shadowColor; + + StrokeStyle strokeStyle { SolidStroke }; + WindRule fillRule { RULE_NONZERO }; + + float alpha { 1 }; + CompositeOperator compositeOperator { CompositeSourceOver }; + BlendMode blendMode { BlendModeNormal }; + InterpolationQuality imageInterpolationQuality { InterpolationDefault }; + + bool shouldAntialias : 1; + bool shouldSmoothFonts : 1; + bool shouldSubpixelQuantizeFonts : 1; + bool shadowsIgnoreTransforms : 1; #if USE(CG) - // Core Graphics incorrectly renders shadows with radius > 8px (<rdar://problem/8103442>), - // but we need to preserve this buggy behavior for canvas and -webkit-box-shadow. - , shadowsUseLegacyRadius(false) + bool shadowsUseLegacyRadius : 1; #endif - , drawLuminanceMask(false) - { - } + bool drawLuminanceMask : 1; +}; + +struct ImagePaintingOptions { + ImagePaintingOptions(CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), InterpolationQuality interpolationQuality = InterpolationDefault) + : m_compositeOperator(compositeOperator) + , m_blendMode(blendMode) + , m_orientationDescription(orientationDescription) + , m_interpolationQuality(interpolationQuality) + { + } + + ImagePaintingOptions(ImageOrientationDescription orientationDescription, InterpolationQuality interpolationQuality = InterpolationDefault, CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal) + : m_compositeOperator(compositeOperator) + , m_blendMode(blendMode) + , m_orientationDescription(orientationDescription) + , m_interpolationQuality(interpolationQuality) + { + } + + ImagePaintingOptions(InterpolationQuality interpolationQuality, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal) + : m_compositeOperator(compositeOperator) + , m_blendMode(blendMode) + , m_orientationDescription(orientationDescription) + , m_interpolationQuality(interpolationQuality) + { + } + + bool usesDefaultInterpolation() const { return m_interpolationQuality == InterpolationDefault; } + + CompositeOperator m_compositeOperator; + BlendMode m_blendMode; + ImageOrientationDescription m_orientationDescription; + InterpolationQuality m_interpolationQuality; +}; + +struct GraphicsContextStateChange { + GraphicsContextStateChange() = default; + GraphicsContextStateChange(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) + : m_state(state) + , m_changeFlags(flags) + { + } + + GraphicsContextState::StateChangeFlags changesFromState(const GraphicsContextState&) const; + + void accumulate(const GraphicsContextState&, GraphicsContextState::StateChangeFlags); + void apply(GraphicsContext&) const; + + void dump(TextStream&) const; + + GraphicsContextState m_state; + GraphicsContextState::StateChangeFlags m_changeFlags { GraphicsContextState::NoChange }; +}; + +TextStream& operator<<(TextStream&, const GraphicsContextStateChange&); + + +class GraphicsContext { + WTF_MAKE_NONCOPYABLE(GraphicsContext); WTF_MAKE_FAST_ALLOCATED; +public: + WEBCORE_EXPORT GraphicsContext(PlatformGraphicsContext*); + GraphicsContext() = default; + WEBCORE_EXPORT ~GraphicsContext(); + + enum class NonPaintingReasons { + NoReasons, + UpdatingControlTints + }; + GraphicsContext(NonPaintingReasons); - RefPtr<Gradient> strokeGradient; - RefPtr<Pattern> strokePattern; - - RefPtr<Gradient> fillGradient; - RefPtr<Pattern> fillPattern; + WEBCORE_EXPORT PlatformGraphicsContext* platformContext() const; - FloatSize shadowOffset; + bool paintingDisabled() const { return !m_data && !isRecording(); } + bool updatingControlTints() const { return m_nonPaintingReasons == NonPaintingReasons::UpdatingControlTints; } - float strokeThickness; - float shadowBlur; + void setDisplayListRecorder(DisplayList::Recorder* recorder) { m_displayListRecorder = recorder; } + bool isRecording() const { return m_displayListRecorder; } - TextDrawingModeFlags textDrawingMode; + void setStrokeThickness(float); + float strokeThickness() const { return m_state.strokeThickness; } - Color strokeColor; - Color fillColor; - Color shadowColor; + void setStrokeStyle(StrokeStyle); + StrokeStyle strokeStyle() const { return m_state.strokeStyle; } - StrokeStyle strokeStyle; - WindRule fillRule; + WEBCORE_EXPORT void setStrokeColor(const Color&); + const Color& strokeColor() const { return m_state.strokeColor; } - ColorSpace strokeColorSpace; - ColorSpace fillColorSpace; - ColorSpace shadowColorSpace; + void setStrokePattern(Ref<Pattern>&&); + Pattern* strokePattern() const { return m_state.strokePattern.get(); } - CompositeOperator compositeOperator; - BlendMode blendMode; + void setStrokeGradient(Ref<Gradient>&&); + Gradient* strokeGradient() const { return m_state.strokeGradient.get(); } -#if PLATFORM(IOS) - bool emojiDrawingEnabled : 1; - bool shouldUseContextColors : 1; -#endif - bool shouldAntialias : 1; - bool shouldSmoothFonts : 1; - bool shouldSubpixelQuantizeFonts : 1; - bool paintingDisabled : 1; - bool shadowsIgnoreTransforms : 1; -#if USE(CG) - bool shadowsUseLegacyRadius : 1; -#endif - bool drawLuminanceMask : 1; - }; + void setFillRule(WindRule); + WindRule fillRule() const { return m_state.fillRule; } -#if PLATFORM(IOS) - void setStrokeAndFillColor(PlatformGraphicsContext*, CGColorRef); + WEBCORE_EXPORT void setFillColor(const Color&); + const Color& fillColor() const { return m_state.fillColor; } + + void setFillPattern(Ref<Pattern>&&); + Pattern* fillPattern() const { return m_state.fillPattern.get(); } + + WEBCORE_EXPORT void setFillGradient(Ref<Gradient>&&); + Gradient* fillGradient() const { return m_state.fillGradient.get(); } + + void setShadowsIgnoreTransforms(bool); + bool shadowsIgnoreTransforms() const { return m_state.shadowsIgnoreTransforms; } + + WEBCORE_EXPORT void setShouldAntialias(bool); + bool shouldAntialias() const { return m_state.shouldAntialias; } + + WEBCORE_EXPORT void setShouldSmoothFonts(bool); + bool shouldSmoothFonts() const { return m_state.shouldSmoothFonts; } + + // Normally CG enables subpixel-quantization because it improves the performance of aligning glyphs. + // In some cases we have to disable to to ensure a high-quality output of the glyphs. + void setShouldSubpixelQuantizeFonts(bool); + bool shouldSubpixelQuantizeFonts() const { return m_state.shouldSubpixelQuantizeFonts; } + + const GraphicsContextState& state() const { return m_state; } + +#if USE(CG) || USE(DIRECT2D) || USE(CAIRO) + WEBCORE_EXPORT void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = ImageOrientation()); #endif - class GraphicsContext { - WTF_MAKE_NONCOPYABLE(GraphicsContext); WTF_MAKE_FAST_ALLOCATED; - public: -#if !PLATFORM(IOS) - GraphicsContext(PlatformGraphicsContext*); -#else - GraphicsContext(PlatformGraphicsContext*, bool shouldUseContextColors = true); +#if USE(CG) || USE(DIRECT2D) + void applyStrokePattern(); + void applyFillPattern(); + void drawPath(const Path&); + + WEBCORE_EXPORT void setIsCALayerContext(bool); + bool isCALayerContext() const; + + WEBCORE_EXPORT void setIsAcceleratedContext(bool); #endif - ~GraphicsContext(); + bool isAcceleratedContext() const; + RenderingMode renderingMode() const { return isAcceleratedContext() ? Accelerated : Unaccelerated; } - PlatformGraphicsContext* platformContext() const; + WEBCORE_EXPORT void save(); + WEBCORE_EXPORT void restore(); - float strokeThickness() const; - void setStrokeThickness(float); - StrokeStyle strokeStyle() const; - void setStrokeStyle(StrokeStyle); - Color strokeColor() const; - ColorSpace strokeColorSpace() const; - void setStrokeColor(const Color&, ColorSpace); + // These draw methods will do both stroking and filling. + // FIXME: ...except drawRect(), which fills properly but always strokes + // using a 1-pixel stroke inset from the rect borders (of the correct + // stroke color). + void drawRect(const FloatRect&, float borderThickness = 1); + void drawLine(const FloatPoint&, const FloatPoint&); - void setStrokePattern(PassRefPtr<Pattern>); - Pattern* strokePattern() const; + void drawEllipse(const FloatRect&); + void drawRaisedEllipse(const FloatRect&, const Color& ellipseColor, const Color& shadowColor); - void setStrokeGradient(PassRefPtr<Gradient>); - Gradient* strokeGradient() const; + WEBCORE_EXPORT void fillPath(const Path&); + void strokePath(const Path&); - WindRule fillRule() const; - void setFillRule(WindRule); - Color fillColor() const; - ColorSpace fillColorSpace() const; - void setFillColor(const Color&, ColorSpace); + void fillEllipse(const FloatRect&); + void strokeEllipse(const FloatRect&); - void setFillPattern(PassRefPtr<Pattern>); - Pattern* fillPattern() const; + WEBCORE_EXPORT void fillRect(const FloatRect&); + WEBCORE_EXPORT void fillRect(const FloatRect&, const Color&); + void fillRect(const FloatRect&, Gradient&); + void fillRect(const FloatRect&, const Color&, CompositeOperator, BlendMode = BlendModeNormal); + void fillRoundedRect(const FloatRoundedRect&, const Color&, BlendMode = BlendModeNormal); + void fillRectWithRoundedHole(const FloatRect&, const FloatRoundedRect& roundedHoleRect, const Color&); - void setFillGradient(PassRefPtr<Gradient>); - Gradient* fillGradient() const; + WEBCORE_EXPORT void clearRect(const FloatRect&); - void setShadowsIgnoreTransforms(bool); - bool shadowsIgnoreTransforms() const; + WEBCORE_EXPORT void strokeRect(const FloatRect&, float lineWidth); - void setShouldAntialias(bool); - bool shouldAntialias() const; + WEBCORE_EXPORT void drawImage(Image&, const FloatPoint& destination, const ImagePaintingOptions& = ImagePaintingOptions()); + WEBCORE_EXPORT void drawImage(Image&, const FloatRect& destination, const ImagePaintingOptions& = ImagePaintingOptions()); + void drawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& = ImagePaintingOptions()); - void setShouldSmoothFonts(bool); - bool shouldSmoothFonts() const; + void drawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& = ImagePaintingOptions()); + void drawTiledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, + Image::TileRule, Image::TileRule, const ImagePaintingOptions& = ImagePaintingOptions()); - // Normally CG enables subpixel-quantization because it improves the performance of aligning glyphs. - // In some cases we have to disable to to ensure a high-quality output of the glyphs. - void setShouldSubpixelQuantizeFonts(bool); - bool shouldSubpixelQuantizeFonts() const; + WEBCORE_EXPORT void drawImageBuffer(ImageBuffer&, const FloatPoint& destination, const ImagePaintingOptions& = ImagePaintingOptions()); + void drawImageBuffer(ImageBuffer&, const FloatRect& destination, const ImagePaintingOptions& = ImagePaintingOptions()); + void drawImageBuffer(ImageBuffer&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& = ImagePaintingOptions()); - const GraphicsContextState& state() const; + void drawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal); -#if USE(CG) - void applyStrokePattern(); - void applyFillPattern(); - void drawPath(const Path&); + WEBCORE_EXPORT void drawConsumingImageBuffer(std::unique_ptr<ImageBuffer>, const FloatPoint& destination, const ImagePaintingOptions& = ImagePaintingOptions()); + void drawConsumingImageBuffer(std::unique_ptr<ImageBuffer>, const FloatRect& destination, const ImagePaintingOptions& = ImagePaintingOptions()); + void drawConsumingImageBuffer(std::unique_ptr<ImageBuffer>, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& = ImagePaintingOptions()); - void drawNativeImage(PassNativeImagePtr, const FloatSize& selfSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, float scale = 1, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = DefaultImageOrientation); + WEBCORE_EXPORT void setImageInterpolationQuality(InterpolationQuality); + InterpolationQuality imageInterpolationQuality() const { return m_state.imageInterpolationQuality; } - // Allow font smoothing (LCD antialiasing). Not part of the graphics state. - void setAllowsFontSmoothing(bool); - - void setIsCALayerContext(bool); - bool isCALayerContext() const; + WEBCORE_EXPORT void clip(const FloatRect&); + void clipRoundedRect(const FloatRoundedRect&); - void setIsAcceleratedContext(bool); -#endif - bool isAcceleratedContext() const; + void clipOut(const FloatRect&); + void clipOutRoundedRect(const FloatRoundedRect&); + void clipPath(const Path&, WindRule = RULE_EVENODD); + void clipToImageBuffer(ImageBuffer&, const FloatRect&); + + IntRect clipBounds() const; - void save(); - void restore(); + void setTextDrawingMode(TextDrawingModeFlags); + TextDrawingModeFlags textDrawingMode() const { return m_state.textDrawingMode; } - // These draw methods will do both stroking and filling. - // FIXME: ...except drawRect(), which fills properly but always strokes - // using a 1-pixel stroke inset from the rect borders (of the correct - // stroke color). - void drawRect(const IntRect&); - void drawLine(const IntPoint&, const IntPoint&); + float drawText(const FontCascade&, const TextRun&, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt); + void drawGlyphs(const FontCascade&, const Font&, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint&); + void drawEmphasisMarks(const FontCascade&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from = 0, std::optional<unsigned> to = std::nullopt); + void drawBidiText(const FontCascade&, const TextRun&, const FloatPoint&, FontCascade::CustomFontNotReadyAction = FontCascade::DoNotPaintIfFontNotReady); -#if PLATFORM(IOS) - void drawJoinedLines(CGPoint points[], unsigned count, bool antialias, CGLineCap = kCGLineCapButt); -#endif + void applyState(const GraphicsContextState&); - void drawEllipse(const IntRect&); -#if PLATFORM(IOS) - void drawEllipse(const FloatRect&); - void drawRaisedEllipse(const FloatRect&, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace); -#endif - void drawConvexPolygon(size_t numPoints, const FloatPoint*, bool shouldAntialias = false); - - void fillPath(const Path&); - void strokePath(const Path&); - - void fillEllipse(const FloatRect&); - void strokeEllipse(const FloatRect&); - - void fillRect(const FloatRect&); - void fillRect(const FloatRect&, const Color&, ColorSpace); - void fillRect(const FloatRect&, Gradient&); - void fillRect(const FloatRect&, const Color&, ColorSpace, CompositeOperator, BlendMode = BlendModeNormal); - void fillRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color&, ColorSpace); - void fillRoundedRect(const RoundedRect&, const Color&, ColorSpace, BlendMode = BlendModeNormal); - void fillRectWithRoundedHole(const IntRect&, const RoundedRect& roundedHoleRect, const Color&, ColorSpace); - - void clearRect(const FloatRect&); - - void strokeRect(const FloatRect&, float lineWidth); - - void drawImage(Image*, ColorSpace styleColorSpace, const IntPoint&, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription()); - void drawImage(Image*, ColorSpace styleColorSpace, const IntRect&, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription(), bool useLowQualityScale = false); - void drawImage(Image*, ColorSpace styleColorSpace, const IntPoint& destPoint, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription()); - void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect); - void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, ImageOrientationDescription = ImageOrientationDescription(), bool useLowQualityScale = false); - void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription = ImageOrientationDescription(), bool useLowQualityScale = false); - - void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, - CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false, BlendMode = BlendModeNormal); - void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect, - const FloatSize& tileScaleFactor, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, - CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); - - void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntPoint&, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal); - void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntRect&, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false); - void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntPoint& destPoint, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal); - void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false); - void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const FloatRect& destRect); - void drawImageBuffer(ImageBuffer*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false); - - void setImageInterpolationQuality(InterpolationQuality); - InterpolationQuality imageInterpolationQuality() const; - - void clip(const IntRect&); - void clip(const FloatRect&); - void clipRoundedRect(const RoundedRect&); - - // FIXME: Consider writing this in terms of a specialized RoundedRect that uses FloatRect and FloatSize radii. - void clipRoundedRect(const FloatRect&, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight); - - void clipOut(const IntRect&); - void clipOutRoundedRect(const RoundedRect&); - void clipPath(const Path&, WindRule); - void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true); - void clipToImageBuffer(ImageBuffer*, const FloatRect&); - - IntRect clipBounds() const; - - TextDrawingModeFlags textDrawingMode() const; - void setTextDrawingMode(TextDrawingModeFlags); + enum RoundingMode { + RoundAllSides, + RoundOriginAndDimensions + }; + FloatRect roundToDevicePixels(const FloatRect&, RoundingMode = RoundAllSides); + FloatRect computeUnderlineBoundsForText(const FloatPoint&, float width, bool printing); + WEBCORE_EXPORT void drawLineForText(const FloatPoint&, float width, bool printing, bool doubleLines = false, StrokeStyle = SolidStroke); + void drawLinesForText(const FloatPoint&, const DashArray& widths, bool printing, bool doubleLines = false, StrokeStyle = SolidStroke); + enum DocumentMarkerLineStyle { #if PLATFORM(IOS) - bool emojiDrawingEnabled(); - void setEmojiDrawingEnabled(bool); + TextCheckingDictationPhraseWithAlternativesLineStyle, #endif - -#if !PLATFORM(IOS) - void drawText(const Font&, const TextRun&, const FloatPoint&, int from = 0, int to = -1); -#else - float drawText(const Font&, const TextRun&, const FloatPoint&, int from = 0, int to = -1); -#endif - void drawEmphasisMarks(const Font&, const TextRun& , const AtomicString& mark, const FloatPoint&, int from = 0, int to = -1); -#if !PLATFORM(IOS) - void drawBidiText(const Font&, const TextRun&, const FloatPoint&, Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady); -#else - float drawBidiText(const Font&, const TextRun&, const FloatPoint&, Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady, BidiStatus* = 0, int length = -1); -#endif - void drawHighlightForText(const Font&, const TextRun&, const FloatPoint&, int h, const Color& backgroundColor, ColorSpace, int from = 0, int to = -1); - - enum RoundingMode { - RoundAllSides, - RoundOriginAndDimensions - }; - FloatRect roundToDevicePixels(const FloatRect&, RoundingMode = RoundAllSides); - - FloatRect computeLineBoundsForText(const FloatPoint&, float width, bool printing); - void drawLineForText(const FloatPoint&, float width, bool printing); - void drawLinesForText(const FloatPoint&, const DashArray& widths, bool printing); - enum DocumentMarkerLineStyle { -#if PLATFORM(IOS) - TextCheckingDictationPhraseWithAlternativesLineStyle, -#endif - DocumentMarkerSpellingLineStyle, - DocumentMarkerGrammarLineStyle, - DocumentMarkerAutocorrectionReplacementLineStyle, - DocumentMarkerDictationAlternativesLineStyle - }; - static void updateDocumentMarkerResources(); - void drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle); - - bool paintingDisabled() const; - void setPaintingDisabled(bool); - - bool updatingControlTints() const; - void setUpdatingControlTints(bool); - - void beginTransparencyLayer(float opacity); - void endTransparencyLayer(); - bool isInTransparencyLayer() const; - - bool hasShadow() const; - void setShadow(const FloatSize&, float blur, const Color&, ColorSpace); - // Legacy shadow blur radius is used for canvas, and -webkit-box-shadow. - // It has different treatment of radii > 8px. - void setLegacyShadow(const FloatSize&, float blur, const Color&, ColorSpace); - - bool getShadow(FloatSize&, float&, Color&, ColorSpace&) const; - void clearShadow(); - - bool hasBlurredShadow() const; + DocumentMarkerSpellingLineStyle, + DocumentMarkerGrammarLineStyle, + DocumentMarkerAutocorrectionReplacementLineStyle, + DocumentMarkerDictationAlternativesLineStyle + }; + static void updateDocumentMarkerResources(); + void drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle); + + WEBCORE_EXPORT void beginTransparencyLayer(float opacity); + WEBCORE_EXPORT void endTransparencyLayer(); + bool isInTransparencyLayer() const { return (m_transparencyCount > 0) && supportsTransparencyLayers(); } + + WEBCORE_EXPORT void setShadow(const FloatSize&, float blur, const Color&); + // Legacy shadow blur radius is used for canvas, and -webkit-box-shadow. + // It has different treatment of radii > 8px. + void setLegacyShadow(const FloatSize&, float blur, const Color&); + + WEBCORE_EXPORT void clearShadow(); + bool getShadow(FloatSize&, float&, Color&) const; + + bool hasVisibleShadow() const { return m_state.shadowColor.isVisible(); } + bool hasShadow() const { return hasVisibleShadow() && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height()); } + bool hasBlurredShadow() const { return hasVisibleShadow() && m_state.shadowBlur; } + #if USE(CAIRO) - bool mustUseShadowBlur() const; + bool mustUseShadowBlur() const; #endif - void drawFocusRing(const Vector<IntRect>&, int width, int offset, const Color&); - void drawFocusRing(const Path&, int width, int offset, const Color&); + void drawFocusRing(const Vector<FloatRect>&, float width, float offset, const Color&); + void drawFocusRing(const Path&, float width, float offset, const Color&); +#if PLATFORM(MAC) + void drawFocusRing(const Path&, double timeOffset, bool& needsRedraw); + void drawFocusRing(const Vector<FloatRect>&, double timeOffset, bool& needsRedraw); +#endif - void setLineCap(LineCap); - void setLineDash(const DashArray&, float dashOffset); - void setLineJoin(LineJoin); - void setMiterLimit(float); + void setLineCap(LineCap); + void setLineDash(const DashArray&, float dashOffset); + void setLineJoin(LineJoin); + void setMiterLimit(float); - void setAlpha(float); + void setAlpha(float); + float alpha() const { return m_state.alpha; } - void setCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal); - CompositeOperator compositeOperation() const; - BlendMode blendModeOperation() const; + WEBCORE_EXPORT void setCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal); + CompositeOperator compositeOperation() const { return m_state.compositeOperator; } + BlendMode blendModeOperation() const { return m_state.blendMode; } - void setDrawLuminanceMask(bool); - bool drawLuminanceMask() const; + void setDrawLuminanceMask(bool); + bool drawLuminanceMask() const { return m_state.drawLuminanceMask; } - void clip(const Path&, WindRule = RULE_EVENODD); + // This clip function is used only by <canvas> code. It allows + // implementations to handle clipping on the canvas differently since + // the discipline is different. + void canvasClip(const Path&, WindRule = RULE_EVENODD); + void clipOut(const Path&); - // This clip function is used only by <canvas> code. It allows - // implementations to handle clipping on the canvas differently since - // the discipline is different. - void canvasClip(const Path&, WindRule = RULE_EVENODD); - void clipOut(const Path&); + void scale(float s) + { + scale({ s, s }); + } + WEBCORE_EXPORT void scale(const FloatSize&); + void rotate(float angleInRadians); + void translate(const FloatSize& size) { translate(size.width(), size.height()); } + WEBCORE_EXPORT void translate(float x, float y); - void scale(const FloatSize&); - void rotate(float angleInRadians); - void translate(const FloatSize& size) { translate(size.width(), size.height()); } - void translate(float x, float y); + void setURLForRect(const URL&, const FloatRect&); - void setURLForRect(const URL&, const IntRect&); + void setDestinationForRect(const String& name, const FloatRect&); + void addDestinationAtPoint(const String& name, const FloatPoint&); - void concatCTM(const AffineTransform&); - void setCTM(const AffineTransform&); + void concatCTM(const AffineTransform&); + void setCTM(const AffineTransform&); - enum IncludeDeviceScale { DefinitelyIncludeDeviceScale, PossiblyIncludeDeviceScale }; - AffineTransform getCTM(IncludeDeviceScale includeScale = PossiblyIncludeDeviceScale) const; + enum IncludeDeviceScale { DefinitelyIncludeDeviceScale, PossiblyIncludeDeviceScale }; + AffineTransform getCTM(IncludeDeviceScale includeScale = PossiblyIncludeDeviceScale) const; -#if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER) - // This is needed when using accelerated-compositing in software mode, like in TextureMapper. - void concat3DTransform(const TransformationMatrix&); - void set3DTransform(const TransformationMatrix&); - TransformationMatrix get3DTransform() const; +#if ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER) + // This is needed when using accelerated-compositing in software mode, like in TextureMapper. + void concat3DTransform(const TransformationMatrix&); + void set3DTransform(const TransformationMatrix&); + TransformationMatrix get3DTransform() const; #endif - // Create an image buffer compatible with this context, with suitable resolution - // for drawing into the buffer and then into this context. - std::unique_ptr<ImageBuffer> createCompatibleBuffer(const IntSize&, bool hasAlpha = true) const; - bool isCompatibleWithBuffer(ImageBuffer*) const; - // This function applies the device scale factor to the context, making the context capable of - // acting as a base-level context for a HiDPI environment. - void applyDeviceScaleFactor(float); - void platformApplyDeviceScaleFactor(float); + // This function applies the device scale factor to the context, making the context capable of + // acting as a base-level context for a HiDPI environment. + WEBCORE_EXPORT void applyDeviceScaleFactor(float); + void platformApplyDeviceScaleFactor(float); + FloatSize scaleFactor() const; #if OS(WINDOWS) - HDC getWindowsContext(const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in rect is used to create a bitmap for compositing inside transparency layers. - void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in HDC should be the one handed back by getWindowsContext. + HDC getWindowsContext(const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in rect is used to create a bitmap for compositing inside transparency layers. + void releaseWindowsContext(HDC, const IntRect&, bool supportAlphaBlend, bool mayCreateBitmap); // The passed in HDC should be the one handed back by getWindowsContext. + HDC hdc() const; #if PLATFORM(WIN) #if USE(WINGDI) - void setBitmap(PassRefPtr<SharedBitmap>); - const AffineTransform& affineTransform() const; - AffineTransform& affineTransform(); - void resetAffineTransform(); - void fillRect(const FloatRect&, const Gradient*); - void drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point); - void drawFrameControl(const IntRect& rect, unsigned type, unsigned state); - void drawFocusRect(const IntRect& rect); - void paintTextField(const IntRect& rect, unsigned state); - void drawBitmap(SharedBitmap*, const IntRect& dstRect, const IntRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode); - void drawBitmapPattern(SharedBitmap*, const FloatRect& tileRectIn, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, const IntSize& origSourceSize); - void drawIcon(HICON icon, const IntRect& dstRect, UINT flags); - void drawRoundCorner(bool newClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height); + void setBitmap(PassRefPtr<SharedBitmap>); + const AffineTransform& affineTransform() const; + AffineTransform& affineTransform(); + void resetAffineTransform(); + void fillRect(const FloatRect&, const Gradient*); + void drawText(const Font&, const GlyphBuffer&, int from, int numGlyphs, const FloatPoint&); + void drawFrameControl(const IntRect& rect, unsigned type, unsigned state); + void drawFocusRect(const IntRect& rect); + void paintTextField(const IntRect& rect, unsigned state); + void drawBitmap(SharedBitmap*, const IntRect& dstRect, const IntRect& srcRect, CompositeOperator, BlendMode); + void drawBitmapPattern(SharedBitmap*, const FloatRect& tileRectIn, const AffineTransform& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect, const IntSize& origSourceSize); + void drawIcon(HICON icon, const IntRect& dstRect, UINT flags); + void drawRoundCorner(bool newClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height); #else - GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed. - - // When set to true, child windows should be rendered into this context - // rather than allowing them just to render to the screen. Defaults to - // false. - // FIXME: This is a layering violation. GraphicsContext shouldn't know - // what a "window" is. It would be much more appropriate for this flag - // to be passed as a parameter alongside the GraphicsContext, but doing - // that would require lots of changes in cross-platform code that we - // aren't sure we want to make. - void setShouldIncludeChildWindows(bool); - bool shouldIncludeChildWindows() const; - - class WindowsBitmap { - WTF_MAKE_NONCOPYABLE(WindowsBitmap); - public: - WindowsBitmap(HDC, const IntSize&); - ~WindowsBitmap(); - - HDC hdc() const { return m_hdc; } - UInt8* buffer() const { return m_pixelData.buffer(); } - unsigned bufferLength() const { return m_pixelData.bufferLength(); } - const IntSize& size() const { return m_pixelData.size(); } - unsigned bytesPerRow() const { return m_pixelData.bytesPerRow(); } - unsigned short bitsPerPixel() const { return m_pixelData.bitsPerPixel(); } - const DIBPixelData& windowsDIB() const { return m_pixelData; } - - private: - HDC m_hdc; - HBITMAP m_bitmap; - DIBPixelData m_pixelData; - }; - - PassOwnPtr<WindowsBitmap> createWindowsBitmap(const IntSize&); - // The bitmap should be non-premultiplied. - void drawWindowsBitmap(WindowsBitmap*, const IntPoint&); + GraphicsContext(HDC, bool hasAlpha = false); // FIXME: To be removed. + + // When set to true, child windows should be rendered into this context + // rather than allowing them just to render to the screen. Defaults to + // false. + // FIXME: This is a layering violation. GraphicsContext shouldn't know + // what a "window" is. It would be much more appropriate for this flag + // to be passed as a parameter alongside the GraphicsContext, but doing + // that would require lots of changes in cross-platform code that we + // aren't sure we want to make. + void setShouldIncludeChildWindows(bool); + bool shouldIncludeChildWindows() const; + + class WindowsBitmap { + WTF_MAKE_NONCOPYABLE(WindowsBitmap); + public: + WindowsBitmap(HDC, const IntSize&); + ~WindowsBitmap(); + + HDC hdc() const { return m_hdc; } + UInt8* buffer() const { return m_pixelData.buffer(); } + unsigned bufferLength() const { return m_pixelData.bufferLength(); } + const IntSize& size() const { return m_pixelData.size(); } + unsigned bytesPerRow() const { return m_pixelData.bytesPerRow(); } + unsigned short bitsPerPixel() const { return m_pixelData.bitsPerPixel(); } + const DIBPixelData& windowsDIB() const { return m_pixelData; } + + private: + HDC m_hdc; + HBITMAP m_bitmap; + DIBPixelData m_pixelData; + }; + + std::unique_ptr<WindowsBitmap> createWindowsBitmap(const IntSize&); + // The bitmap should be non-premultiplied. + void drawWindowsBitmap(WindowsBitmap*, const IntPoint&); +#endif +#if USE(DIRECT2D) + GraphicsContext(HDC, ID2D1DCRenderTarget**, RECT, bool hasAlpha = false); // FIXME: To be removed. + + WEBCORE_EXPORT static ID2D1Factory* systemFactory(); + WEBCORE_EXPORT static ID2D1RenderTarget* defaultRenderTarget(); + + WEBCORE_EXPORT void beginDraw(); + D2D1_COLOR_F colorWithGlobalAlpha(const Color&) const; + WEBCORE_EXPORT void endDraw(); + void flush(); + + ID2D1Brush* solidStrokeBrush() const; + ID2D1Brush* solidFillBrush() const; + ID2D1Brush* patternStrokeBrush() const; + ID2D1Brush* patternFillBrush() const; + ID2D1StrokeStyle* platformStrokeStyle() const; + + ID2D1SolidColorBrush* brushWithColor(const Color&); #endif #else // PLATFORM(WIN) - bool shouldIncludeChildWindows() const { return false; } + bool shouldIncludeChildWindows() const { return false; } #endif // PLATFORM(WIN) #endif // OS(WINDOWS) #if USE(CAIRO) - GraphicsContext(cairo_t*); + GraphicsContext(cairo_t*); #endif -#if PLATFORM(GTK) - void setGdkExposeEvent(GdkEventExpose*); - GdkWindow* gdkWindow() const; - GdkEventExpose* gdkExposeEvent() const; -#endif + static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle); - static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle); + bool supportsInternalLinks() const; - private: -#if !PLATFORM(IOS) - void platformInit(PlatformGraphicsContext*); -#else - void platformInit(PlatformGraphicsContext*, bool shouldUseContextColors); -#endif - void platformDestroy(); +private: + void platformInit(PlatformGraphicsContext*); + void platformDestroy(); #if PLATFORM(WIN) && !USE(WINGDI) - void platformInit(HDC, bool hasAlpha = false); + void platformInit(HDC, bool hasAlpha = false); #endif - void savePlatformState(); - void restorePlatformState(); +#if USE(DIRECT2D) + void platformInit(HDC, ID2D1RenderTarget**, RECT, bool hasAlpha = false); + void drawWithoutShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>&); + void drawWithShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>&); +#endif - void setPlatformTextDrawingMode(TextDrawingModeFlags); + void savePlatformState(); + void restorePlatformState(); - void setPlatformStrokeColor(const Color&, ColorSpace); - void setPlatformStrokeStyle(StrokeStyle); - void setPlatformStrokeThickness(float); + void setPlatformTextDrawingMode(TextDrawingModeFlags); - void setPlatformFillColor(const Color&, ColorSpace); + void setPlatformStrokeColor(const Color&); + void setPlatformStrokeStyle(StrokeStyle); + void setPlatformStrokeThickness(float); - void setPlatformShouldAntialias(bool); - void setPlatformShouldSmoothFonts(bool); + void setPlatformFillColor(const Color&); - void setPlatformShadow(const FloatSize&, float blur, const Color&, ColorSpace); - void clearPlatformShadow(); + void setPlatformShouldAntialias(bool); + void setPlatformShouldSmoothFonts(bool); + void setPlatformImageInterpolationQuality(InterpolationQuality); - void setPlatformCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal); + void setPlatformShadow(const FloatSize&, float blur, const Color&); + void clearPlatformShadow(); - void beginPlatformTransparencyLayer(float opacity); - void endPlatformTransparencyLayer(); - static bool supportsTransparencyLayers(); + void setPlatformAlpha(float); + void setPlatformCompositeOperation(CompositeOperator, BlendMode = BlendModeNormal); - void fillEllipseAsPath(const FloatRect&); - void strokeEllipseAsPath(const FloatRect&); + void beginPlatformTransparencyLayer(float opacity); + void endPlatformTransparencyLayer(); + static bool supportsTransparencyLayers(); - void platformFillEllipse(const FloatRect&); - void platformStrokeEllipse(const FloatRect&); + void fillEllipseAsPath(const FloatRect&); + void strokeEllipseAsPath(const FloatRect&); - GraphicsContextPlatformPrivate* m_data; + void platformFillEllipse(const FloatRect&); + void platformStrokeEllipse(const FloatRect&); - GraphicsContextState m_state; - Vector<GraphicsContextState> m_stack; - bool m_updatingControlTints; - unsigned m_transparencyCount; - }; + void platformFillRoundedRect(const FloatRoundedRect&, const Color&); - class GraphicsContextStateSaver { - WTF_MAKE_FAST_ALLOCATED; - public: - GraphicsContextStateSaver(GraphicsContext& context, bool saveAndRestore = true) - : m_context(context) - , m_saveAndRestore(saveAndRestore) - { - if (m_saveAndRestore) - m_context.save(); - } - - ~GraphicsContextStateSaver() - { - if (m_saveAndRestore) - m_context.restore(); - } - - void save() - { - ASSERT(!m_saveAndRestore); - m_context.save(); - m_saveAndRestore = true; - } + FloatRect computeLineBoundsAndAntialiasingModeForText(const FloatPoint&, float width, bool printing, Color&); - void restore() - { - ASSERT(m_saveAndRestore); - m_context.restore(); - m_saveAndRestore = false; - } - - GraphicsContext* context() const { return &m_context; } + float dashedLineCornerWidthForStrokeWidth(float) const; + float dashedLinePatternWidthForStrokeWidth(float) const; + float dashedLinePatternOffsetForPatternAndStrokeWidth(float patternWidth, float strokeWidth) const; + Vector<FloatPoint> centerLineAndCutOffCorners(bool isVerticalLine, float cornerWidth, FloatPoint point1, FloatPoint point2) const; - private: - GraphicsContext& m_context; - bool m_saveAndRestore; - }; + GraphicsContextPlatformPrivate* m_data { nullptr }; + DisplayList::Recorder* m_displayListRecorder { nullptr }; -} // namespace WebCore + GraphicsContextState m_state; + Vector<GraphicsContextState, 1> m_stack; -#endif // GraphicsContext_h + const NonPaintingReasons m_nonPaintingReasons { NonPaintingReasons::NoReasons }; + unsigned m_transparencyCount { 0 }; +}; +class GraphicsContextStateSaver { + WTF_MAKE_FAST_ALLOCATED; +public: + GraphicsContextStateSaver(GraphicsContext& context, bool saveAndRestore = true) + : m_context(context) + , m_saveAndRestore(saveAndRestore) + { + if (m_saveAndRestore) + m_context.save(); + } + + ~GraphicsContextStateSaver() + { + if (m_saveAndRestore) + m_context.restore(); + } + + void save() + { + ASSERT(!m_saveAndRestore); + m_context.save(); + m_saveAndRestore = true; + } + + void restore() + { + ASSERT(m_saveAndRestore); + m_context.restore(); + m_saveAndRestore = false; + } + + GraphicsContext* context() const { return &m_context; } + +private: + GraphicsContext& m_context; + bool m_saveAndRestore; +}; + +class InterpolationQualityMaintainer { +public: + explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, InterpolationQuality interpolationQualityToUse) + : m_graphicsContext(graphicsContext) + , m_currentInterpolationQuality(graphicsContext.imageInterpolationQuality()) + , m_interpolationQualityChanged(interpolationQualityToUse != InterpolationDefault && m_currentInterpolationQuality != interpolationQualityToUse) + { + if (m_interpolationQualityChanged) + m_graphicsContext.setImageInterpolationQuality(interpolationQualityToUse); + } + + explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, std::optional<InterpolationQuality> interpolationQuality) + : InterpolationQualityMaintainer(graphicsContext, interpolationQuality ? interpolationQuality.value() : graphicsContext.imageInterpolationQuality()) + { + } + + ~InterpolationQualityMaintainer() + { + if (m_interpolationQualityChanged) + m_graphicsContext.setImageInterpolationQuality(m_currentInterpolationQuality); + } + +private: + GraphicsContext& m_graphicsContext; + InterpolationQuality m_currentInterpolationQuality; + bool m_interpolationQualityChanged; +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp index 47ed32f3c..76bab7c7b 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2016 Apple Inc. All rights reserved. * Copyright (C) 2010 Google Inc. All rights reserved. * Copyright (C) 2010 Mozilla Corporation. All rights reserved. * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" @@ -62,6 +62,12 @@ GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum case GraphicsContext3D::LUMINANCE_ALPHA: dstFormat = GraphicsContext3D::DataFormatRA8; break; + case GraphicsContext3D::SRGB: + dstFormat = GraphicsContext3D::DataFormatRGB8; + break; + case GraphicsContext3D::SRGB_ALPHA: + dstFormat = GraphicsContext3D::DataFormatRGBA8; + break; default: ASSERT_NOT_REACHED(); } @@ -92,6 +98,12 @@ GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum case GraphicsContext3D::LUMINANCE_ALPHA: dstFormat = GraphicsContext3D::DataFormatRA16F; break; + case GraphicsContext3D::SRGB: + dstFormat = GraphicsContext3D::DataFormatRGB16F; + break; + case GraphicsContext3D::SRGB_ALPHA: + dstFormat = GraphicsContext3D::DataFormatRGBA16F; + break; default: ASSERT_NOT_REACHED(); } @@ -113,6 +125,12 @@ GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum case GraphicsContext3D::LUMINANCE_ALPHA: dstFormat = GraphicsContext3D::DataFormatRA32F; break; + case GraphicsContext3D::SRGB: + dstFormat = GraphicsContext3D::DataFormatRGB32F; + break; + case GraphicsContext3D::SRGB_ALPHA: + dstFormat = GraphicsContext3D::DataFormatRGBA32F; + break; default: ASSERT_NOT_REACHED(); } @@ -129,9 +147,9 @@ bool GraphicsContext3D::texImage2DResourceSafe(GC3Denum target, GC3Dint level, G { ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8); std::unique_ptr<unsigned char[]> zero; - if (!isResourceSafe() && width > 0 && height > 0) { + if (width > 0 && height > 0) { unsigned int size; - GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, 0); + GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, nullptr); if (error != GraphicsContext3D::NO_ERROR) { synthesizeGLError(error); return false; @@ -149,32 +167,47 @@ bool GraphicsContext3D::texImage2DResourceSafe(GC3Denum target, GC3Dint level, G bool GraphicsContext3D::computeFormatAndTypeParameters(GC3Denum format, GC3Denum type, unsigned int* componentsPerPixel, unsigned int* bytesPerComponent) { switch (format) { + case GraphicsContext3D::RED: + case GraphicsContext3D::RED_INTEGER: case GraphicsContext3D::ALPHA: case GraphicsContext3D::LUMINANCE: case GraphicsContext3D::DEPTH_COMPONENT: case GraphicsContext3D::DEPTH_STENCIL: *componentsPerPixel = 1; break; + case GraphicsContext3D::RG: + case GraphicsContext3D::RG_INTEGER: case GraphicsContext3D::LUMINANCE_ALPHA: *componentsPerPixel = 2; break; case GraphicsContext3D::RGB: + case GraphicsContext3D::RGB_INTEGER: + case Extensions3D::SRGB_EXT: *componentsPerPixel = 3; break; case GraphicsContext3D::RGBA: + case GraphicsContext3D::RGBA_INTEGER: case Extensions3D::BGRA_EXT: // GL_EXT_texture_format_BGRA8888 + case Extensions3D::SRGB_ALPHA_EXT: *componentsPerPixel = 4; break; default: return false; } + switch (type) { case GraphicsContext3D::UNSIGNED_BYTE: *bytesPerComponent = sizeof(GC3Dubyte); break; + case GraphicsContext3D::BYTE: + *bytesPerComponent = sizeof(GC3Dbyte); + break; case GraphicsContext3D::UNSIGNED_SHORT: *bytesPerComponent = sizeof(GC3Dushort); break; + case GraphicsContext3D::SHORT: + *bytesPerComponent = sizeof(GC3Dshort); + break; case GraphicsContext3D::UNSIGNED_SHORT_5_6_5: case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4: case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1: @@ -182,21 +215,113 @@ bool GraphicsContext3D::computeFormatAndTypeParameters(GC3Denum format, GC3Denum *bytesPerComponent = sizeof(GC3Dushort); break; case GraphicsContext3D::UNSIGNED_INT_24_8: + case GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV: + case GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV: + case GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV: + *componentsPerPixel = 1; + *bytesPerComponent = sizeof(GC3Duint); + break; case GraphicsContext3D::UNSIGNED_INT: *bytesPerComponent = sizeof(GC3Duint); break; + case GraphicsContext3D::INT: + *bytesPerComponent = sizeof(GC3Dint); + break; case GraphicsContext3D::FLOAT: // OES_texture_float *bytesPerComponent = sizeof(GC3Dfloat); break; + case GraphicsContext3D::HALF_FLOAT: case GraphicsContext3D::HALF_FLOAT_OES: // OES_texture_half_float *bytesPerComponent = sizeof(GC3Dhalffloat); break; + case GraphicsContext3D::FLOAT_32_UNSIGNED_INT_24_8_REV: + *bytesPerComponent = sizeof(GC3Dfloat) + sizeof(GC3Duint); + break; default: return false; } return true; } +bool GraphicsContext3D::possibleFormatAndTypeForInternalFormat(GC3Denum internalFormat, GC3Denum& format, GC3Denum& type) +{ +#define POSSIBLE_FORMAT_TYPE_CASE(internalFormatMacro, formatMacro, typeMacro) case internalFormatMacro: \ + format = formatMacro; \ + type = GraphicsContext3D::typeMacro; \ + break; + + switch (internalFormat) { + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB , GraphicsContext3D::RGB , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA , GraphicsContext3D::RGBA , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::LUMINANCE_ALPHA , GraphicsContext3D::LUMINANCE_ALPHA, UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::LUMINANCE , GraphicsContext3D::LUMINANCE , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::ALPHA , GraphicsContext3D::ALPHA , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8 , GraphicsContext3D::RED , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8_SNORM , GraphicsContext3D::RED , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R16F , GraphicsContext3D::RED , HALF_FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R32F , GraphicsContext3D::RED , FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8UI , GraphicsContext3D::RED_INTEGER , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R8I , GraphicsContext3D::RED_INTEGER , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R16UI , GraphicsContext3D::RED_INTEGER , UNSIGNED_SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R16I , GraphicsContext3D::RED_INTEGER , SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R32UI , GraphicsContext3D::RED_INTEGER , UNSIGNED_INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R32I , GraphicsContext3D::RED_INTEGER , INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8 , GraphicsContext3D::RG , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8_SNORM , GraphicsContext3D::RG , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG16F , GraphicsContext3D::RG , HALF_FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG32F , GraphicsContext3D::RG , FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8UI , GraphicsContext3D::RG_INTEGER , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG8I , GraphicsContext3D::RG_INTEGER , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG16UI , GraphicsContext3D::RG_INTEGER , UNSIGNED_SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG16I , GraphicsContext3D::RG_INTEGER , SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG32UI , GraphicsContext3D::RG_INTEGER , UNSIGNED_INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RG32I , GraphicsContext3D::RG_INTEGER , INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8 , GraphicsContext3D::RGB , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::SRGB8 , GraphicsContext3D::RGB , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB565 , GraphicsContext3D::RGB , UNSIGNED_SHORT_5_6_5 ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8_SNORM , GraphicsContext3D::RGB , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::R11F_G11F_B10F , GraphicsContext3D::RGB , UNSIGNED_INT_10F_11F_11F_REV ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB9_E5 , GraphicsContext3D::RGB , UNSIGNED_INT_5_9_9_9_REV ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB16F , GraphicsContext3D::RGB , HALF_FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB32F , GraphicsContext3D::RGB , FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8UI , GraphicsContext3D::RGB_INTEGER , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB8I , GraphicsContext3D::RGB_INTEGER , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB16UI , GraphicsContext3D::RGB_INTEGER , UNSIGNED_SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB16I , GraphicsContext3D::RGB_INTEGER , SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB32UI , GraphicsContext3D::RGB_INTEGER , UNSIGNED_INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB32I , GraphicsContext3D::RGB_INTEGER , INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8 , GraphicsContext3D::RGBA , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::SRGB8_ALPHA8 , GraphicsContext3D::RGBA , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8_SNORM , GraphicsContext3D::RGBA , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB5_A1 , GraphicsContext3D::RGBA , UNSIGNED_SHORT_5_5_5_1 ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA4 , GraphicsContext3D::RGBA , UNSIGNED_SHORT_4_4_4_4 ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB10_A2 , GraphicsContext3D::RGBA , UNSIGNED_INT_2_10_10_10_REV ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA16F , GraphicsContext3D::RGBA , HALF_FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA32F , GraphicsContext3D::RGBA , FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA8I , GraphicsContext3D::RGBA_INTEGER , BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGB10_A2UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_INT_2_10_10_10_REV ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA16UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA16I , GraphicsContext3D::RGBA_INTEGER , SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA32I , GraphicsContext3D::RGBA_INTEGER , INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::RGBA32UI , GraphicsContext3D::RGBA_INTEGER , UNSIGNED_INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT16 , GraphicsContext3D::DEPTH_COMPONENT, UNSIGNED_SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT , GraphicsContext3D::DEPTH_COMPONENT, UNSIGNED_SHORT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT24 , GraphicsContext3D::DEPTH_COMPONENT, UNSIGNED_INT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_COMPONENT32F, GraphicsContext3D::DEPTH_COMPONENT, FLOAT ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH_STENCIL , GraphicsContext3D::DEPTH_STENCIL , UNSIGNED_INT_24_8 ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH24_STENCIL8 , GraphicsContext3D::DEPTH_STENCIL , UNSIGNED_INT_24_8 ); + POSSIBLE_FORMAT_TYPE_CASE(GraphicsContext3D::DEPTH32F_STENCIL8 , GraphicsContext3D::DEPTH_STENCIL , FLOAT_32_UNSIGNED_INT_24_8_REV); + POSSIBLE_FORMAT_TYPE_CASE(Extensions3D::SRGB_EXT , Extensions3D::SRGB_EXT , UNSIGNED_BYTE ); + POSSIBLE_FORMAT_TYPE_CASE(Extensions3D::SRGB_ALPHA_EXT , Extensions3D::SRGB_ALPHA_EXT , UNSIGNED_BYTE ); + default: + return false; + } +#undef POSSIBLE_FORMAT_TYPE_CASE + + return true; +} + GC3Denum GraphicsContext3D::computeImageSizeInBytes(GC3Denum format, GC3Denum type, GC3Dsizei width, GC3Dsizei height, GC3Dint alignment, unsigned int* imageSizeInBytes, unsigned int* paddingInBytes) { ASSERT(imageSizeInBytes); @@ -204,7 +329,7 @@ GC3Denum GraphicsContext3D::computeImageSizeInBytes(GC3Denum format, GC3Denum ty if (width < 0 || height < 0) return GraphicsContext3D::INVALID_VALUE; unsigned int bytesPerComponent, componentsPerPixel; - if (!computeFormatAndTypeParameters(format, type, &bytesPerComponent, &componentsPerPixel)) + if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel, &bytesPerComponent)) return GraphicsContext3D::INVALID_ENUM; if (!width || !height) { *imageSizeInBytes = 0; @@ -241,14 +366,14 @@ GraphicsContext3D::ImageExtractor::ImageExtractor(Image* image, ImageHtmlDomSour m_extractSucceeded = extractImage(premultiplyAlpha, ignoreGammaAndColorProfile); } -bool GraphicsContext3D::packImageData( Image* image, const void* pixels, GC3Denum format, GC3Denum type, bool flipY, AlphaOp alphaOp, DataFormat sourceFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, Vector<uint8_t>& data) +bool GraphicsContext3D::packImageData(Image* image, const void* pixels, GC3Denum format, GC3Denum type, bool flipY, AlphaOp alphaOp, DataFormat sourceFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, Vector<uint8_t>& data) { if (!pixels) return false; unsigned packedSize; // Output data is tightly packed (alignment == 1). - if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR) + if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, nullptr) != GraphicsContext3D::NO_ERROR) return false; data.resize(packedSize); @@ -268,7 +393,7 @@ bool GraphicsContext3D::extractImageData(ImageData* imageData, GC3Denum format, unsigned int packedSize; // Output data is tightly packed (alignment == 1). - if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR) + if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, nullptr) != GraphicsContext3D::NO_ERROR) return false; data.resize(packedSize); @@ -341,6 +466,29 @@ bool GraphicsContext3D::packPixels(const uint8_t* sourceData, DataFormat sourceD int remainder = sourceUnpackAlignment ? (validSrc % sourceUnpackAlignment) : 0; int srcStride = remainder ? (validSrc + sourceUnpackAlignment - remainder) : validSrc; + // FIXME: Implement packing pixels to WebGL 2 formats + switch (destinationFormat) { + case GraphicsContext3D::RED: + case GraphicsContext3D::RED_INTEGER: + case GraphicsContext3D::RG: + case GraphicsContext3D::RG_INTEGER: + case GraphicsContext3D::RGB_INTEGER: + case GraphicsContext3D::RGBA_INTEGER: + case GraphicsContext3D::DEPTH_COMPONENT: + case GraphicsContext3D::DEPTH_STENCIL: + return false; + } + switch (destinationType) { + case GraphicsContext3D::BYTE: + case GraphicsContext3D::SHORT: + case GraphicsContext3D::INT: + case GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV: + case GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV: + case GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV: + case GraphicsContext3D::UNSIGNED_INT_24_8: + return false; + } + DataFormat dstDataFormat = getDataFormat(destinationFormat, destinationType); int dstStride = width * TexelBytesForFormat(dstDataFormat); if (flipY) { @@ -404,21 +552,73 @@ unsigned GraphicsContext3D::getClearBitsByAttachmentType(GC3Denum attachment) unsigned GraphicsContext3D::getClearBitsByFormat(GC3Denum format) { switch (format) { - case GraphicsContext3D::ALPHA: - case GraphicsContext3D::LUMINANCE: - case GraphicsContext3D::LUMINANCE_ALPHA: case GraphicsContext3D::RGB: - case GraphicsContext3D::RGB565: case GraphicsContext3D::RGBA: - case GraphicsContext3D::RGBA4: + case GraphicsContext3D::LUMINANCE_ALPHA: + case GraphicsContext3D::LUMINANCE: + case GraphicsContext3D::ALPHA: + case GraphicsContext3D::R8: + case GraphicsContext3D::R8_SNORM: + case GraphicsContext3D::R16F: + case GraphicsContext3D::R32F: + case GraphicsContext3D::R8UI: + case GraphicsContext3D::R8I: + case GraphicsContext3D::R16UI: + case GraphicsContext3D::R16I: + case GraphicsContext3D::R32UI: + case GraphicsContext3D::R32I: + case GraphicsContext3D::RG8: + case GraphicsContext3D::RG8_SNORM: + case GraphicsContext3D::RG16F: + case GraphicsContext3D::RG32F: + case GraphicsContext3D::RG8UI: + case GraphicsContext3D::RG8I: + case GraphicsContext3D::RG16UI: + case GraphicsContext3D::RG16I: + case GraphicsContext3D::RG32UI: + case GraphicsContext3D::RG32I: + case GraphicsContext3D::RGB8: + case GraphicsContext3D::SRGB8: + case GraphicsContext3D::RGB565: + case GraphicsContext3D::RGB8_SNORM: + case GraphicsContext3D::R11F_G11F_B10F: + case GraphicsContext3D::RGB9_E5: + case GraphicsContext3D::RGB16F: + case GraphicsContext3D::RGB32F: + case GraphicsContext3D::RGB8UI: + case GraphicsContext3D::RGB8I: + case GraphicsContext3D::RGB16UI: + case GraphicsContext3D::RGB16I: + case GraphicsContext3D::RGB32UI: + case GraphicsContext3D::RGB32I: + case GraphicsContext3D::RGBA8: + case GraphicsContext3D::SRGB8_ALPHA8: + case GraphicsContext3D::RGBA8_SNORM: case GraphicsContext3D::RGB5_A1: + case GraphicsContext3D::RGBA4: + case GraphicsContext3D::RGB10_A2: + case GraphicsContext3D::RGBA16F: + case GraphicsContext3D::RGBA32F: + case GraphicsContext3D::RGBA8UI: + case GraphicsContext3D::RGBA8I: + case GraphicsContext3D::RGB10_A2UI: + case GraphicsContext3D::RGBA16UI: + case GraphicsContext3D::RGBA16I: + case GraphicsContext3D::RGBA32I: + case GraphicsContext3D::RGBA32UI: + case Extensions3D::SRGB_EXT: + case Extensions3D::SRGB_ALPHA_EXT: return GraphicsContext3D::COLOR_BUFFER_BIT; case GraphicsContext3D::DEPTH_COMPONENT16: + case GraphicsContext3D::DEPTH_COMPONENT24: + case GraphicsContext3D::DEPTH_COMPONENT32F: case GraphicsContext3D::DEPTH_COMPONENT: return GraphicsContext3D::DEPTH_BUFFER_BIT; case GraphicsContext3D::STENCIL_INDEX8: return GraphicsContext3D::STENCIL_BUFFER_BIT; case GraphicsContext3D::DEPTH_STENCIL: + case GraphicsContext3D::DEPTH24_STENCIL8: + case GraphicsContext3D::DEPTH32F_STENCIL8: return GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT; default: return 0; @@ -436,10 +636,12 @@ unsigned GraphicsContext3D::getChannelBitsByFormat(GC3Denum format) return ChannelRGBA; case GraphicsContext3D::RGB: case GraphicsContext3D::RGB565: + case Extensions3D::SRGB_EXT: return ChannelRGB; case GraphicsContext3D::RGBA: case GraphicsContext3D::RGBA4: case GraphicsContext3D::RGB5_A1: + case Extensions3D::SRGB_ALPHA_EXT: return ChannelRGBA; case GraphicsContext3D::DEPTH_COMPONENT16: case GraphicsContext3D::DEPTH_COMPONENT: @@ -453,6 +655,12 @@ unsigned GraphicsContext3D::getChannelBitsByFormat(GC3Denum format) } } +#if !PLATFORM(MAC) +void GraphicsContext3D::setContextVisibility(bool) +{ +} +#endif + } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.h b/Source/WebCore/platform/graphics/GraphicsContext3D.h index 132c5e9e2..75b7e1f92 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext3D.h +++ b/Source/WebCore/platform/graphics/GraphicsContext3D.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,10 +23,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GraphicsContext3D_h -#define GraphicsContext3D_h +#pragma once #include "ANGLEWebKitBridge.h" +#include "GraphicsContext3DAttributes.h" #include "GraphicsTypes3D.h" #include "Image.h" #include "IntRect.h" @@ -43,16 +43,14 @@ #endif // FIXME: Find a better way to avoid the name confliction for NO_ERROR. -#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS)) +#if PLATFORM(WIN) #undef NO_ERROR -#endif - -#if PLATFORM(GTK) +#elif PLATFORM(GTK) // This define is from the X11 headers, but it's used below, so we must undefine it. #undef VERSION #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) #if PLATFORM(IOS) #include <OpenGLES/ES2/gl.h> #ifdef __OBJC__ @@ -62,7 +60,7 @@ #include <wtf/RetainPtr.h> OBJC_CLASS CALayer; OBJC_CLASS WebGLLayer; -#elif PLATFORM(GTK) || PLATFORM(EFL) +#elif PLATFORM(GTK) || PLATFORM(WIN_CAIRO) typedef unsigned int GLuint; #endif @@ -86,7 +84,6 @@ const PlatformGraphicsContext3D NullPlatformGraphicsContext3D = 0; const Platform3DObject NullPlatform3DObject = 0; namespace WebCore { -class DrawingBuffer; class Extensions3D; #if USE(OPENGL_ES_2) class Extensions3DOpenGLES; @@ -100,9 +97,13 @@ class ImageSource; class ImageData; class IntRect; class IntSize; +class WebGLRenderingContextBase; #if USE(CAIRO) class PlatformContextCairo; #endif +#if USE(TEXTURE_MAPPER) +class TextureMapperGC3DPlatformLayer; +#endif typedef WTF::HashMap<CString, uint64_t> ShaderNameHash; @@ -117,6 +118,7 @@ class GraphicsContext3DPrivate; class GraphicsContext3D : public RefCounted<GraphicsContext3D> { public: enum { + // WebGL 1 constants DEPTH_BUFFER_BIT = 0x00000100, STENCIL_BUFFER_BIT = 0x00000400, COLOR_BUFFER_BIT = 0x00004000, @@ -371,6 +373,8 @@ public: VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A, VERTEX_ATTRIB_ARRAY_POINTER = 0x8645, VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F, + IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A, + IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B, COMPILE_STATUS = 0x8B81, INFO_LOG_LENGTH = 0x8B84, SHADER_SOURCE_LENGTH = 0x8B88, @@ -391,9 +395,6 @@ public: DEPTH_COMPONENT16 = 0x81A5, STENCIL_INDEX = 0x1901, STENCIL_INDEX8 = 0x8D48, - DEPTH_STENCIL = 0x84F9, - UNSIGNED_INT_24_8 = 0x84FA, - DEPTH24_STENCIL8 = 0x88F0, RENDERBUFFER_WIDTH = 0x8D42, RENDERBUFFER_HEIGHT = 0x8D43, RENDERBUFFER_INTERNAL_FORMAT = 0x8D44, @@ -410,7 +411,6 @@ public: COLOR_ATTACHMENT0 = 0x8CE0, DEPTH_ATTACHMENT = 0x8D00, STENCIL_ATTACHMENT = 0x8D20, - DEPTH_STENCIL_ATTACHMENT = 0x821A, NONE = 0, FRAMEBUFFER_COMPLETE = 0x8CD5, FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6, @@ -428,37 +428,297 @@ public: CONTEXT_LOST_WEBGL = 0x9242, UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243, BROWSER_DEFAULT_WEBGL = 0x9244, - VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE - }; - - // Context creation attributes. - struct Attributes { - Attributes() - : alpha(true) - , depth(true) - , stencil(false) - , antialias(true) - , premultipliedAlpha(true) - , preserveDrawingBuffer(false) - , noExtensions(false) - , shareResources(true) - , preferDiscreteGPU(false) - , multithreaded(false) - , forceSoftwareRenderer(false) - { - } - - bool alpha; - bool depth; - bool stencil; - bool antialias; - bool premultipliedAlpha; - bool preserveDrawingBuffer; - bool noExtensions; - bool shareResources; - bool preferDiscreteGPU; - bool multithreaded; - bool forceSoftwareRenderer; + VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE, + + // WebGL2 constants + READ_BUFFER = 0x0C02, + UNPACK_ROW_LENGTH = 0x0CF2, + UNPACK_SKIP_ROWS = 0x0CF3, + UNPACK_SKIP_PIXELS = 0x0CF4, + PACK_ROW_LENGTH = 0x0D02, + PACK_SKIP_ROWS = 0x0D03, + PACK_SKIP_PIXELS = 0x0D04, + COLOR = 0x1800, + DEPTH = 0x1801, + STENCIL = 0x1802, + RED = 0x1903, + RGB8 = 0x8051, + RGBA8 = 0x8058, + RGB10_A2 = 0x8059, + TEXTURE_BINDING_3D = 0x806A, + UNPACK_SKIP_IMAGES = 0x806D, + UNPACK_IMAGE_HEIGHT = 0x806E, + TEXTURE_3D = 0x806F, + TEXTURE_WRAP_R = 0x8072, + MAX_3D_TEXTURE_SIZE = 0x8073, + UNSIGNED_INT_2_10_10_10_REV = 0x8368, + MAX_ELEMENTS_VERTICES = 0x80E8, + MAX_ELEMENTS_INDICES = 0x80E9, + TEXTURE_MIN_LOD = 0x813A, + TEXTURE_MAX_LOD = 0x813B, + TEXTURE_BASE_LEVEL = 0x813C, + TEXTURE_MAX_LEVEL = 0x813D, + MIN = 0x8007, + MAX = 0x8008, + DEPTH_COMPONENT24 = 0x81A6, + MAX_TEXTURE_LOD_BIAS = 0x84FD, + TEXTURE_COMPARE_MODE = 0x884C, + TEXTURE_COMPARE_FUNC = 0x884D, + CURRENT_QUERY = 0x8865, + QUERY_RESULT = 0x8866, + QUERY_RESULT_AVAILABLE = 0x8867, + STREAM_READ = 0x88E1, + STREAM_COPY = 0x88E2, + STATIC_READ = 0x88E5, + STATIC_COPY = 0x88E6, + DYNAMIC_READ = 0x88E9, + DYNAMIC_COPY = 0x88EA, + MAX_DRAW_BUFFERS = 0x8824, + DRAW_BUFFER0 = 0x8825, + DRAW_BUFFER1 = 0x8826, + DRAW_BUFFER2 = 0x8827, + DRAW_BUFFER3 = 0x8828, + DRAW_BUFFER4 = 0x8829, + DRAW_BUFFER5 = 0x882A, + DRAW_BUFFER6 = 0x882B, + DRAW_BUFFER7 = 0x882C, + DRAW_BUFFER8 = 0x882D, + DRAW_BUFFER9 = 0x882E, + DRAW_BUFFER10 = 0x882F, + DRAW_BUFFER11 = 0x8830, + DRAW_BUFFER12 = 0x8831, + DRAW_BUFFER13 = 0x8832, + DRAW_BUFFER14 = 0x8833, + DRAW_BUFFER15 = 0x8834, + MAX_FRAGMENT_UNIFORM_COMPONENTS = 0x8B49, + MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A, + SAMPLER_3D = 0x8B5F, + SAMPLER_2D_SHADOW = 0x8B62, + FRAGMENT_SHADER_DERIVATIVE_HINT = 0x8B8B, + PIXEL_PACK_BUFFER = 0x88EB, + PIXEL_UNPACK_BUFFER = 0x88EC, + PIXEL_PACK_BUFFER_BINDING = 0x88ED, + PIXEL_UNPACK_BUFFER_BINDING = 0x88EF, + FLOAT_MAT2x3 = 0x8B65, + FLOAT_MAT2x4 = 0x8B66, + FLOAT_MAT3x2 = 0x8B67, + FLOAT_MAT3x4 = 0x8B68, + FLOAT_MAT4x2 = 0x8B69, + FLOAT_MAT4x3 = 0x8B6A, + SRGB = 0x8C40, + SRGB8 = 0x8C41, + SRGB_ALPHA = 0x8C42, + SRGB8_ALPHA8 = 0x8C43, + COMPARE_REF_TO_TEXTURE = 0x884E, + RGBA32F = 0x8814, + RGB32F = 0x8815, + RGBA16F = 0x881A, + RGB16F = 0x881B, + VERTEX_ATTRIB_ARRAY_INTEGER = 0x88FD, + MAX_ARRAY_TEXTURE_LAYERS = 0x88FF, + MIN_PROGRAM_TEXEL_OFFSET = 0x8904, + MAX_PROGRAM_TEXEL_OFFSET = 0x8905, + MAX_VARYING_COMPONENTS = 0x8B4B, + TEXTURE_2D_ARRAY = 0x8C1A, + TEXTURE_BINDING_2D_ARRAY = 0x8C1D, + R11F_G11F_B10F = 0x8C3A, + UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, + RGB9_E5 = 0x8C3D, + UNSIGNED_INT_5_9_9_9_REV = 0x8C3E, + TRANSFORM_FEEDBACK_BUFFER_MODE = 0x8C7F, + MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = 0x8C80, + TRANSFORM_FEEDBACK_VARYINGS = 0x8C83, + TRANSFORM_FEEDBACK_BUFFER_START = 0x8C84, + TRANSFORM_FEEDBACK_BUFFER_SIZE = 0x8C85, + TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = 0x8C88, + RASTERIZER_DISCARD = 0x8C89, + MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 0x8C8A, + MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = 0x8C8B, + INTERLEAVED_ATTRIBS = 0x8C8C, + SEPARATE_ATTRIBS = 0x8C8D, + TRANSFORM_FEEDBACK_BUFFER = 0x8C8E, + TRANSFORM_FEEDBACK_BUFFER_BINDING = 0x8C8F, + RGBA32UI = 0x8D70, + RGB32UI = 0x8D71, + RGBA16UI = 0x8D76, + RGB16UI = 0x8D77, + RGBA8UI = 0x8D7C, + RGB8UI = 0x8D7D, + RGBA32I = 0x8D82, + RGB32I = 0x8D83, + RGBA16I = 0x8D88, + RGB16I = 0x8D89, + RGBA8I = 0x8D8E, + RGB8I = 0x8D8F, + RED_INTEGER = 0x8D94, + RGB_INTEGER = 0x8D98, + RGBA_INTEGER = 0x8D99, + SAMPLER_2D_ARRAY = 0x8DC1, + SAMPLER_2D_ARRAY_SHADOW = 0x8DC4, + SAMPLER_CUBE_SHADOW = 0x8DC5, + UNSIGNED_INT_VEC2 = 0x8DC6, + UNSIGNED_INT_VEC3 = 0x8DC7, + UNSIGNED_INT_VEC4 = 0x8DC8, + INT_SAMPLER_2D = 0x8DCA, + INT_SAMPLER_3D = 0x8DCB, + INT_SAMPLER_CUBE = 0x8DCC, + INT_SAMPLER_2D_ARRAY = 0x8DCF, + UNSIGNED_INT_SAMPLER_2D = 0x8DD2, + UNSIGNED_INT_SAMPLER_3D = 0x8DD3, + UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4, + UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7, + DEPTH_COMPONENT32F = 0x8CAC, + DEPTH32F_STENCIL8 = 0x8CAD, + FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD, + FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210, + FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE = 0x8211, + FRAMEBUFFER_ATTACHMENT_RED_SIZE = 0x8212, + FRAMEBUFFER_ATTACHMENT_GREEN_SIZE = 0x8213, + FRAMEBUFFER_ATTACHMENT_BLUE_SIZE = 0x8214, + FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE = 0x8215, + FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE = 0x8216, + FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE = 0x8217, + FRAMEBUFFER_DEFAULT = 0x8218, + DEPTH_STENCIL_ATTACHMENT = 0x821A, + DEPTH_STENCIL = 0x84F9, + UNSIGNED_INT_24_8 = 0x84FA, + DEPTH24_STENCIL8 = 0x88F0, + UNSIGNED_NORMALIZED = 0x8C17, + DRAW_FRAMEBUFFER_BINDING = 0x8CA6, /* Same as FRAMEBUFFER_BINDING */ + READ_FRAMEBUFFER = 0x8CA8, + DRAW_FRAMEBUFFER = 0x8CA9, + READ_FRAMEBUFFER_BINDING = 0x8CAA, + RENDERBUFFER_SAMPLES = 0x8CAB, + FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = 0x8CD4, + MAX_COLOR_ATTACHMENTS = 0x8CDF, + COLOR_ATTACHMENT1 = 0x8CE1, + COLOR_ATTACHMENT2 = 0x8CE2, + COLOR_ATTACHMENT3 = 0x8CE3, + COLOR_ATTACHMENT4 = 0x8CE4, + COLOR_ATTACHMENT5 = 0x8CE5, + COLOR_ATTACHMENT6 = 0x8CE6, + COLOR_ATTACHMENT7 = 0x8CE7, + COLOR_ATTACHMENT8 = 0x8CE8, + COLOR_ATTACHMENT9 = 0x8CE9, + COLOR_ATTACHMENT10 = 0x8CEA, + COLOR_ATTACHMENT11 = 0x8CEB, + COLOR_ATTACHMENT12 = 0x8CEC, + COLOR_ATTACHMENT13 = 0x8CED, + COLOR_ATTACHMENT14 = 0x8CEE, + COLOR_ATTACHMENT15 = 0x8CEF, + FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56, + MAX_SAMPLES = 0x8D57, + HALF_FLOAT = 0x140B, + RG = 0x8227, + RG_INTEGER = 0x8228, + R8 = 0x8229, + RG8 = 0x822B, + R16F = 0x822D, + R32F = 0x822E, + RG16F = 0x822F, + RG32F = 0x8230, + R8I = 0x8231, + R8UI = 0x8232, + R16I = 0x8233, + R16UI = 0x8234, + R32I = 0x8235, + R32UI = 0x8236, + RG8I = 0x8237, + RG8UI = 0x8238, + RG16I = 0x8239, + RG16UI = 0x823A, + RG32I = 0x823B, + RG32UI = 0x823C, + VERTEX_ARRAY_BINDING = 0x85B5, + R8_SNORM = 0x8F94, + RG8_SNORM = 0x8F95, + RGB8_SNORM = 0x8F96, + RGBA8_SNORM = 0x8F97, + SIGNED_NORMALIZED = 0x8F9C, + COPY_READ_BUFFER = 0x8F36, + COPY_WRITE_BUFFER = 0x8F37, + COPY_READ_BUFFER_BINDING = 0x8F36, /* Same as COPY_READ_BUFFER */ + COPY_WRITE_BUFFER_BINDING = 0x8F37, /* Same as COPY_WRITE_BUFFER */ + UNIFORM_BUFFER = 0x8A11, + UNIFORM_BUFFER_BINDING = 0x8A28, + UNIFORM_BUFFER_START = 0x8A29, + UNIFORM_BUFFER_SIZE = 0x8A2A, + MAX_VERTEX_UNIFORM_BLOCKS = 0x8A2B, + MAX_FRAGMENT_UNIFORM_BLOCKS = 0x8A2D, + MAX_COMBINED_UNIFORM_BLOCKS = 0x8A2E, + MAX_UNIFORM_BUFFER_BINDINGS = 0x8A2F, + MAX_UNIFORM_BLOCK_SIZE = 0x8A30, + MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = 0x8A31, + MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = 0x8A33, + UNIFORM_BUFFER_OFFSET_ALIGNMENT = 0x8A34, + ACTIVE_UNIFORM_BLOCKS = 0x8A36, + UNIFORM_TYPE = 0x8A37, + UNIFORM_SIZE = 0x8A38, + UNIFORM_BLOCK_INDEX = 0x8A3A, + UNIFORM_OFFSET = 0x8A3B, + UNIFORM_ARRAY_STRIDE = 0x8A3C, + UNIFORM_MATRIX_STRIDE = 0x8A3D, + UNIFORM_IS_ROW_MAJOR = 0x8A3E, + UNIFORM_BLOCK_BINDING = 0x8A3F, + UNIFORM_BLOCK_DATA_SIZE = 0x8A40, + UNIFORM_BLOCK_ACTIVE_UNIFORMS = 0x8A42, + UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8A43, + UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8A44, + UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8A46, + INVALID_INDEX = 0xFFFFFFFF, + MAX_VERTEX_OUTPUT_COMPONENTS = 0x9122, + MAX_FRAGMENT_INPUT_COMPONENTS = 0x9125, + MAX_SERVER_WAIT_TIMEOUT = 0x9111, + OBJECT_TYPE = 0x9112, + SYNC_CONDITION = 0x9113, + SYNC_STATUS = 0x9114, + SYNC_FLAGS = 0x9115, + SYNC_FENCE = 0x9116, + SYNC_GPU_COMMANDS_COMPLETE = 0x9117, + UNSIGNALED = 0x9118, + SIGNALED = 0x9119, + ALREADY_SIGNALED = 0x911A, + TIMEOUT_EXPIRED = 0x911B, + CONDITION_SATISFIED = 0x911C, +#if PLATFORM(WIN) + WAIT_FAILED_WIN = 0x911D, +#else + WAIT_FAILED = 0x911D, +#endif + SYNC_FLUSH_COMMANDS_BIT = 0x00000001, + VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88FE, + ANY_SAMPLES_PASSED = 0x8C2F, + ANY_SAMPLES_PASSED_CONSERVATIVE = 0x8D6A, + SAMPLER_BINDING = 0x8919, + RGB10_A2UI = 0x906F, + TEXTURE_SWIZZLE_R = 0x8E42, + TEXTURE_SWIZZLE_G = 0x8E43, + TEXTURE_SWIZZLE_B = 0x8E44, + TEXTURE_SWIZZLE_A = 0x8E45, + GREEN = 0x1904, + BLUE = 0x1905, + INT_2_10_10_10_REV = 0x8D9F, + TRANSFORM_FEEDBACK = 0x8E22, + TRANSFORM_FEEDBACK_PAUSED = 0x8E23, + TRANSFORM_FEEDBACK_ACTIVE = 0x8E24, + TRANSFORM_FEEDBACK_BINDING = 0x8E25, + COMPRESSED_R11_EAC = 0x9270, + COMPRESSED_SIGNED_R11_EAC = 0x9271, + COMPRESSED_RG11_EAC = 0x9272, + COMPRESSED_SIGNED_RG11_EAC = 0x9273, + COMPRESSED_RGB8_ETC2 = 0x9274, + COMPRESSED_SRGB8_ETC2 = 0x9275, + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276, + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277, + COMPRESSED_RGBA8_ETC2_EAC = 0x9278, + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279, + TEXTURE_IMMUTABLE_FORMAT = 0x912F, + MAX_ELEMENT_INDEX = 0x8D6B, + NUM_SAMPLE_COUNTS = 0x9380, + TEXTURE_IMMUTABLE_LEVELS = 0x82DF, + + // OpenGL ES 3 constants + MAP_READ_BIT = 0x0001 }; enum RenderStyle { @@ -479,26 +739,25 @@ public: virtual ~ErrorMessageCallback() { } }; - void setContextLostCallback(PassOwnPtr<ContextLostCallback>); - void setErrorMessageCallback(PassOwnPtr<ErrorMessageCallback>); + void setContextLostCallback(std::unique_ptr<ContextLostCallback>); + void setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>); - static PassRefPtr<GraphicsContext3D> create(Attributes, HostWindow*, RenderStyle = RenderOffscreen); - static PassRefPtr<GraphicsContext3D> createForCurrentGLContext(); + static RefPtr<GraphicsContext3D> create(GraphicsContext3DAttributes, HostWindow*, RenderStyle = RenderOffscreen); + static RefPtr<GraphicsContext3D> createForCurrentGLContext(); ~GraphicsContext3D(); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) PlatformGraphicsContext3D platformGraphicsContext3D() const { return m_contextObj; } Platform3DObject platformTexture() const { return m_compositorTexture; } CALayer* platformLayer() const { return reinterpret_cast<CALayer*>(m_webGLLayer.get()); } #else PlatformGraphicsContext3D platformGraphicsContext3D(); Platform3DObject platformTexture() const; -#if USE(ACCELERATED_COMPOSITING) PlatformLayer* platformLayer() const; #endif -#endif bool makeContextCurrent(); + void setWebGLContext(WebGLRenderingContextBase* base) { m_webglContext = base; } // With multisampling on, blit from multisampleFBO to regular FBO. void prepareTexture(); @@ -538,6 +797,8 @@ public: unsigned int* imageSizeInBytes, unsigned int* paddingInBytes); + static bool possibleFormatAndTypeForInternalFormat(GC3Denum internalFormat, GC3Denum& format, GC3Denum& type); + // Extracts the contents of the given ImageData into the passed Vector, // packing the pixel data according to the given format and type, // and obeying the flipY and premultiplyAlpha flags. Returns true @@ -611,45 +872,55 @@ public: ALWAYS_INLINE static bool hasAlpha(DataFormat format) { - return format == GraphicsContext3D::DataFormatA8 - || format == GraphicsContext3D::DataFormatA16F - || format == GraphicsContext3D::DataFormatA32F - || format == GraphicsContext3D::DataFormatRA8 - || format == GraphicsContext3D::DataFormatAR8 - || format == GraphicsContext3D::DataFormatRA16F - || format == GraphicsContext3D::DataFormatRA32F - || format == GraphicsContext3D::DataFormatRGBA8 - || format == GraphicsContext3D::DataFormatBGRA8 - || format == GraphicsContext3D::DataFormatARGB8 - || format == GraphicsContext3D::DataFormatABGR8 - || format == GraphicsContext3D::DataFormatRGBA16F - || format == GraphicsContext3D::DataFormatRGBA32F - || format == GraphicsContext3D::DataFormatRGBA4444 - || format == GraphicsContext3D::DataFormatRGBA5551; + switch (format) { + case GraphicsContext3D::DataFormatA8: + case GraphicsContext3D::DataFormatA16F: + case GraphicsContext3D::DataFormatA32F: + case GraphicsContext3D::DataFormatRA8: + case GraphicsContext3D::DataFormatAR8: + case GraphicsContext3D::DataFormatRA16F: + case GraphicsContext3D::DataFormatRA32F: + case GraphicsContext3D::DataFormatRGBA8: + case GraphicsContext3D::DataFormatBGRA8: + case GraphicsContext3D::DataFormatARGB8: + case GraphicsContext3D::DataFormatABGR8: + case GraphicsContext3D::DataFormatRGBA16F: + case GraphicsContext3D::DataFormatRGBA32F: + case GraphicsContext3D::DataFormatRGBA4444: + case GraphicsContext3D::DataFormatRGBA5551: + return true; + default: + return false; + } } ALWAYS_INLINE static bool hasColor(DataFormat format) { - return format == GraphicsContext3D::DataFormatRGBA8 - || format == GraphicsContext3D::DataFormatRGBA16F - || format == GraphicsContext3D::DataFormatRGBA32F - || format == GraphicsContext3D::DataFormatRGB8 - || format == GraphicsContext3D::DataFormatRGB16F - || format == GraphicsContext3D::DataFormatRGB32F - || format == GraphicsContext3D::DataFormatBGR8 - || format == GraphicsContext3D::DataFormatBGRA8 - || format == GraphicsContext3D::DataFormatARGB8 - || format == GraphicsContext3D::DataFormatABGR8 - || format == GraphicsContext3D::DataFormatRGBA5551 - || format == GraphicsContext3D::DataFormatRGBA4444 - || format == GraphicsContext3D::DataFormatRGB565 - || format == GraphicsContext3D::DataFormatR8 - || format == GraphicsContext3D::DataFormatR16F - || format == GraphicsContext3D::DataFormatR32F - || format == GraphicsContext3D::DataFormatRA8 - || format == GraphicsContext3D::DataFormatRA16F - || format == GraphicsContext3D::DataFormatRA32F - || format == GraphicsContext3D::DataFormatAR8; + switch (format) { + case GraphicsContext3D::DataFormatRGBA8: + case GraphicsContext3D::DataFormatRGBA16F: + case GraphicsContext3D::DataFormatRGBA32F: + case GraphicsContext3D::DataFormatRGB8: + case GraphicsContext3D::DataFormatRGB16F: + case GraphicsContext3D::DataFormatRGB32F: + case GraphicsContext3D::DataFormatBGR8: + case GraphicsContext3D::DataFormatBGRA8: + case GraphicsContext3D::DataFormatARGB8: + case GraphicsContext3D::DataFormatABGR8: + case GraphicsContext3D::DataFormatRGBA5551: + case GraphicsContext3D::DataFormatRGBA4444: + case GraphicsContext3D::DataFormatRGB565: + case GraphicsContext3D::DataFormatR8: + case GraphicsContext3D::DataFormatR16F: + case GraphicsContext3D::DataFormatR32F: + case GraphicsContext3D::DataFormatRA8: + case GraphicsContext3D::DataFormatRA16F: + case GraphicsContext3D::DataFormatRA32F: + case GraphicsContext3D::DataFormatAR8: + return true; + default: + return false; + } } // Check if the format is one of the formats from the ImageData or DOM elements. @@ -695,6 +966,13 @@ public: void bufferData(GC3Denum target, GC3Dsizeiptr size, const void* data, GC3Denum usage); void bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr size, const void* data); + void* mapBufferRange(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr length, GC3Dbitfield access); + GC3Dboolean unmapBuffer(GC3Denum target); + void copyBufferSubData(GC3Denum readTarget, GC3Denum writeTarget, GC3Dintptr readOffset, GC3Dintptr writeOffset, GC3Dsizeiptr); + + void texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height); + void texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth); + GC3Denum checkFramebufferStatus(GC3Denum target); void clear(GC3Dbitfield mask); void clearColor(GC3Dclampf red, GC3Dclampf green, GC3Dclampf blue, GC3Dclampf alpha); @@ -734,11 +1012,12 @@ public: GC3Dint getAttribLocation(Platform3DObject, const String& name); void getBooleanv(GC3Denum pname, GC3Dboolean* value); void getBufferParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value); - Attributes getContextAttributes(); + GraphicsContext3DAttributes getContextAttributes(); GC3Denum getError(); void getFloatv(GC3Denum pname, GC3Dfloat* value); void getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum pname, GC3Dint* value); void getIntegerv(GC3Denum pname, GC3Dint* value); + void getInteger64v(GC3Denum pname, GC3Dint64* value); void getProgramiv(Platform3DObject program, GC3Denum pname, GC3Dint* value); void getNonBuiltInActiveSymbolCount(Platform3DObject program, GC3Denum pname, GC3Dint* value); String getProgramInfoLog(Platform3DObject); @@ -813,6 +1092,7 @@ public: void useProgram(Platform3DObject); void validateProgram(Platform3DObject); + bool checkVaryingsPacking(Platform3DObject vertexShader, Platform3DObject fragmentShader) const; bool precisionsMatch(Platform3DObject vertexShader, Platform3DObject fragmentShader) const; void vertexAttrib1f(GC3Duint index, GC3Dfloat x); @@ -834,25 +1114,38 @@ public: void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount); void vertexAttribDivisor(GC3Duint index, GC3Duint divisor); -#if PLATFORM(GTK) || PLATFORM(EFL) || USE(CAIRO) + // VertexArrayOject calls + Platform3DObject createVertexArray(); + void deleteVertexArray(Platform3DObject); + GC3Dboolean isVertexArray(Platform3DObject); + void bindVertexArray(Platform3DObject); + +#if PLATFORM(GTK) || USE(CAIRO) void paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, PlatformContextCairo* context); #elif USE(CG) - void paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, - int canvasWidth, int canvasHeight, GraphicsContext*); + void paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, GraphicsContext&); #endif void markContextChanged(); void markLayerComposited(); bool layerComposited() const; + void forceContextLost(); + void recycleContext(); - void paintRenderingResultsToCanvas(ImageBuffer*, DrawingBuffer*); - PassRefPtr<ImageData> paintRenderingResultsToImageData(DrawingBuffer*); + void paintRenderingResultsToCanvas(ImageBuffer*); + RefPtr<ImageData> paintRenderingResultsToImageData(); bool paintCompositedResultsToCanvas(ImageBuffer*); #if PLATFORM(IOS) void endPaint(); #endif +#if PLATFORM(MAC) + void updateCGLContext(); +#endif + void setContextVisibility(bool); + + GraphicsContext3DPowerPreference powerPreferenceUsedForCreation() const { return m_powerPreferenceUsedForCreation; } // Support for buffer creation and deletion Platform3DObject createBuffer(); @@ -879,11 +1172,15 @@ public: // getError in the order they were added. void synthesizeGLError(GC3Denum error); + // Read real OpenGL errors, and move them to the synthetic + // error list. Return true if at least one error is moved. + bool moveErrorsToSyntheticErrorList(); + // Support for extensions. Returns a non-null object, though not // all methods it contains may necessarily be supported on the // current hardware. Must call Extensions3D::supports() to // determine this. - Extensions3D* getExtensions(); + Extensions3D& getExtensions(); IntSize getInternalFramebufferSize() const; @@ -950,7 +1247,7 @@ public: ImageSource* m_decoder; RefPtr<cairo_surface_t> m_imageSurface; #elif USE(CG) - CGImageRef m_cgImage; + RetainPtr<CGImageRef> m_cgImage; RetainPtr<CGImageRef> m_decodedImage; RetainPtr<CFDataRef> m_pixelData; std::unique_ptr<uint8_t[]> m_formalizedRGBA8Data; @@ -967,7 +1264,8 @@ public: }; private: - GraphicsContext3D(Attributes, HostWindow*, RenderStyle = RenderOffscreen); + GraphicsContext3D(GraphicsContext3DAttributes, HostWindow*, RenderStyle = RenderOffscreen); + static int GPUCheckCounter; // Helper for packImageData/extractImageData/extractTextureData which implement packing of pixel // data into the specified OpenGL destination format and type. @@ -982,6 +1280,9 @@ private: // implementation. void validateDepthStencil(const char* packedDepthStencilExtension); void validateAttributes(); + + // Call to make during draw calls to check on the GPU's status. + void checkGPUStatusIfNecessary(); // Read rendering results into a pixel array with the same format as the // backbuffer. @@ -989,59 +1290,25 @@ private: void readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels); #if PLATFORM(IOS) - bool setRenderbufferStorageFromDrawable(GC3Dsizei width, GC3Dsizei height); + void setRenderbufferStorageFromDrawable(GC3Dsizei width, GC3Dsizei height); #endif bool reshapeFBOs(const IntSize&); void resolveMultisamplingIfNecessary(const IntRect& = IntRect()); -#if PLATFORM(EFL) && USE(GRAPHICS_SURFACE) - void createGraphicsSurfaces(const IntSize&); -#endif + void attachDepthAndStencilBufferIfNeeded(GLuint internalDepthStencilFormat, int width, int height); int m_currentWidth, m_currentHeight; - bool isResourceSafe(); -#if PLATFORM(IOS) - PlatformGraphicsContext3D m_contextObj; - RetainPtr<PlatformLayer> m_webGLLayer; -#elif PLATFORM(MAC) - CGLContextObj m_contextObj; +#if PLATFORM(COCOA) RetainPtr<WebGLLayer> m_webGLLayer; -#elif PLATFORM(WIN) && USE(CA) - RefPtr<PlatformCALayer> m_webGLLayer; + PlatformGraphicsContext3D m_contextObj; #endif - struct SymbolInfo { - SymbolInfo() - : type(0) - , size(0) - , precision(SH_PRECISION_UNDEFINED) - , staticUse(0) - { - } - - SymbolInfo(GC3Denum type, int size, const String& mappedName, ShPrecisionType precision, int staticUse) - : type(type) - , size(size) - , mappedName(mappedName) - , precision(precision) - , staticUse(staticUse) - { - } - - bool operator==(SymbolInfo& other) const - { - return type == other.type && size == other.size && mappedName == other.mappedName; - } - - GC3Denum type; - int size; - String mappedName; - ShPrecisionType precision; - int staticUse; - }; +#if PLATFORM(WIN) && USE(CA) + RefPtr<PlatformCALayer> m_webGLLayer; +#endif - typedef HashMap<String, SymbolInfo> ShaderSymbolMap; + typedef HashMap<String, sh::ShaderVariable> ShaderSymbolMap; struct ShaderSourceEntry { GC3Denum type; @@ -1089,7 +1356,11 @@ private: return filteredToActualUniformIndexMap.size(); } }; - std::unique_ptr<ActiveShaderSymbolCounts> m_shaderSymbolCount; + typedef HashMap<Platform3DObject, ActiveShaderSymbolCounts> ShaderProgramSymbolCountMap; + ShaderProgramSymbolCountMap m_shaderProgramSymbolCountMap; + + typedef HashMap<String, String> HashedSymbolMap; + HashedSymbolMap m_possiblyUnusedAttributeMap; String mappedSymbolName(Platform3DObject program, ANGLEShaderSymbolType, const String& name); String mappedSymbolName(Platform3DObject shaders[2], size_t count, const String& name); @@ -1097,28 +1368,32 @@ private: ANGLEWebKitBridge m_compiler; - OwnPtr<ShaderNameHash> nameHashMapForShaders; + std::unique_ptr<ShaderNameHash> nameHashMapForShaders; -#if ((PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) && USE(OPENGL_ES_2)) +#if ((PLATFORM(GTK) || PLATFORM(WIN)) && USE(OPENGL_ES_2)) friend class Extensions3DOpenGLES; - OwnPtr<Extensions3DOpenGLES> m_extensions; + std::unique_ptr<Extensions3DOpenGLES> m_extensions; #else friend class Extensions3DOpenGL; - OwnPtr<Extensions3DOpenGL> m_extensions; + std::unique_ptr<Extensions3DOpenGL> m_extensions; #endif friend class Extensions3DOpenGLCommon; - Attributes m_attrs; + GraphicsContext3DAttributes m_attrs; + GraphicsContext3DPowerPreference m_powerPreferenceUsedForCreation { GraphicsContext3DPowerPreference::Default }; RenderStyle m_renderStyle; Vector<Vector<float>> m_vertexArray; GC3Duint m_texture; GC3Duint m_compositorTexture; GC3Duint m_fbo; +#if USE(COORDINATED_GRAPHICS_THREADED) + GC3Duint m_intermediateTexture; +#endif - GC3Duint m_depthBuffer; - GC3Duint m_stencilBuffer; - GC3Duint m_depthStencilBuffer; + GC3Duint m_depthBuffer { 0 }; + GC3Duint m_stencilBuffer { 0 }; + GC3Duint m_depthStencilBuffer { 0 }; bool m_layerComposited; GC3Duint m_internalColorFormat; @@ -1145,10 +1420,23 @@ private: // Errors raised by synthesizeGLError(). ListHashSet<GC3Denum> m_syntheticErrors; +#if USE(TEXTURE_MAPPER) + friend class TextureMapperGC3DPlatformLayer; + std::unique_ptr<TextureMapperGC3DPlatformLayer> m_texmapLayer; +#else friend class GraphicsContext3DPrivate; - OwnPtr<GraphicsContext3DPrivate> m_private; + std::unique_ptr<GraphicsContext3DPrivate> m_private; +#endif + + WebGLRenderingContextBase* m_webglContext; + + bool m_isForWebGL2 { false }; + bool m_usingCoreProfile { false }; + +#if USE(CAIRO) + Platform3DObject m_vao { 0 }; +#endif + }; } // namespace WebCore - -#endif // GraphicsContext3D_h diff --git a/Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h b/Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h new file mode 100644 index 000000000..0d3da5ed2 --- /dev/null +++ b/Source/WebCore/platform/graphics/GraphicsContext3DAttributes.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +enum class GraphicsContext3DPowerPreference { + Default, + LowPower, + HighPerformance +}; + +struct GraphicsContext3DAttributes { + // WebGLContextAttributes + bool alpha { true }; + bool depth { true }; + bool stencil { false }; + bool antialias { true }; + bool premultipliedAlpha { true }; + bool preserveDrawingBuffer { false }; + bool failIfMajorPerformanceCaveat { false }; + using PowerPreference = GraphicsContext3DPowerPreference; + PowerPreference powerPreference { PowerPreference::Default }; + + // Additional attributes. + bool forceSoftwareRenderer { false }; + bool shareResources { true }; + bool useGLES3 { false }; + bool noExtensions { false }; + float devicePixelRatio { 1 }; + PowerPreference initialPowerPreference { PowerPreference::Default }; +}; + +} diff --git a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp index 049fdd2d1..fc764654f 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.cpp @@ -18,17 +18,13 @@ */ #include "config.h" -#include "GraphicsContext3DPrivate.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) +#include "GraphicsContext3DPrivate.h" #include "HostWindow.h" -#include "NotImplemented.h" #include <wtf/StdLibExtras.h> -#if USE(CAIRO) -#include "PlatformContextCairo.h" -#endif #if USE(OPENGL_ES_2) #include <GLES2/gl2.h> @@ -37,26 +33,16 @@ #include "OpenGLShims.h" #endif -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) && USE(TEXTURE_MAPPER_GL) -#include <texmap/TextureMapperGL.h> -#endif - using namespace std; namespace WebCore { -PassOwnPtr<GraphicsContext3DPrivate> GraphicsContext3DPrivate::create(GraphicsContext3D* context, GraphicsContext3D::RenderStyle renderStyle) -{ - return adoptPtr(new GraphicsContext3DPrivate(context, renderStyle)); -} - -GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D* context, GraphicsContext3D::RenderStyle renderStyle) - : m_context(context) - , m_renderStyle(renderStyle) +GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D*, GraphicsContext3D::RenderStyle renderStyle) + : m_renderStyle(renderStyle) { switch (renderStyle) { case GraphicsContext3D::RenderOffscreen: - m_glContext = GLContext::createOffscreenContext(GLContext::sharingContext()); + m_glContext = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing()); break; case GraphicsContext3D::RenderToCurrentGLContext: break; @@ -66,13 +52,7 @@ GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D* context, G } } -GraphicsContext3DPrivate::~GraphicsContext3DPrivate() -{ -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - if (client()) - client()->platformLayerWillBeDestroyed(); -#endif -} +GraphicsContext3DPrivate::~GraphicsContext3DPrivate() = default; bool GraphicsContext3DPrivate::makeContextCurrent() { @@ -81,79 +61,9 @@ bool GraphicsContext3DPrivate::makeContextCurrent() PlatformGraphicsContext3D GraphicsContext3DPrivate::platformContext() { - return m_glContext ? m_glContext->platformContext() : GLContext::getCurrent()->platformContext(); -} - -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) -void GraphicsContext3DPrivate::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) -{ - if (!m_glContext) - return; - - ASSERT(m_renderStyle == GraphicsContext3D::RenderOffscreen); - - m_context->markLayerComposited(); - - // FIXME: We do not support mask for the moment with TextureMapperImageBuffer. - if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) { - GraphicsContext* context = textureMapper->graphicsContext(); - context->save(); - context->platformContext()->setGlobalAlpha(opacity); - - const int height = m_context->m_currentHeight; - const int width = m_context->m_currentWidth; - int totalBytes = width * height * 4; - - auto pixels = std::make_unique<unsigned char[]>(totalBytes); - if (!pixels) - return; - - // OpenGL keeps the pixels stored bottom up, so we need to flip the image here. - context->translate(0, height); - context->scale(FloatSize(1, -1)); - - context->concatCTM(matrix.toAffineTransform()); - - m_context->readRenderingResults(pixels.get(), totalBytes); - - // Premultiply alpha. - for (int i = 0; i < totalBytes; i += 4) - if (pixels[i + 3] != 255) { - pixels[i + 0] = min(255, pixels[i + 0] * pixels[i + 3] / 255); - pixels[i + 1] = min(255, pixels[i + 1] * pixels[i + 3] / 255); - pixels[i + 2] = min(255, pixels[i + 2] * pixels[i + 3] / 255); - } - - RefPtr<cairo_surface_t> imageSurface = adoptRef(cairo_image_surface_create_for_data( - const_cast<unsigned char*>(pixels.get()), CAIRO_FORMAT_ARGB32, width, height, width * 4)); - - context->platformContext()->drawSurfaceToContext(imageSurface.get(), targetRect, IntRect(0, 0, width, height), context); - - context->restore(); - return; - } - -#if USE(TEXTURE_MAPPER_GL) - if (m_context->m_attrs.antialias && m_context->m_state.boundFBO == m_context->m_multisampleFBO) { - GLContext* previousActiveContext = GLContext::getCurrent(); - if (previousActiveContext != m_glContext) - m_context->makeContextCurrent(); - - m_context->resolveMultisamplingIfNecessary(); - ::glBindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_context->m_state.boundFBO); - - if (previousActiveContext && previousActiveContext != m_glContext) - previousActiveContext->makeContextCurrent(); - } - - TextureMapperGL* texmapGL = static_cast<TextureMapperGL*>(textureMapper); - TextureMapperGL::Flags flags = TextureMapperGL::ShouldFlipTexture | (m_context->m_attrs.alpha ? TextureMapperGL::ShouldBlend : 0); - IntSize textureSize(m_context->m_currentWidth, m_context->m_currentHeight); - texmapGL->drawTexture(m_context->m_texture, flags, textureSize, targetRect, matrix, opacity); -#endif // USE(ACCELERATED_COMPOSITING_GL) + return m_glContext ? m_glContext->platformContext() : GLContext::current()->platformContext(); } -#endif // USE(ACCELERATED_COMPOSITING) } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h index 42eb6c2e7..507558297 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h +++ b/Source/WebCore/platform/graphics/GraphicsContext3DPrivate.h @@ -22,36 +22,23 @@ #include "GLContext.h" #include "GraphicsContext3D.h" -#include <wtf/PassOwnPtr.h> - -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) -#include "TextureMapperPlatformLayer.h" -#endif +#include "PlatformLayer.h" namespace WebCore { -class GraphicsContext3DPrivate -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - : public TextureMapperPlatformLayer -#endif -{ +class BitmapTextureGL; + +class GraphicsContext3DPrivate { public: - static PassOwnPtr<GraphicsContext3DPrivate> create(GraphicsContext3D*, GraphicsContext3D::RenderStyle); + GraphicsContext3DPrivate(GraphicsContext3D*, GraphicsContext3D::RenderStyle); ~GraphicsContext3DPrivate(); bool makeContextCurrent(); PlatformGraphicsContext3D platformContext(); GraphicsContext3D::RenderStyle renderStyle() { return m_renderStyle; } -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - virtual void paintToTextureMapper(TextureMapper*, const FloatRect& target, const TransformationMatrix&, float opacity); -#endif - private: - GraphicsContext3DPrivate(GraphicsContext3D*, GraphicsContext3D::RenderStyle); - - GraphicsContext3D* m_context; - OwnPtr<GLContext> m_glContext; + std::unique_ptr<GLContext> m_glContext; GraphicsContext3D::RenderStyle m_renderStyle; }; diff --git a/Source/WebCore/platform/graphics/GraphicsLayer.cpp b/Source/WebCore/platform/graphics/GraphicsLayer.cpp index 3d410b701..2e981c66c 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayer.cpp +++ b/Source/WebCore/platform/graphics/GraphicsLayer.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,8 +25,6 @@ #include "config.h" -#if USE(ACCELERATED_COMPOSITING) - #include "GraphicsLayer.h" #include "FloatPoint.h" @@ -36,6 +34,7 @@ #include "RotateTransformOperation.h" #include "TextStream.h" #include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> #include <wtf/text/CString.h> #include <wtf/text/StringBuilder.h> #include <wtf/text/WTFString.h> @@ -49,31 +48,64 @@ namespace WebCore { typedef HashMap<const GraphicsLayer*, Vector<FloatRect>> RepaintMap; static RepaintMap& repaintRectMap() { - DEFINE_STATIC_LOCAL(RepaintMap, map, ()); + static NeverDestroyed<RepaintMap> map; return map; } -void KeyframeValueList::insert(PassOwnPtr<const AnimationValue> value) +void KeyframeValueList::insert(std::unique_ptr<const AnimationValue> value) { for (size_t i = 0; i < m_values.size(); ++i) { const AnimationValue* curValue = m_values[i].get(); if (curValue->keyTime() == value->keyTime()) { ASSERT_NOT_REACHED(); // insert after - m_values.insert(i + 1, value); + m_values.insert(i + 1, WTFMove(value)); return; } if (curValue->keyTime() > value->keyTime()) { // insert before - m_values.insert(i, value); + m_values.insert(i, WTFMove(value)); return; } } - m_values.append(value); + m_values.append(WTFMove(value)); +} + +#if !USE(CA) +bool GraphicsLayer::supportsLayerType(Type type) +{ + switch (type) { + case Type::Normal: + case Type::PageTiledBacking: + case Type::Scrolling: + return true; + case Type::Shape: + return false; + } + ASSERT_NOT_REACHED(); + return false; +} + +bool GraphicsLayer::supportsBackgroundColorContent() +{ +#if USE(TEXTURE_MAPPER) + return true; +#else + return false; +#endif } +#endif -GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) +#if !USE(COORDINATED_GRAPHICS) +bool GraphicsLayer::supportsContentsTiling() +{ + // FIXME: Enable the feature on different ports. + return false; +} +#endif + +GraphicsLayer::GraphicsLayer(Type type, GraphicsLayerClient& client) : m_client(client) , m_anchorPoint(0.5f, 0.5f, 0) , m_opacity(1) @@ -81,30 +113,32 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) #if ENABLE(CSS_COMPOSITING) , m_blendMode(BlendModeNormal) #endif + , m_type(type) , m_contentsOpaque(false) , m_preserves3D(false) , m_backfaceVisibility(true) - , m_usingTiledBacking(false) , m_masksToBounds(false) , m_drawsContent(false) , m_contentsVisible(true) , m_acceleratesDrawing(false) - , m_maintainsPixelAlignment(false) + , m_usesDisplayListDrawing(false) , m_appliesPageScale(false) , m_showDebugBorder(false) , m_showRepaintCounter(false) + , m_isMaskLayer(false) + , m_isTrackingDisplayListReplay(false) + , m_userInteractionEnabled(true) , m_paintingPhase(GraphicsLayerPaintAllWithOverflowClip) , m_contentsOrientation(CompositingCoordinatesTopDown) - , m_parent(0) - , m_maskLayer(0) - , m_replicaLayer(0) - , m_replicatedLayer(0) + , m_parent(nullptr) + , m_maskLayer(nullptr) + , m_replicaLayer(nullptr) + , m_replicatedLayer(nullptr) , m_repaintCount(0) , m_customAppearance(NoCustomAppearance) { #ifndef NDEBUG - if (m_client) - m_client->verifyNotPainting(); + m_client.verifyNotPainting(); #endif } @@ -117,8 +151,7 @@ GraphicsLayer::~GraphicsLayer() void GraphicsLayer::willBeDestroyed() { #ifndef NDEBUG - if (m_client) - m_client->verifyNotPainting(); + m_client.verifyNotPainting(); #endif if (m_replicaLayer) m_replicaLayer->setReplicatedLayer(0); @@ -256,16 +289,62 @@ void GraphicsLayer::removeAllChildren() void GraphicsLayer::removeFromParent() { if (m_parent) { - unsigned i; - for (i = 0; i < m_parent->m_children.size(); i++) { - if (this == m_parent->m_children[i]) { - m_parent->m_children.remove(i); - break; - } - } + m_parent->m_children.removeFirst(this); + setParent(nullptr); + } +} - setParent(0); +void GraphicsLayer::setMaskLayer(GraphicsLayer* layer) +{ + if (layer == m_maskLayer) + return; + + if (layer) { + layer->removeFromParent(); + layer->setParent(this); + layer->setIsMaskLayer(true); + } else if (m_maskLayer) { + m_maskLayer->setParent(nullptr); + m_maskLayer->setIsMaskLayer(false); } + + m_maskLayer = layer; +} + +Path GraphicsLayer::shapeLayerPath() const +{ +#if USE(CA) + return m_shapeLayerPath; +#else + return Path(); +#endif +} + +void GraphicsLayer::setShapeLayerPath(const Path& path) +{ +#if USE(CA) + m_shapeLayerPath = path; +#else + UNUSED_PARAM(path); +#endif +} + +WindRule GraphicsLayer::shapeLayerWindRule() const +{ +#if USE(CA) + return m_shapeLayerWindRule; +#else + return RULE_NONZERO; +#endif +} + +void GraphicsLayer::setShapeLayerWindRule(WindRule windRule) +{ +#if USE(CA) + m_shapeLayerWindRule = windRule; +#else + UNUSED_PARAM(windRule); +#endif } void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants() @@ -284,13 +363,19 @@ void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants() childLayers[i]->noteDeviceOrPageScaleFactorChangedIncludingDescendants(); } +void GraphicsLayer::setIsInWindow(bool inWindow) +{ + if (TiledBacking* tiledBacking = this->tiledBacking()) + tiledBacking->setIsInWindow(inWindow); +} + void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer) { if (m_replicaLayer == layer) return; if (m_replicaLayer) - m_replicaLayer->setReplicatedLayer(0); + m_replicaLayer->setReplicatedLayer(nullptr); if (layer) layer->setReplicatedLayer(this); @@ -298,7 +383,7 @@ void GraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer) m_replicaLayer = layer; } -void GraphicsLayer::setOffsetFromRenderer(const IntSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay) +void GraphicsLayer::setOffsetFromRenderer(const FloatSize& offset, ShouldSetNeedsDisplay shouldSetNeedsDisplay) { if (offset == m_offsetFromRenderer) return; @@ -326,17 +411,15 @@ void GraphicsLayer::setBackgroundColor(const Color& color) m_backgroundColor = color; } -void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip) +void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const FloatRect& clip) { - if (m_client) { - IntSize offset = offsetFromRenderer(); - context.translate(-offset); + FloatSize offset = offsetFromRenderer(); + context.translate(-offset); - IntRect clipRect(clip); - clipRect.move(offset); + FloatRect clipRect(clip); + clipRect.move(offset); - m_client->paintContents(this, context, m_paintingPhase, clipRect); - } + m_client.paintContents(this, context, m_paintingPhase, clipRect); } String GraphicsLayer::animationNameForTransition(AnimatedPropertyID property) @@ -359,32 +442,37 @@ void GraphicsLayer::resumeAnimations() void GraphicsLayer::getDebugBorderInfo(Color& color, float& width) const { + width = 2; + + if (needsBackdrop()) { + color = Color(255, 0, 255, 128); // has backdrop: magenta + width = 12; + return; + } + if (drawsContent()) { - if (m_usingTiledBacking) { + if (tiledBacking()) { color = Color(255, 128, 0, 128); // tiled layer: orange - width = 2; return; } color = Color(0, 128, 32, 128); // normal layer: green - width = 2; return; } - if (hasContentsLayer()) { - color = Color(255, 150, 255, 200); // non-painting layer with contents: pink - width = 2; + if (usesContentsLayer()) { + color = Color(0, 64, 128, 150); // non-painting layer with contents: blue + width = 8; return; } if (masksToBounds()) { color = Color(128, 255, 255, 48); // masking layer: pale blue - width = 20; + width = 16; return; } color = Color(255, 255, 0, 192); // container: yellow - width = 2; } void GraphicsLayer::updateDebugIndicators() @@ -428,7 +516,6 @@ void GraphicsLayer::distributeOpacity(float accumulatedOpacity) } } -#if ENABLE(CSS_FILTERS) static inline const FilterOperations& filterOperationsAt(const KeyframeValueList& valueList, size_t index) { return static_cast<const FilterAnimationValue&>(valueList.at(index)).value(); @@ -436,7 +523,11 @@ static inline const FilterOperations& filterOperationsAt(const KeyframeValueList int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList) { - ASSERT(valueList.property() == AnimatedPropertyWebkitFilter); +#if ENABLE(FILTERS_LEVEL_2) + ASSERT(valueList.property() == AnimatedPropertyFilter || valueList.property() == AnimatedPropertyWebkitBackdropFilter); +#else + ASSERT(valueList.property() == AnimatedPropertyFilter); +#endif if (valueList.size() < 2) return -1; @@ -466,7 +557,6 @@ int GraphicsLayer::validateFilterOperations(const KeyframeValueList& valueList) return firstIndex; } -#endif // An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix // The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is @@ -479,7 +569,7 @@ static inline const TransformOperations& operationsAt(const KeyframeValueList& v int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueList, bool& hasBigRotation) { - ASSERT(valueList.property() == AnimatedPropertyWebkitTransform); + ASSERT(valueList.property() == AnimatedPropertyTransform); hasBigRotation = false; @@ -511,8 +601,8 @@ int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueLis } // Keyframes are valid, check for big rotations. - double lastRotAngle = 0.0; - double maxRotAngle = -1.0; + double lastRotationAngle = 0.0; + double maxRotationAngle = -1.0; for (size_t j = 0; j < firstVal.operations().size(); ++j) { TransformOperation::OperationType type = firstVal.operations().at(j)->type(); @@ -522,23 +612,23 @@ int GraphicsLayer::validateTransformOperations(const KeyframeValueList& valueLis type == TransformOperation::ROTATE_Y || type == TransformOperation::ROTATE_Z || type == TransformOperation::ROTATE_3D) { - lastRotAngle = static_cast<RotateTransformOperation*>(firstVal.operations().at(j).get())->angle(); + lastRotationAngle = downcast<RotateTransformOperation>(*firstVal.operations().at(j)).angle(); - if (maxRotAngle < 0) - maxRotAngle = fabs(lastRotAngle); + if (maxRotationAngle < 0) + maxRotationAngle = fabs(lastRotationAngle); for (size_t i = firstIndex + 1; i < valueList.size(); ++i) { const TransformOperations& val = operationsAt(valueList, i); - double rotAngle = val.operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val.operations().at(j).get())->angle()); - double diffAngle = fabs(rotAngle - lastRotAngle); - if (diffAngle > maxRotAngle) - maxRotAngle = diffAngle; - lastRotAngle = rotAngle; + double rotationAngle = val.operations().isEmpty() ? 0 : downcast<RotateTransformOperation>(*val.operations().at(j)).angle(); + double diffAngle = fabs(rotationAngle - lastRotationAngle); + if (diffAngle > maxRotationAngle) + maxRotationAngle = diffAngle; + lastRotationAngle = rotationAngle; } } } - hasBigRotation = maxRotAngle >= 180.0; + hasBigRotation = maxRotationAngle >= 180.0; return firstIndex; } @@ -559,21 +649,36 @@ void GraphicsLayer::resetTrackedRepaints() void GraphicsLayer::addRepaintRect(const FloatRect& repaintRect) { - if (m_client->isTrackingRepaints()) { - FloatRect largestRepaintRect(FloatPoint(), m_size); - largestRepaintRect.intersect(repaintRect); - RepaintMap::iterator repaintIt = repaintRectMap().find(this); - if (repaintIt == repaintRectMap().end()) { - Vector<FloatRect> repaintRects; - repaintRects.append(largestRepaintRect); - repaintRectMap().set(this, repaintRects); - } else { - Vector<FloatRect>& repaintRects = repaintIt->value; - repaintRects.append(largestRepaintRect); - } + if (!m_client.isTrackingRepaints()) + return; + + FloatRect largestRepaintRect(FloatPoint(), m_size); + largestRepaintRect.intersect(repaintRect); + RepaintMap::iterator repaintIt = repaintRectMap().find(this); + if (repaintIt == repaintRectMap().end()) { + Vector<FloatRect> repaintRects; + repaintRects.append(largestRepaintRect); + repaintRectMap().set(this, repaintRects); + } else { + Vector<FloatRect>& repaintRects = repaintIt->value; + repaintRects.append(largestRepaintRect); } } +void GraphicsLayer::traverse(GraphicsLayer& layer, std::function<void (GraphicsLayer&)> traversalFunc) +{ + traversalFunc(layer); + + for (auto* childLayer : layer.children()) + traverse(*childLayer, traversalFunc); + + if (auto* replicaLayer = layer.replicaLayer()) + traverse(*replicaLayer, traversalFunc); + + if (auto* maskLayer = layer.maskLayer()) + traverse(*maskLayer, traversalFunc); +} + void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const { writeIndent(ts, indent); @@ -590,6 +695,20 @@ void GraphicsLayer::dumpLayer(TextStream& ts, int indent, LayerTreeAsTextBehavio ts << ")\n"; } +static void dumpChildren(TextStream& ts, const Vector<GraphicsLayer*>& children, unsigned& totalChildCount, int indent, LayerTreeAsTextBehavior behavior) +{ + totalChildCount += children.size(); + for (auto* child : children) { + if ((behavior & LayerTreeAsTextDebug) || !child->client().shouldSkipLayerInDump(child, behavior)) { + child->dumpLayer(ts, indent + 2, behavior); + continue; + } + + totalChildCount--; + dumpChildren(ts, child->children(), totalChildCount, indent, behavior); + } +} + void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBehavior behavior) const { if (m_position != FloatPoint()) { @@ -597,6 +716,11 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe ts << "(position " << m_position.x() << " " << m_position.y() << ")\n"; } + if (m_approximatePosition) { + writeIndent(ts, indent + 1); + ts << "(approximate position " << m_approximatePosition.value().x() << " " << m_approximatePosition.value().y() << ")\n"; + } + if (m_boundsOrigin != FloatPoint()) { writeIndent(ts, indent + 1); ts << "(bounds origin " << m_boundsOrigin.x() << " " << m_boundsOrigin.y() << ")\n"; @@ -604,7 +728,10 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe if (m_anchorPoint != FloatPoint3D(0.5f, 0.5f, 0)) { writeIndent(ts, indent + 1); - ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n"; + ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y(); + if (m_anchorPoint.z()) + ts << " " << m_anchorPoint.z(); + ts << ")\n"; } if (m_size != IntSize()) { @@ -616,15 +743,23 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe writeIndent(ts, indent + 1); ts << "(opacity " << m_opacity << ")\n"; } - - if (m_usingTiledBacking) { + +#if ENABLE(CSS_COMPOSITING) + if (m_blendMode != BlendModeNormal) { + writeIndent(ts, indent + 1); + ts << "(blendMode " << compositeOperatorName(CompositeSourceOver, m_blendMode) << ")\n"; + } +#endif + + if (type() == Type::Normal && tiledBacking()) { writeIndent(ts, indent + 1); - ts << "(usingTiledLayer " << m_usingTiledBacking << ")\n"; + ts << "(usingTiledLayer 1)\n"; } - if (m_contentsOpaque) { + bool needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack = m_client.needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack(*this); + if (m_contentsOpaque || needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack) { writeIndent(ts, indent + 1); - ts << "(contentsOpaque " << m_contentsOpaque << ")\n"; + ts << "(contentsOpaque " << (m_contentsOpaque || needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack) << ")\n"; } if (m_preserves3D) { @@ -632,7 +767,7 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe ts << "(preserves3D " << m_preserves3D << ")\n"; } - if (m_drawsContent && m_client->shouldDumpPropertyForLayer(this, "drawsContent")) { + if (m_drawsContent && m_client.shouldDumpPropertyForLayer(this, "drawsContent")) { writeIndent(ts, indent + 1); ts << "(drawsContent " << m_drawsContent << ")\n"; } @@ -649,19 +784,21 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe if (behavior & LayerTreeAsTextDebug) { writeIndent(ts, indent + 1); - ts << "("; - if (m_client) - ts << "client " << static_cast<void*>(m_client); - else - ts << "no client"; - ts << ")\n"; + ts << "(primary-layer-id " << primaryLayerID() << ")\n"; + writeIndent(ts, indent + 1); + ts << "(client " << static_cast<void*>(&m_client) << ")\n"; } - if (m_backgroundColor.isValid() && m_client->shouldDumpPropertyForLayer(this, "backgroundColor")) { + if (m_backgroundColor.isValid() && m_client.shouldDumpPropertyForLayer(this, "backgroundColor")) { writeIndent(ts, indent + 1); ts << "(backgroundColor " << m_backgroundColor.nameForRenderTreeAsText() << ")\n"; } + if (behavior & LayerTreeAsTextIncludeAcceleratesDrawing && m_acceleratesDrawing) { + writeIndent(ts, indent + 1); + ts << "(acceleratesDrawing " << m_acceleratesDrawing << ")\n"; + } + if (!m_transform.isIdentity()) { writeIndent(ts, indent + 1); ts << "(transform "; @@ -682,6 +819,15 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "])\n"; } + if (m_maskLayer) { + writeIndent(ts, indent + 1); + ts << "(mask layer"; + if (behavior & LayerTreeAsTextDebug) + ts << " " << m_maskLayer; + ts << ")\n"; + m_maskLayer->dumpLayer(ts, indent + 2, behavior); + } + if (m_replicaLayer) { writeIndent(ts, indent + 1); ts << "(replica layer"; @@ -699,7 +845,7 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe ts << ")\n"; } - if (behavior & LayerTreeAsTextIncludeRepaintRects && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty() && m_client->shouldDumpPropertyForLayer(this, "repaintRects")) { + if (behavior & LayerTreeAsTextIncludeRepaintRects && repaintRectMap().contains(this) && !repaintRectMap().get(this).isEmpty() && m_client.shouldDumpPropertyForLayer(this, "repaintRects")) { writeIndent(ts, indent + 1); ts << "(repaint rects\n"; for (size_t i = 0; i < repaintRectMap().get(this).size(); ++i) { @@ -732,6 +878,10 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe writeIndent(ts, indent + 2); ts << "GraphicsLayerPaintMask\n"; } + if (paintingPhase() & GraphicsLayerPaintChildClippingMask) { + writeIndent(ts, indent + 2); + ts << "GraphicsLayerPaintChildClippingMask\n"; + } if (paintingPhase() & GraphicsLayerPaintOverflowContents) { writeIndent(ts, indent + 2); ts << "GraphicsLayerPaintOverflowContents\n"; @@ -748,19 +898,9 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe if (m_children.size()) { TextStream childrenStream; - unsigned totalChildCount = m_children.size(); - for (size_t childIndex = 0; childIndex < m_children.size(); childIndex++) { - GraphicsLayer* child = m_children[childIndex]; - if (!m_client->shouldSkipLayerInDump(child)) { - child->dumpLayer(childrenStream, indent + 2, behavior); - continue; - } - - const Vector<GraphicsLayer*>& grandChildren = child->children(); - totalChildCount += grandChildren.size() - 1; - for (size_t grandChildIndex = 0; grandChildIndex < grandChildren.size(); grandChildIndex++) - grandChildren[grandChildIndex]->dumpLayer(childrenStream, indent + 2, behavior); - } + + unsigned totalChildCount = 0; + dumpChildren(childrenStream, m_children, totalChildCount, indent, behavior); writeIndent(childrenStream, indent + 1); childrenStream << ")\n"; @@ -773,9 +913,32 @@ void GraphicsLayer::dumpProperties(TextStream& ts, int indent, LayerTreeAsTextBe } } +TextStream& operator<<(TextStream& ts, const Vector<GraphicsLayer::PlatformLayerID>& layers) +{ + for (size_t i = 0; i < layers.size(); ++i) { + if (i) + ts << " "; + ts << layers[i]; + } + + return ts; +} + +TextStream& operator<<(TextStream& ts, const WebCore::GraphicsLayer::CustomAppearance& customAppearance) +{ + switch (customAppearance) { + case GraphicsLayer::CustomAppearance::NoCustomAppearance: ts << "none"; break; + case GraphicsLayer::CustomAppearance::ScrollingOverhang: ts << "scrolling-overhang"; break; + case GraphicsLayer::CustomAppearance::ScrollingShadow: ts << "scrolling-shadow"; break; + case GraphicsLayer::CustomAppearance::LightBackdropAppearance: ts << "light-backdrop"; break; + case GraphicsLayer::CustomAppearance::DarkBackdropAppearance: ts << "dark-backdrop"; break; + } + return ts; +} + String GraphicsLayer::layerTreeAsText(LayerTreeAsTextBehavior behavior) const { - TextStream ts; + TextStream ts(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect); dumpLayer(ts, 0, behavior); return ts.release(); @@ -783,15 +946,13 @@ String GraphicsLayer::layerTreeAsText(LayerTreeAsTextBehavior behavior) const } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer) { if (!layer) return; - String output = layer->layerTreeAsText(LayerTreeAsTextDebug | LayerTreeAsTextIncludeVisibleRects | LayerTreeAsTextIncludeTileCaches); + String output = layer->layerTreeAsText(WebCore::LayerTreeAsTextDebug | WebCore::LayerTreeAsTextIncludeVisibleRects | WebCore::LayerTreeAsTextIncludeTileCaches | WebCore::LayerTreeAsTextIncludeContentLayers); fprintf(stderr, "%s\n", output.utf8().data()); } #endif - -#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/GraphicsLayer.h b/Source/WebCore/platform/graphics/GraphicsLayer.h index 3d57e804a..b685335d2 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayer.h +++ b/Source/WebCore/platform/graphics/GraphicsLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,45 +23,28 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GraphicsLayer_h -#define GraphicsLayer_h - -#if USE(ACCELERATED_COMPOSITING) +#pragma once #include "Animation.h" #include "Color.h" +#include "FilterOperations.h" #include "FloatPoint.h" #include "FloatPoint3D.h" +#include "FloatRoundedRect.h" #include "FloatSize.h" #include "GraphicsLayerClient.h" -#include "IntRect.h" +#include "Path.h" #include "PlatformLayer.h" #include "TransformOperations.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> - -#if ENABLE(CSS_FILTERS) -#include "FilterOperations.h" -#endif +#include "WindRule.h" +#include <wtf/TypeCasts.h> #if ENABLE(CSS_COMPOSITING) #include "GraphicsTypes.h" #endif -enum LayerTreeAsTextBehaviorFlags { - LayerTreeAsTextBehaviorNormal = 0, - LayerTreeAsTextDebug = 1 << 0, // Dump extra debugging info like layer addresses. - LayerTreeAsTextIncludeVisibleRects = 1 << 1, - LayerTreeAsTextIncludeTileCaches = 1 << 2, - LayerTreeAsTextIncludeRepaintRects = 1 << 3, - LayerTreeAsTextIncludePaintingPhases = 1 << 4, - LayerTreeAsTextIncludeContentLayers = 1 << 5 -}; -typedef unsigned LayerTreeAsTextBehavior; - namespace WebCore { -class FloatRect; class GraphicsContext; class GraphicsLayerFactory; class Image; @@ -70,6 +53,10 @@ class TiledBacking; class TimingFunction; class TransformationMatrix; +namespace DisplayList { +typedef unsigned AsTextFlags; +} + // Base class for animation values (also used for transitions). Here to // represent values for properties being animated via the GraphicsLayer, // without pulling in style-related data from outside of the platform directory. @@ -81,16 +68,26 @@ public: double keyTime() const { return m_keyTime; } const TimingFunction* timingFunction() const { return m_timingFunction.get(); } - virtual PassOwnPtr<AnimationValue> clone() const = 0; + virtual std::unique_ptr<AnimationValue> clone() const = 0; protected: - AnimationValue(double keyTime, PassRefPtr<TimingFunction> timingFunction = 0) + AnimationValue(double keyTime, TimingFunction* timingFunction = nullptr) : m_keyTime(keyTime) , m_timingFunction(timingFunction) { } + AnimationValue(const AnimationValue& other) + : m_keyTime(other.m_keyTime) + , m_timingFunction(other.m_timingFunction ? RefPtr<TimingFunction> { other.m_timingFunction->clone() } : nullptr) + { + } + + AnimationValue(AnimationValue&&) = default; + private: + void operator=(const AnimationValue&) = delete; + double m_keyTime; RefPtr<TimingFunction> m_timingFunction; }; @@ -99,25 +96,20 @@ private: // FIXME: Should be moved to its own header file. class FloatAnimationValue : public AnimationValue { public: - static PassOwnPtr<FloatAnimationValue> create(double keyTime, float value, PassRefPtr<TimingFunction> timingFunction = 0) + FloatAnimationValue(double keyTime, float value, TimingFunction* timingFunction = nullptr) + : AnimationValue(keyTime, timingFunction) + , m_value(value) { - return adoptPtr(new FloatAnimationValue(keyTime, value, timingFunction)); } - virtual PassOwnPtr<AnimationValue> clone() const override + std::unique_ptr<AnimationValue> clone() const override { - return adoptPtr(new FloatAnimationValue(*this)); + return std::make_unique<FloatAnimationValue>(*this); } float value() const { return m_value; } private: - FloatAnimationValue(double keyTime, float value, PassRefPtr<TimingFunction> timingFunction) - : AnimationValue(keyTime, timingFunction) - , m_value(value) - { - } - float m_value; }; @@ -125,55 +117,63 @@ private: // FIXME: Should be moved to its own header file. class TransformAnimationValue : public AnimationValue { public: - static PassOwnPtr<TransformAnimationValue> create(double keyTime, const TransformOperations& value, PassRefPtr<TimingFunction> timingFunction = 0) + TransformAnimationValue(double keyTime, const TransformOperations& value, TimingFunction* timingFunction = nullptr) + : AnimationValue(keyTime, timingFunction) + , m_value(value) { - return adoptPtr(new TransformAnimationValue(keyTime, value, timingFunction)); } - virtual PassOwnPtr<AnimationValue> clone() const override + std::unique_ptr<AnimationValue> clone() const override { - return adoptPtr(new TransformAnimationValue(*this)); + return std::make_unique<TransformAnimationValue>(*this); } - const TransformOperations& value() const { return m_value; } - -private: - TransformAnimationValue(double keyTime, const TransformOperations& value, PassRefPtr<TimingFunction> timingFunction) - : AnimationValue(keyTime, timingFunction) - , m_value(value) + TransformAnimationValue(const TransformAnimationValue& other) + : AnimationValue(other) { + m_value.operations().reserveInitialCapacity(other.m_value.operations().size()); + for (auto& operation : other.m_value.operations()) + m_value.operations().uncheckedAppend(operation->clone()); } + TransformAnimationValue(TransformAnimationValue&&) = default; + + const TransformOperations& value() const { return m_value; } + +private: TransformOperations m_value; }; -#if ENABLE(CSS_FILTERS) // Used to store one filter value in a keyframe list. // FIXME: Should be moved to its own header file. class FilterAnimationValue : public AnimationValue { public: - static PassOwnPtr<FilterAnimationValue> create(double keyTime, const FilterOperations& value, PassRefPtr<TimingFunction> timingFunction = 0) + FilterAnimationValue(double keyTime, const FilterOperations& value, TimingFunction* timingFunction = nullptr) + : AnimationValue(keyTime, timingFunction) + , m_value(value) { - return adoptPtr(new FilterAnimationValue(keyTime, value, timingFunction)); } - virtual PassOwnPtr<AnimationValue> clone() const override + std::unique_ptr<AnimationValue> clone() const override { - return adoptPtr(new FilterAnimationValue(*this)); + return std::make_unique<FilterAnimationValue>(*this); } - const FilterOperations& value() const { return m_value; } - -private: - FilterAnimationValue(double keyTime, const FilterOperations& value, PassRefPtr<TimingFunction> timingFunction) - : AnimationValue(keyTime, timingFunction) - , m_value(value) + FilterAnimationValue(const FilterAnimationValue& other) + : AnimationValue(other) { + m_value.operations().reserveInitialCapacity(other.m_value.operations().size()); + for (auto& operation : other.m_value.operations()) + m_value.operations().uncheckedAppend(operation->clone()); } + FilterAnimationValue(FilterAnimationValue&&) = default; + + const FilterOperations& value() const { return m_value; } + +private: FilterOperations m_value; }; -#endif // Used to store a series of values in a keyframe list. // Values will all be of the same type, which can be inferred from the property. @@ -188,13 +188,12 @@ public: KeyframeValueList(const KeyframeValueList& other) : m_property(other.property()) { - for (size_t i = 0; i < other.m_values.size(); ++i) - m_values.append(other.m_values[i]->clone()); + m_values.reserveInitialCapacity(other.m_values.size()); + for (auto& value : other.m_values) + m_values.uncheckedAppend(value->clone()); } - ~KeyframeValueList() - { - } + KeyframeValueList(KeyframeValueList&&) = default; KeyframeValueList& operator=(const KeyframeValueList& other) { @@ -203,6 +202,8 @@ public: return *this; } + KeyframeValueList& operator=(KeyframeValueList&&) = default; + void swap(KeyframeValueList& other) { std::swap(m_property, other.m_property); @@ -213,12 +214,12 @@ public: size_t size() const { return m_values.size(); } const AnimationValue& at(size_t i) const { return *m_values.at(i); } - + // Insert, sorted by keyTime. - void insert(PassOwnPtr<const AnimationValue>); - + WEBCORE_EXPORT void insert(std::unique_ptr<const AnimationValue>); + protected: - Vector<OwnPtr<const AnimationValue>> m_values; + Vector<std::unique_ptr<const AnimationValue>> m_values; AnimatedPropertyID m_property; }; @@ -228,16 +229,25 @@ protected: class GraphicsLayer { WTF_MAKE_NONCOPYABLE(GraphicsLayer); WTF_MAKE_FAST_ALLOCATED; public: - static std::unique_ptr<GraphicsLayer> create(GraphicsLayerFactory*, GraphicsLayerClient*); + enum class Type { + Normal, + PageTiledBacking, + Scrolling, + Shape + }; - virtual ~GraphicsLayer(); + WEBCORE_EXPORT static std::unique_ptr<GraphicsLayer> create(GraphicsLayerFactory*, GraphicsLayerClient&, Type = Type::Normal); + + WEBCORE_EXPORT virtual ~GraphicsLayer(); + + Type type() const { return m_type; } - virtual void initialize() { } + virtual void initialize(Type) { } - typedef uint64_t PlatformLayerID; + using PlatformLayerID = uint64_t; virtual PlatformLayerID primaryLayerID() const { return 0; } - GraphicsLayerClient* client() const { return m_client; } + GraphicsLayerClient& client() const { return m_client; } // Layer name. Only used to identify layers in debug output const String& name() const { return m_name; } @@ -251,23 +261,36 @@ public: const Vector<GraphicsLayer*>& children() const { return m_children; } // Returns true if the child list changed. - virtual bool setChildren(const Vector<GraphicsLayer*>&); + WEBCORE_EXPORT virtual bool setChildren(const Vector<GraphicsLayer*>&); + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForMedia, + ContentsLayerForCanvas, + ContentsLayerForBackgroundColor, + ContentsLayerForPlugin + }; // Add child layers. If the child is already parented, it will be removed from its old parent. - virtual void addChild(GraphicsLayer*); - virtual void addChildAtIndex(GraphicsLayer*, int index); - virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); - virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); - virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + WEBCORE_EXPORT virtual void addChild(GraphicsLayer*); + WEBCORE_EXPORT virtual void addChildAtIndex(GraphicsLayer*, int index); + WEBCORE_EXPORT virtual void addChildAbove(GraphicsLayer*, GraphicsLayer* sibling); + WEBCORE_EXPORT virtual void addChildBelow(GraphicsLayer*, GraphicsLayer* sibling); + WEBCORE_EXPORT virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); - void removeAllChildren(); - virtual void removeFromParent(); + WEBCORE_EXPORT void removeAllChildren(); + WEBCORE_EXPORT virtual void removeFromParent(); + // The parent() of a maskLayer is set to the layer being masked. GraphicsLayer* maskLayer() const { return m_maskLayer; } - virtual void setMaskLayer(GraphicsLayer* layer) { m_maskLayer = layer; } + virtual void setMaskLayer(GraphicsLayer*); + + void setIsMaskLayer(bool isMask) { m_isMaskLayer = isMask; } + bool isMaskLayer() const { return m_isMaskLayer; } // The given layer will replicate this layer and its children; the replica renders behind this layer. - virtual void setReplicatedByLayer(GraphicsLayer*); + WEBCORE_EXPORT virtual void setReplicatedByLayer(GraphicsLayer*); // Whether this layer is being replicated by another layer. bool isReplicated() const { return m_replicaLayer; } // The layer that replicates this layer (if any). @@ -281,17 +304,21 @@ public: SetNeedsDisplay }; - // Offset is origin of the renderer minus origin of the graphics layer (so either zero or negative). - IntSize offsetFromRenderer() const { return m_offsetFromRenderer; } - void setOffsetFromRenderer(const IntSize&, ShouldSetNeedsDisplay = SetNeedsDisplay); + // Offset is origin of the renderer minus origin of the graphics layer. + FloatSize offsetFromRenderer() const { return m_offsetFromRenderer; } + void setOffsetFromRenderer(const FloatSize&, ShouldSetNeedsDisplay = SetNeedsDisplay); // The position of the layer (the location of its top-left corner in its parent) const FloatPoint& position() const { return m_position; } - virtual void setPosition(const FloatPoint& p) { m_position = p; } + virtual void setPosition(const FloatPoint& p) { m_approximatePosition = std::nullopt; m_position = p; } + + // approximatePosition, if set, overrides position() and is used during coverage rect computation. + FloatPoint approximatePosition() const { return m_approximatePosition ? m_approximatePosition.value() : m_position; } + void setApproximatePosition(std::optional<FloatPoint> p) { m_approximatePosition = p; } // For platforms that move underlying platform layers on a different thread for scrolling; just update the GraphicsLayer state. virtual void syncPosition(const FloatPoint& p) { m_position = p; } - + // Anchor point: (0, 0) is top left, (1, 1) is bottom right. The anchor point // affects the origin of the transforms. const FloatPoint3D& anchorPoint() const { return m_anchorPoint; } @@ -299,12 +326,15 @@ public: // The size of the layer. const FloatSize& size() const { return m_size; } - virtual void setSize(const FloatSize&); + WEBCORE_EXPORT virtual void setSize(const FloatSize&); // The boundOrigin affects the offset at which content is rendered, and sublayers are positioned. const FloatPoint& boundsOrigin() const { return m_boundsOrigin; } virtual void setBoundsOrigin(const FloatPoint& origin) { m_boundsOrigin = origin; } + // For platforms that move underlying platform layers on a different thread for scrolling; just update the GraphicsLayer state. + virtual void syncBoundsOrigin(const FloatPoint& origin) { m_boundsOrigin = origin; } + const TransformationMatrix& transform() const { return m_transform; } virtual void setTransform(const TransformationMatrix& t) { m_transform = t; } @@ -323,14 +353,22 @@ public: bool contentsAreVisible() const { return m_contentsVisible; } virtual void setContentsVisible(bool b) { m_contentsVisible = b; } + bool userInteractionEnabled() const { return m_userInteractionEnabled; } + virtual void setUserInteractionEnabled(bool b) { m_userInteractionEnabled = b; } + bool acceleratesDrawing() const { return m_acceleratesDrawing; } virtual void setAcceleratesDrawing(bool b) { m_acceleratesDrawing = b; } + bool usesDisplayListDrawing() const { return m_usesDisplayListDrawing; } + virtual void setUsesDisplayListDrawing(bool b) { m_usesDisplayListDrawing = b; } + + bool needsBackdrop() const { return !m_backdropFilters.isEmpty(); } + // The color used to paint the layer background. Pass an invalid color to remove it. // Note that this covers the entire layer. Use setContentsToSolidColor() if the color should // only cover the contentsRect. const Color& backgroundColor() const { return m_backgroundColor; } - virtual void setBackgroundColor(const Color&); + WEBCORE_EXPORT virtual void setBackgroundColor(const Color&); // opaque means that we know the layer contents have no alpha bool contentsOpaque() const { return m_contentsOpaque; } @@ -342,12 +380,15 @@ public: float opacity() const { return m_opacity; } virtual void setOpacity(float opacity) { m_opacity = opacity; } -#if ENABLE(CSS_FILTERS) const FilterOperations& filters() const { return m_filters; } - - // Returns true if filter can be rendered by the compositor + // Returns true if filter can be rendered by the compositor. virtual bool setFilters(const FilterOperations& filters) { m_filters = filters; return true; } -#endif + + const FilterOperations& backdropFilters() const { return m_backdropFilters; } + virtual bool setBackdropFilters(const FilterOperations& filters) { m_backdropFilters = filters; return true; } + + virtual void setBackdropFiltersRect(const FloatRoundedRect& backdropFiltersRect) { m_backdropFiltersRect = backdropFiltersRect; } + const FloatRoundedRect& backdropFiltersRect() const { return m_backdropFiltersRect; } #if ENABLE(CSS_COMPOSITING) BlendMode blendMode() const { return m_blendMode; } @@ -370,52 +411,58 @@ public: virtual void setContentsNeedsDisplay() { }; // The tile phase is relative to the GraphicsLayer bounds. - virtual void setContentsTilePhase(const IntPoint& p) { m_contentsTilePhase = p; } - IntPoint contentsTilePhase() const { return m_contentsTilePhase; } + virtual void setContentsTilePhase(const FloatSize& p) { m_contentsTilePhase = p; } + FloatSize contentsTilePhase() const { return m_contentsTilePhase; } - virtual void setContentsTileSize(const IntSize& s) { m_contentsTileSize = s; } - IntSize contentsTileSize() const { return m_contentsTileSize; } + virtual void setContentsTileSize(const FloatSize& s) { m_contentsTileSize = s; } + FloatSize contentsTileSize() const { return m_contentsTileSize; } bool hasContentsTiling() const { return !m_contentsTileSize.isEmpty(); } // Set that the position/size of the contents (image or video). - IntRect contentsRect() const { return m_contentsRect; } - virtual void setContentsRect(const IntRect& r) { m_contentsRect = r; } + FloatRect contentsRect() const { return m_contentsRect; } + virtual void setContentsRect(const FloatRect& r) { m_contentsRect = r; } + + // Set a rounded rect that will be used to clip the layer contents. + FloatRoundedRect contentsClippingRect() const { return m_contentsClippingRect; } + virtual void setContentsClippingRect(const FloatRoundedRect& roundedRect) { m_contentsClippingRect = roundedRect; } + + // Set a rounded rect that is used to clip this layer and its descendants (implies setting masksToBounds). + // Returns false if the platform can't support this rounded clip, and we should fall back to painting a mask. + FloatRoundedRect maskToBoundsRect() const { return m_masksToBoundsRect; }; + virtual bool setMasksToBoundsRect(const FloatRoundedRect& roundedRect) { m_masksToBoundsRect = roundedRect; return false; } - IntRect contentsClippingRect() const { return m_contentsClippingRect; } - virtual void setContentsClippingRect(const IntRect& r) { m_contentsClippingRect = r; } + Path shapeLayerPath() const; + virtual void setShapeLayerPath(const Path&); + + WindRule shapeLayerWindRule() const; + virtual void setShapeLayerWindRule(WindRule); // Transitions are identified by a special animation name that cannot clash with a keyframe identifier. static String animationNameForTransition(AnimatedPropertyID); // Return true if the animation is handled by the compositing system. If this returns - // false, the animation will be run by AnimationController. + // false, the animation will be run by CSSAnimationController. // These methods handle both transitions and keyframe animations. - virtual bool addAnimation(const KeyframeValueList&, const IntSize& /*boxSize*/, const Animation*, const String& /*animationName*/, double /*timeOffset*/) { return false; } + virtual bool addAnimation(const KeyframeValueList&, const FloatSize& /*boxSize*/, const Animation*, const String& /*animationName*/, double /*timeOffset*/) { return false; } virtual void pauseAnimation(const String& /*animationName*/, double /*timeOffset*/) { } virtual void removeAnimation(const String& /*animationName*/) { } - virtual void suspendAnimations(double time); - virtual void resumeAnimations(); + WEBCORE_EXPORT virtual void suspendAnimations(double time); + WEBCORE_EXPORT virtual void resumeAnimations(); // Layer contents virtual void setContentsToImage(Image*) { } virtual bool shouldDirectlyCompositeImage(Image*) const { return true; } - virtual void setContentsToMedia(PlatformLayer*) { } // video or plug-in #if PLATFORM(IOS) virtual PlatformLayer* contentsLayerForMedia() const { return 0; } #endif // Pass an invalid color to remove the contents layer. virtual void setContentsToSolidColor(const Color&) { } - virtual void setContentsToCanvas(PlatformLayer*) { } - // FIXME: webkit.org/b/109658 - // Should unify setContentsToMedia and setContentsToCanvas - virtual void setContentsToPlatformLayer(PlatformLayer* layer) { setContentsToMedia(layer); } - virtual bool hasContentsLayer() const { return false; } + virtual void setContentsToPlatformLayer(PlatformLayer*, ContentsLayerPurpose) { } + virtual bool usesContentsLayer() const { return false; } // Callback from the underlying graphics system to draw layer contents. - void paintGraphicsLayerContents(GraphicsContext&, const IntRect& clip); - // Callback from the underlying graphics system when the layer has been displayed - virtual void layerDidDisplay(PlatformLayer*) { } + void paintGraphicsLayerContents(GraphicsContext&, const FloatRect& clip); // For hosting this GraphicsLayer in a native layer hierarchy. virtual PlatformLayer* platformLayer() const { return 0; } @@ -441,32 +488,36 @@ public: virtual void setDebugBackgroundColor(const Color&) { } virtual void setDebugBorder(const Color&, float /*borderWidth*/) { } - enum CustomAppearance { NoCustomAppearance, ScrollingOverhang, ScrollingShadow }; + enum CustomAppearance { NoCustomAppearance, ScrollingOverhang, ScrollingShadow, LightBackdropAppearance, DarkBackdropAppearance }; virtual void setCustomAppearance(CustomAppearance customAppearance) { m_customAppearance = customAppearance; } CustomAppearance customAppearance() const { return m_customAppearance; } // z-position is the z-equivalent of position(). It's only used for debugging purposes. virtual float zPosition() const { return m_zPosition; } - virtual void setZPosition(float); + WEBCORE_EXPORT virtual void setZPosition(float); - virtual void distributeOpacity(float); - virtual float accumulatedOpacity() const; + WEBCORE_EXPORT virtual void distributeOpacity(float); + WEBCORE_EXPORT virtual float accumulatedOpacity() const; - virtual void setMaintainsPixelAlignment(bool maintainsAlignment) { m_maintainsPixelAlignment = maintainsAlignment; } - virtual bool maintainsPixelAlignment() const { return m_maintainsPixelAlignment; } #if PLATFORM(IOS) - virtual FloatSize pixelAlignmentOffset() const { return FloatSize(); } bool hasFlattenedPerspectiveTransform() const { return !preserves3D() && m_childrenTransform.hasPerspective(); } #endif + virtual FloatSize pixelAlignmentOffset() const { return FloatSize(); } virtual void setAppliesPageScale(bool appliesScale = true) { m_appliesPageScale = appliesScale; } virtual bool appliesPageScale() const { return m_appliesPageScale; } - float pageScaleFactor() const { return m_client ? m_client->pageScaleFactor() : 1; } - float deviceScaleFactor() const { return m_client ? m_client->deviceScaleFactor() : 1; } + float pageScaleFactor() const { return m_client.pageScaleFactor(); } + float deviceScaleFactor() const { return m_client.deviceScaleFactor(); } + + // Whether this layer is viewport constrained, implying that it's moved around externally from GraphicsLayer (e.g. by the scrolling tree). + virtual void setIsViewportConstrained(bool) { } + virtual bool isViewportConstrained() const { return false; } virtual void deviceOrPageScaleFactorChanged() { } - void noteDeviceOrPageScaleFactorChangedIncludingDescendants(); + WEBCORE_EXPORT void noteDeviceOrPageScaleFactorChangedIncludingDescendants(); + + void setIsInWindow(bool); // Some compositing systems may do internal batching to synchronize compositing updates // with updates drawn into the window. These methods flush internal batched state on this layer @@ -480,35 +531,26 @@ public: // Return a string with a human readable form of the layer tree, If debug is true // pointers for the layers and timing data will be included in the returned string. - String layerTreeAsText(LayerTreeAsTextBehavior = LayerTreeAsTextBehaviorNormal) const; + WEBCORE_EXPORT String layerTreeAsText(LayerTreeAsTextBehavior = LayerTreeAsTextBehaviorNormal) const; + + // For testing. + WEBCORE_EXPORT virtual String displayListAsText(DisplayList::AsTextFlags) const { return String(); } + + WEBCORE_EXPORT virtual void setIsTrackingDisplayListReplay(bool isTracking) { m_isTrackingDisplayListReplay = isTracking; } + WEBCORE_EXPORT virtual bool isTrackingDisplayListReplay() const { return m_isTrackingDisplayListReplay; } + WEBCORE_EXPORT virtual String replayDisplayListAsText(DisplayList::AsTextFlags) const { return String(); } // Return an estimate of the backing store memory cost (in bytes). May be incorrect for tiled layers. - virtual double backingStoreMemoryEstimate() const; + WEBCORE_EXPORT virtual double backingStoreMemoryEstimate() const; - bool usingTiledBacking() const { return m_usingTiledBacking; } virtual TiledBacking* tiledBacking() const { return 0; } void resetTrackedRepaints(); void addRepaintRect(const FloatRect&); - static bool supportsBackgroundColorContent() - { -#if USE(CA) || USE(TEXTURE_MAPPER) - return true; -#else - return false; -#endif - } - -#if USE(COORDINATED_GRAPHICS) + static bool supportsBackgroundColorContent(); + static bool supportsLayerType(Type); static bool supportsContentsTiling(); -#else - static bool supportsContentsTiling() - { - // FIXME: Enable the feature on different ports. - return false; - } -#endif void updateDebugIndicators(); @@ -516,20 +558,25 @@ public: virtual bool isGraphicsLayerCA() const { return false; } virtual bool isGraphicsLayerCARemote() const { return false; } + virtual bool isGraphicsLayerTextureMapper() const { return false; } + virtual bool isCoordinatedGraphicsLayer() const { return false; } + + static void traverse(GraphicsLayer&, std::function<void (GraphicsLayer&)>); protected: + WEBCORE_EXPORT explicit GraphicsLayer(Type, GraphicsLayerClient&); + // Should be called from derived class destructors. Should call willBeDestroyed() on super. - virtual void willBeDestroyed(); + WEBCORE_EXPORT virtual void willBeDestroyed(); -#if ENABLE(CSS_FILTERS) // This method is used by platform GraphicsLayer classes to clear the filters // when compositing is not done in hardware. It is not virtual, so the caller // needs to notifiy the change to the platform layer as needed. void clearFilters() { m_filters.clear(); } + void clearBackdropFilters() { m_backdropFilters.clear(); } // Given a KeyframeValueList containing filterOperations, return true if the operations are valid. static int validateFilterOperations(const KeyframeValueList&); -#endif // Given a list of TransformAnimationValues, see if all the operations for each keyframe match. If so // return the index of the KeyframeValueList entry that has that list of operations (it may not be @@ -546,21 +593,23 @@ protected: GraphicsLayer* replicatedLayer() const { return m_replicatedLayer; } virtual void setReplicatedLayer(GraphicsLayer* layer) { m_replicatedLayer = layer; } - GraphicsLayer(GraphicsLayerClient*); - void dumpProperties(TextStream&, int indent, LayerTreeAsTextBehavior) const; virtual void dumpAdditionalProperties(TextStream&, int /*indent*/, LayerTreeAsTextBehavior) const { } - virtual void getDebugBorderInfo(Color&, float& width) const; + WEBCORE_EXPORT virtual void getDebugBorderInfo(Color&, float& width) const; - GraphicsLayerClient* m_client; + GraphicsLayerClient& m_client; String m_name; // Offset from the owning renderer - IntSize m_offsetFromRenderer; + FloatSize m_offsetFromRenderer; // Position is relative to the parent GraphicsLayer FloatPoint m_position; + + // If set, overrides m_position. Only used for coverage computation. + std::optional<FloatPoint> m_approximatePosition; + FloatPoint3D m_anchorPoint; FloatSize m_size; FloatPoint m_boundsOrigin; @@ -572,26 +621,29 @@ protected: float m_opacity; float m_zPosition; -#if ENABLE(CSS_FILTERS) FilterOperations m_filters; -#endif + FilterOperations m_backdropFilters; #if ENABLE(CSS_COMPOSITING) BlendMode m_blendMode; #endif + const Type m_type; + bool m_contentsOpaque : 1; bool m_preserves3D: 1; bool m_backfaceVisibility : 1; - bool m_usingTiledBacking : 1; bool m_masksToBounds : 1; bool m_drawsContent : 1; bool m_contentsVisible : 1; bool m_acceleratesDrawing : 1; - bool m_maintainsPixelAlignment : 1; + bool m_usesDisplayListDrawing : 1; bool m_appliesPageScale : 1; // Set for the layer which has the page scale applied to it. bool m_showDebugBorder : 1; bool m_showRepaintCounter : 1; + bool m_isMaskLayer : 1; + bool m_isTrackingDisplayListReplay : 1; + bool m_userInteractionEnabled : 1; GraphicsLayerPaintingPhase m_paintingPhase; CompositingCoordinatesOrientation m_contentsOrientation; // affects orientation of layer contents @@ -606,25 +658,33 @@ protected: GraphicsLayer* m_replicatedLayer; // For a replica layer, a reference to the original layer. FloatPoint m_replicatedLayerPosition; // For a replica layer, the position of the replica. - IntRect m_contentsRect; - IntRect m_contentsClippingRect; - IntPoint m_contentsTilePhase; - IntSize m_contentsTileSize; + FloatRect m_contentsRect; + FloatRoundedRect m_contentsClippingRect; + FloatRoundedRect m_masksToBoundsRect; + FloatSize m_contentsTilePhase; + FloatSize m_contentsTileSize; + FloatRoundedRect m_backdropFiltersRect; int m_repaintCount; CustomAppearance m_customAppearance; + +#if USE(CA) + Path m_shapeLayerPath; + WindRule m_shapeLayerWindRule { RULE_NONZERO }; +#endif }; -#define GRAPHICSLAYER_TYPE_CASTS(ToValueTypeName, predicate) \ - TYPE_CASTS_BASE(ToValueTypeName, WebCore::GraphicsLayer, value, value->predicate, value.predicate) +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const Vector<GraphicsLayer::PlatformLayerID>&); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const GraphicsLayer::CustomAppearance&); } // namespace WebCore -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. +#define SPECIALIZE_TYPE_TRAITS_GRAPHICSLAYER(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \ + static bool isType(const WebCore::GraphicsLayer& layer) { return layer.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() + +#if ENABLE(TREE_DEBUGGING) +// Outside the WebCore namespace for ease of invocation from the debugger. void showGraphicsLayerTree(const WebCore::GraphicsLayer* layer); #endif - -#endif // USE(ACCELERATED_COMPOSITING) - -#endif // GraphicsLayer_h diff --git a/Source/WebCore/platform/graphics/GraphicsLayerClient.h b/Source/WebCore/platform/graphics/GraphicsLayerClient.h index 90f9e823b..f6e90b8e3 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerClient.h +++ b/Source/WebCore/platform/graphics/GraphicsLayerClient.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,11 +26,14 @@ #ifndef GraphicsLayerClient_h #define GraphicsLayerClient_h -#if USE(ACCELERATED_COMPOSITING) +#include "IntSize.h" +#include "TiledBacking.h" +#include <wtf/Forward.h> namespace WebCore { class FloatPoint; +class FloatRect; class GraphicsContext; class GraphicsLayer; class IntPoint; @@ -38,41 +41,59 @@ class IntRect; class TransformationMatrix; enum GraphicsLayerPaintingPhaseFlags { - GraphicsLayerPaintBackground = (1 << 0), - GraphicsLayerPaintForeground = (1 << 1), - GraphicsLayerPaintMask = (1 << 2), - GraphicsLayerPaintOverflowContents = (1 << 3), - GraphicsLayerPaintCompositedScroll = (1 << 4), - GraphicsLayerPaintAllWithOverflowClip = (GraphicsLayerPaintBackground | GraphicsLayerPaintForeground | GraphicsLayerPaintMask) + GraphicsLayerPaintBackground = 1 << 0, + GraphicsLayerPaintForeground = 1 << 1, + GraphicsLayerPaintMask = 1 << 2, + GraphicsLayerPaintClipPath = 1 << 3, + GraphicsLayerPaintOverflowContents = 1 << 4, + GraphicsLayerPaintCompositedScroll = 1 << 5, + GraphicsLayerPaintChildClippingMask = 1 << 6, + GraphicsLayerPaintAllWithOverflowClip = GraphicsLayerPaintBackground | GraphicsLayerPaintForeground }; typedef unsigned GraphicsLayerPaintingPhase; enum AnimatedPropertyID { AnimatedPropertyInvalid, - AnimatedPropertyWebkitTransform, + AnimatedPropertyTransform, AnimatedPropertyOpacity, AnimatedPropertyBackgroundColor, - AnimatedPropertyWebkitFilter + AnimatedPropertyFilter +#if ENABLE(FILTERS_LEVEL_2) + , AnimatedPropertyWebkitBackdropFilter +#endif +}; + +enum LayerTreeAsTextBehaviorFlags { + LayerTreeAsTextBehaviorNormal = 0, + LayerTreeAsTextDebug = 1 << 0, // Dump extra debugging info like layer addresses. + LayerTreeAsTextIncludeVisibleRects = 1 << 1, + LayerTreeAsTextIncludeTileCaches = 1 << 2, + LayerTreeAsTextIncludeRepaintRects = 1 << 3, + LayerTreeAsTextIncludePaintingPhases = 1 << 4, + LayerTreeAsTextIncludeContentLayers = 1 << 5, + LayerTreeAsTextIncludePageOverlayLayers = 1 << 6, + LayerTreeAsTextIncludeAcceleratesDrawing = 1 << 7, }; +typedef unsigned LayerTreeAsTextBehavior; class GraphicsLayerClient { public: virtual ~GraphicsLayerClient() {} - virtual bool shouldUseTiledBacking(const GraphicsLayer*) const { return false; } virtual void tiledBackingUsageChanged(const GraphicsLayer*, bool /*usingTiledBacking*/) { } // Callback for when hardware-accelerated animation started. - virtual void notifyAnimationStarted(const GraphicsLayer*, double time) = 0; + virtual void notifyAnimationStarted(const GraphicsLayer*, const String& /*animationKey*/, double /*time*/) { } + virtual void notifyAnimationEnded(const GraphicsLayer*, const String& /*animationKey*/) { } // Notification that a layer property changed that requires a subsequent call to flushCompositingState() // to appear on the screen. - virtual void notifyFlushRequired(const GraphicsLayer*) = 0; + virtual void notifyFlushRequired(const GraphicsLayer*) { } // Notification that this layer requires a flush before the next display refresh. virtual void notifyFlushBeforeDisplayRefresh(const GraphicsLayer*) { } - virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) = 0; + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const FloatRect& /* inClip */) { } virtual void didCommitChangesForLayer(const GraphicsLayer*) const { } // Provides current transform (taking transform-origin and animations into account). Input matrix has been @@ -87,14 +108,25 @@ public: virtual float deviceScaleFactor() const { return 1; } // Page scale factor. virtual float pageScaleFactor() const { return 1; } + virtual float zoomedOutPageScaleFactor() const { return 0; } virtual float contentsScaleMultiplierForNewTiles(const GraphicsLayer*) const { return 1; } + virtual bool paintsOpaquelyAtNonIntegralScales(const GraphicsLayer*) const { return false; } virtual bool isTrackingRepaints() const { return false; } - virtual bool shouldSkipLayerInDump(const GraphicsLayer*) const { return false; } + virtual bool shouldSkipLayerInDump(const GraphicsLayer*, LayerTreeAsTextBehavior) const { return false; } virtual bool shouldDumpPropertyForLayer(const GraphicsLayer*, const char*) const { return true; } + virtual bool shouldAggressivelyRetainTiles(const GraphicsLayer*) const { return false; } + virtual bool shouldTemporarilyRetainTileCohorts(const GraphicsLayer*) const { return true; } + + virtual bool useGiantTiles() const { return false; } + + virtual bool needsPixelAligment() const { return false; } + + virtual bool needsIOSDumpRenderTreeMainFrameRenderViewLayerIsAlwaysOpaqueHack(const GraphicsLayer&) const { return false; } + #ifndef NDEBUG // RenderLayerBacking overrides this to verify that it is not // currently painting contents. An ASSERT fails, if it is. @@ -107,6 +139,4 @@ public: } // namespace WebCore -#endif // USE(ACCELERATED_COMPOSITING) - #endif // GraphicsLayerClient_h diff --git a/Source/WebCore/platform/graphics/GraphicsLayerFactory.h b/Source/WebCore/platform/graphics/GraphicsLayerFactory.h index 6413e93cb..bc09caf06 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerFactory.h +++ b/Source/WebCore/platform/graphics/GraphicsLayerFactory.h @@ -26,24 +26,18 @@ #ifndef GraphicsLayerFactory_h #define GraphicsLayerFactory_h -#if USE(ACCELERATED_COMPOSITING) - +#include "GraphicsLayer.h" #include <wtf/Forward.h> namespace WebCore { -class GraphicsLayer; -class GraphicsLayerClient; - class GraphicsLayerFactory { public: virtual ~GraphicsLayerFactory() { } - virtual std::unique_ptr<GraphicsLayer> createGraphicsLayer(GraphicsLayerClient*) = 0; + virtual std::unique_ptr<GraphicsLayer> createGraphicsLayer(GraphicsLayer::Type, GraphicsLayerClient&) = 0; }; } // namespace WebCore -#endif // USE(ACCELERATED_COMPOSITING) - #endif // GraphicsLayerFactory_h diff --git a/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp b/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp index 04363ccdc..95dfab485 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp +++ b/Source/WebCore/platform/graphics/GraphicsLayerTransform.cpp @@ -78,13 +78,13 @@ void GraphicsLayerTransform::setChildrenTransform(const TransformationMatrix& tr m_dirty = true; } -TransformationMatrix GraphicsLayerTransform::combined() +const TransformationMatrix& GraphicsLayerTransform::combined() const { ASSERT(!m_dirty); return m_combined; } -TransformationMatrix GraphicsLayerTransform::combinedForChildren() +const TransformationMatrix& GraphicsLayerTransform::combinedForChildren() const { ASSERT(!m_dirty); if (m_childrenDirty) @@ -96,10 +96,10 @@ void GraphicsLayerTransform::combineTransforms(const TransformationMatrix& paren { float originX = m_anchorPoint.x() * m_size.width(); float originY = m_anchorPoint.y() * m_size.height(); - m_combined = - TransformationMatrix(parentTransform) - .translate3d(originX + m_position.x(), originY + m_position.y(), m_anchorPoint.z() ) - .multiply(m_local); + m_combined = parentTransform; + m_combined + .translate3d(originX + m_position.x(), originY + m_position.y(), m_anchorPoint.z()) + .multiply(m_local); // The children transform will take it from here, if it gets used. m_combinedForChildren = m_combined; @@ -109,7 +109,7 @@ void GraphicsLayerTransform::combineTransforms(const TransformationMatrix& paren m_childrenDirty = true; } -void GraphicsLayerTransform::combineTransformsForChildren() +void GraphicsLayerTransform::combineTransformsForChildren() const { ASSERT(!m_dirty); ASSERT(m_childrenDirty); diff --git a/Source/WebCore/platform/graphics/GraphicsLayerTransform.h b/Source/WebCore/platform/graphics/GraphicsLayerTransform.h index 5da25b4d8..5a2f66ffb 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerTransform.h +++ b/Source/WebCore/platform/graphics/GraphicsLayerTransform.h @@ -36,25 +36,25 @@ public: void setFlattening(bool); void setLocalTransform(const TransformationMatrix&); void setChildrenTransform(const TransformationMatrix&); - TransformationMatrix combined(); - TransformationMatrix combinedForChildren(); + const TransformationMatrix& combined() const; + const TransformationMatrix& combinedForChildren() const; void combineTransforms(const TransformationMatrix& parentTransform); private: - void combineTransformsForChildren(); + void combineTransformsForChildren() const; FloatPoint3D m_anchorPoint; FloatPoint m_position; FloatSize m_size; bool m_flattening; bool m_dirty; - bool m_childrenDirty; + mutable bool m_childrenDirty; TransformationMatrix m_local; TransformationMatrix m_children; TransformationMatrix m_combined; - TransformationMatrix m_combinedForChildren; + mutable TransformationMatrix m_combinedForChildren; }; } diff --git a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp index b3c943644..a911bddb4 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp +++ b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp @@ -24,27 +24,26 @@ */ #include "config.h" - -#if USE(ACCELERATED_COMPOSITING) - #include "GraphicsLayerUpdater.h" +#include "DisplayRefreshMonitorManager.h" #include "GraphicsLayer.h" namespace WebCore { -GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient* client, PlatformDisplayID displayID) +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) +GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient& client, PlatformDisplayID displayID) : m_client(client) - , m_scheduled(false) { -#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) - DisplayRefreshMonitorManager::sharedManager()->registerClient(this); - DisplayRefreshMonitorManager::sharedManager()->windowScreenDidChange(displayID, this); - DisplayRefreshMonitorManager::sharedManager()->scheduleAnimation(this); + DisplayRefreshMonitorManager::sharedManager().registerClient(*this); + DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this); + DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this); +} #else - UNUSED_PARAM(displayID); -#endif +GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient&, PlatformDisplayID) +{ } +#endif GraphicsLayerUpdater::~GraphicsLayerUpdater() { @@ -57,7 +56,7 @@ void GraphicsLayerUpdater::scheduleUpdate() return; #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) - DisplayRefreshMonitorManager::sharedManager()->scheduleAnimation(this); + DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this); #endif m_scheduled = true; } @@ -65,21 +64,25 @@ void GraphicsLayerUpdater::scheduleUpdate() void GraphicsLayerUpdater::screenDidChange(PlatformDisplayID displayID) { #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) - DisplayRefreshMonitorManager::sharedManager()->windowScreenDidChange(displayID, this); + DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this); #else UNUSED_PARAM(displayID); #endif } -void GraphicsLayerUpdater::displayRefreshFired(double timestamp) +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) +void GraphicsLayerUpdater::displayRefreshFired() { - UNUSED_PARAM(timestamp); m_scheduled = false; - - if (m_client) - m_client->flushLayers(this); + m_client.flushLayersSoon(*this); } +#endif -} // namespace WebCore +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) +RefPtr<DisplayRefreshMonitor> GraphicsLayerUpdater::createDisplayRefreshMonitor(PlatformDisplayID displayID) const +{ + return m_client.createDisplayRefreshMonitor(displayID); +} +#endif -#endif // USE(ACCELERATED_COMPOSITING) +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h index 108099ae6..472f2d3b0 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h +++ b/Source/WebCore/platform/graphics/GraphicsLayerUpdater.h @@ -23,12 +23,9 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GraphicsLayerUpdater_h -#define GraphicsLayerUpdater_h +#pragma once -#if USE(ACCELERATED_COMPOSITING) - -#include "DisplayRefreshMonitor.h" +#include "DisplayRefreshMonitorClient.h" #include "PlatformScreen.h" namespace WebCore { @@ -38,7 +35,10 @@ class GraphicsLayerUpdater; class GraphicsLayerUpdaterClient { public: virtual ~GraphicsLayerUpdaterClient() { } - virtual void flushLayers(GraphicsLayerUpdater*) = 0; + virtual void flushLayersSoon(GraphicsLayerUpdater&) = 0; +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + virtual RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const = 0; +#endif }; class GraphicsLayerUpdater @@ -46,22 +46,24 @@ class GraphicsLayerUpdater : public DisplayRefreshMonitorClient #endif { + WTF_MAKE_FAST_ALLOCATED; public: - GraphicsLayerUpdater(GraphicsLayerUpdaterClient*, PlatformDisplayID); + GraphicsLayerUpdater(GraphicsLayerUpdaterClient&, PlatformDisplayID); virtual ~GraphicsLayerUpdater(); void scheduleUpdate(); void screenDidChange(PlatformDisplayID); -private: - virtual void displayRefreshFired(double timestamp); +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const override; +#endif - GraphicsLayerUpdaterClient* m_client; - bool m_scheduled; +private: +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + void displayRefreshFired() override; + GraphicsLayerUpdaterClient& m_client; +#endif + bool m_scheduled { false }; }; } // namespace WebCore - -#endif // USE(ACCELERATED_COMPOSITING) - -#endif // GraphicsLayerUpdater_h diff --git a/Source/WebCore/platform/graphics/GraphicsTypes.cpp b/Source/WebCore/platform/graphics/GraphicsTypes.cpp index 8d1150194..28e5cc6bb 100644 --- a/Source/WebCore/platform/graphics/GraphicsTypes.cpp +++ b/Source/WebCore/platform/graphics/GraphicsTypes.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2012 Rik Cabanier (cabanier@adobe.com) + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,6 +28,7 @@ #include "config.h" #include "GraphicsTypes.h" +#include "TextStream.h" #include <wtf/Assertions.h> #include <wtf/text/WTFString.h> @@ -45,15 +47,17 @@ static const char* const compositeOperatorNames[] = { "destination-atop", "xor", "darker", - "lighter" + "lighter", + "difference" }; static const char* const blendOperatorNames[] = { + "normal", "multiply", "screen", - "overlay", "darken", "lighten", + "overlay", "color-dodge", "color-burn", "hard-light", @@ -63,11 +67,25 @@ static const char* const blendOperatorNames[] = { "hue", "saturation", "color", - "luminosity" + "luminosity", + "plus-darker", + "plus-lighter" }; const int numCompositeOperatorNames = WTF_ARRAY_LENGTH(compositeOperatorNames); const int numBlendOperatorNames = WTF_ARRAY_LENGTH(blendOperatorNames); +bool parseBlendMode(const String& s, BlendMode& blendMode) +{ + for (int i = 0; i < numBlendOperatorNames; i++) { + if (s == blendOperatorNames[i]) { + blendMode = static_cast<BlendMode>(i + BlendModeNormal); + return true; + } + } + + return false; +} + bool parseCompositeAndBlendOperator(const String& s, CompositeOperator& op, BlendMode& blendOp) { for (int i = 0; i < numCompositeOperatorNames; i++) { @@ -78,13 +96,10 @@ bool parseCompositeAndBlendOperator(const String& s, CompositeOperator& op, Blen } } - for (int i = 0; i < numBlendOperatorNames; i++) { - if (s == blendOperatorNames[i]) { - blendOp = static_cast<BlendMode>(i+1); - // For now, blending will always assume source-over. This will be fixed in the future - op = CompositeSourceOver; - return true; - } + if (parseBlendMode(s, blendOp)) { + // For now, blending will always assume source-over. This will be fixed in the future + op = CompositeSourceOver; + return true; } return false; @@ -96,13 +111,20 @@ String compositeOperatorName(CompositeOperator op, BlendMode blendOp) { ASSERT(op >= 0); ASSERT(op < numCompositeOperatorNames); - ASSERT(blendOp >= 0); + ASSERT(blendOp >= BlendModeNormal); ASSERT(blendOp <= numBlendOperatorNames); - if (blendOp != BlendModeNormal) - return blendOperatorNames[blendOp-1]; + if (blendOp > BlendModeNormal) + return blendOperatorNames[blendOp - BlendModeNormal]; return compositeOperatorNames[op]; } +static String blendModeName(BlendMode blendOp) +{ + ASSERT(blendOp >= BlendModeNormal); + ASSERT(blendOp <= BlendModePlusLighter); + return blendOperatorNames[blendOp - BlendModeNormal]; +} + bool parseLineCap(const String& s, LineCap& cap) { if (s == "butt") { @@ -223,4 +245,61 @@ bool parseTextBaseline(const String& s, TextBaseline& baseline) return false; } +TextStream& operator<<(TextStream& ts, CompositeOperator op) +{ + return ts << compositeOperatorName(op, BlendModeNormal); +} + +TextStream& operator<<(TextStream& ts, BlendMode blendMode) +{ + return ts << blendModeName(blendMode); +} + +TextStream& operator<<(TextStream& ts, WindRule rule) +{ + switch (rule) { + case RULE_NONZERO: + ts << "NON-ZERO"; + break; + case RULE_EVENODD: + ts << "EVEN-ODD"; + break; + } + + return ts; +} + +TextStream& operator<<(TextStream& ts, LineCap capStyle) +{ + switch (capStyle) { + case ButtCap: + ts << "BUTT"; + break; + case RoundCap: + ts << "ROUND"; + break; + case SquareCap: + ts << "SQUARE"; + break; + } + return ts; +} + +TextStream& operator<<(TextStream& ts, LineJoin joinStyle) +{ + switch (joinStyle) { + case MiterJoin: + ts << "MITER"; + break; + case RoundJoin: + ts << "ROUND"; + break; + case BevelJoin: + ts << "BEVEL"; + break; + } + return ts; +} + + } diff --git a/Source/WebCore/platform/graphics/GraphicsTypes.h b/Source/WebCore/platform/graphics/GraphicsTypes.h index 586f7ec69..ede792db5 100644 --- a/Source/WebCore/platform/graphics/GraphicsTypes.h +++ b/Source/WebCore/platform/graphics/GraphicsTypes.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,76 +26,101 @@ #ifndef GraphicsTypes_h #define GraphicsTypes_h +#include "WindRule.h" #include <wtf/Forward.h> namespace WebCore { - enum CompositeOperator { - CompositeClear, - CompositeCopy, - CompositeSourceOver, - CompositeSourceIn, - CompositeSourceOut, - CompositeSourceAtop, - CompositeDestinationOver, - CompositeDestinationIn, - CompositeDestinationOut, - CompositeDestinationAtop, - CompositeXOR, - CompositePlusDarker, - CompositePlusLighter, - CompositeDifference - }; - - enum BlendMode { - BlendModeNormal, - BlendModeMultiply, - BlendModeScreen, - BlendModeOverlay, - BlendModeDarken, - BlendModeLighten, - BlendModeColorDodge, - BlendModeColorBurn, - BlendModeHardLight, - BlendModeSoftLight, - BlendModeDifference, - BlendModeExclusion, - BlendModeHue, - BlendModeSaturation, - BlendModeColor, - BlendModeLuminosity - }; - - enum GradientSpreadMethod { - SpreadMethodPad, - SpreadMethodReflect, - SpreadMethodRepeat - }; - - enum LineCap { ButtCap, RoundCap, SquareCap }; - - enum LineJoin { MiterJoin, RoundJoin, BevelJoin }; - - enum HorizontalAlignment { AlignLeft, AlignRight, AlignHCenter }; - - enum TextBaseline { AlphabeticTextBaseline, TopTextBaseline, MiddleTextBaseline, BottomTextBaseline, IdeographicTextBaseline, HangingTextBaseline }; - - enum TextAlign { StartTextAlign, EndTextAlign, LeftTextAlign, CenterTextAlign, RightTextAlign }; - - String compositeOperatorName(CompositeOperator, BlendMode); - bool parseCompositeAndBlendOperator(const String&, CompositeOperator&, BlendMode&); - - String lineCapName(LineCap); - bool parseLineCap(const String&, LineCap&); - - String lineJoinName(LineJoin); - bool parseLineJoin(const String&, LineJoin&); - - String textAlignName(TextAlign); - bool parseTextAlign(const String&, TextAlign&); - - String textBaselineName(TextBaseline); - bool parseTextBaseline(const String&, TextBaseline&); +enum CompositeOperator { + CompositeClear, + CompositeCopy, + CompositeSourceOver, + CompositeSourceIn, + CompositeSourceOut, + CompositeSourceAtop, + CompositeDestinationOver, + CompositeDestinationIn, + CompositeDestinationOut, + CompositeDestinationAtop, + CompositeXOR, + CompositePlusDarker, + CompositePlusLighter, + CompositeDifference +}; + +enum BlendMode { + BlendModeNormal = 1, // Start with 1 to match SVG's blendmode enumeration. + BlendModeMultiply, + BlendModeScreen, + BlendModeDarken, + BlendModeLighten, + BlendModeOverlay, + BlendModeColorDodge, + BlendModeColorBurn, + BlendModeHardLight, + BlendModeSoftLight, + BlendModeDifference, + BlendModeExclusion, + BlendModeHue, + BlendModeSaturation, + BlendModeColor, + BlendModeLuminosity, + BlendModePlusDarker, + BlendModePlusLighter +}; + +enum GradientSpreadMethod { + SpreadMethodPad, + SpreadMethodReflect, + SpreadMethodRepeat +}; + +enum InterpolationQuality { + InterpolationDefault, + InterpolationNone, + InterpolationLow, + InterpolationMedium, + InterpolationHigh +}; + +enum LineCap { ButtCap, RoundCap, SquareCap }; + +enum LineJoin { MiterJoin, RoundJoin, BevelJoin }; + +enum HorizontalAlignment { AlignLeft, AlignRight, AlignHCenter }; + +enum TextBaseline { AlphabeticTextBaseline, TopTextBaseline, MiddleTextBaseline, BottomTextBaseline, IdeographicTextBaseline, HangingTextBaseline }; + +enum TextAlign { StartTextAlign, EndTextAlign, LeftTextAlign, CenterTextAlign, RightTextAlign }; + +enum RenderingMode { + Unaccelerated, + UnacceleratedNonPlatformBuffer, // Use plain memory allocation rather than platform API to allocate backing store. + Accelerated +}; + +String compositeOperatorName(CompositeOperator, BlendMode); +bool parseBlendMode(const String&, BlendMode&); +bool parseCompositeAndBlendOperator(const String&, CompositeOperator&, BlendMode&); + +String lineCapName(LineCap); +bool parseLineCap(const String&, LineCap&); + +String lineJoinName(LineJoin); +bool parseLineJoin(const String&, LineJoin&); + +String textAlignName(TextAlign); +bool parseTextAlign(const String&, TextAlign&); + +String textBaselineName(TextBaseline); +bool parseTextBaseline(const String&, TextBaseline&); + +class TextStream; +WEBCORE_EXPORT TextStream& operator<<(TextStream&, BlendMode); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, CompositeOperator); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, WindRule); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, LineCap); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, LineJoin); } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/GraphicsTypes3D.h b/Source/WebCore/platform/graphics/GraphicsTypes3D.h index 1fa8eaab1..1ab1a4f64 100644 --- a/Source/WebCore/platform/graphics/GraphicsTypes3D.h +++ b/Source/WebCore/platform/graphics/GraphicsTypes3D.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -46,6 +46,8 @@ typedef float GC3Dclampf; typedef intptr_t GC3Dintptr; typedef intptr_t GC3Dsizeiptr; typedef char GC3Dchar; +typedef long long GC3Dint64; +typedef unsigned long long GC3Duint64; typedef GC3Duint Platform3DObject; diff --git a/Source/WebCore/platform/graphics/ISOVTTCue.cpp b/Source/WebCore/platform/graphics/ISOVTTCue.cpp new file mode 100644 index 000000000..f231948cd --- /dev/null +++ b/Source/WebCore/platform/graphics/ISOVTTCue.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ISOVTTCue.h" + +#include "Logging.h" +#include <runtime/ArrayBuffer.h> +#include <runtime/DataView.h> +#include <runtime/Int8Array.h> +#include <runtime/JSCInlines.h> +#include <runtime/TypedArrayInlines.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { +class ArrayBuffer; +} + +namespace WebCore { + +ISOBox::ISOBox(ArrayBuffer* data) +{ + m_type = peekType(data); + m_length = peekLength(data); + ASSERT(m_length >= 8); +} + +String ISOBox::peekType(ArrayBuffer* data) +{ + ASSERT_WITH_SECURITY_IMPLICATION(data->byteLength() >= 2 * sizeof(uint32_t)); + if (data->byteLength() < 2 * sizeof(uint32_t)) + return emptyString(); + + StringBuilder builder; + RefPtr<Int8Array> array = JSC::Int8Array::create(data, 4, sizeof(uint32_t)); + for (int i = 0; i < 4; ++i) + builder.append(array->item(i)); + return builder.toString(); +} + +size_t ISOBox::peekLength(ArrayBuffer* data) +{ + ASSERT_WITH_SECURITY_IMPLICATION(data->byteLength() >= sizeof(uint32_t)); + if (data->byteLength() < sizeof(uint32_t)) + return 0; + + return JSC::DataView::create(data, 0, sizeof(uint32_t))->get<uint32_t>(0, false); +} + +String ISOBox::peekString(ArrayBuffer* data, unsigned offset, unsigned length) +{ + ASSERT_WITH_SECURITY_IMPLICATION(offset + length <= data->byteLength()); + if (data->byteLength() < offset + length) + return emptyString(); + + return String::fromUTF8(JSC::Uint8Array::create(data, offset, length)->data(), length); +} + +static const AtomicString& vttCueBoxType() +{ + static NeverDestroyed<AtomicString> vttc("vttc", AtomicString::ConstructFromLiteral); + return vttc; +} + +static const AtomicString& vttIdBoxType() +{ + static NeverDestroyed<AtomicString> iden("iden", AtomicString::ConstructFromLiteral); + return iden; +} + +static const AtomicString& vttSettingsBoxType() +{ + static NeverDestroyed<AtomicString> sttg("sttg", AtomicString::ConstructFromLiteral); + return sttg; +} + +static const AtomicString& vttPayloadBoxType() +{ + static NeverDestroyed<AtomicString> payl("payl", AtomicString::ConstructFromLiteral); + return payl; +} + +static const AtomicString& vttCurrentTimeBoxType() +{ + static NeverDestroyed<AtomicString> ctim("ctim", AtomicString::ConstructFromLiteral); + return ctim; +} + +static const AtomicString& vttCueSourceIDBoxType() +{ + static NeverDestroyed<AtomicString> vsid("vsid", AtomicString::ConstructFromLiteral); + return vsid; +} + +const AtomicString& ISOWebVTTCue::boxType() +{ + return vttCueBoxType(); +} + +ISOWebVTTCue::ISOWebVTTCue(const MediaTime& presentationTime, const MediaTime& duration, JSC::ArrayBuffer* buffer) + : ISOBox(buffer) + , m_presentationTime(presentationTime) + , m_duration(duration) +{ + size_t offset = ISOBox::boxHeaderSize(); + while (offset < length() && length() - offset > ISOBox::boxHeaderSize()) { + RefPtr<ArrayBuffer> subBuffer = buffer->slice(offset); + String boxType = ISOBox::peekType(subBuffer.get()); + size_t boxSize = ISOBox::peekLength(subBuffer.get()); + size_t boxDataSize = boxSize - ISOBox::boxHeaderSize(); + + if (boxType == vttCueSourceIDBoxType()) + m_sourceID = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize); + else if (boxType == vttIdBoxType()) + m_identifier = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize); + else if (boxType == vttCurrentTimeBoxType()) + m_originalStartTime = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize); + else if (boxType == vttSettingsBoxType()) + m_settings = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize); + else if (boxType == vttPayloadBoxType()) + m_cueText = peekString(subBuffer.get(), ISOBox::boxHeaderSize(), boxDataSize); + else + LOG(Media, "ISOWebVTTCue::ISOWebVTTCue - skipping box id = \"%s\", size = %zu", boxType.utf8().data(), boxSize); + + offset += boxSize; + } +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ISOVTTCue.h b/Source/WebCore/platform/graphics/ISOVTTCue.h new file mode 100644 index 000000000..edc57da45 --- /dev/null +++ b/Source/WebCore/platform/graphics/ISOVTTCue.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ISOVTTCue_h +#define ISOVTTCue_h + +#include <wtf/MediaTime.h> +#include <wtf/text/WTFString.h> + +namespace JSC { +class ArrayBuffer; +} + +namespace WebCore { + +// An ISOBox represents a ISO-BMFF box. Data in the structure is big-endian. The layout of the data structure as follows: +// 4 bytes : 4CC : identifier +// 4 bytes : unsigned : length +class ISOBox { +public: + static String peekType(JSC::ArrayBuffer*); + static size_t peekLength(JSC::ArrayBuffer*); + static String peekString(JSC::ArrayBuffer*, unsigned offset, unsigned length); + static unsigned boxHeaderSize() { return 2 * sizeof(uint32_t); } + + size_t length() const { return m_length; } + const AtomicString& type() const { return m_type; } + +protected: + ISOBox(JSC::ArrayBuffer*); + +private: + size_t m_length; + AtomicString m_type; +}; + +// 4 bytes : 4CC : identifier = 'vttc' +// 4 bytes : unsigned : length +// N bytes : CueSourceIDBox : box : optional +// N bytes : CueIDBox : box : optional +// N bytes : CueTimeBox : box : optional +// N bytes : CueSettingsBox : box : optional +// N bytes : CuePayloadBox : box : required +class ISOWebVTTCue : public ISOBox { +public: + ISOWebVTTCue(const MediaTime& presentationTime, const MediaTime& duration, JSC::ArrayBuffer*); + + static const AtomicString& boxType(); + + const MediaTime& presentationTime() const { return m_presentationTime; } + const MediaTime& duration() const { return m_duration; } + + const String& sourceID() const { return m_sourceID; } + const String& id() const { return m_identifier; } + const String& originalStartTime() const { return m_originalStartTime; } + const String& settings() const { return m_settings; } + const String& cueText() const { return m_cueText; } + +private: + MediaTime m_presentationTime; + MediaTime m_duration; + + String m_sourceID; + String m_identifier; + String m_originalStartTime; + String m_settings; + String m_cueText; +}; + +} + +#endif // ISOVTTCue_h diff --git a/Source/WebCore/platform/graphics/Icon.h b/Source/WebCore/platform/graphics/Icon.h index 82798c306..7b6a30470 100644 --- a/Source/WebCore/platform/graphics/Icon.h +++ b/Source/WebCore/platform/graphics/Icon.h @@ -21,17 +21,16 @@ #ifndef Icon_h #define Icon_h -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> #include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/RetainPtr.h> #include <wtf/Vector.h> #if PLATFORM(IOS) -#include "NativeImagePtr.h" +#include "NativeImage.h" #include <CoreGraphics/CoreGraphics.h> -#include <wtf/RetainPtr.h> #elif PLATFORM(MAC) -#include <wtf/RetainPtr.h> OBJC_CLASS NSImage; #elif PLATFORM(WIN) typedef struct HICON__* HICON; @@ -42,28 +41,33 @@ typedef struct _GdkPixbuf GdkPixbuf; namespace WebCore { class GraphicsContext; -class IntRect; +class FloatRect; class Icon : public RefCounted<Icon> { public: - static PassRefPtr<Icon> createIconForFiles(const Vector<String>& filenames); + WEBCORE_EXPORT static RefPtr<Icon> createIconForFiles(const Vector<String>& filenames); - ~Icon(); + WEBCORE_EXPORT ~Icon(); - void paint(GraphicsContext*, const IntRect&); + void paint(GraphicsContext&, const FloatRect&); #if PLATFORM(WIN) - static PassRefPtr<Icon> create(HICON hIcon) { return adoptRef(new Icon(hIcon)); } + static RefPtr<Icon> create(HICON hIcon) { return adoptRef(new Icon(hIcon)); } #endif #if PLATFORM(IOS) // FIXME: Make this work for non-iOS ports and remove the PLATFORM(IOS)-guard. - static PassRefPtr<Icon> createIconForImage(NativeImagePtr); + WEBCORE_EXPORT static RefPtr<Icon> createIconForImage(const NativeImagePtr&); +#endif + +#if PLATFORM(MAC) + static RefPtr<Icon> createIconForUTI(const String&); + static RefPtr<Icon> createIconForFileExtension(const String&); #endif private: #if PLATFORM(IOS) - Icon(CGImageRef); + Icon(const RetainPtr<CGImageRef>&); RetainPtr<CGImageRef> m_cgImage; #elif PLATFORM(MAC) Icon(NSImage*); @@ -74,9 +78,6 @@ private: #elif PLATFORM(GTK) Icon(); GdkPixbuf* m_icon; -#elif PLATFORM(EFL) - Icon(); - Evas_Object* m_icon; #endif }; diff --git a/Source/WebCore/platform/graphics/Image.cpp b/Source/WebCore/platform/graphics/Image.cpp index 0e3651a3c..bff9854e6 100644 --- a/Source/WebCore/platform/graphics/Image.cpp +++ b/Source/WebCore/platform/graphics/Image.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,10 +31,10 @@ #include "BitmapImage.h" #include "GraphicsContext.h" #include "ImageObserver.h" -#include "IntRect.h" #include "Length.h" #include "MIMETypeRegistry.h" #include "SharedBuffer.h" +#include "TextStream.h" #include <math.h> #include <wtf/MainThread.h> #include <wtf/StdLibExtras.h> @@ -57,8 +57,8 @@ Image::~Image() Image* Image::nullImage() { ASSERT(isMainThread()); - static Image* nullImage = BitmapImage::create().leakRef(); - return nullImage; + static Image& nullImage = BitmapImage::create().leakRef(); + return &nullImage; } bool Image::supportsType(const String& type) @@ -66,9 +66,9 @@ bool Image::supportsType(const String& type) return MIMETypeRegistry::isSupportedImageResourceMIMEType(type); } -bool Image::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived) +bool Image::setData(RefPtr<SharedBuffer>&& data, bool allDataReceived) { - m_encodedImageData = data; + m_encodedImageData = WTFMove(data); if (!m_encodedImageData.get()) return true; @@ -79,32 +79,32 @@ bool Image::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived) return dataChanged(allDataReceived); } -void Image::fillWithSolidColor(GraphicsContext* ctxt, const FloatRect& dstRect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op) +void Image::fillWithSolidColor(GraphicsContext& ctxt, const FloatRect& dstRect, const Color& color, CompositeOperator op) { - if (!color.alpha()) + if (!color.isVisible()) return; - CompositeOperator previousOperator = ctxt->compositeOperation(); - ctxt->setCompositeOperation(!color.hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op); - ctxt->fillRect(dstRect, color, styleColorSpace); - ctxt->setCompositeOperation(previousOperator); + CompositeOperator previousOperator = ctxt.compositeOperation(); + ctxt.setCompositeOperation(color.isOpaque() && op == CompositeSourceOver ? CompositeCopy : op); + ctxt.fillRect(dstRect, color); + ctxt.setCompositeOperation(previousOperator); } -void Image::draw(GraphicsContext* ctx, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode, ImageOrientationDescription description) +void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) { - draw(ctx, dstRect, srcRect, styleColorSpace, op, blendMode, description); -} - -void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode) -{ - if (mayFillWithSolidColor()) { - fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, op); + Color color = singlePixelSolidColor(); + if (color.isValid()) { + fillWithSolidColor(ctxt, destRect, color, op); return; } ASSERT(!isBitmapImage() || notSolidColor()); +#if PLATFORM(IOS) + FloatSize intrinsicTileSize = originalSize(); +#else FloatSize intrinsicTileSize = size(); +#endif if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) @@ -114,32 +114,32 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl scaledTileSize.height() / intrinsicTileSize.height()); FloatRect oneTileRect; - FloatSize actualTileSize(scaledTileSize.width() + spaceSize().width(), scaledTileSize.height() + spaceSize().height()); + FloatSize actualTileSize(scaledTileSize.width() + spacing.width(), scaledTileSize.height() + spacing.height()); oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height())); oneTileRect.setSize(scaledTileSize); - // Check and see if a single draw of the image can cover the entire area we are supposed to tile. - if (oneTileRect.contains(destRect) && !ctxt->drawLuminanceMask()) { + // Check and see if a single draw of the image can cover the entire area we are supposed to tile. + if (oneTileRect.contains(destRect) && !ctxt.drawLuminanceMask()) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); - draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, blendMode, ImageOrientationDescription()); + draw(ctxt, destRect, visibleSrcRect, op, blendMode, ImageOrientationDescription()); return; } #if PLATFORM(IOS) // When using accelerated drawing on iOS, it's faster to stretch an image than to tile it. - if (ctxt->isAcceleratedContext()) { + if (ctxt.isAcceleratedContext()) { if (size().width() == 1 && intersection(oneTileRect, destRect).height() == destRect.height()) { FloatRect visibleSrcRect; visibleSrcRect.setX(0); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(1); visibleSrcRect.setHeight(destRect.height() / scale.height()); - draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, BlendModeNormal, ImageOrientationDescription()); + draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription()); return; } if (size().height() == 1 && intersection(oneTileRect, destRect).width() == destRect.width()) { @@ -148,92 +148,138 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl visibleSrcRect.setY(0); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(1); - draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, BlendModeNormal, ImageOrientationDescription()); + draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription()); return; } } #endif -#if PLATFORM(IOS) - // CGPattern uses lots of memory got caching when the tile size is large (<rdar://problem/4691859>, - // <rdar://problem/6239505>). Memory consumption depends on the transformed tile size which can get + // Patterned images and gradients can use lots of memory for caching when the + // tile size is large (<rdar://problem/4691859>, <rdar://problem/6239505>). + // Memory consumption depends on the transformed tile size which can get // larger than the original tile if user zooms in enough. +#if PLATFORM(IOS) const float maxPatternTilePixels = 512 * 512; - FloatRect transformedTileSize = ctxt->getCTM().mapRect(FloatRect(FloatPoint(), scaledTileSize)); +#else + const float maxPatternTilePixels = 2048 * 2048; +#endif + FloatRect transformedTileSize = ctxt.getCTM().mapRect(FloatRect(FloatPoint(), scaledTileSize)); float transformedTileSizePixels = transformedTileSize.width() * transformedTileSize.height(); + FloatRect currentTileRect = oneTileRect; if (transformedTileSizePixels > maxPatternTilePixels) { - float fromY = (destRect.y() - oneTileRect.y()) / scale.height(); - float toY = oneTileRect.y(); - while (toY < CGRectGetMaxY(destRect)) { - float fromX = (destRect.x() - oneTileRect.x()) / scale.width(); - float toX = oneTileRect.x(); - while (toX < CGRectGetMaxX(destRect)) { - CGRect toRect = CGRectIntersection(destRect, CGRectMake(toX, toY, oneTileRect.width(), oneTileRect.height())); - CGRect fromRect = CGRectMake(fromX, fromY, toRect.size.width / scale.width(), toRect.size.height / scale.height()); - draw(ctxt, toRect, fromRect, styleColorSpace, op, BlendModeNormal, ImageOrientationDescription()); - toX += oneTileRect.width(); - fromX = 0; + GraphicsContextStateSaver stateSaver(ctxt); + ctxt.clip(destRect); + + currentTileRect.shiftYEdgeTo(destRect.y()); + float toY = currentTileRect.y(); + while (toY < destRect.maxY()) { + currentTileRect.shiftXEdgeTo(destRect.x()); + float toX = currentTileRect.x(); + while (toX < destRect.maxX()) { + FloatRect toRect(toX, toY, currentTileRect.width(), currentTileRect.height()); + FloatRect fromRect(toFloatPoint(currentTileRect.location() - oneTileRect.location()), currentTileRect.size()); + fromRect.scale(1 / scale.width(), 1 / scale.height()); + + draw(ctxt, toRect, fromRect, op, BlendModeNormal, ImageOrientationDescription()); + toX += currentTileRect.width(); + currentTileRect.shiftXEdgeTo(oneTileRect.x()); } - toY += oneTileRect.height(); - fromY = 0; + toY += currentTileRect.height(); + currentTileRect.shiftYEdgeTo(oneTileRect.y()); } return; } -#endif AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); - drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), styleColorSpace, op, destRect, blendMode); - -#if PLATFORM(IOS) - startAnimation(false); -#else + drawPattern(ctxt, destRect, tileRect, patternTransform, oneTileRect.location(), spacing, op, blendMode); startAnimation(); -#endif } // FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things. -void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, - const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, ColorSpace styleColorSpace, CompositeOperator op) +void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& dstRect, const FloatRect& srcRect, + const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator op) { - if (mayFillWithSolidColor()) { - fillWithSolidColor(ctxt, dstRect, solidColor(), styleColorSpace, op); + Color color = singlePixelSolidColor(); + if (color.isValid()) { + fillWithSolidColor(ctxt, dstRect, color, op); return; } - // FIXME: We do not support 'round' or 'space' yet. For now just map them to 'repeat'. - if (hRule == RoundTile || hRule == SpaceTile) - hRule = RepeatTile; - if (vRule == RoundTile || vRule == SpaceTile) - vRule = RepeatTile; + FloatSize tileScale = tileScaleFactor; + FloatSize spacing; + + // FIXME: These rules follow CSS border-image rules, but they should not be down here in Image. + bool centerOnGapHorizonally = false; + bool centerOnGapVertically = false; + switch (hRule) { + case RoundTile: { + int numItems = std::max<int>(floorf(dstRect.width() / srcRect.width()), 1); + tileScale.setWidth(dstRect.width() / (srcRect.width() * numItems)); + break; + } + case SpaceTile: { + int numItems = floorf(dstRect.width() / srcRect.width()); + if (!numItems) + return; + spacing.setWidth((dstRect.width() - srcRect.width() * numItems) / (numItems + 1)); + tileScale.setWidth(1); + centerOnGapHorizonally = !(numItems & 1); + break; + } + case StretchTile: + case RepeatTile: + break; + } - AffineTransform patternTransform = AffineTransform().scaleNonUniform(tileScaleFactor.width(), tileScaleFactor.height()); + switch (vRule) { + case RoundTile: { + int numItems = std::max<int>(floorf(dstRect.height() / srcRect.height()), 1); + tileScale.setHeight(dstRect.height() / (srcRect.height() * numItems)); + break; + } + case SpaceTile: { + int numItems = floorf(dstRect.height() / srcRect.height()); + if (!numItems) + return; + spacing.setHeight((dstRect.height() - srcRect.height() * numItems) / (numItems + 1)); + tileScale.setHeight(1); + centerOnGapVertically = !(numItems & 1); + break; + } + case StretchTile: + case RepeatTile: + break; + } + + AffineTransform patternTransform = AffineTransform().scaleNonUniform(tileScale.width(), tileScale.height()); // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). - float hPhase = tileScaleFactor.width() * srcRect.x(); - float vPhase = tileScaleFactor.height() * srcRect.y(); - float scaledTileWidth = tileScaleFactor.width() * srcRect.width(); - float scaledTileHeight = tileScaleFactor.height() * srcRect.height(); - if (hRule == Image::RepeatTile) + float hPhase = tileScale.width() * srcRect.x(); + float vPhase = tileScale.height() * srcRect.y(); + float scaledTileWidth = tileScale.width() * srcRect.width(); + float scaledTileHeight = tileScale.height() * srcRect.height(); + + if (centerOnGapHorizonally) + hPhase -= spacing.width(); + else if (hRule == Image::RepeatTile || hRule == Image::SpaceTile) hPhase -= (dstRect.width() - scaledTileWidth) / 2; - if (vRule == Image::RepeatTile) - vPhase -= (dstRect.height() - scaledTileHeight) / 2; - FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); - - drawPattern(ctxt, srcRect, patternTransform, patternPhase, styleColorSpace, op, dstRect); -#if PLATFORM(IOS) - startAnimation(false); -#else + if (centerOnGapVertically) + vPhase -= spacing.height(); + else if (vRule == Image::RepeatTile || vRule == Image::SpaceTile) + vPhase -= (dstRect.height() - scaledTileHeight) / 2; + + FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); + drawPattern(ctxt, dstRect, srcRect, patternTransform, patternPhase, spacing, op); startAnimation(); -#endif } #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) FloatRect Image::adjustSourceRectForDownSampling(const FloatRect& srcRect, const IntSize& scaledSize) const { - const IntSize unscaledSize = size(); + const FloatSize unscaledSize = size(); if (unscaledSize == scaledSize) return srcRect; @@ -249,9 +295,45 @@ FloatRect Image::adjustSourceRectForDownSampling(const FloatRect& srcRect, const void Image::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) { +#if PLATFORM(IOS) + intrinsicRatio = originalSize(); +#else intrinsicRatio = size(); +#endif intrinsicWidth = Length(intrinsicRatio.width(), Fixed); intrinsicHeight = Length(intrinsicRatio.height(), Fixed); } +void Image::dump(TextStream& ts) const +{ + if (isAnimated()) + ts.dumpProperty("animated", isAnimated()); + + if (isNull()) + ts.dumpProperty("is-null-image", true); + + ts.dumpProperty("size", size()); +} + +TextStream& operator<<(TextStream& ts, const Image& image) +{ + TextStream::GroupScope scope(ts); + + if (image.isBitmapImage()) + ts << "bitmap image"; + else if (image.isCrossfadeGeneratedImage()) + ts << "crossfade image"; + else if (image.isNamedImageGeneratedImage()) + ts << "named image"; + else if (image.isGradientImage()) + ts << "gradient image"; + else if (image.isSVGImage()) + ts << "svg image"; + else if (image.isPDFDocumentImage()) + ts << "pdf image"; + + image.dump(ts); + return ts; +} + } diff --git a/Source/WebCore/platform/graphics/Image.h b/Source/WebCore/platform/graphics/Image.h index 251f5a439..ab414c031 100644 --- a/Source/WebCore/platform/graphics/Image.h +++ b/Source/WebCore/platform/graphics/Image.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2013 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,19 +28,20 @@ #define Image_h #include "Color.h" -#include "ColorSpace.h" +#include "FloatRect.h" #include "FloatSize.h" #include "GraphicsTypes.h" #include "ImageOrientation.h" -#include "IntRect.h" -#include "NativeImagePtr.h" +#include "NativeImage.h" +#include <wtf/Optional.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/RetainPtr.h> +#include <wtf/TypeCasts.h> #include <wtf/text/WTFString.h> -#if PLATFORM(MAC) +#if USE(APPKIT) OBJC_CLASS NSImage; #endif @@ -58,21 +59,10 @@ typedef struct HBITMAP__ *HBITMAP; typedef struct _GdkPixbuf GdkPixbuf; #endif -#if PLATFORM(EFL) -#if USE(EO) -typedef struct _Eo_Opaque Evas; -typedef struct _Eo_Opaque Evas_Object; -#else -typedef struct _Evas Evas; -typedef struct _Evas_Object Evas_Object; -#endif -#endif - namespace WebCore { class AffineTransform; class FloatPoint; -class FloatRect; class FloatSize; class GraphicsContext; class SharedBuffer; @@ -82,43 +72,49 @@ struct Length; class ImageObserver; class Image : public RefCounted<Image> { - friend class GeneratedImage; - friend class CrossfadeGeneratedImage; - friend class GradientImage; friend class GraphicsContext; - public: virtual ~Image(); - static PassRefPtr<Image> create(ImageObserver* = 0); - static PassRefPtr<Image> loadPlatformResource(const char* name); - static bool supportsType(const String&); + static PassRefPtr<Image> create(ImageObserver* = nullptr); + WEBCORE_EXPORT static PassRefPtr<Image> loadPlatformResource(const char* name); + WEBCORE_EXPORT static bool supportsType(const String&); - virtual bool isSVGImage() const { return false; } virtual bool isBitmapImage() const { return false; } + virtual bool isGeneratedImage() const { return false; } + virtual bool isCrossfadeGeneratedImage() const { return false; } + virtual bool isNamedImageGeneratedImage() const { return false; } + virtual bool isGradientImage() const { return false; } + virtual bool isSVGImage() const { return false; } virtual bool isPDFDocumentImage() const { return false; } - virtual bool currentFrameKnownToBeOpaque() = 0; + + virtual bool currentFrameKnownToBeOpaque() const = 0; + virtual bool isAnimated() const { return false; } // Derived classes should override this if they can assure that // the image contains only resources from its own security origin. virtual bool hasSingleSecurityOrigin() const { return false; } - static Image* nullImage(); + WEBCORE_EXPORT static Image* nullImage(); bool isNull() const { return size().isEmpty(); } - virtual void setContainerSize(const IntSize&) { } + virtual void setContainerSize(const FloatSize&) { } virtual bool usesContainerSize() const { return false; } virtual bool hasRelativeWidth() const { return false; } virtual bool hasRelativeHeight() const { return false; } virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio); - virtual IntSize size() const = 0; - IntRect rect() const { return IntRect(IntPoint(), size()); } - int width() const { return size().width(); } - int height() const { return size().height(); } - virtual bool getHotSpot(IntPoint&) const { return false; } + virtual FloatSize size() const = 0; + FloatRect rect() const { return FloatRect(FloatPoint(), size()); } + float width() const { return size().width(); } + float height() const { return size().height(); } + virtual std::optional<IntPoint> hotSpot() const { return std::nullopt; } + +#if PLATFORM(IOS) + virtual FloatSize originalSize() const { return size(); } +#endif - bool setData(PassRefPtr<SharedBuffer> data, bool allDataReceived); + WEBCORE_EXPORT bool setData(RefPtr<SharedBuffer>&& data, bool allDataReceived); virtual bool dataChanged(bool /*allDataReceived*/) { return false; } virtual String filenameExtension() const { return String(); } // null string if unknown @@ -126,12 +122,14 @@ public: virtual void destroyDecodedData(bool destroyAll = true) = 0; SharedBuffer* data() { return m_encodedImageData.get(); } + const SharedBuffer* data() const { return m_encodedImageData.get(); } // Animation begins whenever someone draws the image, so startAnimation() is not normally called. // It will automatically pause once all observers no longer want to render the image anywhere. - virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { } + virtual void startAnimation() { } virtual void stopAnimation() {} virtual void resetAnimation() {} + virtual void newFrameNativeImageAvailableAtIndex(size_t) { } // Typically the CachedImage that owns us. ImageObserver* imageObserver() const { return m_imageObserver; } @@ -139,24 +137,21 @@ public: enum TileRule { StretchTile, RoundTile, SpaceTile, RepeatTile }; - virtual PassNativeImagePtr nativeImageForCurrentFrame() { return 0; } - virtual ImageOrientation orientationForCurrentFrame() { return ImageOrientation(); } + virtual NativeImagePtr nativeImage(const GraphicsContext* = nullptr) { return nullptr; } + virtual NativeImagePtr nativeImageOfSize(const IntSize&, const GraphicsContext* = nullptr) { return nullptr; } + virtual NativeImagePtr nativeImageForCurrentFrame(const GraphicsContext* = nullptr) { return nullptr; } + virtual ImageOrientation orientationForCurrentFrame() const { return ImageOrientation(); } + virtual Vector<NativeImagePtr> framesNativeImages() { return { }; } // Accessors for native image formats. #if USE(APPKIT) - virtual NSImage* getNSImage() { return 0; } + virtual NSImage *nsImage() { return nullptr; } + virtual RetainPtr<NSImage> snapshotNSImage() { return nullptr; } #endif -#if PLATFORM(MAC) - virtual CFDataRef getTIFFRepresentation() { return 0; } -#endif - -#if USE(CG) - virtual CGImageRef getCGImageRef() { return 0; } - virtual CGImageRef getFirstCGImageRefOfSize(const IntSize&) { return 0; } - virtual RetainPtr<CFArrayRef> getCGImageArray() { return 0; } - static RetainPtr<CGImageRef> imageWithColorSpace(CGImageRef originalImage, ColorSpace); +#if PLATFORM(COCOA) + virtual CFDataRef tiffRepresentation() { return nullptr; } #endif #if PLATFORM(WIN) @@ -165,16 +160,11 @@ public: #endif #if PLATFORM(GTK) - virtual GdkPixbuf* getGdkPixbuf() { return 0; } - static PassRefPtr<Image> loadPlatformThemeIcon(const char* name, int size); -#endif - -#if PLATFORM(EFL) - virtual Evas_Object* getEvasObject(Evas*) { return 0; } + virtual GdkPixbuf* getGdkPixbuf() { return nullptr; } #endif - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal); + virtual void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal); #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) FloatRect adjustSourceRectForDownSampling(const FloatRect& srcRect, const IntSize& scaledSize) const; @@ -184,38 +174,35 @@ public: virtual bool notSolidColor() { return true; } #endif - FloatSize spaceSize() const { return m_space; } - void setSpaceSize(const FloatSize& space) - { - m_space = space; - } + virtual void dump(TextStream&) const; + protected: - Image(ImageObserver* = 0); + Image(ImageObserver* = nullptr); - static void fillWithSolidColor(GraphicsContext*, const FloatRect& dstRect, const Color&, ColorSpace styleColorSpace, CompositeOperator); + static void fillWithSolidColor(GraphicsContext&, const FloatRect& dstRect, const Color&, CompositeOperator); - // The ColorSpace parameter will only be used for untagged images. #if PLATFORM(WIN) - virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, ColorSpace styleColorSpace, CompositeOperator) { } + virtual void drawFrameMatchingSourceSize(GraphicsContext&, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) { } #endif - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator, BlendMode, ImageOrientationDescription); - void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatPoint& srcPoint, const FloatSize& tileSize, ColorSpace styleColorSpace, - CompositeOperator , BlendMode); - void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, ColorSpace styleColorSpace, CompositeOperator); + virtual void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) = 0; + void drawTiled(GraphicsContext&, const FloatRect& dstRect, const FloatPoint& srcPoint, const FloatSize& tileSize, const FloatSize& spacing, CompositeOperator, BlendMode); + void drawTiled(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator); // Supporting tiled drawing - virtual bool mayFillWithSolidColor() { return false; } - virtual Color solidColor() const { return Color(); } - + virtual Color singlePixelSolidColor() const { return Color(); } + private: RefPtr<SharedBuffer> m_encodedImageData; ImageObserver* m_imageObserver; - FloatSize m_space; }; -#define IMAGE_TYPE_CASTS(ToClassName) \ - TYPE_CASTS_BASE(ToClassName, Image, image, image->is##ToClassName(), image.is##ToClassName()) +TextStream& operator<<(TextStream&, const Image&); -} +} // namespace WebCore -#endif +#define SPECIALIZE_TYPE_TRAITS_IMAGE(ToClassName) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \ + static bool isType(const WebCore::Image& image) { return image.is##ToClassName(); } \ +SPECIALIZE_TYPE_TRAITS_END() + +#endif // Image_h diff --git a/Source/WebCore/platform/graphics/ImageBackingStore.h b/Source/WebCore/platform/graphics/ImageBackingStore.h new file mode 100644 index 000000000..4c59f6842 --- /dev/null +++ b/Source/WebCore/platform/graphics/ImageBackingStore.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Color.h" +#include "IntRect.h" +#include "IntSize.h" +#include "NativeImage.h" +#include "SharedBuffer.h" + +namespace WebCore { + +class ImageBackingStore { + WTF_MAKE_FAST_ALLOCATED; +public: + static std::unique_ptr<ImageBackingStore> create(const IntSize& size, bool premultiplyAlpha = true) + { + return std::unique_ptr<ImageBackingStore>(new ImageBackingStore(size, premultiplyAlpha)); + } + + static std::unique_ptr<ImageBackingStore> create(const ImageBackingStore& other) + { + return std::unique_ptr<ImageBackingStore>(new ImageBackingStore(other)); + } + + NativeImagePtr image() const; + + bool setSize(const IntSize& size) + { + if (size.isEmpty()) + return false; + + Vector<char> buffer; + size_t bufferSize = size.area().unsafeGet() * sizeof(RGBA32); + + if (!buffer.tryReserveCapacity(bufferSize)) + return false; + + buffer.resize(bufferSize); + m_pixels = SharedBuffer::adoptVector(buffer); + m_pixelsPtr = reinterpret_cast<RGBA32*>(const_cast<char*>(m_pixels->data())); + m_size = size; + m_frameRect = IntRect(IntPoint(), m_size); + clear(); + return true; + } + + void setFrameRect(const IntRect& frameRect) + { + ASSERT(!m_size.isEmpty()); + ASSERT(inBounds(frameRect)); + m_frameRect = frameRect; + } + + const IntSize& size() const { return m_size; } + const IntRect& frameRect() const { return m_frameRect; } + + void clear() + { + memset(m_pixelsPtr, 0, (m_size.area() * sizeof(RGBA32)).unsafeGet()); + } + + void clearRect(const IntRect& rect) + { + if (rect.isEmpty() || !inBounds(rect)) + return; + + size_t rowBytes = rect.width() * sizeof(RGBA32); + RGBA32* start = pixelAt(rect.x(), rect.y()); + for (int i = 0; i < rect.height(); ++i) { + memset(start, 0, rowBytes); + start += m_size.width(); + } + } + + void fillRect(const IntRect &rect, unsigned r, unsigned g, unsigned b, unsigned a) + { + if (rect.isEmpty() || !inBounds(rect)) + return; + + RGBA32* start = pixelAt(rect.x(), rect.y()); + RGBA32 pixelValue = this->pixelValue(r, g, b, a); + for (int i = 0; i < rect.height(); ++i) { + for (int j = 0; j < rect.width(); ++j) + start[j] = pixelValue; + start += m_size.width(); + } + } + + void repeatFirstRow(const IntRect& rect) + { + if (rect.isEmpty() || !inBounds(rect)) + return; + + size_t rowBytes = rect.width() * sizeof(RGBA32); + RGBA32* src = pixelAt(rect.x(), rect.y()); + RGBA32* dest = src + m_size.width(); + for (int i = 1; i < rect.height(); ++i) { + memcpy(dest, src, rowBytes); + dest += m_size.width(); + } + } + + RGBA32* pixelAt(int x, int y) const + { + ASSERT(inBounds(IntPoint(x, y))); + return m_pixelsPtr + y * m_size.width() + x; + } + + void setPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a) + { + ASSERT(dest); + *dest = pixelValue(r, g, b, a); + } + + void setPixel(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a) + { + setPixel(pixelAt(x, y), r, g, b, a); + } + +#if ENABLE(APNG) + void blendPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a) + { + if (!a) + return; + + if (a >= 255 || !alphaChannel(*dest)) { + setPixel(dest, r, g, b, a); + return; + } + + if (!m_premultiplyAlpha) + *dest = makePremultipliedRGBA(redChannel(*dest), greenChannel(*dest), blueChannel(*dest), alphaChannel(*dest), false); + + unsigned d = 255 - a; + + r = fastDivideBy255(r * a + redChannel(*dest) * d); + g = fastDivideBy255(g * a + greenChannel(*dest) * d); + b = fastDivideBy255(b * a + blueChannel(*dest) * d); + a += fastDivideBy255(d * alphaChannel(*dest)); + + if (m_premultiplyAlpha) + *dest = makeRGBA(r, g, b, a); + else + *dest = makeUnPremultipliedRGBA(r, g, b, a); + } +#endif + + static bool isOverSize(const IntSize& size) + { + static unsigned long long MaxPixels = ((1 << 29) - 1); + unsigned long long pixels = static_cast<unsigned long long>(size.width()) * static_cast<unsigned long long>(size.height()); + return pixels > MaxPixels; + } + +private: + ImageBackingStore(const IntSize& size, bool premultiplyAlpha = true) + : m_premultiplyAlpha(premultiplyAlpha) + { + ASSERT(!size.isEmpty() && !isOverSize(size)); + setSize(size); + } + + ImageBackingStore(const ImageBackingStore& other) + : m_size(other.m_size) + , m_premultiplyAlpha(other.m_premultiplyAlpha) + { + ASSERT(!m_size.isEmpty() && !isOverSize(m_size)); + m_pixels = other.m_pixels->copy(); + m_pixelsPtr = reinterpret_cast<RGBA32*>(const_cast<char*>(m_pixels->data())); + } + + bool inBounds(const IntPoint& point) const + { + return IntRect(IntPoint(), m_size).contains(point); + } + + bool inBounds(const IntRect& rect) const + { + return IntRect(IntPoint(), m_size).contains(rect); + } + + RGBA32 pixelValue(unsigned r, unsigned g, unsigned b, unsigned a) const + { + if (m_premultiplyAlpha && !a) + return 0; + + if (m_premultiplyAlpha && a < 255) + return makePremultipliedRGBA(r, g, b, a, false); + + return makeRGBA(r, g, b, a); + } + + RefPtr<SharedBuffer> m_pixels; + RGBA32* m_pixelsPtr { nullptr }; + IntSize m_size; + IntRect m_frameRect; // This will always just be the entire buffer except for GIF and PNG frames + bool m_premultiplyAlpha { true }; +}; + +} diff --git a/Source/WebCore/platform/graphics/ImageBuffer.cpp b/Source/WebCore/platform/graphics/ImageBuffer.cpp index 833534c6c..eae4c4eda 100644 --- a/Source/WebCore/platform/graphics/ImageBuffer.cpp +++ b/Source/WebCore/platform/graphics/ImageBuffer.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) Research In Motion Limited 2011. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,16 +28,90 @@ #include "config.h" #include "ImageBuffer.h" +#include "GraphicsContext.h" #include "IntRect.h" #include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> namespace WebCore { -#if !USE(CG) +static const float MaxClampedLength = 4096; +static const float MaxClampedArea = MaxClampedLength * MaxClampedLength; + +std::unique_ptr<ImageBuffer> ImageBuffer::create(const FloatSize& size, RenderingMode renderingMode, float resolutionScale, ColorSpace colorSpace) +{ + bool success = false; + std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, success)); + if (!success) + return nullptr; + return buffer; +} + +#if USE(DIRECT2D) +std::unique_ptr<ImageBuffer> ImageBuffer::create(const FloatSize& size, RenderingMode renderingMode, const GraphicsContext* targetContext, float resolutionScale, ColorSpace colorSpace) +{ + bool success = false; + std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, targetContext, success)); + if (!success) + return nullptr; + return buffer; +} +#endif + +bool ImageBuffer::sizeNeedsClamping(const FloatSize& size) +{ + if (size.isEmpty()) + return false; + + return floorf(size.height()) * floorf(size.width()) > MaxClampedArea; +} + +bool ImageBuffer::sizeNeedsClamping(const FloatSize& size, FloatSize& scale) +{ + FloatSize scaledSize(size); + scaledSize.scale(scale.width(), scale.height()); + + if (!sizeNeedsClamping(scaledSize)) + return false; + + // The area of scaled size is bigger than the upper limit, adjust the scale to fit. + scale.scale(sqrtf(MaxClampedArea / (scaledSize.width() * scaledSize.height()))); + ASSERT(!sizeNeedsClamping(size, scale)); + return true; +} + +FloatSize ImageBuffer::clampedSize(const FloatSize& size) +{ + return size.shrunkTo(FloatSize(MaxClampedLength, MaxClampedLength)); +} + +FloatSize ImageBuffer::clampedSize(const FloatSize& size, FloatSize& scale) +{ + if (size.isEmpty()) + return size; + + FloatSize clampedSize = ImageBuffer::clampedSize(size); + scale = FloatSize(clampedSize.width() / size.width(), clampedSize.height() / size.height()); + ASSERT(!sizeNeedsClamping(clampedSize)); + ASSERT(!sizeNeedsClamping(size, scale)); + return clampedSize; +} + +FloatRect ImageBuffer::clampedRect(const FloatRect& rect) +{ + return FloatRect(rect.location(), clampedSize(rect.size())); +} + +#if !(USE(CG) || USE(DIRECT2D)) +FloatSize ImageBuffer::sizeForDestinationSize(FloatSize size) const +{ + return size; +} + void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace) { - DEFINE_STATIC_LOCAL(Vector<int>, deviceRgbLUT, ()); - DEFINE_STATIC_LOCAL(Vector<int>, linearRgbLUT, ()); + static NeverDestroyed<Vector<int>> deviceRgbLUT; + static NeverDestroyed<Vector<int>> linearRgbLUT; if (srcColorSpace == dstColorSpace) return; @@ -47,27 +122,27 @@ void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstCo return; if (dstColorSpace == ColorSpaceLinearRGB) { - if (linearRgbLUT.isEmpty()) { + if (linearRgbLUT.get().isEmpty()) { for (unsigned i = 0; i < 256; i++) { float color = i / 255.0f; color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f)); color = std::max(0.0f, color); color = std::min(1.0f, color); - linearRgbLUT.append(static_cast<int>(round(color * 255))); + linearRgbLUT.get().append(static_cast<int>(round(color * 255))); } } - platformTransformColorSpace(linearRgbLUT); + platformTransformColorSpace(linearRgbLUT.get()); } else if (dstColorSpace == ColorSpaceDeviceRGB) { - if (deviceRgbLUT.isEmpty()) { + if (deviceRgbLUT.get().isEmpty()) { for (unsigned i = 0; i < 256; i++) { float color = i / 255.0f; color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f; color = std::max(0.0f, color); color = std::min(1.0f, color); - deviceRgbLUT.append(static_cast<int>(round(color * 255))); + deviceRgbLUT.get().append(static_cast<int>(round(color * 255))); } } - platformTransformColorSpace(deviceRgbLUT); + platformTransformColorSpace(deviceRgbLUT.get()); } } #endif // USE(CG) @@ -98,21 +173,61 @@ void ImageBuffer::convertToLuminanceMask() genericConvertToLuminanceMask(); } -#if USE(ACCELERATED_COMPOSITING) && !USE(CAIRO) +#if !USE(CAIRO) PlatformLayer* ImageBuffer::platformLayer() const { return 0; } -#endif -bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, Platform3DObject, GC3Denum, bool, bool) +bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum, Platform3DObject, GC3Denum, bool, bool) { return false; } +#endif + +std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, ColorSpace colorSpace, const GraphicsContext& context) +{ + if (size.isEmpty()) + return nullptr; + + IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context); + + auto buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, colorSpace, context); + if (!buffer) + return nullptr; + + // Set up a corresponding scale factor on the graphics context. + buffer->context().scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height())); + return buffer; +} + +std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, float resolutionScale, ColorSpace colorSpace, const GraphicsContext& context) +{ + return create(size, context.renderingMode(), resolutionScale, colorSpace); +} -std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const IntSize& size, float resolutionScale, ColorSpace colorSpace, const GraphicsContext* context, bool) +IntSize ImageBuffer::compatibleBufferSize(const FloatSize& size, const GraphicsContext& context) { - return create(size, resolutionScale, colorSpace, context->isAcceleratedContext() ? Accelerated : Unaccelerated); + // Enlarge the buffer size if the context's transform is scaling it so we need a higher + // resolution than one pixel per unit. + return expandedIntSize(size * context.scaleFactor()); } +bool ImageBuffer::isCompatibleWithContext(const GraphicsContext& context) const +{ + return areEssentiallyEqual(context.scaleFactor(), this->context().scaleFactor()); +} + +#if !USE(IOSURFACE_CANVAS_BACKING_STORE) +size_t ImageBuffer::memoryCost() const +{ + return 4 * internalSize().width() * internalSize().height(); +} + +size_t ImageBuffer::externalMemoryCost() const +{ + return 0; +} +#endif + } diff --git a/Source/WebCore/platform/graphics/ImageBuffer.h b/Source/WebCore/platform/graphics/ImageBuffer.h index b62e5069f..397beee5a 100644 --- a/Source/WebCore/platform/graphics/ImageBuffer.h +++ b/Source/WebCore/platform/graphics/ImageBuffer.h @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,143 +30,157 @@ #include "AffineTransform.h" #include "ColorSpace.h" -#include "FloatRect.h" -#include "GraphicsContext.h" -#if USE(ACCELERATED_COMPOSITING) -#include "PlatformLayer.h" -#endif #include "GraphicsTypes.h" #include "GraphicsTypes3D.h" #include "IntSize.h" #include "ImageBufferData.h" +#include "PlatformLayer.h" +#include <memory> #include <runtime/Uint8ClampedArray.h> #include <wtf/Forward.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> #include <wtf/Vector.h> namespace WebCore { - class Image; - class ImageData; - class IntPoint; - class IntRect; - class GraphicsContext3D; - - enum Multiply { - Premultiplied, - Unmultiplied - }; - - enum RenderingMode { - Unaccelerated, - UnacceleratedNonPlatformBuffer, // Use plain memory allocation rather than platform API to allocate backing store. - Accelerated - }; +class FloatRect; +class GraphicsContext; +class GraphicsContext3D; +class Image; +class ImageData; +class IntPoint; +class IntRect; + +enum Multiply { + Premultiplied, + Unmultiplied +}; + +enum BackingStoreCopy { + CopyBackingStore, // Guarantee subsequent draws don't affect the copy. + DontCopyBackingStore // Subsequent draws may affect the copy. +}; + +enum ScaleBehavior { + Scaled, + Unscaled +}; + +class ImageBuffer { + WTF_MAKE_NONCOPYABLE(ImageBuffer); WTF_MAKE_FAST_ALLOCATED; + friend class IOSurface; +public: + // Will return a null pointer on allocation failure. + WEBCORE_EXPORT static std::unique_ptr<ImageBuffer> create(const FloatSize&, RenderingMode, float resolutionScale = 1, ColorSpace = ColorSpaceSRGB); +#if USE(DIRECT2D) + WEBCORE_EXPORT static std::unique_ptr<ImageBuffer> create(const FloatSize&, RenderingMode, const GraphicsContext*, float resolutionScale = 1, ColorSpace = ColorSpaceSRGB); +#endif + + // Create an image buffer compatible with the context, with suitable resolution for drawing into the buffer and then into this context. + static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const FloatSize&, const GraphicsContext&); + static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const FloatSize&, ColorSpace, const GraphicsContext&); + static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const FloatSize&, float resolutionScale, ColorSpace, const GraphicsContext&); + + static IntSize compatibleBufferSize(const FloatSize&, const GraphicsContext&); + bool isCompatibleWithContext(const GraphicsContext&) const; + + WEBCORE_EXPORT ~ImageBuffer(); + + // The actual resolution of the backing store + const IntSize& internalSize() const { return m_size; } + const IntSize& logicalSize() const { return m_logicalSize; } + + FloatSize sizeForDestinationSize(FloatSize) const; + + float resolutionScale() const { return m_resolutionScale; } + + WEBCORE_EXPORT GraphicsContext& context() const; + + WEBCORE_EXPORT RefPtr<Image> copyImage(BackingStoreCopy = CopyBackingStore, ScaleBehavior = Scaled) const; + WEBCORE_EXPORT static RefPtr<Image> sinkIntoImage(std::unique_ptr<ImageBuffer>, ScaleBehavior = Scaled); + // Give hints on the faster copyImage Mode, return DontCopyBackingStore if it supports the DontCopyBackingStore behavior + // or return CopyBackingStore if it doesn't. + static BackingStoreCopy fastCopyImageMode(); + + enum CoordinateSystem { LogicalCoordinateSystem, BackingStoreCoordinateSystem }; + + RefPtr<Uint8ClampedArray> getUnmultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const; + RefPtr<Uint8ClampedArray> getPremultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const; + + void putByteArray(Multiply multiplied, Uint8ClampedArray*, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem = LogicalCoordinateSystem); + + void convertToLuminanceMask(); - enum BackingStoreCopy { - CopyBackingStore, // Guarantee subsequent draws don't affect the copy. - DontCopyBackingStore // Subsequent draws may affect the copy. - }; - - enum ScaleBehavior { - Scaled, - Unscaled - }; - - class ImageBuffer { - WTF_MAKE_NONCOPYABLE(ImageBuffer); WTF_MAKE_FAST_ALLOCATED; - public: - // Will return a null pointer on allocation failure. - static std::unique_ptr<ImageBuffer> create(const IntSize& size, float resolutionScale = 1, ColorSpace colorSpace = ColorSpaceDeviceRGB, RenderingMode renderingMode = Unaccelerated) - { - bool success = false; - std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, success)); - if (!success) - return nullptr; - return buffer; - } - - static std::unique_ptr<ImageBuffer> createCompatibleBuffer(const IntSize&, float resolutionScale, ColorSpace, const GraphicsContext*, bool hasAlpha); - - ~ImageBuffer(); - - // The actual resolution of the backing store - const IntSize& internalSize() const { return m_size; } - const IntSize& logicalSize() const { return m_logicalSize; } - - GraphicsContext* context() const; - - PassRefPtr<Image> copyImage(BackingStoreCopy = CopyBackingStore, ScaleBehavior = Scaled) const; - // Give hints on the faster copyImage Mode, return DontCopyBackingStore if it supports the DontCopyBackingStore behavior - // or return CopyBackingStore if it doesn't. - static BackingStoreCopy fastCopyImageMode(); - - enum CoordinateSystem { LogicalCoordinateSystem, BackingStoreCoordinateSystem }; - - PassRefPtr<Uint8ClampedArray> getUnmultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const; - PassRefPtr<Uint8ClampedArray> getPremultipliedImageData(const IntRect&, CoordinateSystem = LogicalCoordinateSystem) const; - - void putByteArray(Multiply multiplied, Uint8ClampedArray*, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem = LogicalCoordinateSystem); - - void convertToLuminanceMask(); - - String toDataURL(const String& mimeType, const double* quality = 0, CoordinateSystem = LogicalCoordinateSystem) const; + String toDataURL(const String& mimeType, std::optional<double> quality = std::nullopt, CoordinateSystem = LogicalCoordinateSystem) const; #if !USE(CG) - AffineTransform baseTransform() const { return AffineTransform(); } - void transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace); - void platformTransformColorSpace(const Vector<int>&); + AffineTransform baseTransform() const { return AffineTransform(); } + void transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace); + void platformTransformColorSpace(const Vector<int>&); #else - AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_data.m_backingStoreSize.height()); } + AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_data.backingStoreSize.height()); } #endif -#if USE(ACCELERATED_COMPOSITING) - PlatformLayer* platformLayer() const; + PlatformLayer* platformLayer() const; + +#if USE(CAIRO) + NativeImagePtr nativeImage() const; #endif - // FIXME: current implementations of this method have the restriction that they only work - // with textures that are RGB or RGBA format, and UNSIGNED_BYTE type. - bool copyToPlatformTexture(GraphicsContext3D&, Platform3DObject, GC3Denum, bool, bool); + size_t memoryCost() const; + size_t externalMemoryCost() const; + + // FIXME: current implementations of this method have the restriction that they only work + // with textures that are RGB or RGBA format, and UNSIGNED_BYTE type. + bool copyToPlatformTexture(GraphicsContext3D&, GC3Denum, Platform3DObject, GC3Denum, bool, bool); - FloatSize spaceSize() const { return m_space; } - void setSpaceSize(const FloatSize& space) - { - m_space = space; - } + // These functions are used when clamping the ImageBuffer which is created for filter, masker or clipper. + static bool sizeNeedsClamping(const FloatSize&); + static bool sizeNeedsClamping(const FloatSize&, FloatSize& scale); + static FloatSize clampedSize(const FloatSize&); + static FloatSize clampedSize(const FloatSize&, FloatSize& scale); + static FloatRect clampedRect(const FloatRect&); - private: +private: #if USE(CG) - // The returned image might be larger than the internalSize(). If you want the smaller - // image, crop the result. - RetainPtr<CGImageRef> copyNativeImage(BackingStoreCopy = CopyBackingStore) const; - void flushContext() const; + // The returned image might be larger than the internalSize(). If you want the smaller + // image, crop the result. + RetainPtr<CGImageRef> copyNativeImage(BackingStoreCopy = CopyBackingStore) const; + static RetainPtr<CGImageRef> sinkIntoNativeImage(std::unique_ptr<ImageBuffer>); + void flushContext() const; +#elif USE(DIRECT2D) + void flushContext() const; #endif - void clip(GraphicsContext*, const FloatRect&) const; + + void draw(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal); + void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal); - void draw(GraphicsContext*, ColorSpace, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, bool useLowQualityScale = false); - void drawPattern(GraphicsContext*, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect& destRect, BlendMode = BlendModeNormal); + static void drawConsuming(std::unique_ptr<ImageBuffer>, GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal); - inline void genericConvertToLuminanceMask(); + inline void genericConvertToLuminanceMask(); - friend class GraphicsContext; - friend class GeneratedImage; - friend class CrossfadeGeneratedImage; - friend class GradientImage; + friend class GraphicsContext; + friend class GeneratedImage; + friend class CrossfadeGeneratedImage; + friend class NamedImageGeneratedImage; + friend class GradientImage; - private: - ImageBufferData m_data; - IntSize m_size; - IntSize m_logicalSize; - float m_resolutionScale; - OwnPtr<GraphicsContext> m_context; - FloatSize m_space; +private: + ImageBufferData m_data; + IntSize m_size; + IntSize m_logicalSize; + float m_resolutionScale; - // This constructor will place its success into the given out-variable - // so that create() knows when it should return failure. - ImageBuffer(const IntSize&, float resolutionScale, ColorSpace, RenderingMode, bool& success); - }; + // This constructor will place its success into the given out-variable + // so that create() knows when it should return failure. + WEBCORE_EXPORT ImageBuffer(const FloatSize&, float resolutionScale, ColorSpace, RenderingMode, bool& success); +#if USE(CG) + ImageBuffer(const FloatSize&, float resolutionScale, CGColorSpaceRef, RenderingMode, bool& success); +#elif USE(DIRECT2D) + ImageBuffer(const FloatSize&, float resolutionScale, ColorSpace, RenderingMode, const GraphicsContext*, bool& success); +#endif +}; #if USE(CG) - String ImageDataToDataURL(const ImageData&, const String& mimeType, const double* quality); +String dataURL(const ImageData&, const String& mimeType, std::optional<double> quality); #endif } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ImageBufferData.h b/Source/WebCore/platform/graphics/ImageBufferData.h index 97cc6d452..f89ec0d36 100644 --- a/Source/WebCore/platform/graphics/ImageBufferData.h +++ b/Source/WebCore/platform/graphics/ImageBufferData.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,8 +25,8 @@ #if USE(CG) #include "ImageBufferDataCG.h" +#elif USE(DIRECT2D) +#include "ImageBufferDataDirect2D.h" #elif USE(CAIRO) #include "ImageBufferDataCairo.h" -#elif USE(WINGDI) -#include "ImageBufferDataWince.h" #endif diff --git a/Source/WebCore/platform/graphics/ImageFrame.cpp b/Source/WebCore/platform/graphics/ImageFrame.cpp new file mode 100644 index 000000000..ea40ef532 --- /dev/null +++ b/Source/WebCore/platform/graphics/ImageFrame.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageFrame.h" + +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +ImageFrame::ImageFrame() +{ +} + +ImageFrame::~ImageFrame() +{ + clearImage(); +} + +const ImageFrame& ImageFrame::defaultFrame() +{ + static NeverDestroyed<ImageFrame> sharedInstance; + return sharedInstance; +} + +ImageFrame& ImageFrame::operator=(const ImageFrame& other) +{ + if (this == &other) + return *this; + + m_decoding = other.m_decoding; + m_size = other.m_size; + +#if !USE(CG) + if (other.backingStore()) + initialize(*other.backingStore()); + else + m_backingStore = nullptr; + m_disposalMethod = other.m_disposalMethod; +#endif + + m_nativeImage = other.m_nativeImage; + m_subsamplingLevel = other.m_subsamplingLevel; + m_sizeForDrawing = other.m_sizeForDrawing; + + m_orientation = other.m_orientation; + m_duration = other.m_duration; + m_hasAlpha = other.m_hasAlpha; + return *this; +} + +unsigned ImageFrame::clearImage() +{ +#if !USE(CG) + if (hasBackingStore()) + m_backingStore = nullptr; +#endif + + if (!hasNativeImage()) + return 0; + + unsigned frameBytes = this->frameBytes(); + + clearNativeImageSubimages(m_nativeImage); + m_nativeImage = nullptr; + + return frameBytes; +} + +unsigned ImageFrame::clear() +{ + unsigned frameBytes = clearImage(); + *this = ImageFrame(); + return frameBytes; +} + +#if !USE(CG) +bool ImageFrame::initialize(const ImageBackingStore& backingStore) +{ + if (&backingStore == this->backingStore()) + return true; + + m_backingStore = ImageBackingStore::create(backingStore); + return m_backingStore != nullptr; +} + +bool ImageFrame::initialize(const IntSize& size, bool premultiplyAlpha) +{ + if (size.isEmpty()) + return false; + + m_backingStore = ImageBackingStore::create(size, premultiplyAlpha); + return m_backingStore != nullptr; +} +#endif + +IntSize ImageFrame::size() const +{ +#if !USE(CG) + if (hasBackingStore()) + return backingStore()->size(); +#endif + return m_size; +} + +static int maxDimension(const IntSize& size) +{ + return std::max(size.width(), size.height()); +} + +bool ImageFrame::isBeingDecoded(const std::optional<IntSize>& sizeForDrawing) const +{ + if (!m_sizeForDecoding.size()) + return false; + + if (!sizeForDrawing) + return true; + + // Return true if the ImageFrame will be decoded eventually with a suitable sizeForDecoding. + return maxDimension(m_sizeForDecoding.last()) >= maxDimension(*sizeForDrawing); +} + +bool ImageFrame::hasValidNativeImage(const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const +{ + ASSERT_IMPLIES(!subsamplingLevel, !sizeForDrawing); + + if (!hasNativeImage()) + return false; + + // The caller does not care about subsamplingLevel or sizeForDrawing. The current NativeImage is fine. + if (!subsamplingLevel) + return true; + + if (*subsamplingLevel < m_subsamplingLevel) + return false; + + // The NativeImage was decoded with the native size. So it is valid for any size. + if (!m_sizeForDrawing) + return true; + + // The NativeImage was decoded for a specific size. The two sizeForDrawings have to match. + return sizeForDrawing && maxDimension(*m_sizeForDrawing) >= maxDimension(*sizeForDrawing); +} + +Color ImageFrame::singlePixelSolidColor() const +{ + if (!hasNativeImage() || m_size != IntSize(1, 1)) + return Color(); + + return nativeImageSinglePixelSolidColor(m_nativeImage); +} + +} diff --git a/Source/WebCore/platform/graphics/ImageFrame.h b/Source/WebCore/platform/graphics/ImageFrame.h new file mode 100644 index 000000000..60593a42b --- /dev/null +++ b/Source/WebCore/platform/graphics/ImageFrame.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Color.h" +#include "ImageBackingStore.h" +#include "ImageOrientation.h" +#include "IntSize.h" +#include "NativeImage.h" +#include <wtf/Deque.h> + +namespace WebCore { + +class Color; + +// There are four subsampling levels: 0 = 1x, 1 = 0.5x, 2 = 0.25x, 3 = 0.125x. +enum class SubsamplingLevel { + First = 0, + Default = First, + Level0 = First, + Level1, + Level2, + Level3, + Last = Level3, + Max +}; + +inline SubsamplingLevel& operator++(SubsamplingLevel& subsamplingLevel) +{ + subsamplingLevel = static_cast<SubsamplingLevel>(static_cast<int>(subsamplingLevel) + 1); + ASSERT(subsamplingLevel <= SubsamplingLevel::Max); + return subsamplingLevel; +} + +typedef int RepetitionCount; + +enum { + RepetitionCountNone = 0, + RepetitionCountOnce = 1, + RepetitionCountInfinite = -1, +}; + +enum class AlphaOption { + Premultiplied, + NotPremultiplied +}; + +enum class GammaAndColorProfileOption { + Applied, + Ignored +}; + +class ImageFrame { + friend class ImageFrameCache; +public: + enum class Caching { Metadata, MetadataAndImage }; + enum class Decoding { None, Partial, Complete }; + + ImageFrame(); + ImageFrame(const ImageFrame& other) { operator=(other); } + + ~ImageFrame(); + + static const ImageFrame& defaultFrame(); + + ImageFrame& operator=(const ImageFrame& other); + + unsigned clearImage(); + unsigned clear(); + +#if !USE(CG) + bool initialize(const ImageBackingStore&); + bool initialize(const IntSize&, bool premultiplyAlpha); +#endif + + void setDecoding(Decoding decoding) { m_decoding = decoding; } + Decoding decoding() const { return m_decoding; } + void enqueueSizeForDecoding(const IntSize& sizeForDecoding) { m_sizeForDecoding.append(sizeForDecoding); } + void dequeueSizeForDecoding() { m_sizeForDecoding.removeFirst(); } + void clearSizeForDecoding() { m_sizeForDecoding.clear(); } + + bool isEmpty() const { return m_decoding == Decoding::None; } + bool isBeingDecoded(const std::optional<IntSize>& sizeForDrawing = { }) const; + bool isPartial() const { return m_decoding == Decoding::Partial; } + bool isComplete() const { return m_decoding == Decoding::Complete; } + + IntSize size() const; + IntSize sizeRespectingOrientation() const { return !m_orientation.usesWidthAsHeight() ? size() : size().transposedSize(); } + unsigned frameBytes() const { return hasNativeImage() ? (size().area() * sizeof(RGBA32)).unsafeGet() : 0; } + SubsamplingLevel subsamplingLevel() const { return m_subsamplingLevel; } + std::optional<IntSize> sizeForDrawing() const { return m_sizeForDrawing; } + +#if !USE(CG) + enum class DisposalMethod { Unspecified, DoNotDispose, RestoreToBackground, RestoreToPrevious }; + void setDisposalMethod(DisposalMethod method) { m_disposalMethod = method; } + DisposalMethod disposalMethod() const { return m_disposalMethod; } +#endif + + NativeImagePtr nativeImage() const { return m_nativeImage; } + + void setOrientation(ImageOrientation orientation) { m_orientation = orientation; }; + ImageOrientation orientation() const { return m_orientation; } + + void setDuration(float duration) { m_duration = duration; } + float duration() const { return m_duration; } + + void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } + bool hasAlpha() const { return !hasMetadata() || m_hasAlpha; } + + bool hasNativeImage() const { return m_nativeImage; } + bool hasValidNativeImage(const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing) const; + bool hasDecodedNativeImage() const { return hasNativeImage() && sizeForDrawing(); } + bool hasMetadata() const { return !size().isEmpty(); } + +#if !USE(CG) + ImageBackingStore* backingStore() const { return m_backingStore ? m_backingStore.get() : nullptr; } + bool hasBackingStore() const { return backingStore(); } +#endif + + Color singlePixelSolidColor() const; + +private: + Decoding m_decoding { Decoding::None }; + IntSize m_size; + +#if !USE(CG) + std::unique_ptr<ImageBackingStore> m_backingStore; + DisposalMethod m_disposalMethod { DisposalMethod::Unspecified }; +#endif + + NativeImagePtr m_nativeImage; + SubsamplingLevel m_subsamplingLevel { SubsamplingLevel::Default }; + std::optional<IntSize> m_sizeForDrawing; + Deque<IntSize, 4> m_sizeForDecoding; + + ImageOrientation m_orientation { DefaultImageOrientation }; + float m_duration { 0 }; + bool m_hasAlpha { true }; +}; + +} diff --git a/Source/WebCore/platform/graphics/ImageFrameCache.cpp b/Source/WebCore/platform/graphics/ImageFrameCache.cpp new file mode 100644 index 000000000..cb1453aa7 --- /dev/null +++ b/Source/WebCore/platform/graphics/ImageFrameCache.cpp @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageFrameCache.h" + +#include "Image.h" +#include "ImageObserver.h" + +#if USE(CG) +#include "ImageDecoderCG.h" +#elif USE(DIRECT2D) +#include "ImageDecoderDirect2D.h" +#include <WinCodec.h> +#else +#include "ImageDecoder.h" +#endif + +#include <wtf/CheckedArithmetic.h> +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> + +namespace WebCore { + +ImageFrameCache::ImageFrameCache(Image* image) + : m_image(image) +{ +} + +ImageFrameCache::ImageFrameCache(NativeImagePtr&& nativeImage) +{ + m_frameCount = 1; + m_isSizeAvailable = true; + growFrames(); + + setNativeImage(WTFMove(nativeImage)); + + m_decodedSize = m_frames[0].frameBytes(); + + // The assumption is the memory image will be displayed with the default + // orientation. So set m_sizeRespectingOrientation to be the same as m_size. + m_size = m_frames[0].size(); + m_sizeRespectingOrientation = m_size; +} + +ImageFrameCache::~ImageFrameCache() +{ + ASSERT(!hasDecodingQueue()); +} + +void ImageFrameCache::setDecoder(ImageDecoder* decoder) +{ + if (m_decoder == decoder) + return; + + // Changing the decoder has to stop the decoding thread. The current frame will + // continue decoding safely because the decoding thread has its own + // reference of the old decoder. + stopAsyncDecodingQueue(); + m_decoder = decoder; +} + +ImageDecoder* ImageFrameCache::decoder() const +{ + return m_decoder.get(); +} + +void ImageFrameCache::destroyDecodedData(size_t frameCount, size_t excludeFrame) +{ + unsigned decodedSize = 0; + + ASSERT(frameCount <= m_frames.size()); + + for (size_t index = 0; index < frameCount; ++index) { + if (index == excludeFrame) + continue; + decodedSize += m_frames[index++].clearImage(); + } + + decodedSizeReset(decodedSize); +} + +void ImageFrameCache::destroyIncompleteDecodedData() +{ + unsigned decodedSize = 0; + + for (auto& frame : m_frames) { + if (!frame.hasMetadata() || frame.isComplete()) + continue; + + decodedSize += frame.clear(); + } + + decodedSizeDecreased(decodedSize); +} + +void ImageFrameCache::decodedSizeChanged(long long decodedSize) +{ + if (!decodedSize || !m_image || !m_image->imageObserver()) + return; + + m_image->imageObserver()->decodedSizeChanged(m_image, decodedSize); +} + +void ImageFrameCache::decodedSizeIncreased(unsigned decodedSize) +{ + if (!decodedSize) + return; + + m_decodedSize += decodedSize; + + // The fully-decoded frame will subsume the partially decoded data used + // to determine image properties. + long long changeSize = static_cast<long long>(decodedSize) - m_decodedPropertiesSize; + m_decodedPropertiesSize = 0; + decodedSizeChanged(changeSize); +} + +void ImageFrameCache::decodedSizeDecreased(unsigned decodedSize) +{ + if (!decodedSize) + return; + + ASSERT(m_decodedSize >= decodedSize); + m_decodedSize -= decodedSize; + decodedSizeChanged(-static_cast<long long>(decodedSize)); +} + +void ImageFrameCache::decodedSizeReset(unsigned decodedSize) +{ + ASSERT(m_decodedSize >= decodedSize); + m_decodedSize -= decodedSize; + + // Clearing the ImageSource destroys the extra decoded data used for + // determining image properties. + decodedSize += m_decodedPropertiesSize; + m_decodedPropertiesSize = 0; + decodedSizeChanged(-static_cast<long long>(decodedSize)); +} + +void ImageFrameCache::didDecodeProperties(unsigned decodedPropertiesSize) +{ + if (m_decodedSize) + return; + + long long decodedSize = static_cast<long long>(decodedPropertiesSize) - m_decodedPropertiesSize; + m_decodedPropertiesSize = decodedPropertiesSize; + decodedSizeChanged(decodedSize); +} + +void ImageFrameCache::growFrames() +{ + ASSERT(isSizeAvailable()); + ASSERT(m_frames.size() <= frameCount()); + m_frames.grow(frameCount()); +} + +void ImageFrameCache::setNativeImage(NativeImagePtr&& nativeImage) +{ + ASSERT(m_frames.size() == 1); + ImageFrame& frame = m_frames[0]; + + ASSERT(!isDecoderAvailable()); + + frame.m_nativeImage = WTFMove(nativeImage); + + frame.m_decoding = ImageFrame::Decoding::Complete; + frame.m_size = nativeImageSize(frame.m_nativeImage); + frame.m_hasAlpha = nativeImageHasAlpha(frame.m_nativeImage); +} + +void ImageFrameCache::setFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) +{ + ASSERT(index < m_frames.size()); + ImageFrame& frame = m_frames[index]; + + ASSERT(isDecoderAvailable()); + + frame.m_nativeImage = WTFMove(nativeImage); + setFrameMetadataAtIndex(index, subsamplingLevel, sizeForDrawing); +} + +void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) +{ + ASSERT(index < m_frames.size()); + ImageFrame& frame = m_frames[index]; + + ASSERT(isDecoderAvailable()); + frame.m_decoding = m_decoder->frameIsCompleteAtIndex(index) ? ImageFrame::Decoding::Complete : ImageFrame::Decoding::Partial; + if (frame.hasMetadata()) + return; + + frame.m_subsamplingLevel = subsamplingLevel; + + if (!sizeForDrawing) { + frame.m_size = m_decoder->frameSizeAtIndex(index, frame.m_subsamplingLevel); + frame.m_sizeForDrawing = { }; + } else { + ASSERT(frame.nativeImage()); + frame.m_size = nativeImageSize(frame.nativeImage()); + frame.m_sizeForDrawing = sizeForDrawing; + } + + frame.m_orientation = m_decoder->frameOrientationAtIndex(index); + frame.m_hasAlpha = m_decoder->frameHasAlphaAtIndex(index); + + if (repetitionCount()) + frame.m_duration = m_decoder->frameDurationAtIndex(index); +} + +void ImageFrameCache::replaceFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) +{ + ASSERT(index < m_frames.size()); + ImageFrame& frame = m_frames[index]; + + if (!frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing)) { + // Clear the current image frame and update the observer with this clearance. + unsigned decodedSize = frame.clear(); + decodedSizeDecreased(decodedSize); + } + + // Do not cache the NativeImage if adding its frameByes to the MemoryCache will cause numerical overflow. + size_t frameBytes = size().unclampedArea() * sizeof(RGBA32); + if (!WTF::isInBounds<unsigned>(frameBytes + decodedSize())) + return; + + // Copy the new image to the cache. + setFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing); + + // Update the observer with the new image frame bytes. + decodedSizeIncreased(frame.frameBytes()); +} + +void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing) +{ + if (!isDecoderAvailable()) + return; + + ASSERT(index < m_frames.size()); + ASSERT(m_frames[index].isBeingDecoded(sizeForDrawing)); + + // Clean the old native image and set a new one + replaceFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing); + m_frames[index].dequeueSizeForDecoding(); + + // Notify the image with the readiness of the new frame NativeImage. + if (m_image) + m_image->newFrameNativeImageAvailableAtIndex(index); +} + +Ref<WorkQueue> ImageFrameCache::decodingQueue() +{ + if (!m_decodingQueue) + m_decodingQueue = WorkQueue::create("org.webkit.ImageDecoder", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive); + + return *m_decodingQueue; +} + +void ImageFrameCache::startAsyncDecodingQueue() +{ + if (hasDecodingQueue() || !isDecoderAvailable()) + return; + + m_frameRequestQueue.open(); + + Ref<ImageFrameCache> protectedThis = Ref<ImageFrameCache>(*this); + Ref<WorkQueue> protectedQueue = decodingQueue(); + Ref<ImageDecoder> protectedDecoder = Ref<ImageDecoder>(*m_decoder); + + // We need to protect this, m_decodingQueue and m_decoder from being deleted while we are in the decoding loop. + decodingQueue()->dispatch([this, protectedThis = WTFMove(protectedThis), protectedQueue = WTFMove(protectedQueue), protectedDecoder = WTFMove(protectedDecoder)] { + ImageFrameRequest frameRequest; + + while (m_frameRequestQueue.dequeue(frameRequest)) { + // Get the frame NativeImage on the decoding thread. + NativeImagePtr nativeImage = protectedDecoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing); + + // Update the cached frames on the main thread to avoid updating the MemoryCache from a different thread. + callOnMainThread([this, protectedQueue = protectedQueue.copyRef(), nativeImage, frameRequest] () mutable { + // The queue may be closed if after we got the frame NativeImage, stopAsyncDecodingQueue() was called + if (protectedQueue.ptr() == m_decodingQueue) + cacheFrameNativeImageAtIndex(WTFMove(nativeImage), frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing); + }); + } + }); +} + +bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing) +{ + if (!isDecoderAvailable()) + return false; + + ASSERT(index < m_frames.size()); + ImageFrame& frame = m_frames[index]; + + // We need to coalesce multiple requests for decoding the same ImageFrame while it + // is still being decoded. This may happen if the image rectangle is repainted + // multiple times while the ImageFrame has not finished decoding. + if (frame.isBeingDecoded(sizeForDrawing)) + return true; + + if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing)) + return false; + + if (!hasDecodingQueue()) + startAsyncDecodingQueue(); + + frame.enqueueSizeForDecoding(sizeForDrawing); + m_frameRequestQueue.enqueue({ index, subsamplingLevel, sizeForDrawing }); + return true; +} + +void ImageFrameCache::stopAsyncDecodingQueue() +{ + if (!hasDecodingQueue()) + return; + + m_frameRequestQueue.close(); + m_decodingQueue = nullptr; + + for (ImageFrame& frame : m_frames) { + if (frame.isBeingDecoded()) { + frame.clearSizeForDecoding(); + frame.clear(); + } + } +} + +const ImageFrame& ImageFrameCache::frameAtIndexCacheIfNeeded(size_t index, ImageFrame::Caching caching, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) +{ + ASSERT(index < m_frames.size()); + ImageFrame& frame = m_frames[index]; + if (!isDecoderAvailable() || frame.isBeingDecoded(sizeForDrawing)) + return frame; + + SubsamplingLevel subsamplingLevelValue = subsamplingLevel ? subsamplingLevel.value() : frame.subsamplingLevel(); + + switch (caching) { + case ImageFrame::Caching::Metadata: + // Retrieve the metadata from ImageDecoder if the ImageFrame isn't complete. + if (frame.isComplete()) + break; + setFrameMetadataAtIndex(index, subsamplingLevelValue, frame.sizeForDrawing()); + break; + + case ImageFrame::Caching::MetadataAndImage: + // Cache the image and retrieve the metadata from ImageDecoder only if there was not valid image stored. + if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing)) + break; + // We have to perform synchronous image decoding in this code path regardless of the sizeForDrawing value. + // So pass an empty sizeForDrawing to create an ImageFrame with the native size. + replaceFrameNativeImageAtIndex(m_decoder->createFrameImageAtIndex(index, subsamplingLevelValue, { }), index, subsamplingLevelValue, { }); + break; + } + + return frame; +} + +void ImageFrameCache::clearMetadata() +{ + m_frameCount = std::nullopt; + m_singlePixelSolidColor = std::nullopt; +} + +template<typename T, T (ImageDecoder::*functor)() const> +T ImageFrameCache::metadata(const T& defaultValue, std::optional<T>* cachedValue) +{ + if (cachedValue && *cachedValue) + return cachedValue->value(); + + if (!isDecoderAvailable() || !m_decoder->isSizeAvailable()) + return defaultValue; + + if (!cachedValue) + return (*m_decoder.*functor)(); + + *cachedValue = (*m_decoder.*functor)(); + didDecodeProperties(m_decoder->bytesDecodedToDetermineProperties()); + return cachedValue->value(); +} + +template<typename T, typename... Args> +T ImageFrameCache::frameMetadataAtIndex(size_t index, T (ImageFrame::*functor)(Args...) const, Args&&... args) +{ + const ImageFrame& frame = index < m_frames.size() ? m_frames[index] : ImageFrame::defaultFrame(); + return (frame.*functor)(std::forward<Args>(args)...); +} + +template<typename T, typename... Args> +T ImageFrameCache::frameMetadataAtIndexCacheIfNeeded(size_t index, T (ImageFrame::*functor)() const, std::optional<T>* cachedValue, Args&&... args) +{ + if (cachedValue && *cachedValue) + return cachedValue->value(); + + const ImageFrame& frame = index < m_frames.size() ? frameAtIndexCacheIfNeeded(index, std::forward<Args>(args)...) : ImageFrame::defaultFrame(); + + // Don't cache any unavailable frame metadata. + if (!frame.hasMetadata() || !cachedValue) + return (frame.*functor)(); + + *cachedValue = (frame.*functor)(); + return cachedValue->value(); +} + +bool ImageFrameCache::isSizeAvailable() +{ + if (m_isSizeAvailable) + return m_isSizeAvailable.value(); + + if (!isDecoderAvailable() || !m_decoder->isSizeAvailable()) + return false; + + m_isSizeAvailable = true; + didDecodeProperties(m_decoder->bytesDecodedToDetermineProperties()); + return true; +} + +size_t ImageFrameCache::frameCount() +{ + return metadata<size_t, (&ImageDecoder::frameCount)>(m_frames.size(), &m_frameCount); +} + +RepetitionCount ImageFrameCache::repetitionCount() +{ + return metadata<RepetitionCount, (&ImageDecoder::repetitionCount)>(RepetitionCountNone, &m_repetitionCount); +} + +String ImageFrameCache::filenameExtension() +{ + return metadata<String, (&ImageDecoder::filenameExtension)>(String(), &m_filenameExtension); +} + +std::optional<IntPoint> ImageFrameCache::hotSpot() +{ + return metadata<std::optional<IntPoint>, (&ImageDecoder::hotSpot)>(std::nullopt, &m_hotSpot); +} + +IntSize ImageFrameCache::size() +{ +#if !USE(CG) + // It's possible that we have decoded the metadata, but not frame contents yet. In that case ImageDecoder claims to + // have the size available, but the frame cache is empty. Return the decoder size without caching in such case. + if (m_frames.isEmpty() && isDecoderAvailable()) + return m_decoder->size(); +#endif + return frameMetadataAtIndexCacheIfNeeded<IntSize>(0, (&ImageFrame::size), &m_size, ImageFrame::Caching::Metadata, SubsamplingLevel::Default); +} + +IntSize ImageFrameCache::sizeRespectingOrientation() +{ + return frameMetadataAtIndexCacheIfNeeded<IntSize>(0, (&ImageFrame::sizeRespectingOrientation), &m_sizeRespectingOrientation, ImageFrame::Caching::Metadata, SubsamplingLevel::Default); +} + +Color ImageFrameCache::singlePixelSolidColor() +{ + return frameCount() == 1 ? frameMetadataAtIndexCacheIfNeeded<Color>(0, (&ImageFrame::singlePixelSolidColor), &m_singlePixelSolidColor, ImageFrame::Caching::MetadataAndImage) : Color(); +} + +bool ImageFrameCache::frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) +{ + return frameMetadataAtIndex<bool>(index, (&ImageFrame::isBeingDecoded), sizeForDrawing); +} + +bool ImageFrameCache::frameIsCompleteAtIndex(size_t index) +{ + return frameMetadataAtIndex<bool>(index, (&ImageFrame::isComplete)); +} + +bool ImageFrameCache::frameHasAlphaAtIndex(size_t index) +{ + return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasAlpha)); +} + +bool ImageFrameCache::frameHasImageAtIndex(size_t index) +{ + return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasNativeImage)); +} + +bool ImageFrameCache::frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) +{ + return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasValidNativeImage), subsamplingLevel, sizeForDrawing); +} + +bool ImageFrameCache::frameHasDecodedNativeImage(size_t index) +{ + return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasDecodedNativeImage)); +} + +SubsamplingLevel ImageFrameCache::frameSubsamplingLevelAtIndex(size_t index) +{ + return frameMetadataAtIndex<SubsamplingLevel>(index, (&ImageFrame::subsamplingLevel)); +} + +IntSize ImageFrameCache::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel) +{ + return frameMetadataAtIndexCacheIfNeeded<IntSize>(index, (&ImageFrame::size), nullptr, ImageFrame::Caching::Metadata, subsamplingLevel); +} + +unsigned ImageFrameCache::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) +{ + return frameMetadataAtIndexCacheIfNeeded<unsigned>(index, (&ImageFrame::frameBytes), nullptr, ImageFrame::Caching::Metadata, subsamplingLevel); +} + +float ImageFrameCache::frameDurationAtIndex(size_t index) +{ + return frameMetadataAtIndexCacheIfNeeded<float>(index, (&ImageFrame::duration), nullptr, ImageFrame::Caching::Metadata); +} + +ImageOrientation ImageFrameCache::frameOrientationAtIndex(size_t index) +{ + return frameMetadataAtIndexCacheIfNeeded<ImageOrientation>(index, (&ImageFrame::orientation), nullptr, ImageFrame::Caching::Metadata); +} + +NativeImagePtr ImageFrameCache::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) +{ + return frameMetadataAtIndexCacheIfNeeded<NativeImagePtr>(index, (&ImageFrame::nativeImage), nullptr, ImageFrame::Caching::MetadataAndImage, subsamplingLevel, sizeForDrawing); +} + +} diff --git a/Source/WebCore/platform/graphics/ImageFrameCache.h b/Source/WebCore/platform/graphics/ImageFrameCache.h new file mode 100644 index 000000000..d623573e6 --- /dev/null +++ b/Source/WebCore/platform/graphics/ImageFrameCache.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ImageFrame.h" +#include "TextStream.h" + +#include <wtf/Forward.h> +#include <wtf/Optional.h> +#include <wtf/SynchronizedFixedQueue.h> +#include <wtf/WorkQueue.h> +#include <wtf/threads/BinarySemaphore.h> + +namespace WebCore { + +class GraphicsContext; +class Image; +class ImageDecoder; + +class ImageFrameCache : public RefCounted<ImageFrameCache> { + friend class ImageSource; +public: + static Ref<ImageFrameCache> create(Image* image) + { + return adoptRef(*new ImageFrameCache(image)); + } + + static Ref<ImageFrameCache> create(NativeImagePtr&& nativeImage) + { + return adoptRef(*new ImageFrameCache(WTFMove(nativeImage))); + } + + ~ImageFrameCache(); + + void setDecoder(ImageDecoder*); + ImageDecoder* decoder() const; + + unsigned decodedSize() const { return m_decodedSize; } + void destroyAllDecodedData() { destroyDecodedData(frameCount(), frameCount()); } + void destroyAllDecodedDataExcludeFrame(size_t excludeFrame) { destroyDecodedData(frameCount(), excludeFrame); } + void destroyDecodedDataBeforeFrame(size_t beforeFrame) { destroyDecodedData(beforeFrame, beforeFrame); } + void destroyIncompleteDecodedData(); + + void growFrames(); + void clearMetadata(); + + // Asynchronous image decoding + void startAsyncDecodingQueue(); + bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel, const IntSize&); + void stopAsyncDecodingQueue(); + bool hasDecodingQueue() { return m_decodingQueue; } + + // Image metadata which is calculated either by the ImageDecoder or directly + // from the NativeImage if this class was created for a memory image. + bool isSizeAvailable(); + size_t frameCount(); + RepetitionCount repetitionCount(); + String filenameExtension(); + std::optional<IntPoint> hotSpot(); + + // Image metadata which is calculated from the first ImageFrame. + IntSize size(); + IntSize sizeRespectingOrientation(); + + Color singlePixelSolidColor(); + + // ImageFrame metadata which does not require caching the ImageFrame. + bool frameIsBeingDecodedAtIndex(size_t, const std::optional<IntSize>& sizeForDrawing); + bool frameIsCompleteAtIndex(size_t); + bool frameHasAlphaAtIndex(size_t); + bool frameHasImageAtIndex(size_t); + bool frameHasValidNativeImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing); + bool frameHasDecodedNativeImage(size_t); + SubsamplingLevel frameSubsamplingLevelAtIndex(size_t); + + // ImageFrame metadata which forces caching or re-caching the ImageFrame. + IntSize frameSizeAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default); + unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default); + float frameDurationAtIndex(size_t); + ImageOrientation frameOrientationAtIndex(size_t); + NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing); + +private: + ImageFrameCache(Image*); + ImageFrameCache(NativeImagePtr&&); + + template<typename T, T (ImageDecoder::*functor)() const> + T metadata(const T& defaultValue, std::optional<T>* cachedValue = nullptr); + + template<typename T, typename... Args> + T frameMetadataAtIndex(size_t, T (ImageFrame::*functor)(Args...) const, Args&&...); + + template<typename T, typename... Args> + T frameMetadataAtIndexCacheIfNeeded(size_t, T (ImageFrame::*functor)() const, std::optional<T>* cachedValue, Args&&...); + + bool isDecoderAvailable() const { return m_decoder; } + void destroyDecodedData(size_t frameCount, size_t excludeFrame); + void decodedSizeChanged(long long decodedSize); + void didDecodeProperties(unsigned decodedPropertiesSize); + void decodedSizeIncreased(unsigned decodedSize); + void decodedSizeDecreased(unsigned decodedSize); + void decodedSizeReset(unsigned decodedSize); + + void setNativeImage(NativeImagePtr&&); + void setFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing); + void setFrameMetadataAtIndex(size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing); + void replaceFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing); + void cacheFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const IntSize& sizeForDrawing); + + Ref<WorkQueue> decodingQueue(); + + const ImageFrame& frameAtIndexCacheIfNeeded(size_t, ImageFrame::Caching, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }); + + Image* m_image { nullptr }; + RefPtr<ImageDecoder> m_decoder; + unsigned m_decodedSize { 0 }; + unsigned m_decodedPropertiesSize { 0 }; + + Vector<ImageFrame, 1> m_frames; + + // Asynchronous image decoding. + struct ImageFrameRequest { + size_t index; + SubsamplingLevel subsamplingLevel; + IntSize sizeForDrawing; + }; + static const int BufferSize = 8; + using FrameRequestQueue = SynchronizedFixedQueue<ImageFrameRequest, BufferSize>; + FrameRequestQueue m_frameRequestQueue; + RefPtr<WorkQueue> m_decodingQueue; + + // Image metadata. + std::optional<bool> m_isSizeAvailable; + std::optional<size_t> m_frameCount; + std::optional<RepetitionCount> m_repetitionCount; + std::optional<String> m_filenameExtension; + std::optional<std::optional<IntPoint>> m_hotSpot; + + // Image metadata which is calculated from the first ImageFrame. + std::optional<IntSize> m_size; + std::optional<IntSize> m_sizeRespectingOrientation; + std::optional<Color> m_singlePixelSolidColor; +}; + +} diff --git a/Source/WebCore/platform/graphics/ImageObserver.h b/Source/WebCore/platform/graphics/ImageObserver.h index 8b693d984..7c86194c6 100644 --- a/Source/WebCore/platform/graphics/ImageObserver.h +++ b/Source/WebCore/platform/graphics/ImageObserver.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,6 +30,7 @@ namespace WebCore { class Image; class IntRect; +class URL; // Interface for notification about changes to an image, including decoding, // drawing, and animating. @@ -37,13 +38,18 @@ class ImageObserver { protected: virtual ~ImageObserver() {} public: - virtual void decodedSizeChanged(const Image*, int delta) = 0; + virtual URL sourceUrl() const = 0; + virtual bool allowSubsampling() const = 0; + virtual bool allowLargeImageAsyncDecoding() const = 0; + virtual bool allowAnimatedImageAsyncDecoding() const = 0; + virtual bool showDebugBackground() const = 0; + virtual void decodedSizeChanged(const Image*, long long delta) = 0; + virtual void didDraw(const Image*) = 0; - virtual bool shouldPauseAnimation(const Image*) = 0; virtual void animationAdvanced(const Image*) = 0; - virtual void changedInRect(const Image*, const IntRect&) = 0; + virtual void changedInRect(const Image*, const IntRect* changeRect = nullptr) = 0; }; } diff --git a/Source/WebCore/platform/graphics/ImageOrientation.h b/Source/WebCore/platform/graphics/ImageOrientation.h index 4630dc6c0..2b3910690 100644 --- a/Source/WebCore/platform/graphics/ImageOrientation.h +++ b/Source/WebCore/platform/graphics/ImageOrientation.h @@ -84,7 +84,12 @@ struct ImageOrientationDescription { class ImageOrientation { public: - ImageOrientation(ImageOrientationEnum orientation = DefaultImageOrientation) + ImageOrientation() + : m_orientation(DefaultImageOrientation) + { + } + + explicit ImageOrientation(ImageOrientationEnum orientation) : m_orientation(orientation) { } @@ -101,14 +106,16 @@ public: { // Values direct from images may be invalid, in which case we use the default. if (exifValue < OriginTopLeft || exifValue > OriginLeftBottom) - return DefaultImageOrientation; - return static_cast<ImageOrientationEnum>(exifValue); + return ImageOrientation(); + return ImageOrientation(static_cast<ImageOrientationEnum>(exifValue)); } // This transform can be used for drawing an image according to the orientation. // It should be used in a right-handed coordinate system. AffineTransform transformFromDefault(const FloatSize& drawnSize) const; + inline operator ImageOrientationEnum() const { return m_orientation; } + inline bool operator==(const ImageOrientation& other) const { return other.m_orientation == m_orientation; } inline bool operator!=(const ImageOrientation& other) const { return !(*this == other); } diff --git a/Source/WebCore/platform/graphics/ImageRenderingMode.h b/Source/WebCore/platform/graphics/ImageRenderingMode.h new file mode 100644 index 000000000..3e462fba5 --- /dev/null +++ b/Source/WebCore/platform/graphics/ImageRenderingMode.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageRenderingMode_h +#define ImageRenderingMode_h + +namespace WebCore { + +enum ImageRenderingMode { AutoImageRendering, OptimizeContrast }; + +} // namespace WebCore + +#endif // ImageRenderingMode_h diff --git a/Source/WebCore/platform/graphics/ImageSource.cpp b/Source/WebCore/platform/graphics/ImageSource.cpp index c041f5638..688a8c727 100644 --- a/Source/WebCore/platform/graphics/ImageSource.cpp +++ b/Source/WebCore/platform/graphics/ImageSource.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2010, 2011, 2012, 2014, 2016 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk> * Copyright (C) 2008, Google Inc. All rights reserved. * Copyright (C) 2007-2009 Torch Mobile, Inc @@ -13,10 +13,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,19 +29,29 @@ #include "config.h" #include "ImageSource.h" +#if USE(CG) +#include "ImageDecoderCG.h" +#elif USE(DIRECT2D) +#include "GraphicsContext.h" +#include "ImageDecoderDirect2D.h" +#include <WinCodec.h> +#else #include "ImageDecoder.h" +#endif #include "ImageOrientation.h" -#include "NotImplemented.h" -namespace WebCore { +#include <wtf/CurrentTime.h> -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) -unsigned ImageSource::s_maxPixelsPerDecodedImage = 1024 * 1024; -#endif +namespace WebCore { -ImageSource::ImageSource(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) - : m_decoder(0) +ImageSource::ImageSource(NativeImagePtr&& nativeImage) + : m_frameCache(ImageFrameCache::create(WTFMove(nativeImage))) +{ +} + +ImageSource::ImageSource(Image* image, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) + : m_frameCache(ImageFrameCache::create(image)) , m_alphaOption(alphaOption) , m_gammaAndColorProfileOption(gammaAndColorProfileOption) { @@ -49,159 +59,154 @@ ImageSource::ImageSource(ImageSource::AlphaOption alphaOption, ImageSource::Gamm ImageSource::~ImageSource() { - clear(true); } -void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) +void ImageSource::clearFrameBufferCache(size_t clearBeforeFrame) { - if (!destroyAll) { - if (m_decoder) - m_decoder->clearFrameBufferCache(clearBeforeFrame); + if (!isDecoderAvailable()) return; - } - - delete m_decoder; - m_decoder = 0; - if (data) - setData(data, allDataReceived); + m_decoder->clearFrameBufferCache(clearBeforeFrame); } -bool ImageSource::initialized() const +void ImageSource::clear(SharedBuffer* data) { - return m_decoder; + m_decoder = nullptr; + m_frameCache->setDecoder(nullptr); + setData(data, isAllDataReceived()); } -void ImageSource::setData(SharedBuffer* data, bool allDataReceived) +bool ImageSource::ensureDecoderAvailable(SharedBuffer* data) { - // Make the decoder by sniffing the bytes. - // This method will examine the data and instantiate an instance of the appropriate decoder plugin. - // If insufficient bytes are available to determine the image type, no decoder plugin will be - // made. - if (!m_decoder) { - m_decoder = static_cast<NativeImageDecoderPtr>(NativeImageDecoder::create(*data, m_alphaOption, m_gammaAndColorProfileOption)); -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) - if (m_decoder && s_maxPixelsPerDecodedImage) - m_decoder->setMaxNumPixels(s_maxPixelsPerDecodedImage); -#endif - } + if (!data || isDecoderAvailable()) + return true; - if (m_decoder) - m_decoder->setData(data, allDataReceived); -} + m_decoder = ImageDecoder::create(*data, m_alphaOption, m_gammaAndColorProfileOption); + if (!isDecoderAvailable()) + return false; -String ImageSource::filenameExtension() const -{ - return m_decoder ? m_decoder->filenameExtension() : String(); + m_frameCache->setDecoder(m_decoder.get()); + return true; } -bool ImageSource::isSizeAvailable() +void ImageSource::setDecoderTargetContext(const GraphicsContext* targetContext) { - return m_decoder && m_decoder->isSizeAvailable(); +#if USE(DIRECT2D) + if (!isDecoderAvailable()) + return; + + if (targetContext) + m_decoder->setTargetContext(targetContext->platformContext()); +#else + UNUSED_PARAM(targetContext); +#endif } -IntSize ImageSource::size(ImageOrientationDescription description) const +void ImageSource::setData(SharedBuffer* data, bool allDataReceived) { - return frameSizeAtIndex(0, description); + if (!data || !ensureDecoderAvailable(data)) + return; + + m_decoder->setData(*data, allDataReceived); } -IntSize ImageSource::frameSizeAtIndex(size_t index, ImageOrientationDescription description) const +bool ImageSource::dataChanged(SharedBuffer* data, bool allDataReceived) { - if (!m_decoder) - return IntSize(); + m_frameCache->destroyIncompleteDecodedData(); - IntSize size = m_decoder->frameSizeAtIndex(index); - if ((description.respectImageOrientation() == RespectImageOrientation) && m_decoder->orientation().usesWidthAsHeight()) - return IntSize(size.height(), size.width()); +#if PLATFORM(IOS) + // FIXME: We should expose a setting to enable/disable progressive loading and make this + // code conditional on it. Then we can remove the PLATFORM(IOS)-guard. + static const double chunkLoadIntervals[] = {0, 1, 3, 6, 15}; + double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, static_cast<uint16_t>(4))]; - return size; -} + bool needsUpdate = false; -bool ImageSource::getHotSpot(IntPoint& hotSpot) const -{ - return m_decoder ? m_decoder->hotSpot(hotSpot) : false; -} + // The first time through, the chunk time will be 0 and the image will get an update. + if (currentTime() - m_progressiveLoadChunkTime > interval) { + needsUpdate = true; + m_progressiveLoadChunkTime = currentTime(); + ASSERT(m_progressiveLoadChunkCount <= std::numeric_limits<uint16_t>::max()); + ++m_progressiveLoadChunkCount; + } -size_t ImageSource::bytesDecodedToDetermineProperties() const -{ - return 0; + if (needsUpdate || allDataReceived) + setData(data, allDataReceived); +#else + setData(data, allDataReceived); +#endif + + m_frameCache->clearMetadata(); + if (!isSizeAvailable()) + return false; + + m_frameCache->growFrames(); + return true; } -int ImageSource::repetitionCount() +bool ImageSource::isAllDataReceived() { - return m_decoder ? m_decoder->repetitionCount() : cAnimationNone; + return isDecoderAvailable() ? m_decoder->isAllDataReceived() : m_frameCache->frameCount(); } -size_t ImageSource::frameCount() const +bool ImageSource::isAsyncDecodingRequired() { - return m_decoder ? m_decoder->frameCount() : 0; + // FIXME: figure out the best heuristic for enabling async image decoding. + return size().area() * sizeof(RGBA32) >= 100 * KB; } -PassNativeImagePtr ImageSource::createFrameAtIndex(size_t index, float* scale) +SubsamplingLevel ImageSource::maximumSubsamplingLevel() { - UNUSED_PARAM(scale); + if (m_maximumSubsamplingLevel) + return m_maximumSubsamplingLevel.value(); - if (!m_decoder) - return 0; + if (!isDecoderAvailable() || !m_decoder->frameAllowSubsamplingAtIndex(0)) + return SubsamplingLevel::Default; - ImageFrame* buffer = m_decoder->frameBufferAtIndex(index); - if (!buffer || buffer->status() == ImageFrame::FrameEmpty) - return 0; + // FIXME: this value was chosen to be appropriate for iOS since the image + // subsampling is only enabled by default on iOS. Choose a different value + // if image subsampling is enabled on other platform. + const int maximumImageAreaBeforeSubsampling = 5 * 1024 * 1024; + SubsamplingLevel level = SubsamplingLevel::First; - // Zero-height images can cause problems for some ports. If we have an - // empty image dimension, just bail. - if (size().isEmpty()) - return 0; + for (; level < SubsamplingLevel::Last; ++level) { + if (frameSizeAtIndex(0, level).area().unsafeGet() < maximumImageAreaBeforeSubsampling) + break; + } - // Return the buffer contents as a native image. For some ports, the data - // is already in a native container, and this just increments its refcount. - return buffer->asNewNativeImage(); + m_maximumSubsamplingLevel = level; + return m_maximumSubsamplingLevel.value(); } -float ImageSource::frameDurationAtIndex(size_t index) +SubsamplingLevel ImageSource::subsamplingLevelForScale(float scale) { - if (!m_decoder) - return 0; + if (!(scale > 0 && scale <= 1)) + return SubsamplingLevel::Default; - ImageFrame* buffer = m_decoder->frameBufferAtIndex(index); - if (!buffer || buffer->status() == ImageFrame::FrameEmpty) - return 0; - - // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. - // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify - // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> - // for more information. - const float duration = buffer->duration() / 1000.0f; - if (duration < 0.011f) - return 0.100f; - return duration; + int result = std::ceil(std::log2(1 / scale)); + return static_cast<SubsamplingLevel>(std::min(result, static_cast<int>(maximumSubsamplingLevel()))); } -ImageOrientation ImageSource::orientationAtIndex(size_t) const +NativeImagePtr ImageSource::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { - return m_decoder ? m_decoder->orientation() : DefaultImageOrientation; + return isDecoderAvailable() ? m_decoder->createFrameImageAtIndex(index, subsamplingLevel) : nullptr; } -bool ImageSource::frameHasAlphaAtIndex(size_t index) +NativeImagePtr ImageSource::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext) { - if (!m_decoder) - return true; - return m_decoder->frameHasAlphaAtIndex(index); + setDecoderTargetContext(targetContext); + return m_frameCache->frameImageAtIndex(index, subsamplingLevel, sizeForDrawing); } -bool ImageSource::frameIsCompleteAtIndex(size_t index) +void ImageSource::dump(TextStream& ts) { - if (!m_decoder) - return false; - - ImageFrame* buffer = m_decoder->frameBufferAtIndex(index); - return buffer && buffer->status() == ImageFrame::FrameComplete; -} + ts.dumpProperty("type", filenameExtension()); + ts.dumpProperty("frame-count", frameCount()); + ts.dumpProperty("repetitions", repetitionCount()); + ts.dumpProperty("solid-color", singlePixelSolidColor()); -unsigned ImageSource::frameBytesAtIndex(size_t index) const -{ - if (!m_decoder) - return 0; - return m_decoder->frameBytesAtIndex(index); + ImageOrientation orientation = frameOrientationAtIndex(0); + if (orientation != OriginTopLeft) + ts.dumpProperty("orientation", orientation); } } diff --git a/Source/WebCore/platform/graphics/ImageSource.h b/Source/WebCore/platform/graphics/ImageSource.h index 72e119a07..4ccf07973 100644 --- a/Source/WebCore/platform/graphics/ImageSource.h +++ b/Source/WebCore/platform/graphics/ImageSource.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004-2008, 2010, 2012, 2014, 2016 Apple Inc. All rights reserved. * Copyright (C) 2007-2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,171 +24,105 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ImageSource_h -#define ImageSource_h +#pragma once +#include "ImageFrame.h" +#include "ImageFrameCache.h" #include "ImageOrientation.h" -#include "NativeImagePtr.h" - +#include "IntPoint.h" +#include "NativeImage.h" +#include "TextStream.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> -#include <wtf/Vector.h> - -#if USE(CG) -typedef struct CGImageSource* CGImageSourceRef; -typedef const struct __CFData* CFDataRef; -#endif +#include <wtf/Optional.h> namespace WebCore { +class GraphicsContext; +class ImageDecoder; class ImageOrientation; class IntPoint; class IntSize; class SharedBuffer; -#if USE(CG) -typedef CGImageSourceRef NativeImageDecoderPtr; -#else -class ImageDecoder; -typedef ImageDecoder* NativeImageDecoderPtr; -#endif - -#if USE(CG) -#define NativeImageDecoder ImageDecoder -#else -typedef ImageDecoder NativeImageDecoder; -#endif - -// Right now GIFs are the only recognized image format that supports animation. -// The animation system and the constants below are designed with this in mind. -// GIFs have an optional 16-bit unsigned loop count that describes how an -// animated GIF should be cycled. If the loop count is absent, the animation -// cycles once; if it is 0, the animation cycles infinitely; otherwise the -// animation plays n + 1 cycles (where n is the specified loop count). If the -// GIF decoder defaults to cAnimationLoopOnce in the absence of any loop count -// and translates an explicit "0" loop count to cAnimationLoopInfinite, then we -// get a couple of nice side effects: -// * By making cAnimationLoopOnce be 0, we allow the animation cycling code in -// BitmapImage.cpp to avoid special-casing it, and simply treat all -// non-negative loop counts identically. -// * By making the other two constants negative, we avoid conflicts with any -// real loop count values. -const int cAnimationLoopOnce = 0; -const int cAnimationLoopInfinite = -1; -const int cAnimationNone = -2; - class ImageSource { WTF_MAKE_NONCOPYABLE(ImageSource); + friend class BitmapImage; public: - enum AlphaOption { - AlphaPremultiplied, - AlphaNotPremultiplied - }; - - enum GammaAndColorProfileOption { - GammaAndColorProfileApplied, - GammaAndColorProfileIgnored - }; - -#if USE(CG) - enum ShouldSkipMetadata { - DoNotSkipMetadata, - SkipMetadata - }; -#endif - - ImageSource(AlphaOption alphaOption = AlphaPremultiplied, GammaAndColorProfileOption gammaAndColorProfileOption = GammaAndColorProfileApplied); + ImageSource(NativeImagePtr&&); + ImageSource(Image*, AlphaOption = AlphaOption::Premultiplied, GammaAndColorProfileOption = GammaAndColorProfileOption::Applied); ~ImageSource(); - // Tells the ImageSource that the Image no longer cares about decoded frame - // data -- at all (if |destroyAll| is true), or before frame - // |clearBeforeFrame| (if |destroyAll| is false). The ImageSource should - // delete cached decoded data for these frames where possible to keep memory - // usage low. When |destroyAll| is true, the ImageSource should also reset - // any local state so that decoding can begin again. - // - // Implementations that delete less than what's specified above waste - // memory. Implementations that delete more may burn CPU re-decoding frames - // that could otherwise have been cached, or encounter errors if they're - // asked to decode frames they can't decode due to the loss of previous - // decoded frames. - // - // Callers should not call clear(false, n) and subsequently call - // createFrameAtIndex(m) with m < n, unless they first call clear(true). - // This ensures that stateful ImageSources/decoders will work properly. - // - // The |data| and |allDataReceived| parameters should be supplied by callers - // who set |destroyAll| to true if they wish to be able to continue using - // the ImageSource. This way implementations which choose to destroy their - // decoders in some cases can reconstruct them correctly. - void clear(bool destroyAll, - size_t clearBeforeFrame = 0, - SharedBuffer* data = NULL, - bool allDataReceived = false); - - bool initialized() const; + void destroyAllDecodedData() { m_frameCache->destroyAllDecodedData(); } + void destroyAllDecodedDataExcludeFrame(size_t excludeFrame) { m_frameCache->destroyAllDecodedDataExcludeFrame(excludeFrame); } + void destroyDecodedDataBeforeFrame(size_t beforeFrame) { m_frameCache->destroyDecodedDataBeforeFrame(beforeFrame); } + void clearFrameBufferCache(size_t); + void clear(SharedBuffer* data); - void setData(SharedBuffer* data, bool allDataReceived); - String filenameExtension() const; - - bool isSizeAvailable(); - IntSize size(ImageOrientationDescription = ImageOrientationDescription()) const; - IntSize frameSizeAtIndex(size_t, ImageOrientationDescription = ImageOrientationDescription()) const; + bool ensureDecoderAvailable(SharedBuffer*); + bool isDecoderAvailable() const { return m_decoder.get(); } -#if PLATFORM(IOS) - IntSize originalSize(RespectImageOrientationEnum = DoNotRespectImageOrientation) const; - bool isSubsampled() const { return m_baseSubsampling; } -#endif - - bool getHotSpot(IntPoint&) const; - - size_t bytesDecodedToDetermineProperties() const; - - int repetitionCount(); - - size_t frameCount() const; + void setData(SharedBuffer* data, bool allDataReceived); + bool dataChanged(SharedBuffer* data, bool allDataReceived); + + unsigned decodedSize() const { return m_frameCache->decodedSize(); } + bool isAllDataReceived(); + + bool isAsyncDecodingRequired(); + bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing) { return m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel, sizeForDrawing); } + bool hasDecodingQueue() const { return m_frameCache->hasDecodingQueue(); } + void stopAsyncDecodingQueue() { m_frameCache->stopAsyncDecodingQueue(); } + + // Image metadata which is calculated by the decoder or can deduced by the case of the memory NativeImage. + bool isSizeAvailable() { return m_frameCache->isSizeAvailable(); } + size_t frameCount() { return m_frameCache->frameCount(); } + RepetitionCount repetitionCount() { return m_frameCache->repetitionCount(); } + String filenameExtension() { return m_frameCache->filenameExtension(); } + std::optional<IntPoint> hotSpot() { return m_frameCache->hotSpot(); } + + // Image metadata which is calculated from the first ImageFrame. + IntSize size() { return m_frameCache->size(); } + IntSize sizeRespectingOrientation() { return m_frameCache->sizeRespectingOrientation(); } + Color singlePixelSolidColor() { return m_frameCache->singlePixelSolidColor(); } + + // ImageFrame metadata which does not require caching the ImageFrame. + bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameIsBeingDecodedAtIndex(index, sizeForDrawing); } + bool frameIsCompleteAtIndex(size_t index) { return m_frameCache->frameIsCompleteAtIndex(index); } + bool frameHasAlphaAtIndex(size_t index) { return m_frameCache->frameHasAlphaAtIndex(index); } + bool frameHasImageAtIndex(size_t index) { return m_frameCache->frameHasImageAtIndex(index); } + bool frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); } + bool frameHasDecodedNativeImage(size_t index) { return m_frameCache->frameHasDecodedNativeImage(index); } + SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) { return m_frameCache->frameSubsamplingLevelAtIndex(index); } + + // ImageFrame metadata which forces caching or re-caching the ImageFrame. + IntSize frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameSizeAtIndex(index, subsamplingLevel); } + unsigned frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameBytesAtIndex(index, subsamplingLevel); } + float frameDurationAtIndex(size_t index) { return m_frameCache->frameDurationAtIndex(index); } + ImageOrientation frameOrientationAtIndex(size_t index) { return m_frameCache->frameOrientationAtIndex(index); } + NativeImagePtr frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* targetContext = nullptr); + + SubsamplingLevel maximumSubsamplingLevel(); + SubsamplingLevel subsamplingLevelForScale(float); + NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default); - // Callers should not call this after calling clear() with a higher index; - // see comments on clear() above. - PassNativeImagePtr createFrameAtIndex(size_t, float* scale = 0); +private: + void dump(TextStream&); - float frameDurationAtIndex(size_t); - bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha. - bool frameIsCompleteAtIndex(size_t); // Whether or not the frame is completely decoded. - ImageOrientation orientationAtIndex(size_t) const; // EXIF image orientation + void setDecoderTargetContext(const GraphicsContext*); - // Return the number of bytes in the decoded frame. If the frame is not yet - // decoded then return 0. - unsigned frameBytesAtIndex(size_t) const; + Ref<ImageFrameCache> m_frameCache; + RefPtr<ImageDecoder> m_decoder; -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) - static unsigned maxPixelsPerDecodedImage() { return s_maxPixelsPerDecodedImage; } - static void setMaxPixelsPerDecodedImage(unsigned maxPixels) { s_maxPixelsPerDecodedImage = maxPixels; } -#endif + std::optional<SubsamplingLevel> m_maximumSubsamplingLevel; #if PLATFORM(IOS) - static bool acceleratedImageDecodingEnabled() { return s_acceleratedImageDecoding; } - static void setAcceleratedImageDecodingEnabled(bool flag) { s_acceleratedImageDecoding = flag; } + // FIXME: We should expose a setting to enable/disable progressive loading so that we can remove the PLATFORM(IOS)-guard. + double m_progressiveLoadChunkTime { 0 }; + uint16_t m_progressiveLoadChunkCount { 0 }; #endif -private: - NativeImageDecoderPtr m_decoder; - -#if !USE(CG) - AlphaOption m_alphaOption; - GammaAndColorProfileOption m_gammaAndColorProfileOption; -#endif -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) - static unsigned s_maxPixelsPerDecodedImage; -#endif -#if PLATFORM(IOS) - mutable int m_baseSubsampling; - mutable bool m_isProgressive; - CFDictionaryRef imageSourceOptions(ShouldSkipMetadata, int subsampling = 0) const; - static bool s_acceleratedImageDecoding; -#endif + AlphaOption m_alphaOption { AlphaOption::Premultiplied }; + GammaAndColorProfileOption m_gammaAndColorProfileOption { GammaAndColorProfileOption::Applied }; }; } - -#endif diff --git a/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h b/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h index e7e9160a7..363bec272 100644 --- a/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h +++ b/Source/WebCore/platform/graphics/InbandTextTrackPrivate.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -35,6 +35,7 @@ namespace WebCore { class InbandTextTrackPrivate : public TrackPrivateBase { public: enum CueFormat { + Data, Generic, WebVTT }; @@ -42,7 +43,7 @@ public: virtual ~InbandTextTrackPrivate() { } void setClient(InbandTextTrackPrivateClient* client) { m_client = client; } - virtual InbandTextTrackPrivateClient* client() const override { return m_client; } + InbandTextTrackPrivateClient* client() const override { return m_client; } enum Mode { Disabled, @@ -68,9 +69,10 @@ public: virtual bool isMainProgramContent() const { return true; } virtual bool isEasyToRead() const { return false; } virtual bool isDefault() const { return false; } - virtual AtomicString label() const { return emptyAtom; } - virtual AtomicString language() const { return emptyAtom; } - virtual AtomicString id() const { return emptyAtom; } + AtomicString label() const override { return emptyAtom; } + AtomicString language() const override { return emptyAtom; } + AtomicString id() const override { return emptyAtom; } + virtual AtomicString inBandMetadataTrackDispatchType() const { return emptyAtom; } virtual int textTrackIndex() const { return 0; } diff --git a/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h b/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h index 7999f5d4f..9a11079bb 100644 --- a/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h +++ b/Source/WebCore/platform/graphics/InbandTextTrackPrivateClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,35 +23,38 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InbandTextTrackPrivateClient_h -#define InbandTextTrackPrivateClient_h +#pragma once + +#if ENABLE(VIDEO_TRACK) #include "Color.h" #include "TrackPrivateBase.h" +#include <wtf/MediaTime.h> -#if ENABLE(VIDEO_TRACK) +#if ENABLE(DATACUE_VALUE) +#include "SerializedPlatformRepresentation.h" +#endif namespace WebCore { class InbandTextTrackPrivate; +class ISOWebVTTCue; class GenericCueData : public RefCounted<GenericCueData> { public: + static Ref<GenericCueData> create() { return adoptRef(*new GenericCueData); } - static PassRefPtr<GenericCueData> create() { return adoptRef(new GenericCueData()); } - virtual ~GenericCueData() { } - - double startTime() const { return m_startTime; } - void setStartTime(double startTime) { m_startTime = startTime; } + MediaTime startTime() const { return m_startTime; } + void setStartTime(const MediaTime& startTime) { m_startTime = startTime; } - double endTime() const { return m_endTime; } - void setEndTime(double endTime) { m_endTime = endTime; } + MediaTime endTime() const { return m_endTime; } + void setEndTime(const MediaTime& endTime) { m_endTime = endTime; } - String id() const { return m_id; } - void setId(String id) { m_id = id; } + const String& id() const { return m_id; } + void setId(const String& id) { m_id = id; } - String content() const { return m_content; } - void setContent(String content) { m_content = content; } + const String& content() const { return m_content; } + void setContent(const String& content) { m_content = content; } double line() const { return m_line; } void setLine(double line) { m_line = line; } @@ -62,17 +65,12 @@ public: double size() const { return m_size; } void setSize(double size) { m_size = size; } - enum Alignment { - None, - Start, - Middle, - End - }; + enum Alignment { None, Start, Middle, End }; Alignment align() const { return m_align; } void setAlign(Alignment align) { m_align = align; } - String fontName() const { return m_fontName; } - void setFontName(String fontName) { m_fontName = fontName; } + const String& fontName() const { return m_fontName; } + void setFontName(const String& fontName) { m_fontName = fontName; } double baseFontSize() const { return m_baseFontSize; } void setBaseFontSize(double baseFontSize) { m_baseFontSize = baseFontSize; } @@ -80,68 +78,92 @@ public: double relativeFontSize() const { return m_relativeFontSize; } void setRelativeFontSize(double relativeFontSize) { m_relativeFontSize = relativeFontSize; } - Color foregroundColor() const { return m_foregroundColor; } - void setForegroundColor(RGBA32 color) { m_foregroundColor.setRGB(color); } + const Color& foregroundColor() const { return m_foregroundColor; } + void setForegroundColor(const Color& color) { m_foregroundColor = color; } - Color backgroundColor() const { return m_backgroundColor; } - void setBackgroundColor(RGBA32 color) { m_backgroundColor.setRGB(color); } - - Color highlightColor() const { return m_highlightColor; } - void setHighlightColor(RGBA32 color) { m_highlightColor.setRGB(color); } - - enum Status { - Uninitialized, - Partial, - Complete, - }; + const Color& backgroundColor() const { return m_backgroundColor; } + void setBackgroundColor(const Color& color) { m_backgroundColor = color; } + + const Color& highlightColor() const { return m_highlightColor; } + void setHighlightColor(const Color& color) { m_highlightColor = color; } + + enum Status { Uninitialized, Partial, Complete }; Status status() { return m_status; } void setStatus(Status status) { m_status = status; } - + + bool doesExtendCueData(const GenericCueData&) const; + private: - GenericCueData() - : m_startTime(0) - , m_endTime(0) - , m_line(-1) - , m_position(-1) - , m_size(-1) - , m_align(None) - , m_baseFontSize(0) - , m_relativeFontSize(0) - , m_status(Uninitialized) - { - } - - double m_startTime; - double m_endTime; + GenericCueData() = default; + + MediaTime m_startTime; + MediaTime m_endTime; String m_id; String m_content; - double m_line; - double m_position; - double m_size; - Alignment m_align; + double m_line { -1 }; + double m_position { -1 }; + double m_size { -1 }; + Alignment m_align { None }; String m_fontName; - double m_baseFontSize; - double m_relativeFontSize; + double m_baseFontSize { 0 }; + double m_relativeFontSize { 0 }; Color m_foregroundColor; Color m_backgroundColor; Color m_highlightColor; - Status m_status; + Status m_status { Uninitialized }; }; -class WebVTTCueData; - +inline bool GenericCueData::doesExtendCueData(const GenericCueData& other) const +{ + if (m_relativeFontSize != other.m_relativeFontSize) + return false; + if (m_baseFontSize != other.m_baseFontSize) + return false; + if (m_position != other.m_position) + return false; + if (m_line != other.m_line) + return false; + if (m_size != other.m_size) + return false; + if (m_align != other.m_align) + return false; + if (m_foregroundColor != other.m_foregroundColor) + return false; + if (m_backgroundColor != other.m_backgroundColor) + return false; + if (m_highlightColor != other.m_highlightColor) + return false; + if (m_fontName != other.m_fontName) + return false; + if (m_id != other.m_id) + return false; + if (m_content != other.m_content) + return false; + + return true; +} + class InbandTextTrackPrivateClient : public TrackPrivateBaseClient { public: virtual ~InbandTextTrackPrivateClient() { } - virtual void addGenericCue(InbandTextTrackPrivate*, PassRefPtr<GenericCueData>) = 0; - virtual void updateGenericCue(InbandTextTrackPrivate*, GenericCueData*) = 0; - virtual void removeGenericCue(InbandTextTrackPrivate*, GenericCueData*) = 0; + virtual void addDataCue(const MediaTime& start, const MediaTime& end, const void*, unsigned) = 0; + +#if ENABLE(DATACUE_VALUE) + virtual void addDataCue(const MediaTime& start, const MediaTime& end, Ref<SerializedPlatformRepresentation>&&, const String&) = 0; + virtual void updateDataCue(const MediaTime& start, const MediaTime& end, SerializedPlatformRepresentation&) = 0; + virtual void removeDataCue(const MediaTime& start, const MediaTime& end, SerializedPlatformRepresentation&) = 0; +#endif - virtual void parseWebVTTCueData(InbandTextTrackPrivate*, const char* data, unsigned length) = 0; + virtual void addGenericCue(GenericCueData&) = 0; + virtual void updateGenericCue(GenericCueData&) = 0; + virtual void removeGenericCue(GenericCueData&) = 0; + + virtual void parseWebVTTFileHeader(String&&) { ASSERT_NOT_REACHED(); } + virtual void parseWebVTTCueData(const char* data, unsigned length) = 0; + virtual void parseWebVTTCueData(const ISOWebVTTCue&) = 0; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/platform/graphics/IntPoint.cpp b/Source/WebCore/platform/graphics/IntPoint.cpp index b93405195..58df52627 100644 --- a/Source/WebCore/platform/graphics/IntPoint.cpp +++ b/Source/WebCore/platform/graphics/IntPoint.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,13 +26,28 @@ #include "config.h" #include "IntPoint.h" -#include <wtf/PrintStream.h> +#include "FloatPoint.h" +#include "TextStream.h" namespace WebCore { -void IntPoint::dump(PrintStream& out) const +IntPoint::IntPoint(const FloatPoint& p) + : m_x(clampToInteger(p.x())) + , m_y(clampToInteger(p.y())) { - out.printf("(%d, %d)", x(), y()); +} + +IntPoint IntPoint::constrainedBetween(const IntPoint& min, const IntPoint& max) const +{ + return { + std::max(min.x(), std::min(max.x(), m_x)), + std::max(min.y(), std::min(max.y(), m_y)) + }; +} + +TextStream& operator<<(TextStream& ts, const IntPoint& p) +{ + return ts << "(" << p.x() << "," << p.y() << ")"; } } diff --git a/Source/WebCore/platform/graphics/IntPoint.h b/Source/WebCore/platform/graphics/IntPoint.h index 275b73f1b..283f4e1e7 100644 --- a/Source/WebCore/platform/graphics/IntPoint.h +++ b/Source/WebCore/platform/graphics/IntPoint.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,11 @@ #define IntPoint_h #include "IntSize.h" -#include <wtf/MathExtras.h> +#include <cmath> + +#if PLATFORM(MAC) && defined __OBJC__ +#import <Foundation/NSGeometry.h> +#endif #if USE(CG) typedef struct CGPoint CGPoint; @@ -47,21 +51,28 @@ typedef struct _NSPoint NSPoint; #if PLATFORM(WIN) typedef struct tagPOINT POINT; typedef struct tagPOINTS POINTS; -#elif PLATFORM(GTK) -typedef struct _GdkPoint GdkPoint; -#elif PLATFORM(EFL) -typedef struct _Evas_Point Evas_Point; + +struct D2D_POINT_2U; +typedef D2D_POINT_2U D2D1_POINT_2U; + +struct D2D_POINT_2F; +typedef D2D_POINT_2F D2D1_POINT_2F; #endif namespace WebCore { +class FloatPoint; +class TextStream; + class IntPoint { public: IntPoint() : m_x(0), m_y(0) { } IntPoint(int x, int y) : m_x(x), m_y(y) { } - explicit IntPoint(const IntSize& size) : m_x(size.width()), m_y(size.height()) { } + WEBCORE_EXPORT explicit IntPoint(const IntSize& size) : m_x(size.width()), m_y(size.height()) { } + WEBCORE_EXPORT explicit IntPoint(const FloatPoint&); // don't do this implicitly since it's lossy static IntPoint zero() { return IntPoint(); } + bool isZero() const { return !m_x && !m_y; } int x() const { return m_x; } int y() const { return m_y; } @@ -77,19 +88,30 @@ public: m_x = lroundf(static_cast<float>(m_x * sx)); m_y = lroundf(static_cast<float>(m_y * sy)); } + + void scale(float scale) + { + this->scale(scale, scale); + } IntPoint expandedTo(const IntPoint& other) const { - return IntPoint(m_x > other.m_x ? m_x : other.m_x, - m_y > other.m_y ? m_y : other.m_y); + return { + m_x > other.m_x ? m_x : other.m_x, + m_y > other.m_y ? m_y : other.m_y + }; } IntPoint shrunkTo(const IntPoint& other) const { - return IntPoint(m_x < other.m_x ? m_x : other.m_x, - m_y < other.m_y ? m_y : other.m_y); + return { + m_x < other.m_x ? m_x : other.m_x, + m_y < other.m_y ? m_y : other.m_y + }; } + WEBCORE_EXPORT IntPoint constrainedBetween(const IntPoint& min, const IntPoint& max) const; + int distanceSquaredToPoint(const IntPoint&) const; void clampNegativeToZero() @@ -103,14 +125,14 @@ public: } #if USE(CG) - explicit IntPoint(const CGPoint&); // don't do this implicitly since it's lossy - operator CGPoint() const; + WEBCORE_EXPORT explicit IntPoint(const CGPoint&); // don't do this implicitly since it's lossy + WEBCORE_EXPORT operator CGPoint() const; #endif #if !PLATFORM(IOS) #if OS(DARWIN) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) - explicit IntPoint(const NSPoint&); // don't do this implicitly since it's lossy - operator NSPoint() const; + WEBCORE_EXPORT explicit IntPoint(const NSPoint&); // don't do this implicitly since it's lossy + WEBCORE_EXPORT operator NSPoint() const; #endif #endif // !PLATFORM(IOS) @@ -119,15 +141,12 @@ public: operator POINT() const; IntPoint(const POINTS&); operator POINTS() const; -#elif PLATFORM(GTK) - IntPoint(const GdkPoint&); - operator GdkPoint() const; -#elif PLATFORM(EFL) - explicit IntPoint(const Evas_Point&); - operator Evas_Point() const; -#endif - void dump(PrintStream& out) const; + IntPoint(const D2D1_POINT_2U&); + explicit IntPoint(const D2D1_POINT_2F&); // Don't do this implicitly, since it's lossy. + operator D2D1_POINT_2F() const; + operator D2D1_POINT_2U() const; +#endif private: int m_x, m_y; @@ -190,6 +209,8 @@ inline int IntPoint::distanceSquaredToPoint(const IntPoint& point) const return ((*this) - point).diagonalLengthSquared(); } +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const IntPoint&); + } // namespace WebCore #endif // IntPoint_h diff --git a/Source/WebCore/platform/graphics/IntRect.cpp b/Source/WebCore/platform/graphics/IntRect.cpp index 0e8ff9785..f02181000 100644 --- a/Source/WebCore/platform/graphics/IntRect.cpp +++ b/Source/WebCore/platform/graphics/IntRect.cpp @@ -28,8 +28,8 @@ #include "FloatRect.h" #include "LayoutRect.h" +#include "TextStream.h" #include <algorithm> -#include <wtf/PrintStream.h> namespace WebCore { @@ -146,20 +146,12 @@ IntSize IntRect::differenceToPoint(const IntPoint& point) const return IntSize(xdistance, ydistance); } -IntRect unionRect(const Vector<IntRect>& rects) +TextStream& operator<<(TextStream& ts, const IntRect& r) { - IntRect result; + if (ts.hasFormattingFlag(TextStream::Formatting::SVGStyleRect)) + return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) - result.unite(rects[i]); - - return result; -} - -void IntRect::dump(PrintStream& out) const -{ - out.print(location(), " ", size()); + return ts << r.location() << " " << r.size(); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/IntRect.h b/Source/WebCore/platform/graphics/IntRect.h index d910a15be..b038b2b54 100644 --- a/Source/WebCore/platform/graphics/IntRect.h +++ b/Source/WebCore/platform/graphics/IntRect.h @@ -28,13 +28,12 @@ #include "IntPoint.h" #include "LayoutUnit.h" -#include <wtf/Vector.h> #if USE(CG) typedef struct CGRect CGRect; #endif -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGRect NSRect; #else @@ -50,12 +49,12 @@ typedef struct _NSRect NSRect; #if PLATFORM(WIN) typedef struct tagRECT RECT; -#elif PLATFORM(GTK) -#ifdef GTK_API_VERSION_2 -typedef struct _GdkRectangle GdkRectangle; -#endif -#elif PLATFORM(EFL) -typedef struct _Eina_Rectangle Eina_Rectangle; + +struct D2D_RECT_U; +typedef D2D_RECT_U D2D1_RECT_U; + +struct D2D_RECT_F; +typedef D2D_RECT_F D2D1_RECT_F; #endif #if USE(CAIRO) @@ -66,6 +65,7 @@ namespace WebCore { class FloatRect; class LayoutRect; +class TextStream; class IntRect { WTF_MAKE_FAST_ALLOCATED; @@ -76,8 +76,8 @@ public: IntRect(int x, int y, int width, int height) : m_location(IntPoint(x, y)), m_size(IntSize(width, height)) { } - explicit IntRect(const FloatRect&); // don't do this implicitly since it's lossy - explicit IntRect(const LayoutRect&); // don't do this implicitly since it's lossy + WEBCORE_EXPORT explicit IntRect(const FloatRect&); // don't do this implicitly since it's lossy + WEBCORE_EXPORT explicit IntRect(const LayoutRect&); // don't do this implicitly since it's lossy IntPoint location() const { return m_location; } IntSize size() const { return m_size; } @@ -92,6 +92,9 @@ public: int width() const { return m_size.width(); } int height() const { return m_size.height(); } + template <typename T = WTF::CrashOnOverflow> + Checked<unsigned, T> area() const { return m_size.area<T>(); } + void setX(int x) { m_location.setX(x); } void setY(int y) { m_location.setY(y); } void setWidth(int width) { m_size.setWidth(width); } @@ -140,8 +143,8 @@ public: IntPoint minXMaxYCorner() const { return IntPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft IntPoint maxXMaxYCorner() const { return IntPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight - bool intersects(const IntRect&) const; - bool contains(const IntRect&) const; + WEBCORE_EXPORT bool intersects(const IntRect&) const; + WEBCORE_EXPORT bool contains(const IntRect&) const; // This checks to see if the rect contains x,y in the traditional sense. // Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py). @@ -149,8 +152,8 @@ public: { return px >= x() && px < maxX() && py >= y() && py < maxY(); } bool contains(const IntPoint& point) const { return contains(point.x(), point.y()); } - void intersect(const IntRect&); - void unite(const IntRect&); + WEBCORE_EXPORT void intersect(const IntRect&); + WEBCORE_EXPORT void unite(const IntRect&); void uniteIfNonZero(const IntRect&); void inflateX(int dx) @@ -164,7 +167,7 @@ public: m_size.setHeight(m_size.height() + dy + dy); } void inflate(int d) { inflateX(d); inflateY(d); } - void scale(float s); + WEBCORE_EXPORT void scale(float s); IntSize differenceToPoint(const IntPoint&) const; int distanceSquaredToPoint(const IntPoint& p) const { return differenceToPoint(p).diagonalLengthSquared(); } @@ -174,14 +177,10 @@ public: #if PLATFORM(WIN) IntRect(const RECT&); operator RECT() const; -#elif PLATFORM(GTK) -#ifdef GTK_API_VERSION_2 - IntRect(const GdkRectangle&); - operator GdkRectangle() const; -#endif -#elif PLATFORM(EFL) - explicit IntRect(const Eina_Rectangle&); - operator Eina_Rectangle() const; + explicit IntRect(const D2D1_RECT_F&); + IntRect(const D2D1_RECT_U&); + operator D2D1_RECT_F() const; + operator D2D1_RECT_U() const; #endif #if USE(CAIRO) @@ -190,19 +189,12 @@ public: #endif #if USE(CG) - operator CGRect() const; + WEBCORE_EXPORT operator CGRect() const; #endif -#if !PLATFORM(IOS) -#if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) - operator NSRect() const; +#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) + WEBCORE_EXPORT operator NSRect() const; #endif -#endif // !PLATFORM(IOS) - - void dump(PrintStream& out) const; - - static IntRect infiniteRect(); - bool isInfinite() const; private: IntPoint m_location; @@ -223,8 +215,6 @@ inline IntRect unionRect(const IntRect& a, const IntRect& b) return c; } -IntRect unionRect(const Vector<IntRect>&); - inline bool operator==(const IntRect& a, const IntRect& b) { return a.location() == b.location() && a.size() == b.size(); @@ -235,17 +225,6 @@ inline bool operator!=(const IntRect& a, const IntRect& b) return a.location() != b.location() || a.size() != b.size(); } -inline IntRect IntRect::infiniteRect() -{ - static IntRect infiniteRect(-LayoutUnit::max() / 2, -LayoutUnit::max() / 2, LayoutUnit::max(), LayoutUnit::max()); - return infiniteRect; -} - -inline bool IntRect::isInfinite() const -{ - return *this == infiniteRect(); -} - inline IntRect& operator-=(IntRect& r, const IntPoint& offset) { r.move(-offset.x(), -offset.y()); @@ -259,14 +238,14 @@ inline IntRect operator-(const IntRect& r, const IntPoint& offset) } #if USE(CG) -IntRect enclosingIntRect(const CGRect&); +WEBCORE_EXPORT IntRect enclosingIntRect(const CGRect&); #endif -#if !PLATFORM(IOS) -#if (PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES)) -IntRect enclosingIntRect(const NSRect&); +#if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) +WEBCORE_EXPORT IntRect enclosingIntRect(const NSRect&); #endif -#endif // !PLATFORM(IOS) + +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const IntRect&); } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/IntRectExtent.h b/Source/WebCore/platform/graphics/IntRectExtent.h index 6517f8ae0..611739054 100644 --- a/Source/WebCore/platform/graphics/IntRectExtent.h +++ b/Source/WebCore/platform/graphics/IntRectExtent.h @@ -30,8 +30,6 @@ #ifndef IntRectExtent_h #define IntRectExtent_h -#if ENABLE(CSS_FILTERS) - #include "LayoutRect.h" namespace WebCore { @@ -107,6 +105,4 @@ inline void operator+=(IntRectExtent& a, const IntRectExtent& b) } // namespace WebCore -#endif // ENABLE(CSS_FILTERS) - #endif // IntRectExtent_h diff --git a/Source/WebCore/platform/graphics/IntRectHash.h b/Source/WebCore/platform/graphics/IntRectHash.h new file mode 100644 index 000000000..8d6520a16 --- /dev/null +++ b/Source/WebCore/platform/graphics/IntRectHash.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef IntRectHash_h +#define IntRectHash_h + +#include "IntPointHash.h" +#include "IntRect.h" +#include "IntSizeHash.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> + +namespace WTF { + +template<> struct IntHash<WebCore::IntRect> { + static unsigned hash(const WebCore::IntRect& key) + { + return pairIntHash(DefaultHash<WebCore::IntPoint>::Hash::hash(key.location()), DefaultHash<WebCore::IntSize>::Hash::hash(key.size())); + } + static bool equal(const WebCore::IntRect& a, const WebCore::IntRect& b) + { + return DefaultHash<WebCore::IntPoint>::Hash::equal(a.location(), b.location()) && DefaultHash<WebCore::IntSize>::Hash::equal(a.size(), b.size()); + } + static const bool safeToCompareToEmptyOrDeleted = true; +}; +template<> struct DefaultHash<WebCore::IntRect> { typedef IntHash<WebCore::IntRect> Hash; }; + +template<> struct HashTraits<WebCore::IntRect> : GenericHashTraits<WebCore::IntRect> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(WebCore::IntRect& slot) { new (NotNull, &slot) WebCore::IntRect(-1, -1, -1, -1); } + static bool isDeletedValue(const WebCore::IntRect& value) { return value.x() == -1 && value.y() == -1 && value.width() == -1 && value.height() == -1; } +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/IntSize.cpp b/Source/WebCore/platform/graphics/IntSize.cpp index 025dca9ca..a3dccb337 100644 --- a/Source/WebCore/platform/graphics/IntSize.cpp +++ b/Source/WebCore/platform/graphics/IntSize.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,13 +26,28 @@ #include "config.h" #include "IntSize.h" -#include <wtf/PrintStream.h> +#include "FloatSize.h" +#include "TextStream.h" namespace WebCore { -void IntSize::dump(PrintStream& out) const +IntSize::IntSize(const FloatSize& s) + : m_width(clampToInteger(s.width())) + , m_height(clampToInteger(s.height())) { - out.printf("(%d x %d)", width(), height()); +} + +IntSize IntSize::constrainedBetween(const IntSize& min, const IntSize& max) const +{ + return { + std::max(min.width(), std::min(max.width(), m_width)), + std::max(min.height(), std::min(max.height(), m_height)) + }; +} + +TextStream& operator<<(TextStream& ts, const IntSize& size) +{ + return ts << "width=" << size.width() << " height=" << size.height(); } } diff --git a/Source/WebCore/platform/graphics/IntSize.h b/Source/WebCore/platform/graphics/IntSize.h index 3f16a862c..d39b65085 100644 --- a/Source/WebCore/platform/graphics/IntSize.h +++ b/Source/WebCore/platform/graphics/IntSize.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,14 +26,18 @@ #ifndef IntSize_h #define IntSize_h +#include "PlatformExportMacros.h" #include <algorithm> -#include <wtf/PrintStream.h> + +#if PLATFORM(MAC) && defined __OBJC__ +#import <Foundation/NSGeometry.h> +#endif #if USE(CG) typedef struct CGSize CGSize; #endif -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) #ifdef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES typedef struct CGSize NSSize; #else @@ -49,14 +53,24 @@ typedef struct _NSSize NSSize; #if PLATFORM(WIN) typedef struct tagSIZE SIZE; + +struct D2D_SIZE_U; +typedef D2D_SIZE_U D2D1_SIZE_U; + +struct D2D_SIZE_F; +typedef D2D_SIZE_F D2D1_SIZE_F; #endif namespace WebCore { +class FloatSize; +class TextStream; + class IntSize { public: IntSize() : m_width(0), m_height(0) { } IntSize(int width, int height) : m_width(width), m_height(height) { } + WEBCORE_EXPORT explicit IntSize(const FloatSize&); // don't do this implicitly since it's lossy int width() const { return m_width; } int height() const { return m_height; } @@ -75,6 +89,12 @@ public: m_height += height; } + void contract(int width, int height) + { + m_width -= width; + m_height -= height; + } + void scale(float widthScale, float heightScale) { m_width = static_cast<int>(static_cast<float>(m_width) * widthScale); @@ -109,9 +129,17 @@ public: m_height = minimumSize.height(); } - int area() const + WEBCORE_EXPORT IntSize constrainedBetween(const IntSize& min, const IntSize& max) const; + + template <typename T = WTF::CrashOnOverflow> + Checked<unsigned, T> area() const { - return m_width * m_height; + return Checked<unsigned, T>(abs(m_width)) * abs(m_height); + } + + size_t unclampedArea() const + { + return static_cast<size_t>(abs(m_width)) * abs(m_height); } int diagonalLengthSquared() const @@ -125,24 +153,24 @@ public: } #if USE(CG) - explicit IntSize(const CGSize&); // don't do this implicitly since it's lossy - operator CGSize() const; + WEBCORE_EXPORT explicit IntSize(const CGSize&); // don't do this implicitly since it's lossy + WEBCORE_EXPORT operator CGSize() const; #endif -#if !PLATFORM(IOS) #if PLATFORM(MAC) && !defined(NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES) - explicit IntSize(const NSSize &); // don't do this implicitly since it's lossy - operator NSSize() const; + WEBCORE_EXPORT explicit IntSize(const NSSize &); // don't do this implicitly since it's lossy + WEBCORE_EXPORT operator NSSize() const; #endif -#endif // !PLATFORM(IOS) #if PLATFORM(WIN) IntSize(const SIZE&); operator SIZE() const; + IntSize(const D2D1_SIZE_U&); + explicit IntSize(const D2D1_SIZE_F&); // don't do this implicitly since it's lossy; + operator D2D1_SIZE_U() const; + operator D2D1_SIZE_F() const; #endif - void dump(PrintStream& out) const; - private: int m_width, m_height; }; @@ -186,6 +214,8 @@ inline bool operator!=(const IntSize& a, const IntSize& b) return a.width() != b.width() || a.height() != b.height(); } +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const IntSize&); + } // namespace WebCore #endif // IntSize_h diff --git a/Source/WebCore/platform/graphics/Latin1TextIterator.h b/Source/WebCore/platform/graphics/Latin1TextIterator.h index c40189bfe..431d8ff20 100644 --- a/Source/WebCore/platform/graphics/Latin1TextIterator.h +++ b/Source/WebCore/platform/graphics/Latin1TextIterator.h @@ -28,18 +28,18 @@ namespace WebCore { class Latin1TextIterator { public: - // The passed in LChar pointer starts at 'currentCharacter'. The iterator operates on the range [currentCharacter, lastCharacter]. - // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastCharacter'. - Latin1TextIterator(const LChar* characters, int currentCharacter, int lastCharacter, int /*endCharacter*/) + // The passed in LChar pointer starts at 'currentIndex'. The iterator operates on the range [currentIndex, lastIndex]. + // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastIndex'. + Latin1TextIterator(const LChar* characters, unsigned currentIndex, unsigned lastIndex, unsigned /*endCharacter*/) : m_characters(characters) - , m_currentCharacter(currentCharacter) - , m_lastCharacter(lastCharacter) + , m_currentIndex(currentIndex) + , m_lastIndex(lastIndex) { } bool consume(UChar32& character, unsigned& clusterLength) { - if (m_currentCharacter >= m_lastCharacter) + if (m_currentIndex >= m_lastIndex) return false; character = *m_characters; @@ -50,16 +50,16 @@ public: void advance(unsigned advanceLength) { m_characters += advanceLength; - m_currentCharacter += advanceLength; + m_currentIndex += advanceLength; } - int currentCharacter() const { return m_currentCharacter; } + unsigned currentIndex() const { return m_currentIndex; } const LChar* characters() const { return m_characters; } private: const LChar* m_characters; - int m_currentCharacter; - int m_lastCharacter; + unsigned m_currentIndex; + unsigned m_lastIndex; }; } diff --git a/Source/WebCore/platform/graphics/LayoutBoxExtent.cpp b/Source/WebCore/platform/graphics/LayoutBoxExtent.cpp deleted file mode 100644 index fc1c86365..000000000 --- a/Source/WebCore/platform/graphics/LayoutBoxExtent.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2012, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "LayoutBoxExtent.h" - -namespace WebCore { - -LayoutUnit LayoutBoxExtent::logicalTop(WritingMode writingMode) const -{ - return isHorizontalWritingMode(writingMode) ? m_top : m_left; -} - -LayoutUnit LayoutBoxExtent::logicalBottom(WritingMode writingMode) const -{ - return isHorizontalWritingMode(writingMode) ? m_bottom : m_right; -} - -LayoutUnit LayoutBoxExtent::logicalLeft(WritingMode writingMode) const -{ - return isHorizontalWritingMode(writingMode) ? m_left : m_top; -} - -LayoutUnit LayoutBoxExtent::logicalRight(WritingMode writingMode) const -{ - return isHorizontalWritingMode(writingMode) ? m_right : m_bottom; -} - -LayoutUnit LayoutBoxExtent::before(WritingMode writingMode) const -{ - switch (writingMode) { - case TopToBottomWritingMode: - return m_top; - case BottomToTopWritingMode: - return m_bottom; - case LeftToRightWritingMode: - return m_left; - case RightToLeftWritingMode: - return m_right; - } - ASSERT_NOT_REACHED(); - return m_top; -} - -LayoutUnit LayoutBoxExtent::after(WritingMode writingMode) const -{ - switch (writingMode) { - case TopToBottomWritingMode: - return m_bottom; - case BottomToTopWritingMode: - return m_top; - case LeftToRightWritingMode: - return m_right; - case RightToLeftWritingMode: - return m_left; - } - ASSERT_NOT_REACHED(); - return m_bottom; -} - -LayoutUnit LayoutBoxExtent::start(WritingMode writingMode, TextDirection direction) const -{ - if (isHorizontalWritingMode(writingMode)) - return isLeftToRightDirection(direction) ? m_left : m_right; - return isLeftToRightDirection(direction) ? m_top : m_bottom; -} - -LayoutUnit LayoutBoxExtent::end(WritingMode writingMode, TextDirection direction) const -{ - if (isHorizontalWritingMode(writingMode)) - return isLeftToRightDirection(direction) ? m_right : m_left; - return isLeftToRightDirection(direction) ? m_bottom : m_top; -} - -void LayoutBoxExtent::setBefore(WritingMode writingMode, LayoutUnit value) -{ - switch (writingMode) { - case TopToBottomWritingMode: - m_top = value; - break; - case BottomToTopWritingMode: - m_bottom = value; - break; - case LeftToRightWritingMode: - m_left = value; - break; - case RightToLeftWritingMode: - m_right = value; - break; - default: - ASSERT_NOT_REACHED(); - m_top = value; - } -} - -void LayoutBoxExtent::setAfter(WritingMode writingMode, LayoutUnit value) -{ - switch (writingMode) { - case TopToBottomWritingMode: - m_bottom = value; - break; - case BottomToTopWritingMode: - m_top = value; - break; - case LeftToRightWritingMode: - m_right = value; - break; - case RightToLeftWritingMode: - m_left = value; - break; - default: - ASSERT_NOT_REACHED(); - m_bottom = value; - } -} - -void LayoutBoxExtent::setStart(WritingMode writingMode, TextDirection direction, LayoutUnit value) -{ - if (isHorizontalWritingMode(writingMode)) { - if (isLeftToRightDirection(direction)) - m_left = value; - else - m_right = value; - } else { - if (isLeftToRightDirection(direction)) - m_top = value; - else - m_bottom = value; - } -} - -void LayoutBoxExtent::setEnd(WritingMode writingMode, TextDirection direction, LayoutUnit value) -{ - if (isHorizontalWritingMode(writingMode)) { - if (isLeftToRightDirection(direction)) - m_right = value; - else - m_left = value; - } else { - if (isLeftToRightDirection(direction)) - m_bottom = value; - else - m_top = value; - } -} - -LayoutUnit& LayoutBoxExtent::mutableLogicalLeft(WritingMode writingMode) -{ - return isHorizontalWritingMode(writingMode) ? m_left : m_top; -} - -LayoutUnit& LayoutBoxExtent::mutableLogicalRight(WritingMode writingMode) -{ - return isHorizontalWritingMode(writingMode) ? m_right : m_bottom; -} - -LayoutUnit& LayoutBoxExtent::mutableBefore(WritingMode writingMode) -{ - return isHorizontalWritingMode(writingMode) ? (isFlippedBlocksWritingMode(writingMode) ? m_bottom : m_top) : - (isFlippedBlocksWritingMode(writingMode) ? m_right: m_left); -} - -LayoutUnit& LayoutBoxExtent::mutableAfter(WritingMode writingMode) -{ - return isHorizontalWritingMode(writingMode) ? (isFlippedBlocksWritingMode(writingMode) ? m_top : m_bottom) : - (isFlippedBlocksWritingMode(writingMode) ? m_left: m_right); -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/LayoutBoxExtent.h b/Source/WebCore/platform/graphics/LayoutBoxExtent.h deleted file mode 100644 index 1c57275b7..000000000 --- a/Source/WebCore/platform/graphics/LayoutBoxExtent.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef LayoutBoxExtent_h -#define LayoutBoxExtent_h - -#include "LayoutUnit.h" -#include "TextDirection.h" -#include "WritingMode.h" - -namespace WebCore { - -class LayoutBoxExtent { -public: - LayoutBoxExtent() : m_top(0), m_right(0), m_bottom(0), m_left(0) { } - LayoutBoxExtent(LayoutUnit top, LayoutUnit right, LayoutUnit bottom, LayoutUnit left) - : m_top(top), m_right(right), m_bottom(bottom), m_left(left) { } - - inline LayoutUnit top() const { return m_top; } - inline LayoutUnit right() const { return m_right; } - inline LayoutUnit bottom() const { return m_bottom; } - inline LayoutUnit left() const { return m_left; } - - inline void setTop(LayoutUnit value) { m_top = value; } - inline void setRight(LayoutUnit value) { m_right = value; } - inline void setBottom(LayoutUnit value) { m_bottom = value; } - inline void setLeft(LayoutUnit value) { m_left = value; } - - LayoutUnit logicalTop(WritingMode) const; - LayoutUnit logicalBottom(WritingMode) const; - LayoutUnit logicalLeft(WritingMode) const; - LayoutUnit logicalRight(WritingMode) const; - - LayoutUnit before(WritingMode) const; - LayoutUnit after(WritingMode) const; - LayoutUnit start(WritingMode, TextDirection) const; - LayoutUnit end(WritingMode, TextDirection) const; - - void setBefore(WritingMode, LayoutUnit); - void setAfter(WritingMode, LayoutUnit); - void setStart(WritingMode, TextDirection, LayoutUnit); - void setEnd(WritingMode, TextDirection, LayoutUnit); - - LayoutUnit& mutableLogicalLeft(WritingMode); - LayoutUnit& mutableLogicalRight(WritingMode); - - LayoutUnit& mutableBefore(WritingMode); - LayoutUnit& mutableAfter(WritingMode); - -private: - LayoutUnit m_top; - LayoutUnit m_right; - LayoutUnit m_bottom; - LayoutUnit m_left; -}; - -} // namespace WebCore - -#endif // LayoutBoxExtent_h diff --git a/Source/WebCore/platform/graphics/LayoutPoint.cpp b/Source/WebCore/platform/graphics/LayoutPoint.cpp new file mode 100644 index 000000000..eeb0159ad --- /dev/null +++ b/Source/WebCore/platform/graphics/LayoutPoint.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LayoutPoint.h" + +#include "TextStream.h" + +namespace WebCore { + +LayoutPoint LayoutPoint::constrainedBetween(const LayoutPoint& min, const LayoutPoint& max) const +{ + return { + std::max(min.x(), std::min(max.x(), m_x)), + std::max(min.y(), std::min(max.y(), m_y)) + }; +} + +TextStream& operator<<(TextStream& ts, const LayoutPoint& p) +{ + if (ts.hasFormattingFlag(TextStream::Formatting::LayoutUnitsAsIntegers)) + return ts << "(" << p.x().toInt() << "," << p.y().toInt() << ")"; + + return ts << "(" << p.x().toFloat() << "," << p.y().toFloat() << ")"; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/LayoutPoint.h b/Source/WebCore/platform/graphics/LayoutPoint.h index 78ecd922e..78ed0c5b4 100644 --- a/Source/WebCore/platform/graphics/LayoutPoint.h +++ b/Source/WebCore/platform/graphics/LayoutPoint.h @@ -33,7 +33,6 @@ #include "FloatPoint.h" #include "LayoutSize.h" -#include <wtf/MathExtras.h> namespace WebCore { @@ -56,20 +55,29 @@ public: void move(const LayoutSize& s) { move(s.width(), s.height()); } void moveBy(const LayoutPoint& offset) { move(offset.x(), offset.y()); } void move(LayoutUnit dx, LayoutUnit dy) { m_x += dx; m_y += dy; } + + void scale(float s) + { + m_x *= s; + m_y *= s; + } + void scale(float sx, float sy) { m_x *= sx; m_y *= sy; } - + + LayoutPoint constrainedBetween(const LayoutPoint& min, const LayoutPoint& max) const; + LayoutPoint expandedTo(const LayoutPoint& other) const { - return LayoutPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y)); + return { std::max(m_x, other.m_x), std::max(m_y, other.m_y) }; } LayoutPoint shrunkTo(const LayoutPoint& other) const { - return LayoutPoint(std::min(m_x, other.m_x), std::min(m_y, other.m_y)); + return { std::min(m_x, other.m_x), std::min(m_y, other.m_y) }; } void clampNegativeToZero() @@ -79,15 +87,15 @@ public: LayoutPoint transposedPoint() const { - return LayoutPoint(m_y, m_x); + return { m_y, m_x }; } LayoutPoint fraction() const { - return LayoutPoint(m_x.fraction(), m_y.fraction()); + return { m_x.fraction(), m_y.fraction() }; } - operator FloatPoint() const { return FloatPoint(m_x, m_y); } + operator FloatPoint() const { return { m_x, m_y }; } private: LayoutUnit m_x, m_y; @@ -140,19 +148,14 @@ inline bool operator!=(const LayoutPoint& a, const LayoutPoint& b) return a.x() != b.x() || a.y() != b.y(); } -inline LayoutPoint toPoint(const LayoutSize& size) +inline LayoutPoint toLayoutPoint(const LayoutSize& size) { return LayoutPoint(size.width(), size.height()); } -inline LayoutPoint toLayoutPoint(const LayoutSize& p) +inline LayoutSize toLayoutSize(const LayoutPoint& point) { - return LayoutPoint(p.width(), p.height()); -} - -inline LayoutSize toSize(const LayoutPoint& a) -{ - return LayoutSize(a.x(), a.y()); + return LayoutSize(point.x(), point.y()); } inline IntPoint flooredIntPoint(const LayoutPoint& point) @@ -167,7 +170,7 @@ inline IntPoint roundedIntPoint(const LayoutPoint& point) inline IntPoint roundedIntPoint(const LayoutSize& size) { - return IntPoint(size.width().round(), size.height().round()); + return roundedIntPoint(toLayoutPoint(size)); } inline IntPoint ceiledIntPoint(const LayoutPoint& point) @@ -185,30 +188,40 @@ inline LayoutPoint ceiledLayoutPoint(const FloatPoint& p) return LayoutPoint(LayoutUnit::fromFloatCeil(p.x()), LayoutUnit::fromFloatCeil(p.y())); } -inline IntSize pixelSnappedIntSize(const LayoutSize& s, const LayoutPoint& p) +inline IntSize snappedIntSize(const LayoutSize& size, const LayoutPoint& location) +{ + auto snap = [] (LayoutUnit a, LayoutUnit b) { + LayoutUnit fraction = b.fraction(); + return roundToInt(fraction + a) - roundToInt(fraction); + }; + return IntSize(snap(size.width(), location.x()), snap(size.height(), location.y())); +} + +inline FloatPoint roundPointToDevicePixels(const LayoutPoint& point, float pixelSnappingFactor, bool directionalRoundingToRight = true, bool directionalRoundingToBottom = true) { - return IntSize(snapSizeToPixel(s.width(), p.x()), snapSizeToPixel(s.height(), p.y())); + return FloatPoint(roundToDevicePixel(point.x(), pixelSnappingFactor, !directionalRoundingToRight), roundToDevicePixel(point.y(), pixelSnappingFactor, !directionalRoundingToBottom)); } -inline LayoutPoint roundedLayoutPoint(const FloatPoint& p) +inline FloatPoint floorPointToDevicePixels(const LayoutPoint& point, float pixelSnappingFactor) { -#if ENABLE(SUBPIXEL_LAYOUT) - return LayoutPoint(p); -#else - return roundedIntPoint(p); -#endif + return FloatPoint(floorToDevicePixel(point.x(), pixelSnappingFactor), floorToDevicePixel(point.y(), pixelSnappingFactor)); } -inline LayoutSize toLayoutSize(const LayoutPoint& p) +inline FloatPoint ceilPointToDevicePixels(const LayoutPoint& point, float pixelSnappingFactor) { - return LayoutSize(p.x(), p.y()); + return FloatPoint(ceilToDevicePixel(point.x(), pixelSnappingFactor), ceilToDevicePixel(point.y(), pixelSnappingFactor)); } -inline LayoutPoint flooredLayoutPoint(const FloatSize& s) +inline FloatSize snapSizeToDevicePixel(const LayoutSize& size, const LayoutPoint& location, float pixelSnappingFactor) { - return flooredLayoutPoint(FloatPoint(s)); + auto snap = [&] (LayoutUnit a, LayoutUnit b) { + LayoutUnit fraction = b.fraction(); + return roundToDevicePixel(fraction + a, pixelSnappingFactor) - roundToDevicePixel(fraction, pixelSnappingFactor); + }; + return FloatSize(snap(size.width(), location.x()), snap(size.height(), location.y())); } +TextStream& operator<<(TextStream&, const LayoutPoint&); } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/LayoutRect.cpp b/Source/WebCore/platform/graphics/LayoutRect.cpp index dce0e12db..71b7c62c3 100644 --- a/Source/WebCore/platform/graphics/LayoutRect.cpp +++ b/Source/WebCore/platform/graphics/LayoutRect.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "LayoutRect.h" +#include "TextStream.h" #include <algorithm> namespace WebCore { @@ -87,6 +88,27 @@ void LayoutRect::unite(const LayoutRect& other) m_size = newMaxPoint - newLocation; } +bool LayoutRect::checkedUnite(const LayoutRect& other) +{ + if (other.isEmpty()) + return true; + if (isEmpty()) { + *this = other; + return true; + } + if (!isMaxXMaxYRepresentable() || !other.isMaxXMaxYRepresentable()) + return false; + FloatPoint topLeft = FloatPoint(std::min<float>(x(), other.x()), std::min<float>(y(), other.y())); + FloatPoint bottomRight = FloatPoint(std::max<float>(maxX(), other.maxX()), std::max<float>(maxY(), other.maxY())); + FloatSize size = bottomRight - topLeft; + + if (size.width() >= LayoutUnit::nearlyMax() || size.height() >= LayoutUnit::nearlyMax()) + return false; + m_location = LayoutPoint(topLeft); + m_size = LayoutSize(size); + return true; +} + void LayoutRect::uniteIfNonZero(const LayoutRect& other) { // Handle empty special cases first. @@ -104,14 +126,15 @@ void LayoutRect::uniteIfNonZero(const LayoutRect& other) m_size = newMaxPoint - newLocation; } -void LayoutRect::scale(float s) +void LayoutRect::scale(float scaleValue) { - m_location.scale(s, s); - m_size.scale(s); + scale(scaleValue, scaleValue); } void LayoutRect::scale(float xScale, float yScale) { + if (isInfinite()) + return; m_location.scale(xScale, yScale); m_size.scale(xScale, yScale); } @@ -129,22 +152,35 @@ LayoutRect unionRect(const Vector<LayoutRect>& rects) IntRect enclosingIntRect(const LayoutRect& rect) { + // Empty rects with fractional x, y values turn into non-empty rects when converting to enclosing. + // We need to ensure that empty rects stay empty after the conversion, because the selection code expects them to be empty. IntPoint location = flooredIntPoint(rect.minXMinYCorner()); - IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); - + IntPoint maxPoint = IntPoint(rect.width() ? rect.maxX().ceil() : location.x(), rect.height() ? rect.maxY().ceil() : location.y()); return IntRect(location, maxPoint - location); } LayoutRect enclosingLayoutRect(const FloatRect& rect) { -#if ENABLE(SUBPIXEL_LAYOUT) LayoutPoint location = flooredLayoutPoint(rect.minXMinYCorner()); LayoutPoint maxPoint = ceiledLayoutPoint(rect.maxXMaxYCorner()); return LayoutRect(location, maxPoint - location); -#else - return enclosingIntRect(rect); -#endif +} + +FloatRect encloseRectToDevicePixels(const LayoutRect& rect, float pixelSnappingFactor) +{ + FloatPoint location = floorPointToDevicePixels(rect.minXMinYCorner(), pixelSnappingFactor); + FloatPoint maxPoint = ceilPointToDevicePixels(rect.maxXMaxYCorner(), pixelSnappingFactor); + + return FloatRect(location, maxPoint - location); +} + +TextStream& operator<<(TextStream& ts, const LayoutRect& r) +{ + if (ts.hasFormattingFlag(TextStream::Formatting::LayoutUnitsAsIntegers)) + return ts << snappedIntRect(r); + + return ts << FloatRect(r); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/LayoutRect.h b/Source/WebCore/platform/graphics/LayoutRect.h index 66a19bdf1..7442e2fbb 100644 --- a/Source/WebCore/platform/graphics/LayoutRect.h +++ b/Source/WebCore/platform/graphics/LayoutRect.h @@ -33,12 +33,14 @@ #include "FloatRect.h" #include "IntRect.h" -#include "LayoutBoxExtent.h" #include "LayoutPoint.h" +#include "LengthBox.h" #include <wtf/Vector.h> namespace WebCore { +class TextStream; + class LayoutRect { public: LayoutRect() { } @@ -46,18 +48,17 @@ public: : m_location(location), m_size(size) { } LayoutRect(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height) : m_location(LayoutPoint(x, y)), m_size(LayoutSize(width, height)) { } + LayoutRect(const LayoutPoint& topLeft, const LayoutPoint& bottomRight) + : m_location(topLeft), m_size(LayoutSize(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())) { } LayoutRect(const FloatPoint& location, const FloatSize& size) : m_location(location), m_size(size) { } LayoutRect(const IntRect& rect) : m_location(rect.location()), m_size(rect.size()) { } - - explicit LayoutRect(const FloatRect&); // don't do this implicitly since it's lossy + + WEBCORE_EXPORT explicit LayoutRect(const FloatRect&); // don't do this implicitly since it's lossy LayoutPoint location() const { return m_location; } LayoutSize size() const { return m_size; } - IntPoint pixelSnappedLocation() const { return roundedIntPoint(m_location); } - IntSize pixelSnappedSize() const { return IntSize(snapSizeToPixel(m_size.width(), m_location.x()), snapSizeToPixel(m_size.height(), m_location.y())); } - void setLocation(const LayoutPoint& location) { m_location = location; } void setSize(const LayoutSize& size) { m_size = size; } @@ -68,13 +69,6 @@ public: LayoutUnit width() const { return m_size.width(); } LayoutUnit height() const { return m_size.height(); } - int pixelSnappedX() const { return x().round(); } - int pixelSnappedY() const { return y().round(); } - int pixelSnappedWidth() const { return snapSizeToPixel(width(), x()); } - int pixelSnappedHeight() const { return snapSizeToPixel(height(), y()); } - int pixelSnappedMaxX() const { return (m_location.x() + m_size.width()).round(); } - int pixelSnappedMaxY() const { return (m_location.y() + m_size.height()).round(); } - void setX(LayoutUnit x) { m_location.setX(x); } void setY(LayoutUnit y) { m_location.setY(y); } void setWidth(LayoutUnit width) { m_size.setWidth(width); } @@ -132,9 +126,16 @@ public: LayoutPoint maxXMinYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y()); } // typically topRight LayoutPoint minXMaxYCorner() const { return LayoutPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft LayoutPoint maxXMaxYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight + bool isMaxXMaxYRepresentable() const + { + FloatRect rect = *this; + float maxX = rect.maxX(); + float maxY = rect.maxY(); + return maxX > LayoutUnit::nearlyMin() && maxX < LayoutUnit::nearlyMax() && maxY > LayoutUnit::nearlyMin() && maxY < LayoutUnit::nearlyMax(); + } bool intersects(const LayoutRect&) const; - bool contains(const LayoutRect&) const; + WEBCORE_EXPORT bool contains(const LayoutRect&) const; // This checks to see if the rect contains x,y in the traditional sense. // Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py). @@ -143,8 +144,9 @@ public: bool contains(const LayoutPoint& point) const { return contains(point.x(), point.y()); } void intersect(const LayoutRect&); - void unite(const LayoutRect&); + WEBCORE_EXPORT void unite(const LayoutRect&); void uniteIfNonZero(const LayoutRect&); + bool checkedUnite(const LayoutRect&); void inflateX(LayoutUnit dx) { @@ -157,10 +159,11 @@ public: m_size.setHeight(m_size.height() + dy + dy); } void inflate(LayoutUnit d) { inflateX(d); inflateY(d); } - void scale(float s); + WEBCORE_EXPORT void scale(float); void scale(float xScale, float yScale); LayoutRect transposedRect() const { return LayoutRect(m_location.transposedPoint(), m_size.transposedSize()); } + bool isInfinite() const { return *this == LayoutRect::infiniteRect(); } static LayoutRect infiniteRect() { @@ -201,36 +204,51 @@ inline bool operator!=(const LayoutRect& a, const LayoutRect& b) return a.location() != b.location() || a.size() != b.size(); } -inline IntRect pixelSnappedIntRect(const LayoutRect& rect) +// Integral snapping functions. +inline IntRect snappedIntRect(const LayoutRect& rect) { -#if ENABLE(SUBPIXEL_LAYOUT) - return IntRect(roundedIntPoint(rect.location()), IntSize(snapSizeToPixel(rect.width(), rect.x()), - snapSizeToPixel(rect.height(), rect.y()))); + return IntRect(roundedIntPoint(rect.location()), snappedIntSize(rect.size(), rect.location())); +} -#else - return IntRect(rect); -#endif +inline IntRect snappedIntRect(LayoutUnit left, LayoutUnit top, LayoutUnit width, LayoutUnit height) +{ + return IntRect(IntPoint(left.round(), top.round()), snappedIntSize(LayoutSize(width, height), LayoutPoint(left, top))); } -IntRect enclosingIntRect(const LayoutRect&); -LayoutRect enclosingLayoutRect(const FloatRect&); +inline IntRect snappedIntRect(LayoutPoint location, LayoutSize size) +{ + return IntRect(roundedIntPoint(location), snappedIntSize(size, location)); +} +WEBCORE_EXPORT IntRect enclosingIntRect(const LayoutRect&); +WEBCORE_EXPORT LayoutRect enclosingLayoutRect(const FloatRect&); -inline IntRect pixelSnappedIntRect(LayoutUnit left, LayoutUnit top, LayoutUnit width, LayoutUnit height) +// Device pixel snapping functions. +inline FloatRect snapRectToDevicePixels(const LayoutRect& rect, float pixelSnappingFactor) { - return IntRect(left.round(), top.round(), snapSizeToPixel(width, left), snapSizeToPixel(height, top)); + return FloatRect(FloatPoint(roundToDevicePixel(rect.x(), pixelSnappingFactor), roundToDevicePixel(rect.y(), pixelSnappingFactor)), snapSizeToDevicePixel(rect.size(), rect.location(), pixelSnappingFactor)); } -inline IntRect pixelSnappedIntRectFromEdges(LayoutUnit left, LayoutUnit top, LayoutUnit right, LayoutUnit bottom) +inline FloatRect snapRectToDevicePixels(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height, float pixelSnappingFactor) { - return IntRect(left.round(), top.round(), snapSizeToPixel(right - left, left), snapSizeToPixel(bottom - top, top)); + return snapRectToDevicePixels(LayoutRect(x, y, width, height), pixelSnappingFactor); } -inline IntRect pixelSnappedIntRect(LayoutPoint location, LayoutSize size) +// FIXME: This needs to take vertical centering into account too. +inline FloatRect snapRectToDevicePixelsWithWritingDirection(const LayoutRect& rect, float deviceScaleFactor, bool ltr) { - return IntRect(roundedIntPoint(location), pixelSnappedIntSize(size, location)); + if (!ltr) { + FloatPoint snappedTopRight = roundPointToDevicePixels(rect.maxXMinYCorner(), deviceScaleFactor, ltr); + FloatSize snappedSize = snapSizeToDevicePixel(rect.size(), rect.maxXMinYCorner(), deviceScaleFactor); + return FloatRect(snappedTopRight.x() - snappedSize.width(), snappedTopRight.y(), snappedSize.width(), snappedSize.height()); + } + return snapRectToDevicePixels(rect, deviceScaleFactor); } +FloatRect encloseRectToDevicePixels(const LayoutRect&, float pixelSnappingFactor); + +TextStream& operator<<(TextStream&, const LayoutRect&); + } // namespace WebCore #endif // LayoutRect_h diff --git a/Source/WebCore/platform/graphics/cairo/DrawingBufferCairo.cpp b/Source/WebCore/platform/graphics/LayoutSize.cpp index 738e49381..1f5e01df2 100644 --- a/Source/WebCore/platform/graphics/cairo/DrawingBufferCairo.cpp +++ b/Source/WebCore/platform/graphics/LayoutSize.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,27 +24,23 @@ */ #include "config.h" +#include "LayoutSize.h" -#if ENABLE(ACCELERATED_2D_CANVAS) || USE(3D_GRAPHICS) - -#include "DrawingBuffer.h" - -#include "Extensions3D.h" +#include "TextStream.h" namespace WebCore { -#if USE(ACCELERATED_COMPOSITING) - -unsigned DrawingBuffer::frontColorBuffer() const +LayoutSize LayoutSize::constrainedBetween(const LayoutSize& min, const LayoutSize& max) const { - return colorBuffer(); + return { + std::max(min.width(), std::min(max.width(), m_width)), + std::max(min.height(), std::min(max.height(), m_height)) + }; } -void DrawingBuffer::paintCompositedResultsToCanvas(ImageBuffer*) +TextStream& operator<<(TextStream& ts, const LayoutSize& size) { -} -#endif - + return ts << "width=" << size.width().toFloat() << " height=" << size.height().toFloat(); } -#endif +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/LayoutSize.h b/Source/WebCore/platform/graphics/LayoutSize.h index f2d09eee7..c11df04e6 100644 --- a/Source/WebCore/platform/graphics/LayoutSize.h +++ b/Source/WebCore/platform/graphics/LayoutSize.h @@ -38,6 +38,7 @@ namespace WebCore { class LayoutPoint; +class TextStream; enum AspectRatioFit { AspectRatioFitShrink, @@ -86,6 +87,8 @@ public: m_width *= widthScale; m_height *= heightScale; } + + LayoutSize constrainedBetween(const LayoutSize& min, const LayoutSize& max) const; LayoutSize expandedTo(const LayoutSize& other) const { @@ -183,15 +186,13 @@ inline IntSize roundedIntSize(const LayoutSize& s) return IntSize(s.width().round(), s.height().round()); } -inline LayoutSize roundedLayoutSize(const FloatSize& s) +inline FloatSize floorSizeToDevicePixels(const LayoutSize& size, float pixelSnappingFactor) { -#if ENABLE(SUBPIXEL_LAYOUT) - return LayoutSize(s); -#else - return roundedIntSize(s); -#endif + return FloatSize(floorToDevicePixel(size.width(), pixelSnappingFactor), floorToDevicePixel(size.height(), pixelSnappingFactor)); } +TextStream& operator<<(TextStream&, const LayoutSize&); + } // namespace WebCore #endif // LayoutSize_h diff --git a/Source/WebCore/platform/graphics/LegacyCDMSession.h b/Source/WebCore/platform/graphics/LegacyCDMSession.h new file mode 100644 index 000000000..8546c712f --- /dev/null +++ b/Source/WebCore/platform/graphics/LegacyCDMSession.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + +#include <runtime/Uint8Array.h> +#include <wtf/Forward.h> + +namespace WebCore { + +class CDMSessionClient { +public: + virtual ~CDMSessionClient() { } + virtual void sendMessage(Uint8Array*, String destinationURL) = 0; + + enum { + MediaKeyErrorUnknown = 1, + MediaKeyErrorClient, + MediaKeyErrorService, + MediaKeyErrorOutput, + MediaKeyErrorHardwareChange, + MediaKeyErrorDomain, + }; + typedef unsigned short MediaKeyErrorCode; + virtual void sendError(MediaKeyErrorCode, uint32_t systemCode) = 0; + + virtual String mediaKeysStorageDirectory() const = 0; +}; + +enum CDMSessionType { + CDMSessionTypeUnknown, + CDMSessionTypeClearKey, + CDMSessionTypeAVFoundationObjC, + CDMSessionTypeAVStreamSession, + CDMSessionTypeAVContentKeySession, +}; + +class CDMSession { +public: + virtual ~CDMSession() { } + + virtual CDMSessionType type() { return CDMSessionTypeUnknown; } + virtual void setClient(CDMSessionClient*) = 0; + virtual const String& sessionId() const = 0; + virtual RefPtr<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, uint32_t& systemCode) = 0; + virtual void releaseKeys() = 0; + virtual bool update(Uint8Array*, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, uint32_t& systemCode) = 0; + virtual RefPtr<ArrayBuffer> cachedKeyForKeyID(const String&) const { return nullptr; } +}; + +} + +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTarget.h b/Source/WebCore/platform/graphics/MediaPlaybackTarget.h new file mode 100644 index 000000000..1619aff37 --- /dev/null +++ b/Source/WebCore/platform/graphics/MediaPlaybackTarget.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlaybackTarget_h +#define MediaPlaybackTarget_h + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +#include "MediaPlaybackTargetContext.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +static const MediaPlaybackTargetContext& noMediaPlaybackTargetContext() +{ + static NeverDestroyed<MediaPlaybackTargetContext> context; + return context; +} + +class MediaPlaybackTarget : public RefCounted<MediaPlaybackTarget> { +public: + virtual ~MediaPlaybackTarget() { } + + enum TargetType { + None, + AVFoundation, + Mock, + }; + virtual TargetType targetType() const { return None; } + + virtual const MediaPlaybackTargetContext& targetContext() const { return noMediaPlaybackTargetContext(); } + virtual bool hasActiveRoute() const { return false; } + virtual String deviceName() const { return emptyString(); } + +protected: + MediaPlaybackTarget() { } +}; + +} + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) + +#endif diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h b/Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h new file mode 100644 index 000000000..a30421ea6 --- /dev/null +++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetClient.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlaybackTargetClient_h +#define MediaPlaybackTargetClient_h + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +#include "MediaPlaybackTarget.h" +#include "MediaProducer.h" + +namespace WebCore { + +class MediaPlaybackTarget; + +class MediaPlaybackTargetClient { +public: + virtual ~MediaPlaybackTargetClient() { } + + virtual void setPlaybackTarget(Ref<MediaPlaybackTarget>&&) = 0; + virtual void externalOutputDeviceAvailableDidChange(bool) = 0; + virtual void setShouldPlayToPlaybackTarget(bool) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) + +#endif // MediaPlaybackTargetClient_h diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h b/Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h new file mode 100644 index 000000000..1ce4a3ec3 --- /dev/null +++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetContext.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlaybackTargetContext_h +#define MediaPlaybackTargetContext_h + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +#include <wtf/text/WTFString.h> + +OBJC_CLASS AVOutputContext; + +#if PLATFORM(COCOA) +OBJC_CLASS NSKeyedArchiver; +OBJC_CLASS NSKeyedUnarchiver; +#endif + +namespace WebCore { + +class MediaPlaybackTargetContext { +public: + enum Type : int32_t { + None, + AVOutputContextType, + MockType, + }; + + enum ContextState { + Unknown = 0, + OutputDeviceUnavailable = 1, + OutputDeviceAvailable = 2, + }; + typedef unsigned State; + + MediaPlaybackTargetContext() + : m_type(None) + { + } + + MediaPlaybackTargetContext(AVOutputContext *outputContext) + : m_type(AVOutputContextType) + , m_outputContext(outputContext) + { + } + + MediaPlaybackTargetContext(const String& name, State state) + : m_type(MockType) + , m_name(name) + , m_state(state) + { + } + + Type type() const { return m_type; } + + const String& mockDeviceName() const + { + ASSERT(m_type == MockType); + return m_name; + } + + State mockState() const + { + ASSERT(m_type == MockType); + return m_state; + } + + AVOutputContext *avOutputContext() const + { + ASSERT(m_type == AVOutputContextType); + return m_outputContext; + } + + bool encodingRequiresPlatformData() const { return m_type == AVOutputContextType; } + +private: + Type m_type { None }; + AVOutputContext *m_outputContext { nullptr }; + String m_name; + State m_state { Unknown }; +}; + +} + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) + +#endif // MediaPlaybackTargetContext diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp new file mode 100644 index 000000000..4931686b3 --- /dev/null +++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaPlaybackTargetPicker.h" + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +#include "Logging.h" +#include "MediaPlaybackTarget.h" + +namespace WebCore { + +static const double pendingActionInterval = 1.0 / 10.0; + +MediaPlaybackTargetPicker::MediaPlaybackTargetPicker(Client& client) + : m_client(&client) + , m_pendingActionTimer(RunLoop::main(), this, &MediaPlaybackTargetPicker::pendingActionTimerFired) +{ +} + +MediaPlaybackTargetPicker::~MediaPlaybackTargetPicker() +{ + m_pendingActionTimer.stop(); + m_client = nullptr; +} + +void MediaPlaybackTargetPicker::pendingActionTimerFired() +{ + LOG(Media, "MediaPlaybackTargetPicker::pendingActionTimerFired - flags = 0x%x", m_pendingActionFlags); + + PendingActionFlags pendingActions = m_pendingActionFlags; + m_pendingActionFlags = 0; + + if (pendingActions & CurrentDeviceDidChange) + m_client->setPlaybackTarget(playbackTarget()); + + if (pendingActions & OutputDeviceAvailabilityChanged) + m_client->externalOutputDeviceAvailableDidChange(externalOutputDeviceAvailable()); +} + +void MediaPlaybackTargetPicker::addPendingAction(PendingActionFlags action) +{ + if (!m_client) + return; + + m_pendingActionFlags |= action; + m_pendingActionTimer.startOneShot(pendingActionInterval); +} + +void MediaPlaybackTargetPicker::showPlaybackTargetPicker(const FloatRect&, bool) +{ + ASSERT_NOT_REACHED(); +} + +void MediaPlaybackTargetPicker::startingMonitoringPlaybackTargets() +{ + ASSERT_NOT_REACHED(); +} + +void MediaPlaybackTargetPicker::stopMonitoringPlaybackTargets() +{ + ASSERT_NOT_REACHED(); +} + +void MediaPlaybackTargetPicker::invalidatePlaybackTargets() +{ + ASSERT_NOT_REACHED(); +} + +} // namespace WebCore + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) diff --git a/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h new file mode 100644 index 000000000..afe0bd3ee --- /dev/null +++ b/Source/WebCore/platform/graphics/MediaPlaybackTargetPicker.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlaybackTargetPicker_h +#define MediaPlaybackTargetPicker_h + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + +#include <wtf/Ref.h> +#include <wtf/RunLoop.h> + +namespace WebCore { + +class FloatRect; +class MediaPlaybackTarget; + +class MediaPlaybackTargetPicker { +public: + class Client { + protected: + virtual ~Client() { } + + public: + virtual void setPlaybackTarget(Ref<MediaPlaybackTarget>&&) = 0; + virtual void externalOutputDeviceAvailableDidChange(bool) = 0; + + void invalidate(); + }; + + virtual ~MediaPlaybackTargetPicker(); + + virtual void showPlaybackTargetPicker(const FloatRect&, bool checkActiveRoute); + virtual void startingMonitoringPlaybackTargets(); + virtual void stopMonitoringPlaybackTargets(); + virtual void invalidatePlaybackTargets(); + + void availableDevicesDidChange() { addPendingAction(OutputDeviceAvailabilityChanged); } + void currentDeviceDidChange() { addPendingAction(CurrentDeviceDidChange); } + +protected: + explicit MediaPlaybackTargetPicker(Client&); + + enum ActionType { + OutputDeviceAvailabilityChanged = 1 << 0, + CurrentDeviceDidChange = 1 << 1, + }; + typedef unsigned PendingActionFlags; + + void addPendingAction(PendingActionFlags); + void pendingActionTimerFired(); + Client* client() const { return m_client; } + void setClient(Client* client) { m_client = client; } + +private: + virtual bool externalOutputDeviceAvailable() = 0; + virtual Ref<MediaPlaybackTarget> playbackTarget() = 0; + + PendingActionFlags m_pendingActionFlags { 0 }; + Client* m_client; + RunLoop::Timer<MediaPlaybackTargetPicker> m_pendingActionTimer; +}; + +} // namespace WebCore + +#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) + +#endif // MediaPlaybackTargetPicker_h diff --git a/Source/WebCore/platform/graphics/MediaPlayer.cpp b/Source/WebCore/platform/graphics/MediaPlayer.cpp index eac25540b..a1cc538ce 100644 --- a/Source/WebCore/platform/graphics/MediaPlayer.cpp +++ b/Source/WebCore/platform/graphics/MediaPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,14 +30,13 @@ #include "ContentType.h" #include "Document.h" -#include "Frame.h" -#include "FrameView.h" #include "IntRect.h" #include "Logging.h" #include "MIMETypeRegistry.h" #include "MediaPlayerPrivate.h" +#include "PlatformTimeRanges.h" #include "Settings.h" -#include "TimeRanges.h" +#include <wtf/NeverDestroyed.h> #include <wtf/text/CString.h> #if ENABLE(VIDEO_TRACK) @@ -45,33 +44,50 @@ #endif #if ENABLE(MEDIA_SOURCE) -#include "HTMLMediaSource.h" +#include "MediaSourcePrivateClient.h" +#endif + +#if ENABLE(MEDIA_STREAM) +#include "MediaStreamPrivate.h" #endif #if USE(GSTREAMER) #include "MediaPlayerPrivateGStreamer.h" +#if ENABLE(MEDIA_STREAM) && USE(OPENWEBRTC) +#include "MediaPlayerPrivateGStreamerOwr.h" +#endif #define PlatformMediaEngineClassName MediaPlayerPrivateGStreamer +#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK) +#include "MediaPlayerPrivateGStreamerMSE.h" #endif +#endif // USE(GSTREAMER) -#if PLATFORM(MAC) -#if PLATFORM(IOS) -#include "MediaPlayerPrivateIOS.h" -#else +#if USE(MEDIA_FOUNDATION) +#include "MediaPlayerPrivateMediaFoundation.h" +#define PlatformMediaEngineClassName MediaPlayerPrivateMediaFoundation +#endif + +#if PLATFORM(COCOA) +#if USE(QTKIT) #include "MediaPlayerPrivateQTKit.h" #endif + #if USE(AVFOUNDATION) #include "MediaPlayerPrivateAVFoundationObjC.h" -#if ENABLE(MEDIA_SOURCE) +#endif + +#if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION) #include "MediaPlayerPrivateMediaSourceAVFObjC.h" #endif + +#if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION) +#include "MediaPlayerPrivateMediaStreamAVFObjC.h" #endif -#elif OS(WINCE) -#include "MediaPlayerPrivateWinCE.h" -#define PlatformMediaEngineClassName MediaPlayerPrivate -#elif PLATFORM(WIN) && !USE(GSTREAMER) -#if USE(AVFOUNDATION) + +#endif // PLATFORM(COCOA) + +#if PLATFORM(WIN) && USE(AVFOUNDATION) && !USE(GSTREAMER) #include "MediaPlayerPrivateAVFoundationCF.h" -#endif // USE(AVFOUNDATION) #endif namespace WebCore { @@ -82,289 +98,295 @@ const PlatformMedia NoPlatformMedia = { PlatformMedia::None, {0} }; class NullMediaPlayerPrivate : public MediaPlayerPrivateInterface { public: - NullMediaPlayerPrivate(MediaPlayer*) { } + explicit NullMediaPlayerPrivate(MediaPlayer*) { } - virtual void load(const String&) { } + void load(const String&) override { } #if ENABLE(MEDIA_SOURCE) - virtual void load(const String&, PassRefPtr<HTMLMediaSource>) { } + void load(const String&, MediaSourcePrivateClient*) override { } #endif - virtual void cancelLoad() { } - - virtual void prepareToPlay() { } - virtual void play() { } - virtual void pause() { } - - virtual PlatformMedia platformMedia() const { return NoPlatformMedia; } -#if USE(ACCELERATED_COMPOSITING) - virtual PlatformLayer* platformLayer() const { return 0; } +#if ENABLE(MEDIA_STREAM) + void load(MediaStreamPrivate&) override { } #endif + void cancelLoad() override { } - virtual IntSize naturalSize() const { return IntSize(0, 0); } + void prepareToPlay() override { } + void play() override { } + void pause() override { } - virtual bool hasVideo() const { return false; } - virtual bool hasAudio() const { return false; } + PlatformMedia platformMedia() const override { return NoPlatformMedia; } + PlatformLayer* platformLayer() const override { return 0; } - virtual void setVisible(bool) { } + FloatSize naturalSize() const override { return FloatSize(); } - virtual double durationDouble() const { return 0; } + bool hasVideo() const override { return false; } + bool hasAudio() const override { return false; } - virtual double currentTimeDouble() const { return 0; } - virtual void seekDouble(double) { } - virtual bool seeking() const { return false; } + void setVisible(bool) override { } - virtual void setRateDouble(double) { } - virtual void setPreservesPitch(bool) { } - virtual bool paused() const { return false; } + double durationDouble() const override { return 0; } - virtual void setVolumeDouble(double) { } + double currentTimeDouble() const override { return 0; } + void seekDouble(double) override { } + bool seeking() const override { return false; } - virtual bool supportsMuting() const { return false; } - virtual void setMuted(bool) { } + void setRateDouble(double) override { } + void setPreservesPitch(bool) override { } + bool paused() const override { return false; } - virtual bool hasClosedCaptions() const { return false; } - virtual void setClosedCaptionsVisible(bool) { }; + void setVolumeDouble(double) override { } - virtual MediaPlayer::NetworkState networkState() const { return MediaPlayer::Empty; } - virtual MediaPlayer::ReadyState readyState() const { return MediaPlayer::HaveNothing; } + bool supportsMuting() const override { return false; } + void setMuted(bool) override { } - virtual double maxTimeSeekableDouble() const { return 0; } - virtual double minTimeSeekable() const { return 0; } - virtual PassRefPtr<TimeRanges> buffered() const { return TimeRanges::create(); } + bool hasClosedCaptions() const override { return false; } + void setClosedCaptionsVisible(bool) override { }; - virtual unsigned totalBytes() const { return 0; } - virtual bool didLoadingProgress() const { return false; } + MediaPlayer::NetworkState networkState() const override { return MediaPlayer::Empty; } + MediaPlayer::ReadyState readyState() const override { return MediaPlayer::HaveNothing; } - virtual void setSize(const IntSize&) { } + float maxTimeSeekable() const override { return 0; } + double minTimeSeekable() const override { return 0; } + std::unique_ptr<PlatformTimeRanges> buffered() const override { return std::make_unique<PlatformTimeRanges>(); } - virtual void paint(GraphicsContext*, const IntRect&) { } + unsigned long long totalBytes() const override { return 0; } + bool didLoadingProgress() const override { return false; } - virtual bool canLoadPoster() const { return false; } - virtual void setPoster(const String&) { } + void setSize(const IntSize&) override { } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - virtual void deliverNotification(MediaPlayerProxyNotificationType) { } - virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { } - virtual void setControls(bool) { } -#endif + void paint(GraphicsContext&, const FloatRect&) override { } - virtual bool hasSingleSecurityOrigin() const { return true; } + bool canLoadPoster() const override { return false; } + void setPoster(const String&) override { } -#if ENABLE(ENCRYPTED_MEDIA) - virtual MediaPlayer::MediaKeyException generateKeyRequest(const String&, const unsigned char*, unsigned) override { return MediaPlayer::InvalidPlayerState; } - virtual MediaPlayer::MediaKeyException addKey(const String&, const unsigned char*, unsigned, const unsigned char*, unsigned, const String&) override { return MediaPlayer::InvalidPlayerState; } - virtual MediaPlayer::MediaKeyException cancelKeyRequest(const String&, const String&) override { return MediaPlayer::InvalidPlayerState; } -#endif + bool hasSingleSecurityOrigin() const override { return true; } }; -static PassOwnPtr<MediaPlayerPrivateInterface> createNullMediaPlayer(MediaPlayer* player) -{ - return adoptPtr(new NullMediaPlayerPrivate(player)); +static MediaPlayerClient& nullMediaPlayerClient() +{ + static NeverDestroyed<MediaPlayerClient> client; + return client.get(); } - // engine support struct MediaPlayerFactory { - WTF_MAKE_NONCOPYABLE(MediaPlayerFactory); WTF_MAKE_FAST_ALLOCATED; -public: - MediaPlayerFactory(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsTypeAndCodecs, - MediaEngineGetSitesInMediaCache getSitesInMediaCache, MediaEngineClearMediaCache clearMediaCache, MediaEngineClearMediaCacheForSite clearMediaCacheForSite) - : constructor(constructor) - , getSupportedTypes(getSupportedTypes) - , supportsTypeAndCodecs(supportsTypeAndCodecs) - , getSitesInMediaCache(getSitesInMediaCache) - , clearMediaCache(clearMediaCache) - , clearMediaCacheForSite(clearMediaCacheForSite) - { - } - CreateMediaEnginePlayer constructor; MediaEngineSupportedTypes getSupportedTypes; MediaEngineSupportsType supportsTypeAndCodecs; - MediaEngineGetSitesInMediaCache getSitesInMediaCache; + MediaEngineOriginsInMediaCache originsInMediaCache; MediaEngineClearMediaCache clearMediaCache; - MediaEngineClearMediaCacheForSite clearMediaCacheForSite; + MediaEngineClearMediaCacheForOrigins clearMediaCacheForOrigins; + MediaEngineSupportsKeySystem supportsKeySystem; }; -static void addMediaEngine(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType, MediaEngineGetSitesInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForSite); +static void addMediaEngine(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType, MediaEngineOriginsInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForOrigins, MediaEngineSupportsKeySystem); -static MediaPlayerFactory* bestMediaEngineForSupportParameters(const MediaEngineSupportParameters&, MediaPlayerFactory* current = 0); -static MediaPlayerFactory* nextMediaEngine(MediaPlayerFactory* current); - -enum RequeryEngineOptions { DoNotResetEngines, ResetEngines }; -static Vector<MediaPlayerFactory*>& installedMediaEngines(RequeryEngineOptions requeryFlags = DoNotResetEngines ) +static Lock& mediaEngineVectorLock() { - DEFINE_STATIC_LOCAL(Vector<MediaPlayerFactory*>, installedEngines, ()); - static bool enginesQueried = false; - - if (requeryFlags == ResetEngines) { - installedEngines.clear(); - enginesQueried = false; - return installedEngines; - } + static NeverDestroyed<Lock> lock; + return lock; +} - if (enginesQueried) - return installedEngines; +static bool& haveMediaEnginesVector() +{ + static bool haveVector; + return haveVector; +} - enginesQueried = true; +static Vector<MediaPlayerFactory>& mutableInstalledMediaEnginesVector() +{ + static NeverDestroyed<Vector<MediaPlayerFactory>> installedEngines; + return installedEngines; +} -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - if (Settings::isVideoPluginProxyEnabled()) - MediaPlayerPrivateIOS::registerMediaEngine(addMediaEngine); -#endif +static void buildMediaEnginesVector() +{ + ASSERT(mediaEngineVectorLock().isLocked()); #if USE(AVFOUNDATION) if (Settings::isAVFoundationEnabled()) { -#if PLATFORM(MAC) + +#if PLATFORM(COCOA) MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(addMediaEngine); +#endif + #if ENABLE(MEDIA_SOURCE) MediaPlayerPrivateMediaSourceAVFObjC::registerMediaEngine(addMediaEngine); #endif -#elif PLATFORM(WIN) + +#if ENABLE(MEDIA_STREAM) + MediaPlayerPrivateMediaStreamAVFObjC::registerMediaEngine(addMediaEngine); +#endif + +#if PLATFORM(WIN) MediaPlayerPrivateAVFoundationCF::registerMediaEngine(addMediaEngine); #endif } -#endif +#endif // USE(AVFOUNDATION) -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) && USE(QTKIT) if (Settings::isQTKitEnabled()) MediaPlayerPrivateQTKit::registerMediaEngine(addMediaEngine); #endif + +#if ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC) + if (Settings::isGStreamerEnabled()) + MediaPlayerPrivateGStreamerOwr::registerMediaEngine(addMediaEngine); +#endif + #if defined(PlatformMediaEngineClassName) - PlatformMediaEngineClassName::registerMediaEngine(addMediaEngine); +#if USE(GSTREAMER) + if (Settings::isGStreamerEnabled()) +#endif + PlatformMediaEngineClassName::registerMediaEngine(addMediaEngine); #endif - return installedEngines; +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK) + if (Settings::isGStreamerEnabled()) + MediaPlayerPrivateGStreamerMSE::registerMediaEngine(addMediaEngine); +#endif + + haveMediaEnginesVector() = true; +} + +static const Vector<MediaPlayerFactory>& installedMediaEngines() +{ + { + LockHolder lock(mediaEngineVectorLock()); + if (!haveMediaEnginesVector()) + buildMediaEnginesVector(); + } + + return mutableInstalledMediaEnginesVector(); } static void addMediaEngine(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsType, - MediaEngineGetSitesInMediaCache getSitesInMediaCache, MediaEngineClearMediaCache clearMediaCache, MediaEngineClearMediaCacheForSite clearMediaCacheForSite) + MediaEngineOriginsInMediaCache originsInMediaCache, MediaEngineClearMediaCache clearMediaCache, MediaEngineClearMediaCacheForOrigins clearMediaCacheForOrigins, MediaEngineSupportsKeySystem supportsKeySystem) { ASSERT(constructor); ASSERT(getSupportedTypes); ASSERT(supportsType); - installedMediaEngines().append(new MediaPlayerFactory(constructor, getSupportedTypes, supportsType, getSitesInMediaCache, clearMediaCache, clearMediaCacheForSite)); + mutableInstalledMediaEnginesVector().append(MediaPlayerFactory { constructor, getSupportedTypes, supportsType, originsInMediaCache, clearMediaCache, clearMediaCacheForOrigins, supportsKeySystem }); } static const AtomicString& applicationOctetStream() { - DEFINE_STATIC_LOCAL(const AtomicString, applicationOctetStream, ("application/octet-stream", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> applicationOctetStream("application/octet-stream", AtomicString::ConstructFromLiteral); return applicationOctetStream; } static const AtomicString& textPlain() { - DEFINE_STATIC_LOCAL(const AtomicString, textPlain, ("text/plain", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> textPlain("text/plain", AtomicString::ConstructFromLiteral); return textPlain; } static const AtomicString& codecs() { - DEFINE_STATIC_LOCAL(const AtomicString, codecs, ("codecs", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> codecs("codecs", AtomicString::ConstructFromLiteral); return codecs; } -static MediaPlayerFactory* bestMediaEngineForSupportParameters(const MediaEngineSupportParameters& parameters, MediaPlayerFactory* current) +static const MediaPlayerFactory* bestMediaEngineForSupportParameters(const MediaEngineSupportParameters& parameters, const MediaPlayerFactory* current = nullptr) { - if (parameters.type.isEmpty()) - return 0; - - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); - if (engines.isEmpty()) - return 0; + if (parameters.type.isEmpty() && !parameters.isMediaSource && !parameters.isMediaStream) + return nullptr; - // 4.8.10.3 MIME types - In the absence of a specification to the contrary, the MIME type "application/octet-stream" + // 4.8.10.3 MIME types - In the absence of a specification to the contrary, the MIME type "application/octet-stream" // when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows // it cannot render. if (parameters.type == applicationOctetStream()) { if (!parameters.codecs.isEmpty()) - return 0; + return nullptr; } - MediaPlayerFactory* engine = 0; + const MediaPlayerFactory* foundEngine = nullptr; MediaPlayer::SupportsType supported = MediaPlayer::IsNotSupported; - unsigned count = engines.size(); - for (unsigned ndx = 0; ndx < count; ndx++) { + for (auto& engine : installedMediaEngines()) { if (current) { - if (current == engines[ndx]) - current = 0; + if (current == &engine) + current = nullptr; continue; } - MediaPlayer::SupportsType engineSupport = engines[ndx]->supportsTypeAndCodecs(parameters); + MediaPlayer::SupportsType engineSupport = engine.supportsTypeAndCodecs(parameters); if (engineSupport > supported) { supported = engineSupport; - engine = engines[ndx]; + foundEngine = &engine; } } - return engine; + return foundEngine; } -static MediaPlayerFactory* nextMediaEngine(MediaPlayerFactory* current) +static const MediaPlayerFactory* nextMediaEngine(const MediaPlayerFactory* current) { - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + auto& engines = installedMediaEngines(); if (engines.isEmpty()) - return 0; + return nullptr; if (!current) - return engines.first(); + return &engines.first(); - size_t currentIndex = engines.find(current); - if (currentIndex == WTF::notFound || currentIndex + 1 >= engines.size()) - return 0; + size_t currentIndex = current - &engines.first(); + if (currentIndex + 1 >= engines.size()) + return nullptr; - return engines[currentIndex + 1]; + return &engines[currentIndex + 1]; } // media player -MediaPlayer::MediaPlayer(MediaPlayerClient* client) - : m_mediaPlayerClient(client) - , m_reloadTimer(this, &MediaPlayer::reloadTimerFired) - , m_private(createNullMediaPlayer(this)) +Ref<MediaPlayer> MediaPlayer::create(MediaPlayerClient& client) +{ + return adoptRef(*new MediaPlayer(client)); +} + +MediaPlayer::MediaPlayer(MediaPlayerClient& client) + : m_client(&client) + , m_reloadTimer(*this, &MediaPlayer::reloadTimerFired) + , m_private(std::make_unique<NullMediaPlayerPrivate>(this)) , m_currentMediaEngine(0) - , m_frameView(0) , m_preload(Auto) , m_visible(false) - , m_rate(1.0f) , m_volume(1.0f) , m_muted(false) , m_preservesPitch(true) , m_privateBrowsing(false) , m_shouldPrepareToRender(false) , m_contentMIMETypeWasInferredFromExtension(false) -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - , m_playerProxy(0) -#endif { -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); - if (Settings::isVideoPluginProxyEnabled() && !engines.isEmpty()) { - m_currentMediaEngine = engines[0]; - m_private = engines[0]->constructor(this); - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerEngineUpdated(this); - } -#endif } MediaPlayer::~MediaPlayer() { - m_mediaPlayerClient = 0; + ASSERT(!m_initializingMediaEngine); +} + +void MediaPlayer::invalidate() +{ + m_client = &nullMediaPlayerClient(); } bool MediaPlayer::load(const URL& url, const ContentType& contentType, const String& keySystem) { - m_contentMIMEType = contentType.type().lower(); + ASSERT(!m_reloadTimer.isActive()); + + // Protect against MediaPlayer being destroyed during a MediaPlayerClient callback. + Ref<MediaPlayer> protectedThis(*this); + + m_contentMIMEType = contentType.type().convertToASCIILowercase(); m_contentTypeCodecs = contentType.parameter(codecs()); m_url = url; - m_keySystem = keySystem.lower(); + m_keySystem = keySystem.convertToASCIILowercase(); m_contentMIMETypeWasInferredFromExtension = false; #if ENABLE(MEDIA_SOURCE) - m_mediaSource = 0; + m_mediaSource = nullptr; +#endif +#if ENABLE(MEDIA_STREAM) + m_mediaStream = nullptr; #endif // If the MIME type is missing or is not meaningful, try to figure it out from the URL. @@ -390,40 +412,73 @@ bool MediaPlayer::load(const URL& url, const ContentType& contentType, const Str } #if ENABLE(MEDIA_SOURCE) -bool MediaPlayer::load(const URL& url, const ContentType& contentType, PassRefPtr<HTMLMediaSource> mediaSource) +bool MediaPlayer::load(const URL& url, const ContentType& contentType, MediaSourcePrivateClient* mediaSource) { + ASSERT(!m_reloadTimer.isActive()); + ASSERT(mediaSource); + m_mediaSource = mediaSource; - m_contentMIMEType = contentType.type().lower(); + m_contentMIMEType = contentType.type().convertToASCIILowercase(); m_contentTypeCodecs = contentType.parameter(codecs()); m_url = url; - m_keySystem = ""; + m_keySystem = emptyString(); m_contentMIMETypeWasInferredFromExtension = false; loadWithNextMediaEngine(0); return m_currentMediaEngine; } #endif -MediaPlayerFactory* MediaPlayer::nextBestMediaEngine(MediaPlayerFactory* current) const +#if ENABLE(MEDIA_STREAM) +bool MediaPlayer::load(MediaStreamPrivate* mediaStream) +{ + ASSERT(!m_reloadTimer.isActive()); + ASSERT(mediaStream); + + m_mediaStream = mediaStream; + m_keySystem = emptyString(); + m_contentMIMEType = emptyString(); + m_contentMIMETypeWasInferredFromExtension = false; + loadWithNextMediaEngine(0); + return m_currentMediaEngine; +} +#endif + +const MediaPlayerFactory* MediaPlayer::nextBestMediaEngine(const MediaPlayerFactory* current) const { MediaEngineSupportParameters parameters; parameters.type = m_contentMIMEType; parameters.codecs = m_contentTypeCodecs; parameters.url = m_url; -#if ENABLE(ENCRYPTED_MEDIA) - parameters.keySystem = m_keySystem; -#endif #if ENABLE(MEDIA_SOURCE) parameters.isMediaSource = !!m_mediaSource; #endif +#if ENABLE(MEDIA_STREAM) + parameters.isMediaStream = !!m_mediaStream; +#endif return bestMediaEngineForSupportParameters(parameters, current); } -void MediaPlayer::loadWithNextMediaEngine(MediaPlayerFactory* current) +void MediaPlayer::loadWithNextMediaEngine(const MediaPlayerFactory* current) { - MediaPlayerFactory* engine = 0; +#if ENABLE(MEDIA_SOURCE) +#define MEDIASOURCE m_mediaSource +#else +#define MEDIASOURCE 0 +#endif - if (!m_contentMIMEType.isEmpty()) +#if ENABLE(MEDIA_STREAM) +#define MEDIASTREAM m_mediaStream +#else +#define MEDIASTREAM 0 +#endif + + ASSERT(!m_initializingMediaEngine); + m_initializingMediaEngine = true; + + const MediaPlayerFactory* engine = nullptr; + + if (!m_contentMIMEType.isEmpty() || MEDIASTREAM || MEDIASOURCE) engine = nextBestMediaEngine(current); // If no MIME type is specified or the type was inferred from the file extension, just use the next engine. @@ -438,11 +493,7 @@ void MediaPlayer::loadWithNextMediaEngine(MediaPlayerFactory* current) } else if (m_currentMediaEngine != engine) { m_currentMediaEngine = engine; m_private = engine->constructor(this); - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerEngineUpdated(this); -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - m_private->setMediaPlayerProxy(m_playerProxy); -#endif + client().mediaPlayerEngineUpdated(this); m_private->setPrivateBrowsingMode(m_privateBrowsing); m_private->setPreload(m_preload); m_private->setPreservesPitch(preservesPitch()); @@ -453,17 +504,22 @@ void MediaPlayer::loadWithNextMediaEngine(MediaPlayerFactory* current) if (m_private) { #if ENABLE(MEDIA_SOURCE) if (m_mediaSource) - m_private->load(m_url.string(), m_mediaSource); + m_private->load(m_url.string(), m_mediaSource.get()); + else +#endif +#if ENABLE(MEDIA_STREAM) + if (m_mediaStream) + m_private->load(*m_mediaStream); else #endif m_private->load(m_url.string()); } else { - m_private = createNullMediaPlayer(this); - if (m_mediaPlayerClient) { - m_mediaPlayerClient->mediaPlayerEngineUpdated(this); - m_mediaPlayerClient->mediaPlayerResourceNotSupported(this); - } + m_private = std::make_unique<NullMediaPlayerPrivate>(this); + client().mediaPlayerEngineUpdated(this); + client().mediaPlayerResourceNotSupported(this); } + + m_initializingMediaEngine = false; } bool MediaPlayer::hasAvailableVideoFrame() const @@ -507,51 +563,61 @@ void MediaPlayer::pause() m_private->pause(); } -#if ENABLE(ENCRYPTED_MEDIA) -MediaPlayer::MediaKeyException MediaPlayer::generateKeyRequest(const String& keySystem, const unsigned char* initData, unsigned initDataLength) +void MediaPlayer::setShouldBufferData(bool shouldBuffer) { - return m_private->generateKeyRequest(keySystem.lower(), initData, initDataLength); + m_private->setShouldBufferData(shouldBuffer); } -MediaPlayer::MediaKeyException MediaPlayer::addKey(const String& keySystem, const unsigned char* key, unsigned keyLength, const unsigned char* initData, unsigned initDataLength, const String& sessionId) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +std::unique_ptr<CDMSession> MediaPlayer::createSession(const String& keySystem, CDMSessionClient* client) { - return m_private->addKey(keySystem.lower(), key, keyLength, initData, initDataLength, sessionId); + return m_private->createSession(keySystem, client); } -MediaPlayer::MediaKeyException MediaPlayer::cancelKeyRequest(const String& keySystem, const String& sessionId) +void MediaPlayer::setCDMSession(CDMSession* session) { - return m_private->cancelKeyRequest(keySystem.lower(), sessionId); + m_private->setCDMSession(session); } -#endif -double MediaPlayer::duration() const +void MediaPlayer::keyAdded() +{ + m_private->keyAdded(); +} +#endif + +MediaTime MediaPlayer::duration() const { - return m_private->durationDouble(); + return m_private->durationMediaTime(); } -double MediaPlayer::startTime() const +MediaTime MediaPlayer::startTime() const { - return m_private->startTimeDouble(); + return m_private->startTime(); } -double MediaPlayer::initialTime() const +MediaTime MediaPlayer::initialTime() const { return m_private->initialTime(); } -double MediaPlayer::currentTime() const +MediaTime MediaPlayer::currentTime() const +{ + return m_private->currentMediaTime(); +} + +MediaTime MediaPlayer::getStartDate() const { - return m_private->currentTimeDouble(); + return m_private->getStartDate(); } -void MediaPlayer::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance) +void MediaPlayer::seekWithTolerance(const MediaTime& time, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance) { m_private->seekWithTolerance(time, negativeTolerance, positiveTolerance); } -void MediaPlayer::seek(double time) +void MediaPlayer::seek(const MediaTime& time) { - m_private->seekDouble(time); + m_private->seek(time); } bool MediaPlayer::paused() const @@ -569,9 +635,9 @@ bool MediaPlayer::supportsFullscreen() const return m_private->supportsFullscreen(); } -bool MediaPlayer::supportsSave() const +bool MediaPlayer::canSaveMediaData() const { - return m_private->supportsSave(); + return m_private->canSaveMediaData(); } bool MediaPlayer::supportsScanning() const @@ -584,7 +650,7 @@ bool MediaPlayer::requiresImmediateCompositing() const return m_private->requiresImmediateCompositing(); } -IntSize MediaPlayer::naturalSize() +FloatSize MediaPlayer::naturalSize() { return m_private->naturalSize(); } @@ -599,12 +665,9 @@ bool MediaPlayer::hasAudio() const return m_private->hasAudio(); } -bool MediaPlayer::inMediaDocument() +bool MediaPlayer::inMediaDocument() const { - if (!m_frameView) - return false; - Document* document = m_frameView->frame().document(); - return document && document->isMediaDocument(); + return m_visible && client().mediaPlayerIsInMediaDocument(); } PlatformMedia MediaPlayer::platformMedia() const @@ -612,11 +675,53 @@ PlatformMedia MediaPlayer::platformMedia() const return m_private->platformMedia(); } -#if USE(ACCELERATED_COMPOSITING) PlatformLayer* MediaPlayer::platformLayer() const { return m_private->platformLayer(); } + +#if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) +void MediaPlayer::setVideoFullscreenLayer(PlatformLayer* layer, std::function<void()> completionHandler) +{ + m_private->setVideoFullscreenLayer(layer, completionHandler); +} + +void MediaPlayer::setVideoFullscreenFrame(FloatRect frame) +{ + m_private->setVideoFullscreenFrame(frame); +} + +void MediaPlayer::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity) +{ + m_private->setVideoFullscreenGravity(gravity); +} + +void MediaPlayer::setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode mode) +{ + m_private->setVideoFullscreenMode(mode); +} + +MediaPlayer::VideoFullscreenMode MediaPlayer::fullscreenMode() const +{ + return client().mediaPlayerFullscreenMode(); +} +#endif + +#if PLATFORM(IOS) +NSArray* MediaPlayer::timedMetadata() const +{ + return m_private->timedMetadata(); +} + +String MediaPlayer::accessLog() const +{ + return m_private->accessLog(); +} + +String MediaPlayer::errorLog() const +{ + return m_private->errorLog(); +} #endif MediaPlayer::NetworkState MediaPlayer::networkState() @@ -669,15 +774,19 @@ void MediaPlayer::setClosedCaptionsVisible(bool closedCaptionsVisible) double MediaPlayer::rate() const { - return m_rate; + return m_private->rate(); } void MediaPlayer::setRate(double rate) { - m_rate = rate; m_private->setRateDouble(rate); } +double MediaPlayer::requestedRate() const +{ + return client().mediaPlayerRequestedPlaybackRate(); +} + bool MediaPlayer::preservesPitch() const { return m_preservesPitch; @@ -689,24 +798,24 @@ void MediaPlayer::setPreservesPitch(bool preservesPitch) m_private->setPreservesPitch(preservesPitch); } -PassRefPtr<TimeRanges> MediaPlayer::buffered() +std::unique_ptr<PlatformTimeRanges> MediaPlayer::buffered() { return m_private->buffered(); } -PassRefPtr<TimeRanges> MediaPlayer::seekable() +std::unique_ptr<PlatformTimeRanges> MediaPlayer::seekable() { return m_private->seekable(); } -double MediaPlayer::maxTimeSeekable() +MediaTime MediaPlayer::maxTimeSeekable() { - return m_private->maxTimeSeekableDouble(); + return m_private->maxMediaTimeSeekable(); } -double MediaPlayer::minTimeSeekable() +MediaTime MediaPlayer::minTimeSeekable() { - return m_private->minTimeSeekable(); + return m_private->minMediaTimeSeekable(); } bool MediaPlayer::didLoadingProgress() @@ -742,22 +851,22 @@ void MediaPlayer::setPreload(MediaPlayer::Preload preload) m_private->setPreload(preload); } -void MediaPlayer::paint(GraphicsContext* p, const IntRect& r) +void MediaPlayer::paint(GraphicsContext& p, const FloatRect& r) { m_private->paint(p, r); } -void MediaPlayer::paintCurrentFrameInContext(GraphicsContext* p, const IntRect& r) +void MediaPlayer::paintCurrentFrameInContext(GraphicsContext& p, const FloatRect& r) { m_private->paintCurrentFrameInContext(p, r); } -bool MediaPlayer::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY) +bool MediaPlayer::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY) { - return m_private->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY); + return m_private->copyVideoTextureToPlatformTexture(context, texture, target, level, internalFormat, format, type, premultiplyAlpha, flipY); } -PassNativeImagePtr MediaPlayer::nativeImageForCurrentTime() +NativeImagePtr MediaPlayer::nativeImageForCurrentTime() { return m_private->nativeImageForCurrentTime(); } @@ -769,11 +878,11 @@ MediaPlayer::SupportsType MediaPlayer::supportsType(const MediaEngineSupportPara if (parameters.type == applicationOctetStream()) return IsNotSupported; - MediaPlayerFactory* engine = bestMediaEngineForSupportParameters(parameters); + const MediaPlayerFactory* engine = bestMediaEngineForSupportParameters(parameters); if (!engine) return IsNotSupported; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // YouTube will ask if the HTMLMediaElement canPlayType video/webm, then // video/x-flv, then finally video/mp4, and will then load a URL of the first type // in that list which returns "probably". When Perian is installed, @@ -782,7 +891,7 @@ MediaPlayer::SupportsType MediaPlayer::supportsType(const MediaEngineSupportPara // slow connections. <https://bugs.webkit.org/show_bug.cgi?id=86409> if (client && client->mediaPlayerNeedsSiteSpecificHacks()) { String host = client->mediaPlayerDocumentHost(); - if ((host.endsWith(".youtube.com", false) || equalIgnoringCase("youtube.com", host)) + if ((host.endsWith(".youtube.com", false) || equalLettersIgnoringASCIICase(host, "youtube.com")) && (parameters.type.startsWith("video/webm", false) || parameters.type.startsWith("video/x-flv", false))) return IsNotSupported; } @@ -793,15 +902,13 @@ MediaPlayer::SupportsType MediaPlayer::supportsType(const MediaEngineSupportPara return engine->supportsTypeAndCodecs(parameters); } -void MediaPlayer::getSupportedTypes(HashSet<String>& types) +void MediaPlayer::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types) { - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); - if (engines.isEmpty()) - return; - - unsigned count = engines.size(); - for (unsigned ndx = 0; ndx < count; ndx++) - engines[ndx]->getSupportedTypes(types); + for (auto& engine : installedMediaEngines()) { + HashSet<String, ASCIICaseInsensitiveHash> engineTypes; + engine.getSupportedTypes(engineTypes); + types.add(engineTypes.begin(), engineTypes.end()); + } } bool MediaPlayer::isAvailable() @@ -809,25 +916,7 @@ bool MediaPlayer::isAvailable() return !installedMediaEngines().isEmpty(); } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -void MediaPlayer::deliverNotification(MediaPlayerProxyNotificationType notification) -{ - m_private->deliverNotification(notification); -} - -void MediaPlayer::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) -{ - m_playerProxy = proxy; - m_private->setMediaPlayerProxy(proxy); -} - -void MediaPlayer::setControls(bool controls) -{ - m_private->setControls(controls); -} -#endif - -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || USE(NATIVE_FULLSCREEN_VIDEO) +#if USE(NATIVE_FULLSCREEN_VIDEO) void MediaPlayer::enterFullscreen() { m_private->enterFullscreen(); @@ -839,20 +928,20 @@ void MediaPlayer::exitFullscreen() } #endif -#if ENABLE(IOS_AIRPLAY) +#if ENABLE(WIRELESS_PLAYBACK_TARGET) bool MediaPlayer::isCurrentPlaybackTargetWireless() const { return m_private->isCurrentPlaybackTargetWireless(); } -void MediaPlayer::showPlaybackTargetPicker() +String MediaPlayer::wirelessPlaybackTargetName() const { - m_private->showPlaybackTargetPicker(); + return m_private->wirelessPlaybackTargetName(); } -bool MediaPlayer::hasWirelessPlaybackTargets() const +MediaPlayer::WirelessPlaybackTargetType MediaPlayer::wirelessPlaybackTargetType() const { - return m_private->hasWirelessPlaybackTargets(); + return m_private->wirelessPlaybackTargetType(); } bool MediaPlayer::wirelessVideoPlaybackDisabled() const @@ -865,24 +954,37 @@ void MediaPlayer::setWirelessVideoPlaybackDisabled(bool disabled) m_private->setWirelessVideoPlaybackDisabled(disabled); } -void MediaPlayer::setHasPlaybackTargetAvailabilityListeners(bool hasListeners) +void MediaPlayer::currentPlaybackTargetIsWirelessChanged() { - m_private->setHasPlaybackTargetAvailabilityListeners(hasListeners); + client().mediaPlayerCurrentPlaybackTargetIsWirelessChanged(this); } -void MediaPlayer::currentPlaybackTargetIsWirelessChanged() +bool MediaPlayer::canPlayToWirelessPlaybackTarget() const +{ + return m_private->canPlayToWirelessPlaybackTarget(); +} + +void MediaPlayer::setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&& device) { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerCurrentPlaybackTargetIsWirelessChanged(this); + m_private->setWirelessPlaybackTarget(WTFMove(device)); } -void MediaPlayer::playbackTargetAvailabilityChanged() +void MediaPlayer::setShouldPlayToPlaybackTarget(bool shouldPlay) { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerPlaybackTargetAvailabilityChanged(this); + m_private->setShouldPlayToPlaybackTarget(shouldPlay); } #endif +double MediaPlayer::maxFastForwardRate() const +{ + return m_private->maxFastForwardRate(); +} + +double MediaPlayer::minFastReverseRate() const +{ + return m_private->minFastReverseRate(); +} + #if USE(NATIVE_FULLSCREEN_VIDEO) bool MediaPlayer::canEnterFullscreen() const { @@ -890,7 +992,6 @@ bool MediaPlayer::canEnterFullscreen() const } #endif -#if USE(ACCELERATED_COMPOSITING) void MediaPlayer::acceleratedRenderingStateChanged() { m_private->acceleratedRenderingStateChanged(); @@ -900,7 +1001,6 @@ bool MediaPlayer::supportsAcceleratedRendering() const { return m_private->supportsAcceleratedRendering(); } -#endif // USE(ACCELERATED_COMPOSITING) bool MediaPlayer::shouldMaintainAspectRatio() const { @@ -927,9 +1027,9 @@ MediaPlayer::MovieLoadType MediaPlayer::movieLoadType() const return m_private->movieLoadType(); } -double MediaPlayer::mediaTimeForTimeValue(double timeValue) const +MediaTime MediaPlayer::mediaTimeForTimeValue(const MediaTime& timeValue) const { - return m_private->mediaTimeForTimeValueDouble(timeValue); + return m_private->mediaTimeForTimeValue(timeValue); } double MediaPlayer::maximumDurationToCacheMediaTime() const @@ -957,84 +1057,81 @@ unsigned MediaPlayer::videoDecodedByteCount() const return m_private->videoDecodedByteCount(); } -void MediaPlayer::reloadTimerFired(Timer<MediaPlayer>&) +void MediaPlayer::reloadTimerFired() { m_private->cancelLoad(); loadWithNextMediaEngine(m_currentMediaEngine); } -void MediaPlayer::getSitesInMediaCache(Vector<String>& sites) +template<typename T> +static void addToHash(HashSet<T>& toHash, HashSet<T>&& fromHash) { - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); - unsigned size = engines.size(); - for (unsigned i = 0; i < size; i++) { - if (!engines[i]->getSitesInMediaCache) - continue; - Vector<String> engineSites; - engines[i]->getSitesInMediaCache(engineSites); - sites.appendVector(engineSites); - } + if (toHash.isEmpty()) + toHash = WTFMove(fromHash); + else + toHash.add(fromHash.begin(), fromHash.end()); } - -void MediaPlayer::clearMediaCache() + +HashSet<RefPtr<SecurityOrigin>> MediaPlayer::originsInMediaCache(const String& path) { - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); - unsigned size = engines.size(); - for (unsigned i = 0; i < size; i++) { - if (engines[i]->clearMediaCache) - engines[i]->clearMediaCache(); + HashSet<RefPtr<SecurityOrigin>> origins; + for (auto& engine : installedMediaEngines()) { + if (!engine.originsInMediaCache) + continue; + addToHash(origins, engine.originsInMediaCache(path)); } + return origins; } -void MediaPlayer::clearMediaCacheForSite(const String& site) +void MediaPlayer::clearMediaCache(const String& path, std::chrono::system_clock::time_point modifiedSince) { - Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); - unsigned size = engines.size(); - for (unsigned i = 0; i < size; i++) { - if (engines[i]->clearMediaCacheForSite) - engines[i]->clearMediaCacheForSite(site); + for (auto& engine : installedMediaEngines()) { + if (engine.clearMediaCache) + engine.clearMediaCache(path, modifiedSince); } } -void MediaPlayer::setPrivateBrowsingMode(bool privateBrowsingMode) +void MediaPlayer::clearMediaCacheForOrigins(const String& path, const HashSet<RefPtr<SecurityOrigin>>& origins) { - m_privateBrowsing = privateBrowsingMode; - m_private->setPrivateBrowsingMode(m_privateBrowsing); + for (auto& engine : installedMediaEngines()) { + if (engine.clearMediaCacheForOrigins) + engine.clearMediaCacheForOrigins(path, origins); + } } -#if PLATFORM(IOS) -void MediaPlayer::attributeChanged(const String& name, const String& value) +bool MediaPlayer::supportsKeySystem(const String& keySystem, const String& mimeType) { - m_private->attributeChanged(name, value); + for (auto& engine : installedMediaEngines()) { + if (engine.supportsKeySystem && engine.supportsKeySystem(keySystem, mimeType)) + return true; + } + return false; } -bool MediaPlayer::readyForPlayback() const +void MediaPlayer::setPrivateBrowsingMode(bool privateBrowsingMode) { - return m_private->readyForPlayback(); + m_privateBrowsing = privateBrowsingMode; + m_private->setPrivateBrowsingMode(m_privateBrowsing); } -#endif // Client callbacks. void MediaPlayer::networkStateChanged() { // If more than one media engine is installed and this one failed before finding metadata, // let the next engine try. - if (m_private->networkState() >= FormatError - && m_private->readyState() < HaveMetadata - && installedMediaEngines().size() > 1) { - if (m_contentMIMEType.isEmpty() || nextBestMediaEngine(m_currentMediaEngine)) { + if (m_private->networkState() >= FormatError && m_private->readyState() < HaveMetadata) { + client().mediaPlayerEngineFailedToLoad(); + if (installedMediaEngines().size() > 1 && (m_contentMIMEType.isEmpty() || nextBestMediaEngine(m_currentMediaEngine))) { m_reloadTimer.startOneShot(0); return; } } - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerNetworkStateChanged(this); + client().mediaPlayerNetworkStateChanged(this); } void MediaPlayer::readyStateChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerReadyStateChanged(this); + client().mediaPlayerReadyStateChanged(this); } void MediaPlayer::volumeChanged(double newVolume) @@ -1045,63 +1142,53 @@ void MediaPlayer::volumeChanged(double newVolume) #else m_volume = newVolume; #endif - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerVolumeChanged(this); + client().mediaPlayerVolumeChanged(this); } void MediaPlayer::muteChanged(bool newMuted) { m_muted = newMuted; - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerMuteChanged(this); + client().mediaPlayerMuteChanged(this); } void MediaPlayer::timeChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerTimeChanged(this); + client().mediaPlayerTimeChanged(this); } void MediaPlayer::sizeChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerSizeChanged(this); + client().mediaPlayerSizeChanged(this); } void MediaPlayer::repaint() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerRepaint(this); + client().mediaPlayerRepaint(this); } void MediaPlayer::durationChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerDurationChanged(this); + client().mediaPlayerDurationChanged(this); } void MediaPlayer::rateChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerRateChanged(this); + client().mediaPlayerRateChanged(this); } void MediaPlayer::playbackStateChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerPlaybackStateChanged(this); + client().mediaPlayerPlaybackStateChanged(this); } void MediaPlayer::firstVideoFrameAvailable() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerFirstVideoFrameAvailable(this); + client().mediaPlayerFirstVideoFrameAvailable(this); } void MediaPlayer::characteristicChanged() { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerCharacteristicChanged(this); + client().mediaPlayerCharacteristicChanged(this); } #if ENABLE(WEB_AUDIO) @@ -1111,56 +1198,31 @@ AudioSourceProvider* MediaPlayer::audioSourceProvider() } #endif // WEB_AUDIO -#if ENABLE(ENCRYPTED_MEDIA) -void MediaPlayer::keyAdded(const String& keySystem, const String& sessionId) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +RefPtr<ArrayBuffer> MediaPlayer::cachedKeyForKeyId(const String& keyId) const { - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerKeyAdded(this, keySystem, sessionId); + return client().mediaPlayerCachedKeyForKeyId(keyId); } -void MediaPlayer::keyError(const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode) -{ - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerKeyError(this, keySystem, sessionId, errorCode, systemCode); -} - -void MediaPlayer::keyMessage(const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const URL& defaultURL) -{ - if (m_mediaPlayerClient) - m_mediaPlayerClient->mediaPlayerKeyMessage(this, keySystem, sessionId, message, messageLength, defaultURL); -} - -bool MediaPlayer::keyNeeded(const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength) +bool MediaPlayer::keyNeeded(Uint8Array* initData) { - if (m_mediaPlayerClient) - return m_mediaPlayerClient->mediaPlayerKeyNeeded(this, keySystem, sessionId, initData, initDataLength); - return false; + return client().mediaPlayerKeyNeeded(this, initData); } -#endif -#if ENABLE(ENCRYPTED_MEDIA_V2) -bool MediaPlayer::keyNeeded(Uint8Array* initData) +String MediaPlayer::mediaKeysStorageDirectory() const { - if (m_mediaPlayerClient) - return m_mediaPlayerClient->mediaPlayerKeyNeeded(this, initData); - return false; + return client().mediaPlayerMediaKeysStorageDirectory(); } #endif String MediaPlayer::referrer() const { - if (!m_mediaPlayerClient) - return String(); - - return m_mediaPlayerClient->mediaPlayerReferrer(); + return client().mediaPlayerReferrer(); } String MediaPlayer::userAgent() const { - if (!m_mediaPlayerClient) - return String(); - - return m_mediaPlayerClient->mediaPlayerUserAgent(); + return client().mediaPlayerUserAgent(); } String MediaPlayer::engineDescription() const @@ -1171,71 +1233,61 @@ String MediaPlayer::engineDescription() const return m_private->engineDescription(); } +long MediaPlayer::platformErrorCode() const +{ + if (!m_private) + return 0; + + return m_private->platformErrorCode(); +} + #if PLATFORM(WIN) && USE(AVFOUNDATION) GraphicsDeviceAdapter* MediaPlayer::graphicsDeviceAdapter() const { - if (!m_mediaPlayerClient) - return 0; - - return m_mediaPlayerClient->mediaPlayerGraphicsDeviceAdapter(this); + return client().mediaPlayerGraphicsDeviceAdapter(this); } #endif CachedResourceLoader* MediaPlayer::cachedResourceLoader() { - if (!m_mediaPlayerClient) - return 0; + return client().mediaPlayerCachedResourceLoader(); +} - return m_mediaPlayerClient->mediaPlayerCachedResourceLoader(); +PassRefPtr<PlatformMediaResourceLoader> MediaPlayer::createResourceLoader() +{ + return client().mediaPlayerCreateResourceLoader(); } #if ENABLE(VIDEO_TRACK) -void MediaPlayer::addAudioTrack(PassRefPtr<AudioTrackPrivate> track) -{ - if (!m_mediaPlayerClient) - return; - m_mediaPlayerClient->mediaPlayerDidAddAudioTrack(track); +void MediaPlayer::addAudioTrack(AudioTrackPrivate& track) +{ + client().mediaPlayerDidAddAudioTrack(track); } -void MediaPlayer::removeAudioTrack(PassRefPtr<AudioTrackPrivate> track) +void MediaPlayer::removeAudioTrack(AudioTrackPrivate& track) { - if (!m_mediaPlayerClient) - return; - - m_mediaPlayerClient->mediaPlayerDidRemoveAudioTrack(track); + client().mediaPlayerDidRemoveAudioTrack(track); } -void MediaPlayer::addTextTrack(PassRefPtr<InbandTextTrackPrivate> track) +void MediaPlayer::addTextTrack(InbandTextTrackPrivate& track) { - if (!m_mediaPlayerClient) - return; - - m_mediaPlayerClient->mediaPlayerDidAddTextTrack(track); + client().mediaPlayerDidAddTextTrack(track); } -void MediaPlayer::removeTextTrack(PassRefPtr<InbandTextTrackPrivate> track) +void MediaPlayer::removeTextTrack(InbandTextTrackPrivate& track) { - if (!m_mediaPlayerClient) - return; - - m_mediaPlayerClient->mediaPlayerDidRemoveTextTrack(track); + client().mediaPlayerDidRemoveTextTrack(track); } -void MediaPlayer::addVideoTrack(PassRefPtr<VideoTrackPrivate> track) +void MediaPlayer::addVideoTrack(VideoTrackPrivate& track) { - if (!m_mediaPlayerClient) - return; - - m_mediaPlayerClient->mediaPlayerDidAddVideoTrack(track); + client().mediaPlayerDidAddVideoTrack(track); } -void MediaPlayer::removeVideoTrack(PassRefPtr<VideoTrackPrivate> track) +void MediaPlayer::removeVideoTrack(VideoTrackPrivate& track) { - if (!m_mediaPlayerClient) - return; - - m_mediaPlayerClient->mediaPlayerDidRemoveVideoTrack(track); + client().mediaPlayerDidRemoveVideoTrack(track); } bool MediaPlayer::requiresTextTrackRepresentation() const @@ -1247,23 +1299,40 @@ void MediaPlayer::setTextTrackRepresentation(TextTrackRepresentation* representa { m_private->setTextTrackRepresentation(representation); } -#endif // ENABLE(VIDEO_TRACK) -#if USE(PLATFORM_TEXT_TRACK_MENU) -bool MediaPlayer::implementsTextTrackControls() const +void MediaPlayer::syncTextTrackBounds() +{ + m_private->syncTextTrackBounds(); +} + +void MediaPlayer::tracksChanged() +{ + m_private->tracksChanged(); +} + +#if ENABLE(AVF_CAPTIONS) + +void MediaPlayer::notifyTrackModeChanged() { - return m_private->implementsTextTrackControls(); + if (m_private) + m_private->notifyTrackModeChanged(); } -PassRefPtr<PlatformTextTrackMenuInterface> MediaPlayer::textTrackMenu() +Vector<RefPtr<PlatformTextTrack>> MediaPlayer::outOfBandTrackSources() { - return m_private->textTrackMenu(); + return client().outOfBandTrackSources(); } -#endif // USE(PLATFORM_TEXT_TRACK_MENU) + +#endif + +#endif // ENABLE(VIDEO_TRACK) void MediaPlayer::resetMediaEngines() { - installedMediaEngines(ResetEngines); + LockHolder lock(mediaEngineVectorLock()); + + mutableInstalledMediaEnginesVector().clear(); + haveMediaEnginesVector() = false; } #if USE(GSTREAMER) @@ -1300,6 +1369,11 @@ unsigned long long MediaPlayer::fileSize() const return m_private->fileSize(); } +bool MediaPlayer::ended() const +{ + return m_private->ended(); +} + #if ENABLE(MEDIA_SOURCE) unsigned long MediaPlayer::totalVideoFrames() { @@ -1325,10 +1399,10 @@ unsigned long MediaPlayer::corruptedVideoFrames() return m_private->corruptedVideoFrames(); } -double MediaPlayer::totalFrameDelay() +MediaTime MediaPlayer::totalFrameDelay() { if (!m_private) - return 0; + return MediaTime::zeroTime(); return m_private->totalFrameDelay(); } @@ -1336,10 +1410,22 @@ double MediaPlayer::totalFrameDelay() bool MediaPlayer::shouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge& challenge) { - if (!m_mediaPlayerClient) - return false; + return client().mediaPlayerShouldWaitForResponseToAuthenticationChallenge(challenge); +} + +void MediaPlayer::handlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType command) +{ + client().mediaPlayerHandlePlaybackCommand(command); +} + +String MediaPlayer::sourceApplicationIdentifier() const +{ + return client().mediaPlayerSourceApplicationIdentifier(); +} - return m_mediaPlayerClient->mediaPlayerShouldWaitForResponseToAuthenticationChallenge(challenge); +Vector<String> MediaPlayer::preferredAudioCharacteristics() const +{ + return client().mediaPlayerPreferredAudioCharacteristics(); } void MediaPlayerFactorySupport::callRegisterMediaEngine(MediaEngineRegister registerMediaEngine) @@ -1347,6 +1433,34 @@ void MediaPlayerFactorySupport::callRegisterMediaEngine(MediaEngineRegister regi registerMediaEngine(addMediaEngine); } +bool MediaPlayer::doesHaveAttribute(const AtomicString& attribute, AtomicString* value) const +{ + return client().doesHaveAttribute(attribute, value); +} + +#if PLATFORM(IOS) +String MediaPlayer::mediaPlayerNetworkInterfaceName() const +{ + return client().mediaPlayerNetworkInterfaceName(); +} + +bool MediaPlayer::getRawCookies(const URL& url, Vector<Cookie>& cookies) const +{ + return client().mediaPlayerGetRawCookies(url, cookies); +} +#endif + +void MediaPlayer::setShouldDisableSleep(bool flag) +{ + if (m_private) + m_private->setShouldDisableSleep(flag); +} + +bool MediaPlayer::shouldDisableSleep() const +{ + return client().mediaPlayerShouldDisableSleep(); +} + } #endif diff --git a/Source/WebCore/platform/graphics/MediaPlayer.h b/Source/WebCore/platform/graphics/MediaPlayer.h index c6480e4b0..c9dbfc34a 100644 --- a/Source/WebCore/platform/graphics/MediaPlayer.h +++ b/Source/WebCore/platform/graphics/MediaPlayer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007-2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,16 +28,19 @@ #if ENABLE(VIDEO) #include "GraphicsTypes3D.h" -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -#include "MediaPlayerProxy.h" -#endif #include "AudioTrackPrivate.h" +#include "LegacyCDMSession.h" #include "InbandTextTrackPrivate.h" #include "IntRect.h" #include "URL.h" #include "LayoutRect.h" -#include "NativeImagePtr.h" +#include "MediaPlayerEnums.h" +#include "NativeImage.h" +#include "PlatformLayer.h" +#include "PlatformMediaResourceLoader.h" +#include "PlatformMediaSession.h" +#include "SecurityOriginHash.h" #include "Timer.h" #include "VideoTrackPrivate.h" #include <runtime/Uint8Array.h> @@ -45,20 +48,17 @@ #include <wtf/HashSet.h> #include <wtf/MediaTime.h> #include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> #include <wtf/text/StringHash.h> -#if USE(ACCELERATED_COMPOSITING) -#include "PlatformLayer.h" -#endif - -#if USE(PLATFORM_TEXT_TRACK_MENU) -#include "PlatformTextTrackMenu.h" +#if ENABLE(AVF_CAPTIONS) +#include "PlatformTextTrack.h" #endif OBJC_CLASS AVAsset; OBJC_CLASS AVPlayer; +OBJC_CLASS NSArray; OBJC_CLASS QTMovie; class AVCFPlayer; @@ -69,12 +69,16 @@ namespace WebCore { class AudioSourceProvider; class AuthenticationChallenge; -class Document; +class MediaPlaybackTarget; #if ENABLE(MEDIA_SOURCE) -class HTMLMediaSource; +class MediaSourcePrivateClient; +#endif +#if ENABLE(MEDIA_STREAM) +class MediaStreamPrivate; #endif class MediaPlayerPrivateInterface; class TextTrackRepresentation; +struct Cookie; // Structure that will hold every native // types supported by the current media player. @@ -86,8 +90,6 @@ struct PlatformMedia { QTMovieType, QTMovieGWorldType, QTMovieVisualContextType, - ChromiumMediaPlayerType, - QtMediaPlayerType, AVFoundationMediaPlayerType, AVFoundationCFMediaPlayerType, AVFoundationAssetType, @@ -97,8 +99,6 @@ struct PlatformMedia { QTMovie* qtMovie; QTMovieGWorld* qtMovieGWorld; QTMovieVisualContext* qtMovieVisualContext; - MediaPlayerPrivateInterface* chromiumMediaPlayer; - MediaPlayerPrivateInterface* qtMediaPlayer; AVPlayer* avfMediaPlayer; AVCFPlayer* avcfMediaPlayer; AVAsset* avfAsset; @@ -106,51 +106,42 @@ struct PlatformMedia { }; struct MediaEngineSupportParameters { + + MediaEngineSupportParameters() { } + String type; String codecs; URL url; -#if ENABLE(ENCRYPTED_MEDIA) - String keySystem; -#endif -#if ENABLE(MEDIA_SOURCE) - bool isMediaSource; -#endif - - MediaEngineSupportParameters() -#if ENABLE(MEDIA_SOURCE) - : isMediaSource(false) -#endif - { - } + bool isMediaSource { false }; + bool isMediaStream { false }; }; extern const PlatformMedia NoPlatformMedia; +class CDMSessionClient; class CachedResourceLoader; class ContentType; -class FrameView; class GraphicsContext; class GraphicsContext3D; class IntRect; class IntSize; class MediaPlayer; +class PlatformTimeRanges; + struct MediaPlayerFactory; -class TimeRanges; -class HostWindow; #if PLATFORM(WIN) && USE(AVFOUNDATION) struct GraphicsDeviceAdapter; #endif +#if USE(GSTREAMER) +class MediaPlayerRequestInstallMissingPluginsCallback; +#endif + class MediaPlayerClient { public: - enum CORSMode { Unspecified, Anonymous, UseCredentials }; - virtual ~MediaPlayerClient() { } - // Get the document which the media player is owned by - virtual Document* mediaPlayerOwningDocument() { return 0; } - // the network state has changed virtual void mediaPlayerNetworkStateChanged(MediaPlayer*) { } @@ -199,67 +190,89 @@ public: // A characteristic of the media file, eg. video, audio, closed captions, etc, has changed. virtual void mediaPlayerCharacteristicChanged(MediaPlayer*) { } -#if USE(ACCELERATED_COMPOSITING) // whether the rendering system can accelerate the display of this MediaPlayer. virtual bool mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) { return false; } // called when the media player's rendering mode changed, which indicates a change in the // availability of the platformLayer(). virtual void mediaPlayerRenderingModeChanged(MediaPlayer*) { } -#endif + + // whether accelerated compositing is enabled for video rendering + virtual bool mediaPlayerAcceleratedCompositingEnabled() { return false; } + + virtual void mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*) { } #if PLATFORM(WIN) && USE(AVFOUNDATION) virtual GraphicsDeviceAdapter* mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const { return 0; } #endif -#if ENABLE(ENCRYPTED_MEDIA) - enum MediaKeyErrorCode { UnknownError = 1, ClientError, ServiceError, OutputError, HardwareChangeError, DomainError }; - virtual void mediaPlayerKeyAdded(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */) { } - virtual void mediaPlayerKeyError(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */, MediaKeyErrorCode, unsigned short /* systemCode */) { } - virtual void mediaPlayerKeyMessage(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */, const unsigned char* /* message */, unsigned /* messageLength */, const URL& /* defaultURL */) { } - virtual bool mediaPlayerKeyNeeded(MediaPlayer*, const String& /* keySystem */, const String& /* sessionId */, const unsigned char* /* initData */, unsigned /* initDataLength */) { return false; } -#endif - -#if ENABLE(ENCRYPTED_MEDIA_V2) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + virtual RefPtr<ArrayBuffer> mediaPlayerCachedKeyForKeyId(const String&) const { return nullptr; } virtual bool mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array*) { return false; } + virtual String mediaPlayerMediaKeysStorageDirectory() const { return emptyString(); } #endif -#if ENABLE(IOS_AIRPLAY) +#if ENABLE(WIRELESS_PLAYBACK_TARGET) virtual void mediaPlayerCurrentPlaybackTargetIsWirelessChanged(MediaPlayer*) { }; - virtual void mediaPlayerPlaybackTargetAvailabilityChanged(MediaPlayer*) { }; #endif virtual String mediaPlayerReferrer() const { return String(); } virtual String mediaPlayerUserAgent() const { return String(); } - virtual CORSMode mediaPlayerCORSMode() const { return Unspecified; } virtual void mediaPlayerEnterFullscreen() { } virtual void mediaPlayerExitFullscreen() { } virtual bool mediaPlayerIsFullscreen() const { return false; } virtual bool mediaPlayerIsFullscreenPermitted() const { return false; } virtual bool mediaPlayerIsVideo() const { return false; } virtual LayoutRect mediaPlayerContentBoxRect() const { return LayoutRect(); } + virtual float mediaPlayerContentsScale() const { return 1; } virtual void mediaPlayerSetSize(const IntSize&) { } virtual void mediaPlayerPause() { } virtual void mediaPlayerPlay() { } virtual bool mediaPlayerPlatformVolumeConfigurationRequired() const { return false; } virtual bool mediaPlayerIsPaused() const { return true; } virtual bool mediaPlayerIsLooping() const { return false; } - virtual HostWindow* mediaPlayerHostWindow() { return 0; } - virtual IntRect mediaPlayerWindowClipRect() { return IntRect(); } virtual CachedResourceLoader* mediaPlayerCachedResourceLoader() { return 0; } + virtual RefPtr<PlatformMediaResourceLoader> mediaPlayerCreateResourceLoader() { return nullptr; } + virtual bool doesHaveAttribute(const AtomicString&, AtomicString* = 0) const { return false; } + virtual bool mediaPlayerShouldUsePersistentCache() const { return true; } + virtual const String& mediaPlayerMediaCacheDirectory() const { return emptyString(); } #if ENABLE(VIDEO_TRACK) - virtual void mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrackPrivate>) { } - virtual void mediaPlayerDidAddTextTrack(PassRefPtr<InbandTextTrackPrivate>) { } - virtual void mediaPlayerDidAddVideoTrack(PassRefPtr<VideoTrackPrivate>) { } - virtual void mediaPlayerDidRemoveAudioTrack(PassRefPtr<AudioTrackPrivate>) { } - virtual void mediaPlayerDidRemoveTextTrack(PassRefPtr<InbandTextTrackPrivate>) { } - virtual void mediaPlayerDidRemoveVideoTrack(PassRefPtr<VideoTrackPrivate>) { } + virtual void mediaPlayerDidAddAudioTrack(AudioTrackPrivate&) { } + virtual void mediaPlayerDidAddTextTrack(InbandTextTrackPrivate&) { } + virtual void mediaPlayerDidAddVideoTrack(VideoTrackPrivate&) { } + virtual void mediaPlayerDidRemoveAudioTrack(AudioTrackPrivate&) { } + virtual void mediaPlayerDidRemoveTextTrack(InbandTextTrackPrivate&) { } + virtual void mediaPlayerDidRemoveVideoTrack(VideoTrackPrivate&) { } virtual void textTrackRepresentationBoundsChanged(const IntRect&) { } +#if ENABLE(AVF_CAPTIONS) + virtual Vector<RefPtr<PlatformTextTrack>> outOfBandTrackSources() { return Vector<RefPtr<PlatformTextTrack>>(); } +#endif #endif +#if PLATFORM(IOS) + virtual String mediaPlayerNetworkInterfaceName() const { return String(); } + virtual bool mediaPlayerGetRawCookies(const URL&, Vector<Cookie>&) const { return false; } +#endif + virtual bool mediaPlayerShouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&) { return false; } + virtual void mediaPlayerHandlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType) { } + + virtual String mediaPlayerSourceApplicationIdentifier() const { return emptyString(); } + + virtual bool mediaPlayerIsInMediaDocument() const { return false; } + virtual void mediaPlayerEngineFailedToLoad() const { } + + virtual double mediaPlayerRequestedPlaybackRate() const { return 0; } + virtual MediaPlayerEnums::VideoFullscreenMode mediaPlayerFullscreenMode() const { return MediaPlayerEnums::VideoFullscreenModeNone; } + virtual Vector<String> mediaPlayerPreferredAudioCharacteristics() const { return Vector<String>(); } + +#if USE(GSTREAMER) + virtual void requestInstallMissingPlugins(const String&, const String&, MediaPlayerRequestInstallMissingPluginsCallback&) { }; +#endif + + virtual bool mediaPlayerShouldDisableSleep() const { return false; } }; class MediaPlayerSupportsTypeClient { @@ -270,48 +283,62 @@ public: virtual String mediaPlayerDocumentHost() const { return String(); } }; -class MediaPlayer { +class MediaPlayer : public MediaPlayerEnums, public RefCounted<MediaPlayer> { WTF_MAKE_NONCOPYABLE(MediaPlayer); WTF_MAKE_FAST_ALLOCATED; public: - - static PassOwnPtr<MediaPlayer> create(MediaPlayerClient* client) - { - return adoptPtr(new MediaPlayer(client)); - } + static Ref<MediaPlayer> create(MediaPlayerClient&); virtual ~MediaPlayer(); + void invalidate(); + // Media engine support. enum SupportsType { IsNotSupported, IsSupported, MayBeSupported }; static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&, const MediaPlayerSupportsTypeClient*); - static void getSupportedTypes(HashSet<String>&); + static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&); static bool isAvailable(); - static void getSitesInMediaCache(Vector<String>&); - static void clearMediaCache(); - static void clearMediaCacheForSite(const String&); + static HashSet<RefPtr<SecurityOrigin>> originsInMediaCache(const String& path); + static void clearMediaCache(const String& path, std::chrono::system_clock::time_point modifiedSince); + static void clearMediaCacheForOrigins(const String& path, const HashSet<RefPtr<SecurityOrigin>>&); + static bool supportsKeySystem(const String& keySystem, const String& mimeType); bool supportsFullscreen() const; - bool supportsSave() const; bool supportsScanning() const; + bool canSaveMediaData() const; bool requiresImmediateCompositing() const; + bool doesHaveAttribute(const AtomicString&, AtomicString* value = nullptr) const; PlatformMedia platformMedia() const; -#if USE(ACCELERATED_COMPOSITING) PlatformLayer* platformLayer() const; + +#if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) + void setVideoFullscreenLayer(PlatformLayer*, std::function<void()> completionHandler = [] { }); + void setVideoFullscreenFrame(FloatRect); + using MediaPlayerEnums::VideoGravity; + void setVideoFullscreenGravity(VideoGravity); + void setVideoFullscreenMode(VideoFullscreenMode); + VideoFullscreenMode fullscreenMode() const; #endif - IntSize naturalSize(); +#if PLATFORM(IOS) + NSArray *timedMetadata() const; + String accessLog() const; + String errorLog() const; +#endif + + FloatSize naturalSize(); bool hasVideo() const; bool hasAudio() const; - void setFrameView(FrameView* frameView) { m_frameView = frameView; } - FrameView* frameView() { return m_frameView; } - bool inMediaDocument(); + bool inMediaDocument() const; IntSize size() const { return m_size; } void setSize(const IntSize& size); bool load(const URL&, const ContentType&, const String& keySystem); #if ENABLE(MEDIA_SOURCE) - bool load(const URL&, const ContentType&, PassRefPtr<HTMLMediaSource>); + bool load(const URL&, const ContentType&, MediaSourcePrivateClient*); +#endif +#if ENABLE(MEDIA_STREAM) + bool load(MediaStreamPrivate*); #endif void cancelLoad(); @@ -321,46 +348,49 @@ public: void prepareToPlay(); void play(); void pause(); + void setShouldBufferData(bool); -#if ENABLE(ENCRYPTED_MEDIA) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) // Represents synchronous exceptions that can be thrown from the Encrypted Media methods. // This is different from the asynchronous MediaKeyError. enum MediaKeyException { NoError, InvalidPlayerState, KeySystemNotSupported }; - MediaKeyException generateKeyRequest(const String& keySystem, const unsigned char* initData, unsigned initDataLength); - MediaKeyException addKey(const String& keySystem, const unsigned char* key, unsigned keyLength, const unsigned char* initData, unsigned initDataLength, const String& sessionId); - MediaKeyException cancelKeyRequest(const String& keySystem, const String& sessionId); + std::unique_ptr<CDMSession> createSession(const String& keySystem, CDMSessionClient*); + void setCDMSession(CDMSession*); + void keyAdded(); #endif bool paused() const; bool seeking() const; static double invalidTime() { return -1.0;} - double duration() const; - double currentTime() const; - void seek(double time); - void seekWithTolerance(double time, double negativeTolerance, double positiveTolerance); + MediaTime duration() const; + MediaTime currentTime() const; + void seek(const MediaTime&); + void seekWithTolerance(const MediaTime&, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance); - double startTime() const; + MediaTime startTime() const; + MediaTime initialTime() const; - double initialTime() const; + MediaTime getStartDate() const; double rate() const; void setRate(double); + double requestedRate() const; bool preservesPitch() const; void setPreservesPitch(bool); - PassRefPtr<TimeRanges> buffered(); - PassRefPtr<TimeRanges> seekable(); - double minTimeSeekable(); - double maxTimeSeekable(); + std::unique_ptr<PlatformTimeRanges> buffered(); + std::unique_ptr<PlatformTimeRanges> seekable(); + MediaTime minTimeSeekable(); + MediaTime maxTimeSeekable(); bool didLoadingProgress(); double volume() const; void setVolume(double); - bool platformVolumeConfigurationRequired() const { return m_mediaPlayerClient->mediaPlayerPlatformVolumeConfigurationRequired(); } + bool platformVolumeConfigurationRequired() const { return client().mediaPlayerPlatformVolumeConfigurationRequired(); } bool muted() const; void setMuted(bool); @@ -371,8 +401,8 @@ public: bool autoplay() const; void setAutoplay(bool); - void paint(GraphicsContext*, const IntRect&); - void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); + void paint(GraphicsContext&, const FloatRect&); + void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&); // copyVideoTextureToPlatformTexture() is used to do the GPU-GPU textures copy without a readback to system memory. // The first five parameters denote the corresponding GraphicsContext, destination texture, requested level, requested type and the required internalFormat for destination texture. @@ -387,20 +417,20 @@ public: // In chromium, the implementation is based on GL_CHROMIUM_copy_texture extension which is documented at // http://src.chromium.org/viewvc/chrome/trunk/src/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_copy_texture.txt and implemented at // http://src.chromium.org/viewvc/chrome/trunk/src/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc via shaders. - bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY); + bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject texture, GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY); - PassNativeImagePtr nativeImageForCurrentTime(); + NativeImagePtr nativeImageForCurrentTime(); - enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; + using MediaPlayerEnums::NetworkState; NetworkState networkState(); - enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; + using MediaPlayerEnums::ReadyState; ReadyState readyState(); - enum MovieLoadType { Unknown, Download, StoredStream, LiveStream }; + using MediaPlayerEnums::MovieLoadType; MovieLoadType movieLoadType() const; - enum Preload { None, MetaData, Auto }; + using MediaPlayerEnums::Preload; Preload preload() const; void setPreload(Preload); @@ -418,8 +448,7 @@ public: void repaint(); - MediaPlayerClient* mediaPlayerClient() const { return m_mediaPlayerClient; } - void clearMediaPlayerClient() { m_mediaPlayerClient = 0; } + MediaPlayerClient& client() const { return *m_client; } bool hasAvailableVideoFrame() const; void prepareForRendering(); @@ -427,22 +456,16 @@ public: bool canLoadPoster() const; void setPoster(const String&); -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - void deliverNotification(MediaPlayerProxyNotificationType notification); - void setMediaPlayerProxy(WebMediaPlayerProxy* proxy); - void setControls(bool); -#endif - -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || USE(NATIVE_FULLSCREEN_VIDEO) +#if USE(NATIVE_FULLSCREEN_VIDEO) void enterFullscreen(); void exitFullscreen(); #endif -#if ENABLE(IOS_AIRPLAY) - bool isCurrentPlaybackTargetWireless() const; - void showPlaybackTargetPicker(); +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + enum WirelessPlaybackTargetType { TargetTypeNone, TargetTypeAirPlay, TargetTypeTVOut }; + WirelessPlaybackTargetType wirelessPlaybackTargetType() const; - bool hasWirelessPlaybackTargets() const; + String wirelessPlaybackTargetName() const; bool wirelessVideoPlaybackDisabled() const; void setWirelessVideoPlaybackDisabled(bool); @@ -450,19 +473,24 @@ public: void currentPlaybackTargetIsWirelessChanged(); void playbackTargetAvailabilityChanged(); - void setHasPlaybackTargetAvailabilityListeners(bool); + bool isCurrentPlaybackTargetWireless() const; + bool canPlayToWirelessPlaybackTarget() const; + void setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&&); + + void setShouldPlayToPlaybackTarget(bool); #endif + double minFastReverseRate() const; + double maxFastForwardRate() const; + #if USE(NATIVE_FULLSCREEN_VIDEO) bool canEnterFullscreen() const; #endif -#if USE(ACCELERATED_COMPOSITING) // whether accelerated rendering is supported by the media engine for the current media. bool supportsAcceleratedRendering() const; // called when the rendering system flips the into or out of accelerated rendering mode. void acceleratedRenderingStateChanged(); -#endif bool shouldMaintainAspectRatio() const; void setShouldMaintainAspectRatio(bool); @@ -475,7 +503,7 @@ public: bool didPassCORSAccessCheck() const; - double mediaTimeForTimeValue(double) const; + MediaTime mediaTimeForTimeValue(const MediaTime&) const; double maximumDurationToCacheMediaTime() const; @@ -490,50 +518,48 @@ public: AudioSourceProvider* audioSourceProvider(); #endif -#if ENABLE(ENCRYPTED_MEDIA) - void keyAdded(const String& keySystem, const String& sessionId); - void keyError(const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode, unsigned short systemCode); - void keyMessage(const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const URL& defaultURL); - bool keyNeeded(const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength); -#endif - -#if ENABLE(ENCRYPTED_MEDIA_V2) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + RefPtr<ArrayBuffer> cachedKeyForKeyId(const String& keyId) const; bool keyNeeded(Uint8Array* initData); + String mediaKeysStorageDirectory() const; #endif String referrer() const; String userAgent() const; String engineDescription() const; - -#if PLATFORM(IOS) - void attributeChanged(const String& name, const String& value); - bool readyForPlayback() const; -#endif + long platformErrorCode() const; CachedResourceLoader* cachedResourceLoader(); + PassRefPtr<PlatformMediaResourceLoader> createResourceLoader(); #if ENABLE(VIDEO_TRACK) - void addAudioTrack(PassRefPtr<AudioTrackPrivate>); - void addTextTrack(PassRefPtr<InbandTextTrackPrivate>); - void addVideoTrack(PassRefPtr<VideoTrackPrivate>); - void removeAudioTrack(PassRefPtr<AudioTrackPrivate>); - void removeTextTrack(PassRefPtr<InbandTextTrackPrivate>); - void removeVideoTrack(PassRefPtr<VideoTrackPrivate>); + void addAudioTrack(AudioTrackPrivate&); + void addTextTrack(InbandTextTrackPrivate&); + void addVideoTrack(VideoTrackPrivate&); + void removeAudioTrack(AudioTrackPrivate&); + void removeTextTrack(InbandTextTrackPrivate&); + void removeVideoTrack(VideoTrackPrivate&); bool requiresTextTrackRepresentation() const; void setTextTrackRepresentation(TextTrackRepresentation*); + void syncTextTrackBounds(); + void tracksChanged(); +#if ENABLE(AVF_CAPTIONS) + void notifyTrackModeChanged(); + Vector<RefPtr<PlatformTextTrack>> outOfBandTrackSources(); +#endif #endif - static void resetMediaEngines(); - -#if USE(PLATFORM_TEXT_TRACK_MENU) - bool implementsTextTrackControls() const; - PassRefPtr<PlatformTextTrackMenuInterface> textTrackMenu(); +#if PLATFORM(IOS) + String mediaPlayerNetworkInterfaceName() const; + bool getRawCookies(const URL&, Vector<Cookie>&) const; #endif + static void resetMediaEngines(); + #if USE(GSTREAMER) - void simulateAudioInterruption(); + WEBCORE_EXPORT void simulateAudioInterruption(); #endif String languageOfPrimaryAudioTrack() const; @@ -546,61 +572,70 @@ public: unsigned long totalVideoFrames(); unsigned long droppedVideoFrames(); unsigned long corruptedVideoFrames(); - double totalFrameDelay(); + MediaTime totalFrameDelay(); #endif bool shouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&); + void handlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType); + String sourceApplicationIdentifier() const; + Vector<String> preferredAudioCharacteristics() const; + + bool ended() const; + + void setShouldDisableSleep(bool); + bool shouldDisableSleep() const; private: - MediaPlayer(MediaPlayerClient*); - MediaPlayerFactory* nextBestMediaEngine(MediaPlayerFactory*) const; - void loadWithNextMediaEngine(MediaPlayerFactory*); - void reloadTimerFired(Timer<MediaPlayer>&); + MediaPlayer(MediaPlayerClient&); + + const MediaPlayerFactory* nextBestMediaEngine(const MediaPlayerFactory*) const; + void loadWithNextMediaEngine(const MediaPlayerFactory*); + void reloadTimerFired(); static void initializeMediaEngines(); - MediaPlayerClient* m_mediaPlayerClient; - Timer<MediaPlayer> m_reloadTimer; - OwnPtr<MediaPlayerPrivateInterface> m_private; - MediaPlayerFactory* m_currentMediaEngine; + MediaPlayerClient* m_client; + Timer m_reloadTimer; + std::unique_ptr<MediaPlayerPrivateInterface> m_private; + const MediaPlayerFactory* m_currentMediaEngine; URL m_url; String m_contentMIMEType; String m_contentTypeCodecs; String m_keySystem; - FrameView* m_frameView; IntSize m_size; Preload m_preload; bool m_visible; - double m_rate; double m_volume; bool m_muted; bool m_preservesPitch; bool m_privateBrowsing; bool m_shouldPrepareToRender; bool m_contentMIMETypeWasInferredFromExtension; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - WebMediaPlayerProxy* m_playerProxy; // not owned or used, passed to m_private -#endif + bool m_initializingMediaEngine { false }; #if ENABLE(MEDIA_SOURCE) - RefPtr<HTMLMediaSource> m_mediaSource; + RefPtr<MediaSourcePrivateClient> m_mediaSource; +#endif +#if ENABLE(MEDIA_STREAM) + RefPtr<MediaStreamPrivate> m_mediaStream; #endif }; -typedef PassOwnPtr<MediaPlayerPrivateInterface> (*CreateMediaEnginePlayer)(MediaPlayer*); -typedef void (*MediaEngineSupportedTypes)(HashSet<String>& types); +typedef std::function<std::unique_ptr<MediaPlayerPrivateInterface> (MediaPlayer*)> CreateMediaEnginePlayer; +typedef void (*MediaEngineSupportedTypes)(HashSet<String, ASCIICaseInsensitiveHash>& types); typedef MediaPlayer::SupportsType (*MediaEngineSupportsType)(const MediaEngineSupportParameters& parameters); -typedef void (*MediaEngineGetSitesInMediaCache)(Vector<String>&); -typedef void (*MediaEngineClearMediaCache)(); -typedef void (*MediaEngineClearMediaCacheForSite)(const String&); +typedef HashSet<RefPtr<SecurityOrigin>> (*MediaEngineOriginsInMediaCache)(const String& path); +typedef void (*MediaEngineClearMediaCache)(const String& path, std::chrono::system_clock::time_point modifiedSince); +typedef void (*MediaEngineClearMediaCacheForOrigins)(const String& path, const HashSet<RefPtr<SecurityOrigin>>&); +typedef bool (*MediaEngineSupportsKeySystem)(const String& keySystem, const String& mimeType); typedef void (*MediaEngineRegistrar)(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType, - MediaEngineGetSitesInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForSite); + MediaEngineOriginsInMediaCache, MediaEngineClearMediaCache, MediaEngineClearMediaCacheForOrigins, MediaEngineSupportsKeySystem); typedef void (*MediaEngineRegister)(MediaEngineRegistrar); class MediaPlayerFactorySupport { public: - static void callRegisterMediaEngine(MediaEngineRegister); + WEBCORE_EXPORT static void callRegisterMediaEngine(MediaEngineRegister); }; } diff --git a/Source/WebCore/platform/graphics/FontWidthVariant.h b/Source/WebCore/platform/graphics/MediaPlayerEnums.h index f44329745..3486be3eb 100644 --- a/Source/WebCore/platform/graphics/FontWidthVariant.h +++ b/Source/WebCore/platform/graphics/MediaPlayerEnums.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,23 +23,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FontWidthVariant_h -#define FontWidthVariant_h +#ifndef MediaPlayerEnums_h +#define MediaPlayerEnums_h namespace WebCore { -enum FontWidthVariant { - RegularWidth, - HalfWidth, - ThirdWidth, - QuarterWidth, - LastFontWidthVariant = QuarterWidth +class MediaPlayerEnums { +public: + enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; + enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; + enum MovieLoadType { Unknown, Download, StoredStream, LiveStream }; + enum Preload { None, MetaData, Auto }; + enum VideoGravity { VideoGravityResize, VideoGravityResizeAspect, VideoGravityResizeAspectFill }; + enum { + VideoFullscreenModeNone = 0, + VideoFullscreenModeStandard = 1 << 0, + VideoFullscreenModePictureInPicture = 1 << 1, + }; + typedef uint32_t VideoFullscreenMode; }; -const unsigned FontWidthVariantWidth = 2; +} -COMPILE_ASSERT(LastFontWidthVariant >> FontWidthVariantWidth == 0, FontWidthVariantWidth_is_correct); - -} // namespace WebCore - -#endif // FontWidthVariant_h +#endif diff --git a/Source/WebCore/platform/graphics/MediaPlayerPrivate.h b/Source/WebCore/platform/graphics/MediaPlayerPrivate.h index fbafd70cd..43db87b13 100644 --- a/Source/WebCore/platform/graphics/MediaPlayerPrivate.h +++ b/Source/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2009-2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,13 +29,14 @@ #if ENABLE(VIDEO) #include "MediaPlayer.h" -#include "TimeRanges.h" +#include "PlatformTimeRanges.h" #include <wtf/Forward.h> namespace WebCore { class IntRect; class IntSize; +class MediaPlaybackTarget; class PlatformTextTrack; class MediaPlayerPrivateInterface { @@ -46,25 +47,42 @@ public: virtual void load(const String& url) = 0; #if ENABLE(MEDIA_SOURCE) - virtual void load(const String& url, PassRefPtr<HTMLMediaSource>) = 0; + virtual void load(const String& url, MediaSourcePrivateClient*) = 0; +#endif +#if ENABLE(MEDIA_STREAM) + virtual void load(MediaStreamPrivate&) = 0; #endif virtual void cancelLoad() = 0; virtual void prepareToPlay() { } virtual PlatformMedia platformMedia() const { return NoPlatformMedia; } -#if USE(ACCELERATED_COMPOSITING) virtual PlatformLayer* platformLayer() const { return 0; } + +#if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) + virtual void setVideoFullscreenLayer(PlatformLayer*, std::function<void()> completionHandler) { completionHandler(); } + virtual void setVideoFullscreenFrame(FloatRect) { } + virtual void setVideoFullscreenGravity(MediaPlayer::VideoGravity) { } + virtual void setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode) { } +#endif + +#if PLATFORM(IOS) + virtual NSArray *timedMetadata() const { return 0; } + virtual String accessLog() const { return emptyString(); } + virtual String errorLog() const { return emptyString(); } #endif + virtual long platformErrorCode() const { return 0; } virtual void play() = 0; virtual void pause() = 0; + virtual void setShouldBufferData(bool) { } virtual bool supportsFullscreen() const { return false; } - virtual bool supportsSave() const { return false; } virtual bool supportsScanning() const { return false; } virtual bool requiresImmediateCompositing() const { return false; } - virtual IntSize naturalSize() const = 0; + virtual bool canSaveMediaData() const { return false; } + + virtual FloatSize naturalSize() const = 0; virtual bool hasVideo() const = 0; virtual bool hasAudio() const = 0; @@ -73,23 +91,27 @@ public: virtual float duration() const { return 0; } virtual double durationDouble() const { return duration(); } + virtual MediaTime durationMediaTime() const { return MediaTime::createWithDouble(durationDouble()); } virtual float currentTime() const { return 0; } virtual double currentTimeDouble() const { return currentTime(); } + virtual MediaTime currentMediaTime() const { return MediaTime::createWithDouble(currentTimeDouble()); } + + virtual MediaTime getStartDate() const { return MediaTime::createWithDouble(std::numeric_limits<double>::quiet_NaN()); } virtual void seek(float) { } virtual void seekDouble(double time) { seek(time); } - virtual void seekWithTolerance(double time, double, double) { seekDouble(time); } + virtual void seek(const MediaTime& time) { seekDouble(time.toDouble()); } + virtual void seekWithTolerance(const MediaTime& time, const MediaTime&, const MediaTime&) { seek(time); } virtual bool seeking() const = 0; - virtual float startTime() const { return 0; } - virtual double startTimeDouble() const { return startTime(); } - - virtual double initialTime() const { return 0; } + virtual MediaTime startTime() const { return MediaTime::zeroTime(); } + virtual MediaTime initialTime() const { return MediaTime::zeroTime(); } virtual void setRate(float) { } virtual void setRateDouble(double rate) { setRate(rate); } + virtual double rate() const { return 0; } virtual void setPreservesPitch(bool) { } @@ -107,24 +129,29 @@ public: virtual bool hasClosedCaptions() const { return false; } virtual void setClosedCaptionsVisible(bool) { } + virtual double maxFastForwardRate() const { return std::numeric_limits<double>::infinity(); } + virtual double minFastReverseRate() const { return -std::numeric_limits<double>::infinity(); } + virtual MediaPlayer::NetworkState networkState() const = 0; virtual MediaPlayer::ReadyState readyState() const = 0; - virtual PassRefPtr<TimeRanges> seekable() const { return maxTimeSeekableDouble() ? TimeRanges::create(minTimeSeekable(), maxTimeSeekableDouble()) : TimeRanges::create(); } + virtual std::unique_ptr<PlatformTimeRanges> seekable() const { return maxMediaTimeSeekable() == MediaTime::zeroTime() ? std::make_unique<PlatformTimeRanges>() : std::make_unique<PlatformTimeRanges>(minMediaTimeSeekable(), maxMediaTimeSeekable()); } virtual float maxTimeSeekable() const { return 0; } - virtual double maxTimeSeekableDouble() const { return maxTimeSeekable(); } + virtual MediaTime maxMediaTimeSeekable() const { return MediaTime::createWithDouble(maxTimeSeekable()); } virtual double minTimeSeekable() const { return 0; } - virtual PassRefPtr<TimeRanges> buffered() const = 0; + virtual MediaTime minMediaTimeSeekable() const { return MediaTime::createWithDouble(minTimeSeekable()); } + virtual std::unique_ptr<PlatformTimeRanges> buffered() const = 0; + virtual unsigned long long totalBytes() const { return 0; } virtual bool didLoadingProgress() const = 0; virtual void setSize(const IntSize&) = 0; - virtual void paint(GraphicsContext*, const IntRect&) = 0; + virtual void paint(GraphicsContext&, const FloatRect&) = 0; - virtual void paintCurrentFrameInContext(GraphicsContext* c, const IntRect& r) { paint(c, r); } - virtual bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject, GC3Dint, GC3Denum, GC3Denum, bool, bool) { return false; } - virtual PassNativeImagePtr nativeImageForCurrentTime() { return 0; } + virtual void paintCurrentFrameInContext(GraphicsContext& c, const FloatRect& r) { paint(c, r); } + virtual bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject, GC3Denum, GC3Dint, GC3Denum, GC3Denum, GC3Denum, bool, bool) { return false; } + virtual NativeImagePtr nativeImageForCurrentTime() { return nullptr; } virtual void setPreload(MediaPlayer::Preload) { } @@ -133,39 +160,34 @@ public: virtual bool canLoadPoster() const { return false; } virtual void setPoster(const String&) { } -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - virtual void deliverNotification(MediaPlayerProxyNotificationType) { } - virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { } - virtual void setControls(bool) { } -#endif - -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || USE(NATIVE_FULLSCREEN_VIDEO) +#if USE(NATIVE_FULLSCREEN_VIDEO) virtual void enterFullscreen() { } virtual void exitFullscreen() { } #endif -#if ENABLE(IOS_AIRPLAY) - virtual bool isCurrentPlaybackTargetWireless() const { return false; } - virtual void showPlaybackTargetPicker() { } +#if ENABLE(WIRELESS_PLAYBACK_TARGET) - virtual bool hasWirelessPlaybackTargets() const { return false; } + virtual String wirelessPlaybackTargetName() const { return emptyString(); } + virtual MediaPlayer::WirelessPlaybackTargetType wirelessPlaybackTargetType() const { return MediaPlayer::TargetTypeNone; } - virtual bool wirelessVideoPlaybackDisabled() const { return false; } + virtual bool wirelessVideoPlaybackDisabled() const { return true; } virtual void setWirelessVideoPlaybackDisabled(bool) { } - virtual void setHasPlaybackTargetAvailabilityListeners(bool) { } + virtual bool canPlayToWirelessPlaybackTarget() const { return false; } + virtual bool isCurrentPlaybackTargetWireless() const { return false; } + virtual void setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&&) { } + + virtual void setShouldPlayToPlaybackTarget(bool) { } #endif #if USE(NATIVE_FULLSCREEN_VIDEO) virtual bool canEnterFullscreen() const { return false; } #endif -#if USE(ACCELERATED_COMPOSITING) // whether accelerated rendering is supported by the media engine for the current media. virtual bool supportsAcceleratedRendering() const { return false; } // called when the rendering system flips the into or out of accelerated rendering mode. virtual void acceleratedRenderingStateChanged() { } -#endif virtual bool shouldMaintainAspectRatio() const { return true; } virtual void setShouldMaintainAspectRatio(bool) { } @@ -180,8 +202,7 @@ public: // Time value in the movie's time scale. It is only necessary to override this if the media // engine uses rational numbers to represent media time. - virtual float mediaTimeForTimeValue(float timeValue) const { return timeValue; } - virtual double mediaTimeForTimeValueDouble(double timeValue) const { return timeValue; } + virtual MediaTime mediaTimeForTimeValue(const MediaTime& timeValue) const { return timeValue; } // Overide this if it is safe for HTMLMediaElement to cache movie time and report // 'currentTime' as [cached time + elapsed wall time]. Returns the maximum wall time @@ -193,9 +214,9 @@ public: virtual unsigned audioDecodedByteCount() const { return 0; } virtual unsigned videoDecodedByteCount() const { return 0; } - void getSitesInMediaCache(Vector<String>&) { } - void clearMediaCache() { } - void clearMediaCacheForSite(const String&) { } + HashSet<RefPtr<SecurityOrigin>> originsInMediaCache(const String&) { return { }; } + void clearMediaCache(const String&, std::chrono::system_clock::time_point) { } + void clearMediaCacheForOrigins(const String&, const HashSet<RefPtr<SecurityOrigin>>&) { } virtual void setPrivateBrowsingMode(bool) { } @@ -205,43 +226,53 @@ public: virtual AudioSourceProvider* audioSourceProvider() { return 0; } #endif -#if ENABLE(ENCRYPTED_MEDIA) - virtual MediaPlayer::MediaKeyException addKey(const String&, const unsigned char*, unsigned, const unsigned char*, unsigned, const String&) { return MediaPlayer::KeySystemNotSupported; } - virtual MediaPlayer::MediaKeyException generateKeyRequest(const String&, const unsigned char*, unsigned) { return MediaPlayer::KeySystemNotSupported; } - virtual MediaPlayer::MediaKeyException cancelKeyRequest(const String&, const String&) { return MediaPlayer::KeySystemNotSupported; } +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + virtual std::unique_ptr<CDMSession> createSession(const String&, CDMSessionClient*) { return nullptr; } + virtual void setCDMSession(CDMSession*) { } + virtual void keyAdded() { } #endif #if ENABLE(VIDEO_TRACK) virtual bool requiresTextTrackRepresentation() const { return false; } virtual void setTextTrackRepresentation(TextTrackRepresentation*) { } -#endif - -#if USE(PLATFORM_TEXT_TRACK_MENU) - virtual bool implementsTextTrackControls() const { return false; } - virtual PassRefPtr<PlatformTextTrackMenuInterface> textTrackMenu() { return 0; } + virtual void syncTextTrackBounds() { }; + virtual void tracksChanged() { }; #endif #if USE(GSTREAMER) virtual void simulateAudioInterruption() { } #endif -#if PLATFORM(IOS) - virtual void attributeChanged(const String&, const String&) { } - virtual bool readyForPlayback() const { return true; } -#endif - virtual String languageOfPrimaryAudioTrack() const { return emptyString(); } - virtual size_t extraMemoryCost() const { return 0; } - + virtual size_t extraMemoryCost() const + { + MediaTime duration = this->durationMediaTime(); + if (!duration) + return 0; + + unsigned long long extra = totalBytes() * buffered()->totalDuration().toDouble() / duration.toDouble(); + return static_cast<unsigned>(extra); + } + virtual unsigned long long fileSize() const { return 0; } + virtual bool ended() const { return false; } + #if ENABLE(MEDIA_SOURCE) virtual unsigned long totalVideoFrames() { return 0; } virtual unsigned long droppedVideoFrames() { return 0; } virtual unsigned long corruptedVideoFrames() { return 0; } - virtual double totalFrameDelay() { return 0; } + virtual MediaTime totalFrameDelay() { return MediaTime::zeroTime(); } #endif + +#if ENABLE(AVF_CAPTIONS) + virtual void notifyTrackModeChanged() { } +#endif + + virtual void notifyActiveSourceBuffersChanged() { } + + virtual void setShouldDisableSleep(bool) { } }; } diff --git a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h b/Source/WebCore/platform/graphics/MediaSourcePrivate.h index 4a9eb4abd..718658910 100644 --- a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/MediaSourcePrivate.h @@ -1,6 +1,5 @@ /* * Copyright (C) 2013 Google Inc. All rights reserved. - * Copyright (C) 2013 Orange * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,35 +27,40 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef MediaSourcePrivate_h +#define MediaSourcePrivate_h -#ifndef SourceBufferPrivateGStreamer_h -#define SourceBufferPrivateGStreamer_h +#if ENABLE(MEDIA_SOURCE) -#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) - -#include "SourceBufferPrivate.h" -#include "WebKitMediaSourceGStreamer.h" +#include "MediaPlayer.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> namespace WebCore { -class SourceBufferPrivateGStreamer final : public SourceBufferPrivate { +class ContentType; +class SourceBufferPrivate; + +class MediaSourcePrivate : public RefCounted<MediaSourcePrivate> { public: - SourceBufferPrivateGStreamer(PassRefPtr<MediaSourceClientGstreamer>, const ContentType&); - ~SourceBufferPrivateGStreamer() { } + typedef Vector<String> CodecsArray; + + MediaSourcePrivate() { } + virtual ~MediaSourcePrivate() { } + + enum AddStatus { Ok, NotSupported, ReachedIdLimit }; + virtual AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) = 0; + virtual void durationChanged() = 0; + enum EndOfStreamStatus { EosNoError, EosNetworkError, EosDecodeError }; + virtual void markEndOfStream(EndOfStreamStatus) = 0; + virtual void unmarkEndOfStream() = 0; - void setClient(SourceBufferPrivateClient*) { } - AppendResult append(const unsigned char*, unsigned); - void abort(); - void removedFromMediaSource(); - MediaPlayer::ReadyState readyState() const { return m_readyState; } - void setReadyState(MediaPlayer::ReadyState readyState) { m_readyState = readyState; } - void evictCodedFrames() { } - bool isFull() { return false; } + virtual MediaPlayer::ReadyState readyState() const = 0; + virtual void setReadyState(MediaPlayer::ReadyState) = 0; -private: - String m_type; - RefPtr<MediaSourceClientGstreamer> m_client; - MediaPlayer::ReadyState m_readyState; + virtual void waitForSeekCompleted() = 0; + virtual void seekCompleted() = 0; }; } diff --git a/Source/WebCore/platform/graphics/win/SharedGDIObject.h b/Source/WebCore/platform/graphics/MediaSourcePrivateClient.h index dd2683a77..3f6863593 100644 --- a/Source/WebCore/platform/graphics/win/SharedGDIObject.h +++ b/Source/WebCore/platform/graphics/MediaSourcePrivateClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple, Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,44 +20,35 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SharedGDIObject_h -#define SharedGDIObject_h +#ifndef MediaSourcePrivateClient_h +#define MediaSourcePrivateClient_h -#include <wtf/PassRefPtr.h> +#if ENABLE(MEDIA_SOURCE) + +#include "PlatformTimeRanges.h" #include <wtf/RefCounted.h> -#include <wtf/win/GDIObject.h> namespace WebCore { -template <typename T> class SharedGDIObject : public RefCounted<SharedGDIObject<T>> { +class MediaSourcePrivate; + +class MediaSourcePrivateClient : public RefCounted<MediaSourcePrivateClient> { public: - static PassRefPtr<SharedGDIObject> create(GDIObject<T> object) - { - return adoptRef(new SharedGDIObject<T>(std::move(object))); - } - - T get() const - { - return m_object.get(); - } - - unsigned hash() const - { - return WTF::PtrHash<T>::hash(m_object.get()); - } - -private: - explicit SharedGDIObject(GDIObject<T> object) - : m_object(std::move(object)) - { - } - - GDIObject<T> m_object; + virtual ~MediaSourcePrivateClient() { } + + virtual void setPrivateAndOpen(Ref<MediaSourcePrivate>&&) = 0; + virtual MediaTime duration() const = 0; + virtual void durationChanged(const MediaTime&) = 0; + virtual std::unique_ptr<PlatformTimeRanges> buffered() const = 0; + virtual void seekToTime(const MediaTime&) = 0; + virtual void monitorSourceBuffers() = 0; }; -} // namespace WebCore +} + +#endif // ENABLE(MEDIA_SOURCE) -#endif // SharedGDIObject_h +#endif diff --git a/Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp new file mode 100644 index 000000000..591b4dde2 --- /dev/null +++ b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NamedImageGeneratedImage.h" + +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "TextStream.h" +#include "Theme.h" + +namespace WebCore { + +NamedImageGeneratedImage::NamedImageGeneratedImage(String name, const FloatSize& size) + : m_name(name) +{ + setContainerSize(size); +} + +void NamedImageGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription) +{ +#if USE(NEW_THEME) || PLATFORM(IOS) + GraphicsContextStateSaver stateSaver(context); + context.setCompositeOperation(compositeOp, blendMode); + context.clip(dstRect); + context.translate(dstRect.x(), dstRect.y()); + if (dstRect.size() != srcRect.size()) + context.scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); + context.translate(-srcRect.x(), -srcRect.y()); + + platformTheme()->drawNamedImage(m_name, context, dstRect); +#else + UNUSED_PARAM(context); + UNUSED_PARAM(dstRect); + UNUSED_PARAM(srcRect); + UNUSED_PARAM(compositeOp); + UNUSED_PARAM(blendMode); +#endif +} + +void NamedImageGeneratedImage::drawPattern(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOp, BlendMode blendMode) +{ +#if USE(NEW_THEME) + auto imageBuffer = ImageBuffer::createCompatibleBuffer(size(), context); + if (!imageBuffer) + return; + + GraphicsContext& graphicsContext = imageBuffer->context(); + platformTheme()->drawNamedImage(m_name, graphicsContext, FloatRect(0, 0, size().width(), size().height())); + + // Tile the image buffer into the context. + imageBuffer->drawPattern(context, dstRect, srcRect, patternTransform, phase, spacing, compositeOp, blendMode); +#else + UNUSED_PARAM(context); + UNUSED_PARAM(srcRect); + UNUSED_PARAM(patternTransform); + UNUSED_PARAM(phase); + UNUSED_PARAM(spacing); + UNUSED_PARAM(dstRect); + UNUSED_PARAM(compositeOp); + UNUSED_PARAM(blendMode); +#endif +} + +void NamedImageGeneratedImage::dump(TextStream& ts) const +{ + GeneratedImage::dump(ts); + ts.dumpProperty("name", m_name); +} + +} diff --git a/Source/WebCore/platform/graphics/NamedImageGeneratedImage.h b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.h new file mode 100644 index 000000000..1bac56af9 --- /dev/null +++ b/Source/WebCore/platform/graphics/NamedImageGeneratedImage.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NamedImageGeneratedImage_h +#define NamedImageGeneratedImage_h + +#include "FloatSize.h" +#include "GeneratedImage.h" +#include "Image.h" + +namespace WebCore { + +class NamedImageGeneratedImage final : public GeneratedImage { +public: + static Ref<NamedImageGeneratedImage> create(String name, const FloatSize& size) + { + return adoptRef(*new NamedImageGeneratedImage(name, size)); + } + +protected: + void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override; + void drawPattern(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override; + + NamedImageGeneratedImage(String name, const FloatSize&); + +private: + bool isNamedImageGeneratedImage() const override { return true; } + void dump(TextStream&) const override; + + String m_name; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/NativeImagePtr.h b/Source/WebCore/platform/graphics/NativeImage.h index bd2c3267b..4a4f1d0ef 100644 --- a/Source/WebCore/platform/graphics/NativeImagePtr.h +++ b/Source/WebCore/platform/graphics/NativeImage.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2016 Apple Inc. All rights reserved. * Copyright (C) 2007-2008 Torch Mobile, Inc. * Copyright (C) 2012 Company 100 Inc. * @@ -12,23 +12,26 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NativeImagePtr_h -#define NativeImagePtr_h +#pragma once + +#include "GraphicsTypes.h" +#include "ImageOrientation.h" #if USE(CG) +#include <wtf/RetainPtr.h> typedef struct CGImage* CGImageRef; #elif USE(CAIRO) #include "RefPtrCairo.h" @@ -36,23 +39,35 @@ typedef struct CGImage* CGImageRef; #include "SharedBitmap.h" #endif +#if USE(DIRECT2D) +#include "COMPtr.h" +#include <d2d1.h> +#endif + namespace WebCore { -// FIXME: NativeImagePtr and PassNativeImagePtr should be smart -// pointers (see SVGImage::nativeImageForCurrentFrame()). +class Color; +class FloatRect; +class IntSize; +class GraphicsContext; + #if USE(CG) -typedef CGImageRef NativeImagePtr; +typedef RetainPtr<CGImageRef> NativeImagePtr; +#elif USE(DIRECT2D) +typedef COMPtr<ID2D1Bitmap> NativeImagePtr; #elif USE(CAIRO) typedef RefPtr<cairo_surface_t> NativeImagePtr; -typedef PassRefPtr<cairo_surface_t> PassNativeImagePtr; #elif USE(WINGDI) typedef RefPtr<SharedBitmap> NativeImagePtr; #endif -#if !USE(CAIRO) -typedef NativeImagePtr PassNativeImagePtr; -#endif +IntSize nativeImageSize(const NativeImagePtr&); +bool nativeImageHasAlpha(const NativeImagePtr&); +Color nativeImageSinglePixelSolidColor(const NativeImagePtr&); -} +float subsamplingScale(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect); -#endif +void drawNativeImage(const NativeImagePtr&, GraphicsContext&, const FloatRect&, const FloatRect&, const IntSize&, CompositeOperator, BlendMode, const ImageOrientation&); +void clearNativeImageSubimages(const NativeImagePtr&); + +} diff --git a/Source/WebCore/platform/graphics/OpenGLESShims.h b/Source/WebCore/platform/graphics/OpenGLESShims.h index f299156c8..b4e951572 100644 --- a/Source/WebCore/platform/graphics/OpenGLESShims.h +++ b/Source/WebCore/platform/graphics/OpenGLESShims.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,7 +26,7 @@ #ifndef OpenGLESShims_h #define OpenGLESShims_h -#if PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN) +#if PLATFORM(GTK) || PLATFORM(WIN) #define glBindFramebufferEXT glBindFramebuffer #define glFramebufferTexture2DEXT glFramebufferTexture2D #define glBindRenderbufferEXT glBindRenderbuffer @@ -53,6 +53,12 @@ #define GL_READ_FRAMEBUFFER_EXT 0x8CA8 #define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 #define FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x9134 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_NONE 0 #endif #endif // OpenGLESShims_h diff --git a/Source/WebCore/platform/graphics/OpenGLShims.cpp b/Source/WebCore/platform/graphics/OpenGLShims.cpp index d77a8b0a6..d3cfd27d5 100644 --- a/Source/WebCore/platform/graphics/OpenGLShims.cpp +++ b/Source/WebCore/platform/graphics/OpenGLShims.cpp @@ -17,12 +17,12 @@ */ #include "config.h" -#if USE(3D_GRAPHICS) || defined(QT_OPENGL_SHIMS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #define DISABLE_SHIMS #include "OpenGLShims.h" -#if !OS(WINDOWS) +#if !PLATFORM(WIN) #include <dlfcn.h> #endif @@ -37,10 +37,10 @@ OpenGLFunctionTable* openGLFunctionTable() return &table; } -#if OS(WINDOWS) +#if PLATFORM(WIN) static void* getProcAddress(const char* procName) { - return reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA("libGLESv2"), procName)); + return GetProcAddress(GetModuleHandleA("libGLESv2"), procName); } #else typedef void* (*glGetProcAddressType) (const char* procName); @@ -70,26 +70,19 @@ static void* lookupOpenGLFunctionAddress(const char* functionName, bool* success if (target) return target; - String fullFunctionName(functionName); - fullFunctionName.append("ARB"); - target = getProcAddress(fullFunctionName.utf8().data()); + target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "ARB").characters8())); if (target) return target; - fullFunctionName = functionName; - fullFunctionName.append("EXT"); - target = getProcAddress(fullFunctionName.utf8().data()); + // FIXME: <https://webkit.org/b/143964> OpenGLShims appears to have a dead store if GLES2 + target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "EXT").characters8())); #if defined(GL_ES_VERSION_2_0) - fullFunctionName = functionName; - fullFunctionName.append("ANGLE"); - target = getProcAddress(fullFunctionName.utf8().data()); + target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "ANGLE").characters8())); if (target) return target; - fullFunctionName = functionName; - fullFunctionName.append("APPLE"); - target = getProcAddress(fullFunctionName.utf8().data()); + target = getProcAddress(reinterpret_cast<const char*>(makeString(functionName, "APPLE").characters8())); #endif // A null address is still a failure case. @@ -146,6 +139,9 @@ bool initializeOpenGLShims() ASSIGN_FUNCTION_TABLE_ENTRY_EXT(glDeleteVertexArrays); ASSIGN_FUNCTION_TABLE_ENTRY(glDetachShader, success); ASSIGN_FUNCTION_TABLE_ENTRY(glDisableVertexAttribArray, success); + ASSIGN_FUNCTION_TABLE_ENTRY(glDrawArraysInstanced, success); + ASSIGN_FUNCTION_TABLE_ENTRY(glDrawBuffers, success); + ASSIGN_FUNCTION_TABLE_ENTRY(glDrawElementsInstanced, success); ASSIGN_FUNCTION_TABLE_ENTRY(glEnableVertexAttribArray, success); ASSIGN_FUNCTION_TABLE_ENTRY(glFramebufferRenderbuffer, success); ASSIGN_FUNCTION_TABLE_ENTRY(glFramebufferTexture2D, success); @@ -166,6 +162,9 @@ bool initializeOpenGLShims() ASSIGN_FUNCTION_TABLE_ENTRY(glGetShaderInfoLog, success); ASSIGN_FUNCTION_TABLE_ENTRY(glGetShaderiv, success); ASSIGN_FUNCTION_TABLE_ENTRY(glGetShaderSource, success); + // glGetStringi is only available on OpenGL or GLES versions >= 3.0. + // Add it with _EXT so it doesn't cause an initialization failure on lower versions. + ASSIGN_FUNCTION_TABLE_ENTRY_EXT(glGetStringi); ASSIGN_FUNCTION_TABLE_ENTRY(glGetUniformfv, success); ASSIGN_FUNCTION_TABLE_ENTRY(glGetUniformiv, success); ASSIGN_FUNCTION_TABLE_ENTRY(glGetUniformLocation, success); @@ -220,6 +219,7 @@ bool initializeOpenGLShims() ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttrib3fv, success); ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttrib4f, success); ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttrib4fv, success); + ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttribDivisor, success); ASSIGN_FUNCTION_TABLE_ENTRY(glVertexAttribPointer, success); if (!success) @@ -229,4 +229,4 @@ bool initializeOpenGLShims() } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/OpenGLShims.h b/Source/WebCore/platform/graphics/OpenGLShims.h index c0e6e6012..a0b8e2ae5 100644 --- a/Source/WebCore/platform/graphics/OpenGLShims.h +++ b/Source/WebCore/platform/graphics/OpenGLShims.h @@ -69,6 +69,9 @@ typedef void (GLAPIENTRY *glDeleteShaderType) (GLuint); typedef void (GLAPIENTRY *glDeleteVertexArraysType) (GLsizei, const GLuint*); typedef void (GLAPIENTRY *glDetachShaderType) (GLuint, GLuint); typedef void (GLAPIENTRY *glDisableVertexAttribArrayType) (GLuint); +typedef void (GLAPIENTRY *glDrawArraysInstancedType) (GLenum, GLint, GLsizei, GLsizei); +typedef void (GLAPIENTRY *glDrawBuffersType) (GLsizei, const GLenum*); +typedef void (GLAPIENTRY *glDrawElementsInstancedType) (GLenum, GLsizei, GLenum, const GLvoid*, GLsizei); typedef void (GLAPIENTRY *glEnableVertexAttribArrayType) (GLuint); typedef void (GLAPIENTRY *glFramebufferRenderbufferType) (GLenum, GLenum, GLenum, GLuint); typedef void (GLAPIENTRY *glFramebufferTexture2DType) (GLenum, GLenum, GLenum, GLuint, GLint); @@ -89,6 +92,7 @@ typedef void (GLAPIENTRY *glGetRenderbufferParameterivType) (GLenum, GLenum, GLi typedef void (GLAPIENTRY *glGetShaderInfoLogType) (GLuint, GLsizei, GLsizei*, char*); typedef void (GLAPIENTRY *glGetShaderivType) (GLuint, GLenum, GLint*); typedef void (GLAPIENTRY *glGetShaderSourceType) (GLuint, GLsizei, GLsizei*, char*); +typedef const GLubyte* (GLAPIENTRY *glGetStringiType) (GLenum, GLuint); typedef GLint (GLAPIENTRY *glGetUniformLocationType) (GLuint, const char*); typedef void (GLAPIENTRY *glGetUniformfvType) (GLuint, GLint, GLfloat*); typedef void (GLAPIENTRY *glGetUniformivType) (GLuint, GLint, GLint*); @@ -138,6 +142,7 @@ typedef void (GLAPIENTRY *glVertexAttrib3fType) (GLuint, const GLfloat, const GL typedef void (GLAPIENTRY *glVertexAttrib3fvType) (GLuint, const GLfloat*); typedef void (GLAPIENTRY *glVertexAttrib4fType) (GLuint, const GLfloat, const GLfloat, const GLfloat, const GLfloat); typedef void (GLAPIENTRY *glVertexAttrib4fvType) (GLuint, const GLfloat*); +typedef void (GLAPIENTRY *glVertexAttribDivisorType) (GLuint, GLuint); typedef void (GLAPIENTRY *glVertexAttribPointerType) (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*); #define FUNCTION_TABLE_ENTRY(FunctionName) FunctionName##Type FunctionName @@ -171,6 +176,9 @@ typedef struct _OpenGLFunctionTable { FUNCTION_TABLE_ENTRY(glDeleteVertexArrays); FUNCTION_TABLE_ENTRY(glDetachShader); FUNCTION_TABLE_ENTRY(glDisableVertexAttribArray); + FUNCTION_TABLE_ENTRY(glDrawArraysInstanced); + FUNCTION_TABLE_ENTRY(glDrawBuffers); + FUNCTION_TABLE_ENTRY(glDrawElementsInstanced); FUNCTION_TABLE_ENTRY(glEnableVertexAttribArray); FUNCTION_TABLE_ENTRY(glFramebufferRenderbuffer); FUNCTION_TABLE_ENTRY(glFramebufferTexture2D); @@ -191,6 +199,7 @@ typedef struct _OpenGLFunctionTable { FUNCTION_TABLE_ENTRY(glGetShaderInfoLog); FUNCTION_TABLE_ENTRY(glGetShaderiv); FUNCTION_TABLE_ENTRY(glGetShaderSource); + FUNCTION_TABLE_ENTRY(glGetStringi); FUNCTION_TABLE_ENTRY(glGetUniformfv); FUNCTION_TABLE_ENTRY(glGetUniformiv); FUNCTION_TABLE_ENTRY(glGetUniformLocation); @@ -240,6 +249,7 @@ typedef struct _OpenGLFunctionTable { FUNCTION_TABLE_ENTRY(glVertexAttrib3fv); FUNCTION_TABLE_ENTRY(glVertexAttrib4f); FUNCTION_TABLE_ENTRY(glVertexAttrib4fv); + FUNCTION_TABLE_ENTRY(glVertexAttribDivisor); FUNCTION_TABLE_ENTRY(glVertexAttribPointer); } OpenGLFunctionTable; @@ -282,6 +292,12 @@ typedef struct _OpenGLFunctionTable { #define glDeleteVertexArrays LOOKUP_GL_FUNCTION(glDeleteVertexArrays) #define glDetachShader LOOKUP_GL_FUNCTION(glDetachShader) #define glDisableVertexAttribArray LOOKUP_GL_FUNCTION(glDisableVertexAttribArray) +#define glDrawArraysInstancedEXT glDrawArraysInstanced +#define glDrawArraysInstanced LOOKUP_GL_FUNCTION(glDrawArraysInstanced) +#define glDrawBuffersEXT glDrawBuffers +#define glDrawBuffers LOOKUP_GL_FUNCTION(glDrawBuffers) +#define glDrawElementsInstancedEXT glDrawElementsInstanced +#define glDrawElementsInstanced LOOKUP_GL_FUNCTION(glDrawElementsInstanced) #define glEnableVertexAttribArray LOOKUP_GL_FUNCTION(glEnableVertexAttribArray) #define glFramebufferRenderbufferEXT glFramebufferRenderbuffer #define glFramebufferRenderbuffer LOOKUP_GL_FUNCTION(glFramebufferRenderbuffer) @@ -311,6 +327,7 @@ typedef struct _OpenGLFunctionTable { #define glGetShaderInfoLog LOOKUP_GL_FUNCTION(glGetShaderInfoLog) #define glGetShaderiv LOOKUP_GL_FUNCTION(glGetShaderiv) #define glGetShaderSource LOOKUP_GL_FUNCTION(glGetShaderSource) +#define glGetStringi LOOKUP_GL_FUNCTION(glGetStringi) #define glGetUniformfv LOOKUP_GL_FUNCTION(glGetUniformfv) #define glGetUniformiv LOOKUP_GL_FUNCTION(glGetUniformiv) #define glGetUniformLocation LOOKUP_GL_FUNCTION(glGetUniformLocation) @@ -365,6 +382,8 @@ typedef struct _OpenGLFunctionTable { #define glVertexAttrib3fv LOOKUP_GL_FUNCTION(glVertexAttrib3fv) #define glVertexAttrib4f LOOKUP_GL_FUNCTION(glVertexAttrib4f) #define glVertexAttrib4fv LOOKUP_GL_FUNCTION(glVertexAttrib4fv) +#define glVertexAttribDivisorEXT glVertexAttribDivisor +#define glVertexAttribDivisor LOOKUP_GL_FUNCTION(glVertexAttribDivisor) #define glVertexAttribPointer LOOKUP_GL_FUNCTION(glVertexAttribPointer) #endif diff --git a/Source/WebCore/platform/graphics/Path.cpp b/Source/WebCore/platform/graphics/Path.cpp index 88d453c3a..9a3596bf2 100644 --- a/Source/WebCore/platform/graphics/Path.cpp +++ b/Source/WebCore/platform/graphics/Path.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * 2006 Rob Buis <buis@kde.org> * Copyright (C) 2007 Eric Seidel <eric@webkit.org> * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,69 +31,48 @@ #include "FloatPoint.h" #include "FloatRect.h" +#include "FloatRoundedRect.h" #include "PathTraversalState.h" #include "RoundedRect.h" +#include "TextStream.h" #include <math.h> #include <wtf/MathExtras.h> namespace WebCore { -static void pathLengthApplierFunction(void* info, const PathElement* element) -{ - PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info); - if (traversalState.m_success) - return; - FloatPoint* points = element->points; - float segmentLength = 0; - switch (element->type) { - case PathElementMoveToPoint: - segmentLength = traversalState.moveTo(points[0]); - break; - case PathElementAddLineToPoint: - segmentLength = traversalState.lineTo(points[0]); - break; - case PathElementAddQuadCurveToPoint: - segmentLength = traversalState.quadraticBezierTo(points[0], points[1]); - break; - case PathElementAddCurveToPoint: - segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]); - break; - case PathElementCloseSubpath: - segmentLength = traversalState.closeSubpath(); - break; - } - traversalState.m_totalLength += segmentLength; - traversalState.processSegment(); -} - +#if !USE(DIRECT2D) float Path::length() const { - PathTraversalState traversalState(PathTraversalState::TraversalTotalLength); - apply(&traversalState, pathLengthApplierFunction); - return traversalState.m_totalLength; + PathTraversalState traversalState(PathTraversalState::Action::TotalLength); + + apply([&traversalState](const PathElement& element) { + traversalState.processPathElement(element); + }); + + return traversalState.totalLength(); } +#endif -FloatPoint Path::pointAtLength(float length, bool& ok) const +PathTraversalState Path::traversalStateAtLength(float length, bool& success) const { - PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength); - traversalState.m_desiredLength = length; - apply(&traversalState, pathLengthApplierFunction); - ok = traversalState.m_success; - return traversalState.m_current; + PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length); + + apply([&traversalState](const PathElement& element) { + traversalState.processPathElement(element); + }); + + success = traversalState.success(); + return traversalState; } -float Path::normalAngleAtLength(float length, bool& ok) const +FloatPoint Path::pointAtLength(float length, bool& success) const { - PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength); - traversalState.m_desiredLength = length ? length : std::numeric_limits<float>::epsilon(); - apply(&traversalState, pathLengthApplierFunction); - ok = traversalState.m_success; - return traversalState.m_normalAngle; + return traversalStateAtLength(length, success).current(); } -void Path::addRoundedRect(const RoundedRect& r) +float Path::normalAngleAtLength(float length, bool& success) const { - addRoundedRect(r.rect(), r.radii().topLeft(), r.radii().topRight(), r.radii().bottomLeft(), r.radii().bottomRight()); + return traversalStateAtLength(length, success).normalAngle(); } void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy) @@ -105,7 +84,7 @@ void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, FloatSize halfSize(rect.width() / 2, rect.height() / 2); // Apply the SVG corner radius constraints, per the rect section of the SVG shapes spec: if - // one of rx,ry is negative, then the other corner radius value is used. If both values are + // one of rx,ry is negative, then the other corner radius value is used. If both values are // negative then rx = ry = 0. If rx is greater than half of the width of the rectangle // then set rx to half of the width; ry is handled similarly. @@ -121,41 +100,37 @@ void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, if (radius.height() > halfSize.height()) radius.setHeight(halfSize.height()); - addPathForRoundedRect(rect, radius, radius, radius, radius, strategy); + addRoundedRect(FloatRoundedRect(rect, radius, radius, radius, radius), strategy); } -void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy strategy) +void Path::addRoundedRect(const FloatRoundedRect& r, RoundedRectStrategy strategy) { - if (rect.isEmpty()) + if (r.isEmpty()) return; - if (rect.width() < topLeftRadius.width() + topRightRadius.width() - || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() - || rect.height() < topLeftRadius.height() + bottomLeftRadius.height() - || rect.height() < topRightRadius.height() + bottomRightRadius.height()) { + const FloatRoundedRect::Radii& radii = r.radii(); + const FloatRect& rect = r.rect(); + + if (!r.isRenderable()) { // If all the radii cannot be accommodated, return a rect. addRect(rect); return; } - addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, strategy); -} - -void Path::addPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy strategy) -{ if (strategy == PreferNativeRoundedRect) { -#if USE(CG) - platformAddPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); +#if USE(CG) || USE(DIRECT2D) + platformAddPathForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); return; #endif } - addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + addBeziersForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); } -// Approximation of control point positions on a bezier to simulate a quarter of a circle. -// This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3 -static const float gCircleControlPoint = 0.447715f; +void Path::addRoundedRect(const RoundedRect& r) +{ + addRoundedRect(FloatRoundedRect(r)); +} void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) { @@ -163,33 +138,85 @@ void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topL addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y())); if (topRightRadius.width() > 0 || topRightRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()), - FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint), + addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * circleControlPoint(), rect.y()), + FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * circleControlPoint()), FloatPoint(rect.maxX(), rect.y() + topRightRadius.height())); addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height())); if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint), - FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()), + addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * circleControlPoint()), + FloatPoint(rect.maxX() - bottomRightRadius.width() * circleControlPoint(), rect.maxY()), FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY())); addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY())); if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()), - FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint), + addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * circleControlPoint(), rect.maxY()), + FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * circleControlPoint()), FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height())); addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint), - FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()), + addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * circleControlPoint()), + FloatPoint(rect.x() + topLeftRadius.width() * circleControlPoint(), rect.y()), FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); closeSubpath(); } -#if !USE(CG) +#if !USE(CG) && !USE(DIRECT2D) +Path Path::polygonPathFromPoints(const Vector<FloatPoint>& points) +{ + Path path; + if (points.size() < 2) + return path; + + path.moveTo(points[0]); + for (size_t i = 1; i < points.size(); ++i) + path.addLineTo(points[i]); + + path.closeSubpath(); + return path; +} + FloatRect Path::fastBoundingRect() const { return boundingRect(); } #endif +#ifndef NDEBUG +void Path::dump() const +{ + TextStream stream; + stream << *this; + WTFLogAlways("%s", stream.release().utf8().data()); +} +#endif + +TextStream& operator<<(TextStream& stream, const Path& path) +{ + bool isFirst = true; + path.apply([&stream, &isFirst](const PathElement& element) { + if (!isFirst) + stream << ", "; + isFirst = false; + switch (element.type) { + case PathElementMoveToPoint: // The points member will contain 1 value. + stream << "move to " << element.points[0]; + break; + case PathElementAddLineToPoint: // The points member will contain 1 value. + stream << "add line to " << element.points[0]; + break; + case PathElementAddQuadCurveToPoint: // The points member will contain 2 values. + stream << "add quad curve to " << element.points[0] << " " << element.points[1]; + break; + case PathElementAddCurveToPoint: // The points member will contain 3 values. + stream << "add curve to " << element.points[0] << " " << element.points[1] << " " << element.points[2]; + break; + case PathElementCloseSubpath: // The points member will contain no values. + stream << "close subpath"; + break; + } + }); + + return stream; +} + } diff --git a/Source/WebCore/platform/graphics/Path.h b/Source/WebCore/platform/graphics/Path.h index 161f846ae..2831cfe33 100644 --- a/Source/WebCore/platform/graphics/Path.h +++ b/Source/WebCore/platform/graphics/Path.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved. - * 2006 Rob Buis <buis@kde.org> + * Copyright (C) 2003, 2006, 2009, 2016 Apple Inc. All rights reserved. + * Copyright (C) 2006 Rob Buis <buis@kde.org> * Copyright (C) 2007-2008 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,24 +28,47 @@ #ifndef Path_h #define Path_h +#include "FloatRect.h" #include "WindRule.h" +#include <functional> #include <wtf/FastMalloc.h> #include <wtf/Forward.h> +#include <wtf/Vector.h> #if USE(CG) + +#include <wtf/RetainPtr.h> +#include <CoreGraphics/CGPath.h> typedef struct CGPath PlatformPath; + +#elif USE(DIRECT2D) +#include "COMPtr.h" + +interface ID2D1Geometry; +interface ID2D1GeometryGroup; +interface ID2D1PathGeometry; +interface ID2D1GeometrySink; + +typedef ID2D1GeometryGroup PlatformPath; + #elif USE(CAIRO) + namespace WebCore { class CairoPath; } typedef WebCore::CairoPath PlatformPath; + #elif USE(WINGDI) + namespace WebCore { - class PlatformPath; +class PlatformPath; } typedef WebCore::PlatformPath PlatformPath; + #else + typedef void PlatformPath; + #endif typedef PlatformPath* PlatformPathPtr; @@ -54,11 +77,13 @@ namespace WebCore { class AffineTransform; class FloatPoint; - class FloatRect; + class FloatRoundedRect; class FloatSize; class GraphicsContext; + class PathTraversalState; class RoundedRect; class StrokeStyleApplier; + class TextStream; enum PathElementType { PathElementMoveToPoint, // The points member will contain 1 value. @@ -68,7 +93,7 @@ namespace WebCore { PathElementCloseSubpath // The points member will contain no values. }; - // The points in the sturcture are the same as those that would be used with the + // The points in the structure are the same as those that would be used with the // add... method. For example, a line returns the endpoint, while a cubic returns // two tangent points and the endpoint. struct PathElement { @@ -76,30 +101,36 @@ namespace WebCore { FloatPoint* points; }; - typedef void (*PathApplierFunction)(void* info, const PathElement*); + typedef std::function<void (const PathElement&)> PathApplierFunction; class Path { WTF_MAKE_FAST_ALLOCATED; public: - Path(); - ~Path(); + WEBCORE_EXPORT Path(); +#if USE(CG) + Path(RetainPtr<CGMutablePathRef>); +#endif + WEBCORE_EXPORT ~Path(); - Path(const Path&); - Path& operator=(const Path&); + WEBCORE_EXPORT Path(const Path&); + WEBCORE_EXPORT Path& operator=(const Path&); + + static Path polygonPathFromPoints(const Vector<FloatPoint>&); bool contains(const FloatPoint&, WindRule rule = RULE_NONZERO) const; bool strokeContains(StrokeStyleApplier*, const FloatPoint&) const; // fastBoundingRect() should equal or contain boundingRect(); boundingRect() // should perfectly bound the points within the path. FloatRect boundingRect() const; - FloatRect fastBoundingRect() const; + WEBCORE_EXPORT FloatRect fastBoundingRect() const; FloatRect strokeBoundingRect(StrokeStyleApplier* = 0) const; - + float length() const; - FloatPoint pointAtLength(float length, bool& ok) const; - float normalAngleAtLength(float length, bool& ok) const; + PathTraversalState traversalStateAtLength(float length, bool& success) const; + FloatPoint pointAtLength(float length, bool& success) const; + float normalAngleAtLength(float length, bool& success) const; - void clear(); + WEBCORE_EXPORT void clear(); bool isNull() const { return !m_path; } bool isEmpty() const; // Gets the current point of the current path, which is conceptually the final point reached by the path so far. @@ -107,15 +138,16 @@ namespace WebCore { bool hasCurrentPoint() const; FloatPoint currentPoint() const; - void moveTo(const FloatPoint&); - void addLineTo(const FloatPoint&); - void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint); - void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); + WEBCORE_EXPORT void moveTo(const FloatPoint&); + WEBCORE_EXPORT void addLineTo(const FloatPoint&); + WEBCORE_EXPORT void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint); + WEBCORE_EXPORT void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); void addArcTo(const FloatPoint&, const FloatPoint&, float radius); - void closeSubpath(); + WEBCORE_EXPORT void closeSubpath(); void addArc(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise); void addRect(const FloatRect&); + void addEllipse(FloatPoint, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise); void addEllipse(const FloatRect&); enum RoundedRectStrategy { @@ -123,32 +155,65 @@ namespace WebCore { PreferBezierRoundedRect }; - void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii, RoundedRectStrategy = PreferNativeRoundedRect); - void addRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy = PreferNativeRoundedRect); + WEBCORE_EXPORT void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii, RoundedRectStrategy = PreferNativeRoundedRect); + WEBCORE_EXPORT void addRoundedRect(const FloatRoundedRect&, RoundedRectStrategy = PreferNativeRoundedRect); void addRoundedRect(const RoundedRect&); + void addPath(const Path&, const AffineTransform&); + void translate(const FloatSize&); // To keep Path() cheap, it does not allocate a PlatformPath immediately // meaning Path::platformPath() can return null. +#if USE(DIRECT2D) + PlatformPathPtr platformPath() const { return m_path.get(); } +#else PlatformPathPtr platformPath() const { return m_path; } +#endif // ensurePlatformPath() will allocate a PlatformPath if it has not yet been and will never return null. - PlatformPathPtr ensurePlatformPath(); + WEBCORE_EXPORT PlatformPathPtr ensurePlatformPath(); - void apply(void* info, PathApplierFunction) const; + WEBCORE_EXPORT void apply(const PathApplierFunction&) const; void transform(const AffineTransform&); - void addPathForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, RoundedRectStrategy = PreferNativeRoundedRect); + static float circleControlPoint() + { + // Approximation of control point positions on a bezier to simulate a quarter of a circle. + // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3 + return 0.447715; + } + void addBeziersForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); -#if USE(CG) +#if USE(CG) || USE(DIRECT2D) void platformAddPathForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); #endif +#if USE(DIRECT2D) + ID2D1GeometrySink* activePath() const { return m_activePath.get(); } + void appendGeometry(ID2D1Geometry*); + void createGeometryWithFillMode(WindRule, COMPtr<ID2D1GeometryGroup>&) const; + void drawDidComplete() const; + + HRESULT initializePathState(); +#endif + +#ifndef NDEBUG + void dump() const; +#endif + private: - PlatformPathPtr m_path; +#if USE(DIRECT2D) + COMPtr<ID2D1GeometryGroup> m_path; + COMPtr<ID2D1PathGeometry> m_activePathGeometry; + COMPtr<ID2D1GeometrySink> m_activePath; +#else + PlatformPathPtr m_path { nullptr }; +#endif }; +TextStream& operator<<(TextStream&, const Path&); + } #endif diff --git a/Source/WebCore/platform/graphics/PathTraversalState.cpp b/Source/WebCore/platform/graphics/PathTraversalState.cpp index cf5371727..c6bf7a6bf 100644 --- a/Source/WebCore/platform/graphics/PathTraversalState.cpp +++ b/Source/WebCore/platform/graphics/PathTraversalState.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org> + * Copyright (C) 2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -34,7 +35,9 @@ static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& sec static inline float distanceLine(const FloatPoint& start, const FloatPoint& end) { - return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - start.y()) * (end.y() - start.y())); + float dx = end.x() - start.x(); + float dy = end.y() - start.y(); + return sqrtf(dx * dx + dy * dy); } struct QuadraticBezier { @@ -114,110 +117,149 @@ struct CubicBezier { // Another check which is possible up-front (to send us down the fast path) would be to check if // approximateDistance() + current total distance > desired distance template<class CurveType> -static float curveLength(PathTraversalState& traversalState, CurveType curve) +static float curveLength(const PathTraversalState& traversalState, const CurveType& originalCurve, FloatPoint& previous, FloatPoint& current) { static const unsigned curveStackDepthLimit = 20; - - Vector<CurveType> curveStack; - curveStack.append(curve); - + CurveType curve = originalCurve; + Vector<CurveType, curveStackDepthLimit> curveStack; float totalLength = 0; - do { + + while (true) { float length = curve.approximateDistance(); - if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() <= curveStackDepthLimit) { + + if ((length - distanceLine(curve.start, curve.end)) > kPathSegmentLengthTolerance && curveStack.size() < curveStackDepthLimit) { CurveType leftCurve; CurveType rightCurve; curve.split(leftCurve, rightCurve); curve = leftCurve; curveStack.append(rightCurve); - } else { - totalLength += length; - if (traversalState.m_action == PathTraversalState::TraversalPointAtLength - || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) { - traversalState.m_previous = curve.start; - traversalState.m_current = curve.end; - if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength) - return totalLength; - } - curve = curveStack.last(); - curveStack.removeLast(); + continue; } - } while (!curveStack.isEmpty()); - + + totalLength += length; + if (traversalState.action() == PathTraversalState::Action::VectorAtLength) { + previous = curve.start; + current = curve.end; + if (traversalState.totalLength() + totalLength > traversalState.desiredLength()) + break; + } + + if (curveStack.isEmpty()) + break; + + curve = curveStack.last(); + curveStack.removeLast(); + } + + if (traversalState.action() != PathTraversalState::Action::VectorAtLength) { + ASSERT(curve.end == originalCurve.end); + previous = curve.start; + current = curve.end; + } + return totalLength; } -PathTraversalState::PathTraversalState(PathTraversalAction action) +PathTraversalState::PathTraversalState(Action action, float desiredLength) : m_action(action) - , m_success(false) - , m_totalLength(0) - , m_segmentIndex(0) - , m_desiredLength(0) - , m_normalAngle(0) + , m_desiredLength(desiredLength) { + ASSERT(action != Action::TotalLength || !desiredLength); } -float PathTraversalState::closeSubpath() +void PathTraversalState::closeSubpath() { - float distance = distanceLine(m_current, m_start); - m_current = m_control1 = m_control2 = m_start; - return distance; + m_totalLength += distanceLine(m_current, m_start); + m_current = m_start; } -float PathTraversalState::moveTo(const FloatPoint& point) +void PathTraversalState::moveTo(const FloatPoint& point) { - m_current = m_start = m_control1 = m_control2 = point; - return 0; + m_previous = m_current = m_start = point; } -float PathTraversalState::lineTo(const FloatPoint& point) +void PathTraversalState::lineTo(const FloatPoint& point) { - float distance = distanceLine(m_current, point); - m_current = m_control1 = m_control2 = point; - return distance; + m_totalLength += distanceLine(m_current, point); + m_current = point; } -float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd) +void PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd) { - float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd)); - - m_control1 = newControl; - m_control2 = newEnd; - - if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) - m_current = newEnd; + m_totalLength += curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd), m_previous, m_current); +} - return distance; +void PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd) +{ + m_totalLength += curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd), m_previous, m_current); } -float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd) +bool PathTraversalState::finalizeAppendPathElement() { - float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd)); + if (m_action == Action::TotalLength) + return false; - m_control1 = newEnd; - m_control2 = newControl2; - - if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) - m_current = newEnd; + if (m_action == Action::SegmentAtLength) { + if (m_totalLength >= m_desiredLength) + m_success = true; + return m_success; + } - return distance; -} + ASSERT(m_action == Action::VectorAtLength); -void PathTraversalState::processSegment() -{ - if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength) - m_success = true; - - if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleAtLength) && m_totalLength >= m_desiredLength) { + if (m_totalLength >= m_desiredLength) { float slope = FloatPoint(m_current - m_previous).slopeAngleRadians(); - if (m_action == TraversalPointAtLength) { - float offset = m_desiredLength - m_totalLength; - m_current.move(offset * cosf(slope), offset * sinf(slope)); - } else + float offset = m_desiredLength - m_totalLength; + m_current.move(offset * cosf(slope), offset * sinf(slope)); + + if (!m_isZeroVector && !m_desiredLength) + m_isZeroVector = true; + else { + m_success = true; m_normalAngle = rad2deg(slope); - m_success = true; + } } + m_previous = m_current; + return m_success; +} + +bool PathTraversalState::appendPathElement(PathElementType type, const FloatPoint* points) +{ + switch (type) { + case PathElementMoveToPoint: + moveTo(points[0]); + break; + case PathElementAddLineToPoint: + lineTo(points[0]); + break; + case PathElementAddQuadCurveToPoint: + quadraticBezierTo(points[0], points[1]); + break; + case PathElementAddCurveToPoint: + cubicBezierTo(points[0], points[1], points[2]); + break; + case PathElementCloseSubpath: + closeSubpath(); + break; + } + + return finalizeAppendPathElement(); +} + +bool PathTraversalState::processPathElement(PathElementType type, const FloatPoint* points) +{ + if (m_success) + return true; + + if (m_isZeroVector) { + PathTraversalState traversalState(*this); + m_success = traversalState.appendPathElement(type, points); + m_normalAngle = traversalState.m_normalAngle; + return m_success; + } + + return appendPathElement(type, points); } } diff --git a/Source/WebCore/platform/graphics/PathTraversalState.h b/Source/WebCore/platform/graphics/PathTraversalState.h index 6a29dcd5e..bd3c0f1d2 100644 --- a/Source/WebCore/platform/graphics/PathTraversalState.h +++ b/Source/WebCore/platform/graphics/PathTraversalState.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org> + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,45 +28,60 @@ #define PathTraversalState_h #include "FloatPoint.h" +#include "Path.h" namespace WebCore { class PathTraversalState { public: - enum PathTraversalAction { - TraversalTotalLength, - TraversalPointAtLength, - TraversalSegmentAtLength, - TraversalNormalAngleAtLength + enum class Action { + TotalLength, + VectorAtLength, + SegmentAtLength, }; - PathTraversalState(PathTraversalAction); - - float closeSubpath(); - float moveTo(const FloatPoint&); - float lineTo(const FloatPoint&); - float quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd); - float cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd); - - void processSegment(); + PathTraversalState(Action, float desiredLength = 0); public: - PathTraversalAction m_action; - bool m_success; + bool processPathElement(PathElementType, const FloatPoint*); + bool processPathElement(const PathElement& element) { return processPathElement(element.type, element.points); } + + Action action() const { return m_action; } + void setAction(Action action) { m_action = action; } + float desiredLength() const { return m_desiredLength; } + void setDesiredLength(float desiredLength) { m_desiredLength = desiredLength; } + + // Traversing output -- should be read only + bool success() const { return m_success; } + float totalLength() const { return m_totalLength; } + FloatPoint current() const { return m_current; } + float normalAngle() const { return m_normalAngle; } + +private: + void closeSubpath(); + void moveTo(const FloatPoint&); + void lineTo(const FloatPoint&); + void quadraticBezierTo(const FloatPoint&, const FloatPoint&); + void cubicBezierTo(const FloatPoint&, const FloatPoint&, const FloatPoint&); + + bool finalizeAppendPathElement(); + bool appendPathElement(PathElementType, const FloatPoint*); + +private: + Action m_action; + bool m_success { false }; FloatPoint m_current; FloatPoint m_start; - FloatPoint m_control1; - FloatPoint m_control2; - float m_totalLength; - unsigned m_segmentIndex; - float m_desiredLength; + float m_totalLength { 0 }; + float m_desiredLength { 0 }; // For normal calculations FloatPoint m_previous; - float m_normalAngle; // degrees -}; + float m_normalAngle { 0 }; // degrees + bool m_isZeroVector { false }; +}; } #endif diff --git a/Source/WebCore/platform/graphics/PathUtilities.cpp b/Source/WebCore/platform/graphics/PathUtilities.cpp new file mode 100644 index 000000000..914a0a277 --- /dev/null +++ b/Source/WebCore/platform/graphics/PathUtilities.cpp @@ -0,0 +1,596 @@ +/* + * Copyright (C) 2014-2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "PathUtilities.h" + +#include "AffineTransform.h" +#include "BorderData.h" +#include "FloatPoint.h" +#include "FloatRect.h" +#include "FloatRoundedRect.h" +#include "GeometryUtilities.h" +#include <math.h> +#include <wtf/MathExtras.h> + +namespace WebCore { + +class FloatPointGraph { + WTF_MAKE_NONCOPYABLE(FloatPointGraph); +public: + FloatPointGraph() { } + + class Node : public FloatPoint { + WTF_MAKE_NONCOPYABLE(Node); + public: + Node(FloatPoint point) + : FloatPoint(point) + { } + + const Vector<Node*>& nextPoints() const { return m_nextPoints; } + void addNextPoint(Node* node) + { + if (!m_nextPoints.contains(node)) + m_nextPoints.append(node); + } + + bool isVisited() const { return m_visited; } + void visit() { m_visited = true; } + + void reset() { m_visited = false; m_nextPoints.clear(); } + + private: + Vector<Node*> m_nextPoints; + bool m_visited { false }; + }; + + typedef std::pair<Node*, Node*> Edge; + typedef Vector<Edge> Polygon; + + Node* findOrCreateNode(FloatPoint); + + void reset() + { + for (auto& node : m_allNodes) + node->reset(); + } + +private: + Vector<std::unique_ptr<Node>> m_allNodes; +}; + +FloatPointGraph::Node* FloatPointGraph::findOrCreateNode(FloatPoint point) +{ + for (auto& testNode : m_allNodes) { + if (areEssentiallyEqual(*testNode, point)) + return testNode.get(); + } + + m_allNodes.append(std::make_unique<FloatPointGraph::Node>(point)); + return m_allNodes.last().get(); +} + +static bool findLineSegmentIntersection(const FloatPointGraph::Edge& edgeA, const FloatPointGraph::Edge& edgeB, FloatPoint& intersectionPoint) +{ + if (!findIntersection(*edgeA.first, *edgeA.second, *edgeB.first, *edgeB.second, intersectionPoint)) + return false; + + FloatPoint edgeAVec(*edgeA.second - *edgeA.first); + FloatPoint edgeBVec(*edgeB.second - *edgeB.first); + + float dotA = edgeAVec.dot(toFloatPoint(intersectionPoint - *edgeA.first)); + if (dotA < 0 || dotA > edgeAVec.lengthSquared()) + return false; + + float dotB = edgeBVec.dot(toFloatPoint(intersectionPoint - *edgeB.first)); + if (dotB < 0 || dotB > edgeBVec.lengthSquared()) + return false; + + return true; +} + +static bool addIntersectionPoints(Vector<FloatPointGraph::Polygon>& polys, FloatPointGraph& graph) +{ + bool foundAnyIntersections = false; + + Vector<FloatPointGraph::Edge> allEdges; + for (auto& poly : polys) + allEdges.appendVector(poly); + + for (const FloatPointGraph::Edge& edgeA : allEdges) { + Vector<FloatPointGraph::Node*> intersectionPoints({edgeA.first, edgeA.second}); + + for (const FloatPointGraph::Edge& edgeB : allEdges) { + if (&edgeA == &edgeB) + continue; + + FloatPoint intersectionPoint; + if (!findLineSegmentIntersection(edgeA, edgeB, intersectionPoint)) + continue; + foundAnyIntersections = true; + intersectionPoints.append(graph.findOrCreateNode(intersectionPoint)); + } + + std::sort(intersectionPoints.begin(), intersectionPoints.end(), [edgeA](auto* a, auto* b) { + return FloatPoint(*edgeA.first - *b).lengthSquared() > FloatPoint(*edgeA.first - *a).lengthSquared(); + }); + + for (unsigned pointIndex = 1; pointIndex < intersectionPoints.size(); pointIndex++) + intersectionPoints[pointIndex - 1]->addNextPoint(intersectionPoints[pointIndex]); + } + + return foundAnyIntersections; +} + +static FloatPointGraph::Polygon walkGraphAndExtractPolygon(FloatPointGraph::Node* startNode) +{ + FloatPointGraph::Polygon outPoly; + + FloatPointGraph::Node* currentNode = startNode; + FloatPointGraph::Node* previousNode = startNode; + + do { + currentNode->visit(); + + FloatPoint currentVec(*previousNode - *currentNode); + currentVec.normalize(); + + // Walk the graph, at each node choosing the next non-visited + // point with the greatest internal angle. + FloatPointGraph::Node* nextNode = nullptr; + float nextNodeAngle = 0; + for (auto* potentialNextNode : currentNode->nextPoints()) { + if (potentialNextNode == currentNode) + continue; + + // If we can get back to the start, we should, ignoring the fact that we already visited it. + // Otherwise we'll head inside the shape. + if (potentialNextNode == startNode) { + nextNode = startNode; + break; + } + + if (potentialNextNode->isVisited()) + continue; + + FloatPoint nextVec(*potentialNextNode - *currentNode); + nextVec.normalize(); + + float angle = acos(nextVec.dot(currentVec)); + float crossZ = nextVec.x() * currentVec.y() - nextVec.y() * currentVec.x(); + + if (crossZ < 0) + angle = (2 * piFloat) - angle; + + if (!nextNode || angle > nextNodeAngle) { + nextNode = potentialNextNode; + nextNodeAngle = angle; + } + } + + // If we don't end up at a node adjacent to the starting node, + // something went wrong (there's probably a hole in the shape), + // so bail out. We'll use a bounding box instead. + if (!nextNode) + return FloatPointGraph::Polygon(); + + outPoly.append(std::make_pair(currentNode, nextNode)); + + previousNode = currentNode; + currentNode = nextNode; + } while (currentNode != startNode); + + return outPoly; +} + +static FloatPointGraph::Node* findUnvisitedPolygonStartPoint(Vector<FloatPointGraph::Polygon>& polys) +{ + for (auto& poly : polys) { + for (auto& edge : poly) { + if (edge.first->isVisited() || edge.second->isVisited()) + goto nextPolygon; + } + + // FIXME: We should make sure we find an outside edge to start with. + return poly[0].first; + nextPolygon: + continue; + } + return nullptr; +} + +static Vector<FloatPointGraph::Polygon> unitePolygons(Vector<FloatPointGraph::Polygon>& polys, FloatPointGraph& graph) +{ + graph.reset(); + + // There are no intersections, so the polygons are disjoint (we already removed wholly-contained rects in an earlier step). + if (!addIntersectionPoints(polys, graph)) + return polys; + + Vector<FloatPointGraph::Polygon> unitedPolygons; + + while (FloatPointGraph::Node* startNode = findUnvisitedPolygonStartPoint(polys)) { + FloatPointGraph::Polygon unitedPolygon = walkGraphAndExtractPolygon(startNode); + if (unitedPolygon.isEmpty()) + return Vector<FloatPointGraph::Polygon>(); + unitedPolygons.append(unitedPolygon); + } + + return unitedPolygons; +} + +static FloatPointGraph::Polygon edgesForRect(FloatRect rect, FloatPointGraph& graph) +{ + auto minMin = graph.findOrCreateNode(rect.minXMinYCorner()); + auto minMax = graph.findOrCreateNode(rect.minXMaxYCorner()); + auto maxMax = graph.findOrCreateNode(rect.maxXMaxYCorner()); + auto maxMin = graph.findOrCreateNode(rect.maxXMinYCorner()); + + return FloatPointGraph::Polygon({ + std::make_pair(minMin, maxMin), + std::make_pair(maxMin, maxMax), + std::make_pair(maxMax, minMax), + std::make_pair(minMax, minMin) + }); +} + +static Vector<FloatPointGraph::Polygon> polygonsForRect(const Vector<FloatRect>& rects, FloatPointGraph& graph) +{ + Vector<FloatRect> sortedRects = rects; + std::sort(sortedRects.begin(), sortedRects.end(), [](FloatRect a, FloatRect b) { return b.y() > a.y(); }); + + Vector<FloatPointGraph::Polygon> rectPolygons; + rectPolygons.reserveInitialCapacity(sortedRects.size()); + + for (auto& rect : sortedRects) { + bool isContained = false; + for (auto& otherRect : sortedRects) { + if (&rect == &otherRect) + continue; + if (otherRect.contains(rect)) { + isContained = true; + break; + } + } + + if (!isContained) + rectPolygons.uncheckedAppend(edgesForRect(rect, graph)); + } + return unitePolygons(rectPolygons, graph); +} + +Vector<Path> PathUtilities::pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius) +{ + Vector<Path> paths; + + if (rects.isEmpty()) + return paths; + + if (rects.size() > 20) { + Path path; + path.addRoundedRect(unionRect(rects), FloatSize(radius, radius)); + paths.append(path); + return paths; + } + + FloatPointGraph graph; + Vector<FloatPointGraph::Polygon> polys = polygonsForRect(rects, graph); + if (polys.isEmpty()) { + Path path; + path.addRoundedRect(unionRect(rects), FloatSize(radius, radius)); + paths.append(path); + return paths; + } + + for (auto& poly : polys) { + Path path; + for (unsigned i = 0; i < poly.size(); ++i) { + FloatPointGraph::Edge& toEdge = poly[i]; + // Connect the first edge to the last. + FloatPointGraph::Edge& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1]; + + FloatPoint fromEdgeVec = toFloatPoint(*fromEdge.second - *fromEdge.first); + FloatPoint toEdgeVec = toFloatPoint(*toEdge.second - *toEdge.first); + + // Clamp the radius to no more than half the length of either adjacent edge, + // because we want a smooth curve and don't want unequal radii. + float clampedRadius = std::min(radius, fabsf(fromEdgeVec.x() ? fromEdgeVec.x() : fromEdgeVec.y()) / 2); + clampedRadius = std::min(clampedRadius, fabsf(toEdgeVec.x() ? toEdgeVec.x() : toEdgeVec.y()) / 2); + + FloatPoint fromEdgeNorm = fromEdgeVec; + fromEdgeNorm.normalize(); + FloatPoint toEdgeNorm = toEdgeVec; + toEdgeNorm.normalize(); + + // Project the radius along the incoming and outgoing edge. + FloatSize fromOffset = clampedRadius * toFloatSize(fromEdgeNorm); + FloatSize toOffset = clampedRadius * toFloatSize(toEdgeNorm); + + if (!i) + path.moveTo(*fromEdge.second - fromOffset); + else + path.addLineTo(*fromEdge.second - fromOffset); + path.addArcTo(*fromEdge.second, *toEdge.first + toOffset, clampedRadius); + } + path.closeSubpath(); + paths.append(path); + } + return paths; +} + +Path PathUtilities::pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius) +{ + Vector<Path> paths = pathsWithShrinkWrappedRects(rects, radius); + + Path unionPath; + for (const auto& path : paths) + unionPath.addPath(path, AffineTransform()); + + return unionPath; +} + +static std::pair<FloatPoint, FloatPoint> startAndEndPointsForCorner(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const FloatSize& radius) +{ + FloatPoint startPoint; + FloatPoint endPoint; + + FloatSize fromEdgeVector = *fromEdge.second - *fromEdge.first; + FloatSize toEdgeVector = *toEdge.second - *toEdge.first; + + FloatPoint fromEdgeNorm = toFloatPoint(fromEdgeVector); + fromEdgeNorm.normalize(); + FloatSize fromOffset = FloatSize(radius.width() * fromEdgeNorm.x(), radius.height() * fromEdgeNorm.y()); + startPoint = *fromEdge.second - fromOffset; + + FloatPoint toEdgeNorm = toFloatPoint(toEdgeVector); + toEdgeNorm.normalize(); + FloatSize toOffset = FloatSize(radius.width() * toEdgeNorm.x(), radius.height() * toEdgeNorm.y()); + endPoint = *toEdge.first + toOffset; + return std::make_pair(startPoint, endPoint); +} + +enum class CornerType { TopLeft, TopRight, BottomRight, BottomLeft, Other }; +static CornerType cornerType(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge) +{ + auto fromEdgeVector = *fromEdge.second - *fromEdge.first; + auto toEdgeVector = *toEdge.second - *toEdge.first; + + if (fromEdgeVector.height() < 0 && toEdgeVector.width() > 0) + return CornerType::TopLeft; + if (fromEdgeVector.width() > 0 && toEdgeVector.height() > 0) + return CornerType::TopRight; + if (fromEdgeVector.height() > 0 && toEdgeVector.width() < 0) + return CornerType::BottomRight; + if (fromEdgeVector.width() < 0 && toEdgeVector.height() < 0) + return CornerType::BottomLeft; + return CornerType::Other; +} + +static CornerType cornerTypeForMultiline(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const Vector<FloatPoint>& corners) +{ + auto corner = cornerType(fromEdge, toEdge); + if (corner == CornerType::TopLeft && corners.at(0) == *fromEdge.second) + return corner; + if (corner == CornerType::TopRight && corners.at(1) == *fromEdge.second) + return corner; + if (corner == CornerType::BottomRight && corners.at(2) == *fromEdge.second) + return corner; + if (corner == CornerType::BottomLeft && corners.at(3) == *fromEdge.second) + return corner; + return CornerType::Other; +} + +static std::pair<FloatPoint, FloatPoint> controlPointsForBezierCurve(CornerType cornerType, const FloatPointGraph::Edge& fromEdge, + const FloatPointGraph::Edge& toEdge, const FloatSize& radius) +{ + FloatPoint cp1; + FloatPoint cp2; + switch (cornerType) { + case CornerType::TopLeft: { + cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() + radius.height() * Path::circleControlPoint()); + cp2 = FloatPoint(toEdge.first->x() + radius.width() * Path::circleControlPoint(), toEdge.first->y()); + break; + } + case CornerType::TopRight: { + cp1 = FloatPoint(fromEdge.second->x() - radius.width() * Path::circleControlPoint(), fromEdge.second->y()); + cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() + radius.height() * Path::circleControlPoint()); + break; + } + case CornerType::BottomRight: { + cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() - radius.height() * Path::circleControlPoint()); + cp2 = FloatPoint(toEdge.first->x() - radius.width() * Path::circleControlPoint(), toEdge.first->y()); + break; + } + case CornerType::BottomLeft: { + cp1 = FloatPoint(fromEdge.second->x() + radius.width() * Path::circleControlPoint(), fromEdge.second->y()); + cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() - radius.height() * Path::circleControlPoint()); + break; + } + case CornerType::Other: { + ASSERT_NOT_REACHED(); + break; + } + } + return std::make_pair(cp1, cp2); +} + +static FloatRoundedRect::Radii adjustedtRadiiForHuggingCurve(const FloatSize& topLeftRadius, const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, float outlineOffset) +{ + FloatRoundedRect::Radii radii; + // This adjusts the radius so that it follows the border curve even when offset is present. + auto adjustedRadius = [outlineOffset](const FloatSize& radius) + { + FloatSize expandSize; + if (radius.width() > outlineOffset) + expandSize.setWidth(std::min(outlineOffset, radius.width() - outlineOffset)); + if (radius.height() > outlineOffset) + expandSize.setHeight(std::min(outlineOffset, radius.height() - outlineOffset)); + FloatSize adjustedRadius = radius; + adjustedRadius.expand(expandSize.width(), expandSize.height()); + // Do not go to negative radius. + return adjustedRadius.expandedTo(FloatSize(0, 0)); + }; + + radii.setTopLeft(adjustedRadius(topLeftRadius)); + radii.setTopRight(adjustedRadius(topRightRadius)); + radii.setBottomRight(adjustedRadius(bottomRightRadius)); + radii.setBottomLeft(adjustedRadius(bottomLeftRadius)); + return radii; +} + +static std::optional<FloatRect> rectFromPolygon(const FloatPointGraph::Polygon& poly) +{ + if (poly.size() != 4) + return std::optional<FloatRect>(); + + std::optional<FloatPoint> topLeft; + std::optional<FloatPoint> bottomRight; + for (unsigned i = 0; i < poly.size(); ++i) { + const auto& toEdge = poly[i]; + const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1]; + auto corner = cornerType(fromEdge, toEdge); + if (corner == CornerType::TopLeft) { + ASSERT(!topLeft); + topLeft = *fromEdge.second; + } else if (corner == CornerType::BottomRight) { + ASSERT(!bottomRight); + bottomRight = *fromEdge.second; + } + } + if (!topLeft || !bottomRight) + return std::optional<FloatRect>(); + return FloatRect(topLeft.value(), bottomRight.value()); +} + +Path PathUtilities::pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>& rects, const BorderData& borderData, + float outlineOffset, TextDirection direction, WritingMode writingMode, float deviceScaleFactor) +{ + ASSERT(borderData.hasBorderRadius()); + + FloatSize topLeftRadius { borderData.topLeft().width.value(), borderData.topLeft().height.value() }; + FloatSize topRightRadius { borderData.topRight().width.value(), borderData.topRight().height.value() }; + FloatSize bottomRightRadius { borderData.bottomRight().width.value(), borderData.bottomRight().height.value() }; + FloatSize bottomLeftRadius { borderData.bottomLeft().width.value(), borderData.bottomLeft().height.value() }; + + auto roundedRect = [topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius, outlineOffset, deviceScaleFactor] (const FloatRect& rect) + { + auto radii = adjustedtRadiiForHuggingCurve(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, outlineOffset); + radii.scale(calcBorderRadiiConstraintScaleFor(rect, radii)); + RoundedRect roundedRect(LayoutRect(rect), + RoundedRect::Radii(LayoutSize(radii.topLeft()), LayoutSize(radii.topRight()), LayoutSize(radii.bottomLeft()), LayoutSize(radii.bottomRight()))); + Path path; + path.addRoundedRect(roundedRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor)); + return path; + }; + + if (rects.size() == 1) + return roundedRect(rects.at(0)); + + FloatPointGraph graph; + const auto polys = polygonsForRect(rects, graph); + // Fall back to corner painting with no radius for empty and disjoint rectangles. + if (!polys.size() || polys.size() > 1) + return Path(); + const auto& poly = polys.at(0); + // Fast path when poly has one rect only. + std::optional<FloatRect> rect = rectFromPolygon(poly); + if (rect) + return roundedRect(rect.value()); + + Path path; + // Multiline outline needs to match multiline border painting. Only first and last lines are getting rounded borders. + auto isLeftToRight = isLeftToRightDirection(direction); + auto firstLineRect = isLeftToRight ? rects.at(0) : rects.at(rects.size() - 1); + auto lastLineRect = isLeftToRight ? rects.at(rects.size() - 1) : rects.at(0); + // Adjust radius so that it matches the box border. + auto firstLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + auto lastLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + firstLineRadii.scale(calcBorderRadiiConstraintScaleFor(firstLineRect, firstLineRadii)); + lastLineRadii.scale(calcBorderRadiiConstraintScaleFor(lastLineRect, lastLineRadii)); + topLeftRadius = firstLineRadii.topLeft(); + bottomLeftRadius = firstLineRadii.bottomLeft(); + topRightRadius = lastLineRadii.topRight(); + bottomRightRadius = lastLineRadii.bottomRight(); + Vector<FloatPoint> corners; + // physical topLeft/topRight/bottomRight/bottomLeft + auto isHorizontal = isHorizontalWritingMode(writingMode); + corners.append(firstLineRect.minXMinYCorner()); + corners.append(isHorizontal ? lastLineRect.maxXMinYCorner() : firstLineRect.maxXMinYCorner()); + corners.append(lastLineRect.maxXMaxYCorner()); + corners.append(isHorizontal ? firstLineRect.minXMaxYCorner() : lastLineRect.minXMaxYCorner()); + + for (unsigned i = 0; i < poly.size(); ++i) { + auto moveOrAddLineTo = [i, &path] (const FloatPoint& startPoint) + { + if (!i) + path.moveTo(startPoint); + else + path.addLineTo(startPoint); + }; + const auto& toEdge = poly[i]; + const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1]; + FloatSize radius; + auto corner = cornerTypeForMultiline(fromEdge, toEdge, corners); + switch (corner) { + case CornerType::TopLeft: { + radius = topLeftRadius; + break; + } + case CornerType::TopRight: { + radius = topRightRadius; + break; + } + case CornerType::BottomRight: { + radius = bottomRightRadius; + break; + } + case CornerType::BottomLeft: { + radius = bottomLeftRadius; + break; + } + case CornerType::Other: { + // Do not apply border radius on corners that normal border painting skips. (multiline content) + moveOrAddLineTo(*fromEdge.second); + continue; + } + } + FloatPoint startPoint; + FloatPoint endPoint; + std::tie(startPoint, endPoint) = startAndEndPointsForCorner(fromEdge, toEdge, radius); + moveOrAddLineTo(startPoint); + + FloatPoint cp1; + FloatPoint cp2; + std::tie(cp1, cp2) = controlPointsForBezierCurve(corner, fromEdge, toEdge, radius); + path.addBezierCurveTo(cp1, cp2, endPoint); + } + path.closeSubpath(); + return path; +} + + +} diff --git a/Source/WebCore/platform/graphics/PathUtilities.h b/Source/WebCore/platform/graphics/PathUtilities.h new file mode 100644 index 000000000..500041a2d --- /dev/null +++ b/Source/WebCore/platform/graphics/PathUtilities.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014-2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PathUtilities_h +#define PathUtilities_h + +#include "Path.h" +#include "WritingMode.h" +#include <wtf/Vector.h> + +namespace WebCore { +class BorderData; + +class PathUtilities { +public: + WEBCORE_EXPORT static Path pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius); + WEBCORE_EXPORT static Vector<Path> pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius); + + static Path pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>&, const BorderData&, float outlineOffset, TextDirection, WritingMode, float deviceScaleFactor); +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/Pattern.cpp b/Source/WebCore/platform/graphics/Pattern.cpp index 3b32ebbb8..a9accc284 100644 --- a/Source/WebCore/platform/graphics/Pattern.cpp +++ b/Source/WebCore/platform/graphics/Pattern.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Eric Seidel <eric@webkit.org> * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,9 +31,9 @@ namespace WebCore { -PassRefPtr<Pattern> Pattern::create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY) +Ref<Pattern> Pattern::create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY) { - return adoptRef(new Pattern(tileImage, repeatX, repeatY)); + return adoptRef(*new Pattern(tileImage, repeatX, repeatY)); } Pattern::Pattern(PassRefPtr<Image> image, bool repeatX, bool repeatY) diff --git a/Source/WebCore/platform/graphics/Pattern.h b/Source/WebCore/platform/graphics/Pattern.h index 58508582c..256fa2d5f 100644 --- a/Source/WebCore/platform/graphics/Pattern.h +++ b/Source/WebCore/platform/graphics/Pattern.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Eric Seidel <eric@webkit.org> * Copyright (C) 2007-2008 Torch Mobile, Inc. * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -37,6 +37,9 @@ #if USE(CG) typedef struct CGPattern* CGPatternRef; typedef CGPatternRef PlatformPatternPtr; +#elif USE(DIRECT2D) +interface ID2D1BitmapBrush; +typedef ID2D1BitmapBrush* PlatformPatternPtr; #elif USE(CAIRO) #include <cairo.h> typedef cairo_pattern_t* PlatformPatternPtr; @@ -47,19 +50,24 @@ typedef void* PlatformPatternPtr; namespace WebCore { class AffineTransform; +class GraphicsContext; class Image; -class Pattern : public RefCounted<Pattern> { +class Pattern final : public RefCounted<Pattern> { public: - static PassRefPtr<Pattern> create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY); - virtual ~Pattern(); + static Ref<Pattern> create(PassRefPtr<Image> tileImage, bool repeatX, bool repeatY); + ~Pattern(); Image* tileImage() const { return m_tileImage.get(); } void platformDestroy(); - // Pattern space is an abstract space that maps to the default user space by the transformation 'userSpaceTransformation' + // Pattern space is an abstract space that maps to the default user space by the transformation 'userSpaceTransformation' +#if !USE(DIRECT2D) PlatformPatternPtr createPlatformPattern(const AffineTransform& userSpaceTransformation) const; +#else + PlatformPatternPtr createPlatformPattern(const GraphicsContext&, float alpha, const AffineTransform& userSpaceTransformation) const; +#endif void setPatternSpaceTransform(const AffineTransform& patternSpaceTransformation); const AffineTransform& getPatternSpaceTransform() { return m_patternSpaceTransformation; }; void setPlatformPatternSpaceTransform(); diff --git a/Source/WebCore/platform/graphics/PlatformDisplay.cpp b/Source/WebCore/platform/graphics/PlatformDisplay.cpp new file mode 100644 index 000000000..e387bf9e2 --- /dev/null +++ b/Source/WebCore/platform/graphics/PlatformDisplay.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformDisplay.h" + +#include "GLContext.h" +#include <cstdlib> +#include <mutex> + +#if PLATFORM(X11) +#include "PlatformDisplayX11.h" +#endif + +#if PLATFORM(WAYLAND) +#include "PlatformDisplayWayland.h" +#endif + +#if PLATFORM(WIN) +#include "PlatformDisplayWin.h" +#endif + +#if PLATFORM(GTK) +#include <gdk/gdk.h> +#endif + +#if PLATFORM(GTK) && PLATFORM(X11) +#include <gdk/gdkx.h> +#endif + +#if PLATFORM(GTK) && PLATFORM(WAYLAND) && !defined(GTK_API_VERSION_2) +#include <gdk/gdkwayland.h> +#endif + +#if USE(EGL) +#include <EGL/egl.h> +#include <wtf/HashSet.h> +#include <wtf/NeverDestroyed.h> +#endif + +namespace WebCore { + +std::unique_ptr<PlatformDisplay> PlatformDisplay::createPlatformDisplay() +{ +#if PLATFORM(GTK) +#if defined(GTK_API_VERSION_2) + return std::make_unique<PlatformDisplayX11>(GDK_DISPLAY_XDISPLAY(gdk_display_get_default())); +#else + GdkDisplay* display = gdk_display_manager_get_default_display(gdk_display_manager_get()); +#if PLATFORM(X11) + if (GDK_IS_X11_DISPLAY(display)) + return std::make_unique<PlatformDisplayX11>(GDK_DISPLAY_XDISPLAY(display)); +#endif +#if PLATFORM(WAYLAND) + if (GDK_IS_WAYLAND_DISPLAY(display)) + return std::make_unique<PlatformDisplayWayland>(gdk_wayland_display_get_wl_display(display)); +#endif +#endif +#elif PLATFORM(WIN) + return std::make_unique<PlatformDisplayWin>(); +#endif + +#if PLATFORM(WAYLAND) + if (auto platformDisplay = PlatformDisplayWayland::create()) + return platformDisplay; +#endif + +#if PLATFORM(X11) + if (auto platformDisplay = PlatformDisplayX11::create()) + return platformDisplay; +#endif + + // If at this point we still don't have a display, just create a fake display with no native. +#if PLATFORM(WAYLAND) + return std::make_unique<PlatformDisplayWayland>(nullptr); +#endif +#if PLATFORM(X11) + return std::make_unique<PlatformDisplayX11>(nullptr); +#endif + + ASSERT_NOT_REACHED(); + return nullptr; +} + +PlatformDisplay& PlatformDisplay::sharedDisplay() +{ + static std::once_flag onceFlag; +#if COMPILER(CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + static std::unique_ptr<PlatformDisplay> display; +#if COMPILER(CLANG) +#pragma clang diagnostic pop +#endif + std::call_once(onceFlag, []{ + display = createPlatformDisplay(); + }); + return *display; +} + +static PlatformDisplay* s_sharedDisplayForCompositing; + +PlatformDisplay& PlatformDisplay::sharedDisplayForCompositing() +{ + return s_sharedDisplayForCompositing ? *s_sharedDisplayForCompositing : sharedDisplay(); +} + +void PlatformDisplay::setSharedDisplayForCompositing(PlatformDisplay& display) +{ + s_sharedDisplayForCompositing = &display; +} + +PlatformDisplay::PlatformDisplay(NativeDisplayOwned displayOwned) + : m_nativeDisplayOwned(displayOwned) +#if USE(EGL) + , m_eglDisplay(EGL_NO_DISPLAY) +#endif +{ +} + +PlatformDisplay::~PlatformDisplay() +{ +#if USE(EGL) + ASSERT(m_eglDisplay == EGL_NO_DISPLAY); +#endif +} + +#if USE(EGL) || USE(GLX) +GLContext* PlatformDisplay::sharingGLContext() +{ + if (!m_sharingGLContext) + m_sharingGLContext = GLContext::createSharingContext(*this); + return m_sharingGLContext.get(); +} +#endif + +#if USE(EGL) +static HashSet<PlatformDisplay*>& eglDisplays() +{ + static NeverDestroyed<HashSet<PlatformDisplay*>> displays; + return displays; +} + +EGLDisplay PlatformDisplay::eglDisplay() const +{ + if (!m_eglDisplayInitialized) + const_cast<PlatformDisplay*>(this)->initializeEGLDisplay(); + return m_eglDisplay; +} + +bool PlatformDisplay::eglCheckVersion(int major, int minor) const +{ + if (!m_eglDisplayInitialized) + const_cast<PlatformDisplay*>(this)->initializeEGLDisplay(); + + return (m_eglMajorVersion > major) || ((m_eglMajorVersion == major) && (m_eglMinorVersion >= minor)); +} + +void PlatformDisplay::initializeEGLDisplay() +{ + m_eglDisplayInitialized = true; + + if (m_eglDisplay == EGL_NO_DISPLAY) { + // EGL is optionally soft linked on Windows. +#if PLATFORM(WIN) + auto eglGetDisplay = eglGetDisplayPtr(); + if (!eglGetDisplay) + return; +#endif + m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (m_eglDisplay == EGL_NO_DISPLAY) + return; + } + + EGLint majorVersion, minorVersion; + if (eglInitialize(m_eglDisplay, &majorVersion, &minorVersion) == EGL_FALSE) { + LOG_ERROR("EGLDisplay Initialization failed."); + terminateEGLDisplay(); + return; + } + + m_eglMajorVersion = majorVersion; + m_eglMinorVersion = minorVersion; + + eglDisplays().add(this); + + static bool eglAtexitHandlerInitialized = false; + if (!eglAtexitHandlerInitialized) { + // EGL registers atexit handlers to cleanup its global display list. + // Since the global PlatformDisplay instance is created before, + // when the PlatformDisplay destructor is called, EGL has already removed the + // display from the list, causing eglTerminate() to crash. So, here we register + // our own atexit handler, after EGL has been initialized and after the global + // instance has been created to ensure we call eglTerminate() before the other + // EGL atexit handlers and the PlatformDisplay destructor. + // See https://bugs.webkit.org/show_bug.cgi?id=157973. + eglAtexitHandlerInitialized = true; + std::atexit([] { + while (!eglDisplays().isEmpty()) { + auto* display = eglDisplays().takeAny(); + display->terminateEGLDisplay(); + } + }); + } +} + +void PlatformDisplay::terminateEGLDisplay() +{ + m_sharingGLContext = nullptr; + ASSERT(m_eglDisplayInitialized); + if (m_eglDisplay == EGL_NO_DISPLAY) + return; + eglTerminate(m_eglDisplay); + m_eglDisplay = EGL_NO_DISPLAY; +} +#endif // USE(EGL) + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/PlatformDisplay.h b/Source/WebCore/platform/graphics/PlatformDisplay.h new file mode 100644 index 000000000..680b43a69 --- /dev/null +++ b/Source/WebCore/platform/graphics/PlatformDisplay.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PlatformDisplay_h +#define PlatformDisplay_h + +#include <wtf/Noncopyable.h> +#include <wtf/TypeCasts.h> + +#if USE(EGL) +typedef void *EGLDisplay; +#endif + +namespace WebCore { + +class GLContext; + +class PlatformDisplay { + WTF_MAKE_NONCOPYABLE(PlatformDisplay); WTF_MAKE_FAST_ALLOCATED; +public: + static PlatformDisplay& sharedDisplay(); + static PlatformDisplay& sharedDisplayForCompositing(); + virtual ~PlatformDisplay(); + + enum class Type { +#if PLATFORM(X11) + X11, +#endif +#if PLATFORM(WAYLAND) + Wayland, +#endif +#if PLATFORM(WIN) + Windows, +#endif + }; + + virtual Type type() const = 0; + +#if USE(EGL) || USE(GLX) + GLContext* sharingGLContext(); +#endif + +#if USE(EGL) + EGLDisplay eglDisplay() const; + bool eglCheckVersion(int major, int minor) const; +#endif + +protected: + enum class NativeDisplayOwned { No, Yes }; + explicit PlatformDisplay(NativeDisplayOwned = NativeDisplayOwned::No); + + static void setSharedDisplayForCompositing(PlatformDisplay&); + + NativeDisplayOwned m_nativeDisplayOwned { NativeDisplayOwned::No }; + +#if USE(EGL) + virtual void initializeEGLDisplay(); + + EGLDisplay m_eglDisplay; +#endif + +#if USE(EGL) || USE(GLX) + std::unique_ptr<GLContext> m_sharingGLContext; +#endif + +private: + static std::unique_ptr<PlatformDisplay> createPlatformDisplay(); + +#if USE(EGL) + void terminateEGLDisplay(); + + bool m_eglDisplayInitialized { false }; + int m_eglMajorVersion { 0 }; + int m_eglMinorVersion { 0 }; +#endif +}; + +} // namespace WebCore + +#define SPECIALIZE_TYPE_TRAITS_PLATFORM_DISPLAY(ToClassName, DisplayType) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \ + static bool isType(const WebCore::PlatformDisplay& display) { return display.type() == WebCore::PlatformDisplay::Type::DisplayType; } \ +SPECIALIZE_TYPE_TRAITS_END() + +#endif // PltformDisplay_h diff --git a/Source/WebCore/platform/graphics/PlatformLayer.h b/Source/WebCore/platform/graphics/PlatformLayer.h index a18f9b9c3..29f9decb2 100644 --- a/Source/WebCore/platform/graphics/PlatformLayer.h +++ b/Source/WebCore/platform/graphics/PlatformLayer.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,36 +26,23 @@ #ifndef PlatformLayer_h #define PlatformLayer_h -#if USE(ACCELERATED_COMPOSITING) - -#if PLATFORM(MAC) +#if PLATFORM(COCOA) OBJC_CLASS CALayer; typedef CALayer PlatformLayer; #elif PLATFORM(WIN) && USE(CA) typedef struct _CACFLayer PlatformLayer; -#elif PLATFORM(WIN) && USE(TEXTURE_MAPPER) +#elif USE(COORDINATED_GRAPHICS_THREADED) namespace WebCore { -class TextureMapperPlatformLayer; -typedef TextureMapperPlatformLayer PlatformLayer; +class TextureMapperPlatformLayerProxyProvider; +typedef TextureMapperPlatformLayerProxyProvider PlatformLayer; }; -#elif PLATFORM(GTK) -#if USE(TEXTURE_MAPPER_GL) +#elif USE(TEXTURE_MAPPER) namespace WebCore { class TextureMapperPlatformLayer; typedef TextureMapperPlatformLayer PlatformLayer; }; -#endif -#elif PLATFORM(EFL) -#if USE(TEXTURE_MAPPER) -namespace WebCore { -class TextureMapperPlatformLayer; -typedef TextureMapperPlatformLayer PlatformLayer; -}; -#endif // USE(TEXTURE_MAPPER) #else typedef void* PlatformLayer; #endif -#endif // USE(ACCELERATED_COMPOSITING) - #endif // PlatformLayer_h diff --git a/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h b/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h new file mode 100644 index 000000000..bc0631626 --- /dev/null +++ b/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(VIDEO) + +#include <wtf/Noncopyable.h> +#include <wtf/RefCounted.h> +#include <wtf/ThreadSafeRefCounted.h> + +namespace WebCore { + +class PlatformMediaResource; +class ResourceError; +class ResourceRequest; +class ResourceResponse; + +class PlatformMediaResourceClient { +public: + virtual ~PlatformMediaResourceClient() { } + + virtual void responseReceived(PlatformMediaResource&, const ResourceResponse&) { } + virtual void redirectReceived(PlatformMediaResource&, ResourceRequest&, const ResourceResponse&) { } + virtual bool shouldCacheResponse(PlatformMediaResource&, const ResourceResponse&) { return true; } + virtual void dataSent(PlatformMediaResource&, unsigned long long, unsigned long long) { } + virtual void dataReceived(PlatformMediaResource&, const char*, int) { } + virtual void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) { } + virtual void loadFailed(PlatformMediaResource&, const ResourceError&) { } + virtual void loadFinished(PlatformMediaResource&) { } +#if USE(SOUP) + virtual char* getOrCreateReadBuffer(PlatformMediaResource&, size_t /*requestedSize*/, size_t& /*actualSize*/) { return nullptr; }; +#endif +}; + +class PlatformMediaResourceLoader : public ThreadSafeRefCounted<PlatformMediaResourceLoader> { + WTF_MAKE_NONCOPYABLE(PlatformMediaResourceLoader); WTF_MAKE_FAST_ALLOCATED; +public: + enum LoadOption { + BufferData = 1 << 0, + DisallowCaching = 1 << 1, + }; + typedef unsigned LoadOptions; + + virtual ~PlatformMediaResourceLoader() { } + + virtual RefPtr<PlatformMediaResource> requestResource(ResourceRequest&&, LoadOptions) = 0; + +protected: + PlatformMediaResourceLoader() { } +}; + +class PlatformMediaResource : public RefCounted<PlatformMediaResource> { + WTF_MAKE_NONCOPYABLE(PlatformMediaResource); WTF_MAKE_FAST_ALLOCATED; +public: + PlatformMediaResource() { } + virtual ~PlatformMediaResource() { } + virtual void stop() { } + virtual void setDefersLoading(bool) { } + virtual bool didPassAccessControlCheck() const { return false; } + + void setClient(std::unique_ptr<PlatformMediaResourceClient>&& client) { m_client = WTFMove(client); } + +protected: + std::unique_ptr<PlatformMediaResourceClient> m_client; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/PlatformTextTrack.h b/Source/WebCore/platform/graphics/PlatformTextTrack.h new file mode 100644 index 000000000..20be3d17a --- /dev/null +++ b/Source/WebCore/platform/graphics/PlatformTextTrack.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PlatformTextTrack_h +#define PlatformTextTrack_h + +#if ENABLE(AVF_CAPTIONS) + +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class TextTrack; +class InbandTextTrackPrivate; + +class PlatformTextTrackClient { +public: + virtual ~PlatformTextTrackClient() { } + + virtual TextTrack* publicTrack() = 0; + virtual InbandTextTrackPrivate* privateTrack() { return 0; } +}; + +class PlatformTextTrack : public RefCounted<PlatformTextTrack> { +public: + enum TrackKind { + Subtitle = 0, + Caption = 1, + Description = 2, + Chapter = 3, + MetaData = 4, + Forced = 5, + }; + enum TrackType { + InBand = 0, + OutOfBand = 1, + Script = 2 + }; + enum TrackMode { + Disabled, + Hidden, + Showing + }; + + static PassRefPtr<PlatformTextTrack> create(PlatformTextTrackClient* client, const String& label, const String& language, TrackMode mode, TrackKind kind, TrackType type, int uniqueId) + { + return adoptRef(new PlatformTextTrack(client, label, language, String(), mode, kind, type, uniqueId, false)); + } + + static PassRefPtr<PlatformTextTrack> createOutOfBand(const String& label, const String& language, const String& url, TrackMode mode, TrackKind kind, int uniqueId, bool isDefault) + { + return adoptRef(new PlatformTextTrack(nullptr, label, language, url, mode, kind, OutOfBand, uniqueId, isDefault)); + } + + virtual ~PlatformTextTrack() { } + + TrackType type() const { return m_type; } + TrackKind kind() const { return m_kind; } + TrackMode mode() const { return m_mode; } + const String& label() const { return m_label; } + const String& language() const { return m_language; } + const String& url() const { return m_url; } + PlatformTextTrackClient* client() const { return m_client; } + int uniqueId() const { return m_uniqueId; } + bool isDefault() const { return m_isDefault; } + + static PlatformTextTrack* captionMenuOffItem() + { + static PlatformTextTrack* off = PlatformTextTrack::create(nullptr, "off menu item", "", Showing, Subtitle, InBand, 0).leakRef(); + return off; + } + + static PlatformTextTrack* captionMenuAutomaticItem() + { + static PlatformTextTrack* automatic = PlatformTextTrack::create(nullptr, "automatic menu item", "", Showing, Subtitle, InBand, 0).leakRef(); + return automatic; + } + +protected: + PlatformTextTrack(PlatformTextTrackClient* client, const String& label, const String& language, const String& url, TrackMode mode, TrackKind kind, TrackType type, int uniqueId, bool isDefault) + : m_label(label) + , m_language(language) + , m_url(url) + , m_mode(mode) + , m_kind(kind) + , m_type(type) + , m_client(client) + , m_uniqueId(uniqueId) + , m_isDefault(isDefault) + { + } + + String m_label; + String m_language; + String m_url; + TrackMode m_mode; + TrackKind m_kind; + TrackType m_type; + PlatformTextTrackClient* m_client; + int m_uniqueId; + bool m_isDefault; +}; + +} + +#endif + +#endif // PlatformTextTrack_h diff --git a/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp b/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp new file mode 100644 index 000000000..c7b978400 --- /dev/null +++ b/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformTimeRanges.h" + +#include <math.h> +#include <wtf/PrintStream.h> + +namespace WebCore { + +PlatformTimeRanges::PlatformTimeRanges(const MediaTime& start, const MediaTime& end) +{ + add(start, end); +} + +void PlatformTimeRanges::invert() +{ + PlatformTimeRanges inverted; + MediaTime posInf = MediaTime::positiveInfiniteTime(); + MediaTime negInf = MediaTime::negativeInfiniteTime(); + + if (!m_ranges.size()) + inverted.add(negInf, posInf); + else { + MediaTime start = m_ranges.first().m_start; + if (start != negInf) + inverted.add(negInf, start); + + for (size_t index = 0; index + 1 < m_ranges.size(); ++index) + inverted.add(m_ranges[index].m_end, m_ranges[index + 1].m_start); + + MediaTime end = m_ranges.last().m_end; + if (end != posInf) + inverted.add(end, posInf); + } + + m_ranges.swap(inverted.m_ranges); +} + +void PlatformTimeRanges::intersectWith(const PlatformTimeRanges& other) +{ + PlatformTimeRanges invertedOther(other); + + invertedOther.invert(); + invert(); + unionWith(invertedOther); + invert(); +} + +void PlatformTimeRanges::unionWith(const PlatformTimeRanges& other) +{ + PlatformTimeRanges unioned(*this); + + for (size_t index = 0; index < other.m_ranges.size(); ++index) { + const Range& range = other.m_ranges[index]; + unioned.add(range.m_start, range.m_end); + } + + m_ranges.swap(unioned.m_ranges); +} + +MediaTime PlatformTimeRanges::start(unsigned index) const +{ + bool ignoredValid; + return start(index, ignoredValid); +} + +MediaTime PlatformTimeRanges::start(unsigned index, bool& valid) const +{ + if (index >= length()) { + valid = false; + return MediaTime::zeroTime(); + } + + valid = true; + return m_ranges[index].m_start; +} + +MediaTime PlatformTimeRanges::end(unsigned index) const +{ + bool ignoredValid; + return end(index, ignoredValid); +} + +MediaTime PlatformTimeRanges::end(unsigned index, bool& valid) const +{ + if (index >= length()) { + valid = false; + return MediaTime::zeroTime(); + } + + valid = true; + return m_ranges[index].m_end; +} + +MediaTime PlatformTimeRanges::duration(unsigned index) const +{ + if (index >= length()) + return MediaTime::invalidTime(); + + return m_ranges[index].m_end - m_ranges[index].m_start; +} + +MediaTime PlatformTimeRanges::maximumBufferedTime() const +{ + if (!length()) + return MediaTime::invalidTime(); + + return m_ranges[length() - 1].m_end; +} + +void PlatformTimeRanges::add(const MediaTime& start, const MediaTime& end) +{ + ASSERT(start <= end); + unsigned overlappingArcIndex; + Range addedRange(start, end); + + // For each present range check if we need to: + // - merge with the added range, in case we are overlapping or contiguous + // - Need to insert in place, we we are completely, not overlapping and not contiguous + // in between two ranges. + // + // TODO: Given that we assume that ranges are correctly ordered, this could be optimized. + + for (overlappingArcIndex = 0; overlappingArcIndex < m_ranges.size(); overlappingArcIndex++) { + if (addedRange.isOverlappingRange(m_ranges[overlappingArcIndex]) || addedRange.isContiguousWithRange(m_ranges[overlappingArcIndex])) { + // We need to merge the addedRange and that range. + addedRange = addedRange.unionWithOverlappingOrContiguousRange(m_ranges[overlappingArcIndex]); + m_ranges.remove(overlappingArcIndex); + overlappingArcIndex--; + } else { + // Check the case for which there is no more to do + if (!overlappingArcIndex) { + if (addedRange.isBeforeRange(m_ranges[0])) { + // First index, and we are completely before that range (and not contiguous, nor overlapping). + // We just need to be inserted here. + break; + } + } else { + if (m_ranges[overlappingArcIndex - 1].isBeforeRange(addedRange) && addedRange.isBeforeRange(m_ranges[overlappingArcIndex])) { + // We are exactly after the current previous range, and before the current range, while + // not overlapping with none of them. Insert here. + break; + } + } + } + } + + // Now that we are sure we don't overlap with any range, just add it. + m_ranges.insert(overlappingArcIndex, addedRange); +} + +bool PlatformTimeRanges::contain(const MediaTime& time) const +{ + return find(time) != notFound; +} + +size_t PlatformTimeRanges::find(const MediaTime& time) const +{ + bool ignoreInvalid; + for (unsigned n = 0; n < length(); n++) { + if (time >= start(n, ignoreInvalid) && time <= end(n, ignoreInvalid)) + return n; + } + return notFound; +} + +MediaTime PlatformTimeRanges::nearest(const MediaTime& time) const +{ + MediaTime closestDelta = MediaTime::positiveInfiniteTime(); + MediaTime closestTime = MediaTime::zeroTime(); + unsigned count = length(); + if (!count) + return MediaTime::invalidTime(); + + bool ignoreInvalid; + + for (unsigned ndx = 0; ndx < count; ndx++) { + MediaTime startTime = start(ndx, ignoreInvalid); + MediaTime endTime = end(ndx, ignoreInvalid); + if (time >= startTime && time <= endTime) + return time; + + MediaTime startTimeDelta = abs(startTime - time); + if (startTimeDelta < closestDelta) { + closestTime = startTime; + closestDelta = startTimeDelta; + } + + MediaTime endTimeDelta = abs(endTime - time); + if (endTimeDelta < closestDelta) { + closestTime = endTime; + closestDelta = endTimeDelta; + } + } + return closestTime; +} + +MediaTime PlatformTimeRanges::totalDuration() const +{ + MediaTime total = MediaTime::zeroTime(); + + for (unsigned n = 0; n < length(); n++) + total += abs(end(n) - start(n)); + return total; +} + +void PlatformTimeRanges::dump(PrintStream& out) const +{ + if (!length()) + return; + + for (size_t i = 0; i < length(); ++i) + out.print("[", start(i), "..", end(i), "] "); +} + +} diff --git a/Source/WebCore/platform/graphics/PlatformTimeRanges.h b/Source/WebCore/platform/graphics/PlatformTimeRanges.h new file mode 100644 index 000000000..81de8c557 --- /dev/null +++ b/Source/WebCore/platform/graphics/PlatformTimeRanges.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PlatformTimeRanges_h +#define PlatformTimeRanges_h + +#include <algorithm> +#include <wtf/MediaTime.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WTF { +class PrintStream; +} + +namespace WebCore { + +class PlatformTimeRanges { +public: + explicit PlatformTimeRanges() { } + PlatformTimeRanges(const MediaTime& start, const MediaTime& end); + + WEBCORE_EXPORT MediaTime start(unsigned index) const; + MediaTime start(unsigned index, bool& valid) const; + WEBCORE_EXPORT MediaTime end(unsigned index) const; + MediaTime end(unsigned index, bool& valid) const; + MediaTime duration(unsigned index) const; + MediaTime maximumBufferedTime() const; + + void invert(); + void intersectWith(const PlatformTimeRanges&); + void unionWith(const PlatformTimeRanges&); + + unsigned length() const { return m_ranges.size(); } + + void add(const MediaTime& start, const MediaTime& end); + + bool contain(const MediaTime&) const; + + size_t find(const MediaTime&) const; + + MediaTime nearest(const MediaTime&) const; + + MediaTime totalDuration() const; + + void dump(WTF::PrintStream&) const; + +private: + // We consider all the Ranges to be semi-bounded as follow: [start, end[ + struct Range { + Range() { } + Range(const MediaTime& start, const MediaTime& end) + : m_start(start) + , m_end(end) + { + } + + MediaTime m_start; + MediaTime m_end; + + inline bool isPointInRange(const MediaTime& point) const + { + return m_start <= point && point < m_end; + } + + inline bool isOverlappingRange(const Range& range) const + { + return isPointInRange(range.m_start) || isPointInRange(range.m_end) || range.isPointInRange(m_start); + } + + inline bool isContiguousWithRange(const Range& range) const + { + return range.m_start == m_end || range.m_end == m_start; + } + + inline Range unionWithOverlappingOrContiguousRange(const Range& range) const + { + Range ret; + + ret.m_start = std::min(m_start, range.m_start); + ret.m_end = std::max(m_end, range.m_end); + + return ret; + } + + inline bool isBeforeRange(const Range& range) const + { + return range.m_start >= m_end; + } + }; + + Vector<Range> m_ranges; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/Region.cpp b/Source/WebCore/platform/graphics/Region.cpp index 465a37cde..ce555b721 100644 --- a/Source/WebCore/platform/graphics/Region.cpp +++ b/Source/WebCore/platform/graphics/Region.cpp @@ -319,10 +319,10 @@ Region::Shape::SegmentIterator Region::Shape::segments_end(SpanIterator it) cons #ifndef NDEBUG void Region::Shape::dump() const { - for (Shape::SpanIterator span = spans_begin(), end = spans_end(); span != end; ++span) { + for (auto span = spans_begin(), end = spans_end(); span != end; ++span) { printf("%6d: (", span->y); - for (Shape::SegmentIterator segment = segments_begin(span), end = segments_end(span); segment != end; ++segment) + for (auto segment = segments_begin(span), end = segments_end(span); segment != end; ++segment) printf("%d ", *segment); printf(")\n"); } @@ -331,6 +331,27 @@ void Region::Shape::dump() const } #endif +bool Region::Shape::isValid() const +{ + for (auto span = spans_begin(), end = spans_end(); span != end && span + 1 != end; ++span) { + int y = span->y; + int height = (span + 1)->y - y; + + if (height < 0) + return false; + + for (auto segment = segments_begin(span), end = segments_end(span); segment != end && segment + 1 != end; segment += 2) { + int x = *segment; + int width = *(segment + 1) - x; + + if (width < 0) + return false; + } + } + + return true; +} + IntRect Region::Shape::bounds() const { if (isEmpty()) @@ -550,6 +571,11 @@ void Region::dump() const } #endif +void Region::updateBoundsFromShape() +{ + m_bounds = m_shape.bounds(); +} + void Region::intersect(const Region& region) { if (m_bounds.isEmpty()) diff --git a/Source/WebCore/platform/graphics/Region.h b/Source/WebCore/platform/graphics/Region.h index 565b49b04..d72ec4b26 100644 --- a/Source/WebCore/platform/graphics/Region.h +++ b/Source/WebCore/platform/graphics/Region.h @@ -32,31 +32,33 @@ namespace WebCore { class Region { + WTF_MAKE_FAST_ALLOCATED; + public: - Region(); - Region(const IntRect&); + WEBCORE_EXPORT Region(); + WEBCORE_EXPORT Region(const IntRect&); IntRect bounds() const { return m_bounds; } bool isEmpty() const { return m_bounds.isEmpty(); } bool isRect() const { return m_shape.isRect(); } - Vector<IntRect> rects() const; + WEBCORE_EXPORT Vector<IntRect> rects() const; - void unite(const Region&); - void intersect(const Region&); - void subtract(const Region&); + WEBCORE_EXPORT void unite(const Region&); + WEBCORE_EXPORT void intersect(const Region&); + WEBCORE_EXPORT void subtract(const Region&); - void translate(const IntSize&); + WEBCORE_EXPORT void translate(const IntSize&); // Returns true if the query region is a subset of this region. - bool contains(const Region&) const; + WEBCORE_EXPORT bool contains(const Region&) const; bool contains(const IntPoint&) const; // Returns true if the query region intersects any part of this region. bool intersects(const Region&) const; - unsigned totalArea() const; + WEBCORE_EXPORT unsigned totalArea() const; unsigned gridSize() const { return m_shape.gridSize(); } @@ -64,10 +66,20 @@ public: void dump() const; #endif -private: + bool isValid() const { return m_shape.isValid(); } + + // This is internal to Region, but exposed just for encoding. + // FIXME: figure out a better way to encode WebCore classes. struct Span { + Span() + : y(0) + , segmentIndex(0) + { + } + Span(int y, size_t segmentIndex) - : y(y), segmentIndex(segmentIndex) + : y(y) + , segmentIndex(segmentIndex) { } @@ -75,6 +87,16 @@ private: size_t segmentIndex; }; + // For encoding/decoding only. + const Vector<int, 32>& shapeSegments() const { return m_shape.segments(); } + const Vector<Span, 16>& shapeSpans() const { return m_shape.spans(); } + + void setShapeSegments(const Vector<int>& segments) { m_shape.setSegments(segments); } + void setShapeSpans(const Vector<Span>& spans) { m_shape.setSpans(spans); } + WEBCORE_EXPORT void updateBoundsFromShape(); + +private: + class Shape { public: Shape(); @@ -97,7 +119,7 @@ private: static Shape intersectShapes(const Shape& shape1, const Shape& shape2); static Shape subtractShapes(const Shape& shape1, const Shape& shape2); - void translate(const IntSize&); + WEBCORE_EXPORT void translate(const IntSize&); void swap(Shape&); struct CompareContainsOperation; @@ -105,6 +127,15 @@ private: template<typename CompareOperation> static bool compareShapes(const Shape& shape1, const Shape& shape2); + + WEBCORE_EXPORT bool isValid() const; + + // For encoding/decoding only. + const Vector<int, 32>& segments() const { return m_segments; } + const Vector<Span, 16>& spans() const { return m_spans; } + + void setSegments(const Vector<int>& segments) { m_segments = segments; } + void setSpans(const Vector<Span>& spans) { m_spans = spans; } #ifndef NDEBUG void dump() const; @@ -167,6 +198,10 @@ inline bool operator==(const Region& a, const Region& b) { return a.m_bounds == b.m_bounds && a.m_shape == b.m_shape; } +inline bool operator!=(const Region& a, const Region& b) +{ + return !(a == b); +} inline bool operator==(const Region::Shape& a, const Region::Shape& b) { @@ -177,6 +212,10 @@ inline bool operator==(const Region::Span& a, const Region::Span& b) { return a.y == b.y && a.segmentIndex == b.segmentIndex; } +inline bool operator!=(const Region::Span& a, const Region::Span& b) +{ + return !(a == b); +} } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/RoundedRect.cpp b/Source/WebCore/platform/graphics/RoundedRect.cpp index 8f2f1cf91..60a451f9e 100644 --- a/Source/WebCore/platform/graphics/RoundedRect.cpp +++ b/Source/WebCore/platform/graphics/RoundedRect.cpp @@ -28,6 +28,11 @@ #include "config.h" #include "RoundedRect.h" +#include "FloatRoundedRect.h" +#include "GeometryUtilities.h" +#include "LayoutRect.h" +#include "LayoutUnit.h" + #include <algorithm> namespace WebCore { @@ -45,42 +50,41 @@ void RoundedRect::Radii::scale(float factor) // If either radius on a corner becomes zero, reset both radii on that corner. m_topLeft.scale(factor); if (!m_topLeft.width() || !m_topLeft.height()) - m_topLeft = IntSize(); + m_topLeft = LayoutSize(); m_topRight.scale(factor); if (!m_topRight.width() || !m_topRight.height()) - m_topRight = IntSize(); + m_topRight = LayoutSize(); m_bottomLeft.scale(factor); if (!m_bottomLeft.width() || !m_bottomLeft.height()) - m_bottomLeft = IntSize(); + m_bottomLeft = LayoutSize(); m_bottomRight.scale(factor); if (!m_bottomRight.width() || !m_bottomRight.height()) - m_bottomRight = IntSize(); - + m_bottomRight = LayoutSize(); } -void RoundedRect::Radii::expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth) +void RoundedRect::Radii::expand(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth) { if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { - m_topLeft.setWidth(std::max<int>(0, m_topLeft.width() + leftWidth)); - m_topLeft.setHeight(std::max<int>(0, m_topLeft.height() + topWidth)); + m_topLeft.setWidth(std::max<LayoutUnit>(0, m_topLeft.width() + leftWidth)); + m_topLeft.setHeight(std::max<LayoutUnit>(0, m_topLeft.height() + topWidth)); } if (m_topRight.width() > 0 && m_topRight.height() > 0) { - m_topRight.setWidth(std::max<int>(0, m_topRight.width() + rightWidth)); - m_topRight.setHeight(std::max<int>(0, m_topRight.height() + topWidth)); + m_topRight.setWidth(std::max<LayoutUnit>(0, m_topRight.width() + rightWidth)); + m_topRight.setHeight(std::max<LayoutUnit>(0, m_topRight.height() + topWidth)); } if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { - m_bottomLeft.setWidth(std::max<int>(0, m_bottomLeft.width() + leftWidth)); - m_bottomLeft.setHeight(std::max<int>(0, m_bottomLeft.height() + bottomWidth)); + m_bottomLeft.setWidth(std::max<LayoutUnit>(0, m_bottomLeft.width() + leftWidth)); + m_bottomLeft.setHeight(std::max<LayoutUnit>(0, m_bottomLeft.height() + bottomWidth)); } if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { - m_bottomRight.setWidth(std::max<int>(0, m_bottomRight.width() + rightWidth)); - m_bottomRight.setHeight(std::max<int>(0, m_bottomRight.height() + bottomWidth)); + m_bottomRight.setWidth(std::max<LayoutUnit>(0, m_bottomRight.width() + rightWidth)); + m_bottomRight.setHeight(std::max<LayoutUnit>(0, m_bottomRight.height() + bottomWidth)); } } -void RoundedRect::inflateWithRadii(int size) +void RoundedRect::inflateWithRadii(const LayoutUnit& size) { - IntRect old = m_rect; + LayoutRect old = m_rect; m_rect.inflate(size); // Considering the inflation factor of shorter size to scale the radii seems appropriate here @@ -131,18 +135,18 @@ void RoundedRect::Radii::excludeLogicalEdges(bool isHorizontal, bool excludeLogi } } -RoundedRect::RoundedRect(int x, int y, int width, int height) +RoundedRect::RoundedRect(const LayoutUnit& x, const LayoutUnit& y, const LayoutUnit& width, const LayoutUnit& height) : m_rect(x, y, width, height) { } -RoundedRect::RoundedRect(const IntRect& rect, const Radii& radii) +RoundedRect::RoundedRect(const LayoutRect& rect, const Radii& radii) : m_rect(rect) , m_radii(radii) { } -RoundedRect::RoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) +RoundedRect::RoundedRect(const LayoutRect& rect, const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight) : m_rect(rect) , m_radii(topLeft, topRight, bottomLeft, bottomRight) { @@ -186,7 +190,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const if (!quad.intersectsRect(rect)) return false; - const IntSize& topLeft = m_radii.topLeft(); + const LayoutSize& topLeft = m_radii.topLeft(); if (!topLeft.isEmpty()) { FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height()); if (quad.intersectsRect(rect)) { @@ -197,7 +201,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const } } - const IntSize& topRight = m_radii.topRight(); + const LayoutSize& topRight = m_radii.topRight(); if (!topRight.isEmpty()) { FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height()); if (quad.intersectsRect(rect)) { @@ -208,7 +212,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const } } - const IntSize& bottomLeft = m_radii.bottomLeft(); + const LayoutSize& bottomLeft = m_radii.bottomLeft(); if (!bottomLeft.isEmpty()) { FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height()); if (quad.intersectsRect(rect)) { @@ -219,7 +223,7 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const } } - const IntSize& bottomRight = m_radii.bottomRight(); + const LayoutSize& bottomRight = m_radii.bottomRight(); if (!bottomRight.isEmpty()) { FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height()); if (quad.intersectsRect(rect)) { @@ -233,4 +237,72 @@ bool RoundedRect::intersectsQuad(const FloatQuad& quad) const return true; } +bool RoundedRect::contains(const LayoutRect& otherRect) const +{ + if (!rect().contains(otherRect)) + return false; + + const LayoutSize& topLeft = m_radii.topLeft(); + if (!topLeft.isEmpty()) { + FloatPoint center = { m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height() }; + if (otherRect.x() <= center.x() && otherRect.y() <= center.y()) { + if (!ellipseContainsPoint(center, topLeft, otherRect.location())) + return false; + } + } + + const LayoutSize& topRight = m_radii.topRight(); + if (!topRight.isEmpty()) { + FloatPoint center = { m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height() }; + if (otherRect.maxX() >= center.x() && otherRect.y() <= center.y()) { + if (!ellipseContainsPoint(center, topRight, otherRect.location())) + return false; + } + } + + const LayoutSize& bottomLeft = m_radii.bottomLeft(); + if (!bottomLeft.isEmpty()) { + FloatPoint center = { m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height() }; + if (otherRect.maxX() >= center.x() && otherRect.maxY() >= center.y()) { + if (!ellipseContainsPoint(center, bottomLeft, otherRect.location())) + return false; + } + } + + const LayoutSize& bottomRight = m_radii.bottomRight(); + if (!bottomRight.isEmpty()) { + FloatPoint center = { m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height() }; + if (otherRect.x() <= center.x() && otherRect.maxY() >= center.y()) { + if (!ellipseContainsPoint(center, bottomRight, otherRect.location())) + return false; + } + } + + return true; +} + +FloatRoundedRect RoundedRect::pixelSnappedRoundedRectForPainting(float deviceScaleFactor) const +{ + LayoutRect originalRect = rect(); + if (originalRect.isEmpty()) + return FloatRoundedRect(originalRect, radii()); + + FloatRect pixelSnappedRect = snapRectToDevicePixels(originalRect, deviceScaleFactor); + + if (!isRenderable()) + return FloatRoundedRect(pixelSnappedRect, radii()); + + // Snapping usually does not alter size, but when it does, we need to make sure that the final rect is still renderable by distributing the size delta proportionally. + FloatRoundedRect::Radii adjustedRadii = radii(); + adjustedRadii.scale(pixelSnappedRect.width() / originalRect.width().toFloat(), pixelSnappedRect.height() / originalRect.height().toFloat()); + FloatRoundedRect snappedRoundedRect = FloatRoundedRect(pixelSnappedRect, adjustedRadii); + if (!snappedRoundedRect.isRenderable()) { + // Floating point mantissa overflow can produce a non-renderable rounded rect. + adjustedRadii.shrink(1 / deviceScaleFactor); + snappedRoundedRect.setRadii(adjustedRadii); + } + ASSERT(snappedRoundedRect.isRenderable()); + return snappedRoundedRect; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/RoundedRect.h b/Source/WebCore/platform/graphics/RoundedRect.h index d923e353f..3f2f7ebc8 100644 --- a/Source/WebCore/platform/graphics/RoundedRect.h +++ b/Source/WebCore/platform/graphics/RoundedRect.h @@ -28,17 +28,20 @@ #define RoundedRect_h #include "FloatQuad.h" -#include "IntRect.h" +#include "LayoutRect.h" +#include "LayoutSize.h" namespace WebCore { +class FloatRoundedRect; +class LayoutUnit; class RoundedRect { public: class Radii { public: Radii() {} - Radii(const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) + Radii(const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight) : m_topLeft(topLeft) , m_topRight(topRight) , m_bottomLeft(bottomLeft) @@ -46,14 +49,14 @@ public: { } - void setTopLeft(const IntSize& size) { m_topLeft = size; } - void setTopRight(const IntSize& size) { m_topRight = size; } - void setBottomLeft(const IntSize& size) { m_bottomLeft = size; } - void setBottomRight(const IntSize& size) { m_bottomRight = size; } - const IntSize& topLeft() const { return m_topLeft; } - const IntSize& topRight() const { return m_topRight; } - const IntSize& bottomLeft() const { return m_bottomLeft; } - const IntSize& bottomRight() const { return m_bottomRight; } + void setTopLeft(const LayoutSize& size) { m_topLeft = size; } + void setTopRight(const LayoutSize& size) { m_topRight = size; } + void setBottomLeft(const LayoutSize& size) { m_bottomLeft = size; } + void setBottomRight(const LayoutSize& size) { m_bottomRight = size; } + const LayoutSize& topLeft() const { return m_topLeft; } + const LayoutSize& topRight() const { return m_topRight; } + const LayoutSize& bottomLeft() const { return m_bottomLeft; } + const LayoutSize& bottomRight() const { return m_bottomRight; } bool isZero() const; @@ -61,35 +64,38 @@ public: void excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge); void scale(float factor); - void expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth); - void expand(int size) { expand(size, size, size, size); } - void shrink(int topWidth, int bottomWidth, int leftWidth, int rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); } - void shrink(int size) { shrink(size, size, size, size); } + void expand(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth); + void expand(const LayoutUnit& size) { expand(size, size, size, size); } + void shrink(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); } + void shrink(const LayoutUnit& size) { shrink(size, size, size, size); } + + Radii transposedRadii() const { return Radii(m_topLeft.transposedSize(), m_topRight.transposedSize(), m_bottomLeft.transposedSize(), m_bottomRight.transposedSize()); } private: - IntSize m_topLeft; - IntSize m_topRight; - IntSize m_bottomLeft; - IntSize m_bottomRight; + LayoutSize m_topLeft; + LayoutSize m_topRight; + LayoutSize m_bottomLeft; + LayoutSize m_bottomRight; }; - explicit RoundedRect(const IntRect&, const Radii& = Radii()); - RoundedRect(int x, int y, int width, int height); - RoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight); + explicit RoundedRect(const LayoutRect&, const Radii& = Radii()); + RoundedRect(const LayoutUnit&, const LayoutUnit&, const LayoutUnit& width, const LayoutUnit& height); + RoundedRect(const LayoutRect&, const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight); - const IntRect& rect() const { return m_rect; } + const LayoutRect& rect() const { return m_rect; } const Radii& radii() const { return m_radii; } bool isRounded() const { return !m_radii.isZero(); } bool isEmpty() const { return m_rect.isEmpty(); } - void setRect(const IntRect& rect) { m_rect = rect; } + void setRect(const LayoutRect& rect) { m_rect = rect; } void setRadii(const Radii& radii) { m_radii = radii; } - void move(const IntSize& size) { m_rect.move(size); } - void inflate(int size) { m_rect.inflate(size); } - void inflateWithRadii(int size); - void expandRadii(int size) { m_radii.expand(size); } - void shrinkRadii(int size) { m_radii.shrink(size); } + void move(const LayoutSize& size) { m_rect.move(size); } + void moveBy(const LayoutPoint& offset) { m_rect.moveBy(offset); } + void inflate(const LayoutUnit& size) { m_rect.inflate(size); } + void inflateWithRadii(const LayoutUnit& size); + void expandRadii(const LayoutUnit& size) { m_radii.expand(size); } + void shrinkRadii(const LayoutUnit& size) { m_radii.shrink(size); } void includeLogicalEdges(const Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge); void excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge); @@ -100,9 +106,14 @@ public: // Tests whether the quad intersects any part of this rounded rectangle. // This only works for convex quads. bool intersectsQuad(const FloatQuad&) const; + bool contains(const LayoutRect&) const; + + FloatRoundedRect pixelSnappedRoundedRectForPainting(float deviceScaleFactor) const; + + RoundedRect transposedRect() const { return RoundedRect(m_rect.transposedRect(), m_radii.transposedRadii()); } private: - IntRect m_rect; + LayoutRect m_rect; Radii m_radii; }; diff --git a/Source/WebCore/platform/graphics/SVGGlyph.cpp b/Source/WebCore/platform/graphics/SVGGlyph.cpp deleted file mode 100644 index ad3be1efc..000000000 --- a/Source/WebCore/platform/graphics/SVGGlyph.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" - -#if ENABLE(SVG_FONTS) -#include "SVGGlyph.h" - -#include <wtf/unicode/Unicode.h> - -namespace WebCore { - -// Helper functions to determine the arabic character forms (initial, medial, terminal, isolated) -enum ArabicCharShapingMode { - SNone = 0, - SRight = 1, - SDual = 2 -}; - -static const ArabicCharShapingMode s_arabicCharShapingMode[222] = { - SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, SDual , SDual , SDual , SDual , SDual , SRight, /* 0x0622 - 0x062F */ - SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SNone , SNone , SNone , SNone , SNone , /* 0x0630 - 0x063F */ - SNone , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SDual , SDual , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0640 - 0x064F */ - SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0650 - 0x065F */ - SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0660 - 0x066F */ - SNone , SRight, SRight, SRight, SNone , SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0670 - 0x067F */ - SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, /* 0x0680 - 0x068F */ - SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0690 - 0x069F */ - SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06A0 - 0x06AF */ - SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06B0 - 0x06BF */ - SRight, SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, /* 0x06C0 - 0x06CF */ - SDual , SDual , SRight, SRight, SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06D0 - 0x06DF */ - SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06E0 - 0x06EF */ - SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SDual , SDual , SDual , SNone , SNone , SNone /* 0x06F0 - 0x06FF */ -}; - -static inline SVGGlyph::ArabicForm processArabicFormDetection(const UChar& curChar, bool& lastCharShapesRight, SVGGlyph::ArabicForm* prevForm) -{ - SVGGlyph::ArabicForm curForm; - - ArabicCharShapingMode shapingMode = SNone; - if (curChar >= 0x0622 && curChar <= 0x06FF) - shapingMode = s_arabicCharShapingMode[curChar - 0x0622]; - - // Use a simple state machine to identify the actual arabic form - // It depends on the order of the arabic form enum: - // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial }; - - if (lastCharShapesRight && shapingMode == SDual) { - if (prevForm) { - int correctedForm = (int) *prevForm + 1; - ASSERT(correctedForm >= SVGGlyph::None && correctedForm <= SVGGlyph::Medial); - *prevForm = static_cast<SVGGlyph::ArabicForm>(correctedForm); - } - - curForm = SVGGlyph::Initial; - } else - curForm = shapingMode == SNone ? SVGGlyph::None : SVGGlyph::Isolated; - - lastCharShapesRight = shapingMode != SNone; - return curForm; -} - -Vector<SVGGlyph::ArabicForm> charactersWithArabicForm(const String& input, bool rtl) -{ - Vector<SVGGlyph::ArabicForm> forms; - unsigned length = input.length(); - - bool containsArabic = false; - for (unsigned i = 0; i < length; ++i) { - if (ublock_getCode(input[i]) == UBLOCK_ARABIC) { - containsArabic = true; - break; - } - } - - if (!containsArabic) - return forms; - - bool lastCharShapesRight = false; - - // Start identifying arabic forms - if (rtl) { - for (int i = length - 1; i >= 0; --i) - forms.insert(0, processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first())); - } else { - for (unsigned i = 0; i < length; ++i) - forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last())); - } - - return forms; -} - -static inline bool isCompatibleArabicForm(const SVGGlyph& identifier, const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition) -{ - if (chars.isEmpty()) - return true; - - Vector<SVGGlyph::ArabicForm>::const_iterator realEnd = chars.end(); - Vector<SVGGlyph::ArabicForm>::const_iterator it = chars.begin() + startPosition; - if (it >= realEnd) - return true; - - Vector<SVGGlyph::ArabicForm>::const_iterator end = chars.begin() + endPosition; - if (end >= realEnd) - end = realEnd; - - for (; it != end; ++it) { - if (*it != static_cast<SVGGlyph::ArabicForm>(identifier.arabicForm) && *it != SVGGlyph::None) - return false; - } - - return true; -} - -bool isCompatibleGlyph(const SVGGlyph& identifier, bool isVerticalText, const String& language, - const Vector<SVGGlyph::ArabicForm>& chars, unsigned startPosition, unsigned endPosition) -{ - bool valid = true; - - // Check wheter orientation if glyph fits within the request - switch (identifier.orientation) { - case SVGGlyph::Vertical: - valid = isVerticalText; - break; - case SVGGlyph::Horizontal: - valid = !isVerticalText; - break; - case SVGGlyph::Both: - break; - } - - if (!valid) - return false; - - // Check wheter languages are compatible - if (!identifier.languages.isEmpty()) { - // This glyph exists only in certain languages, if we're not specifying a - // language on the referencing element we're unable to use this glyph. - if (language.isEmpty()) - return false; - - // Split subcode from language, if existant. - String languagePrefix; - - size_t subCodeSeparator = language.find('-'); - if (subCodeSeparator != notFound) - languagePrefix = language.left(subCodeSeparator); - - Vector<String>::const_iterator it = identifier.languages.begin(); - Vector<String>::const_iterator end = identifier.languages.end(); - - bool found = false; - for (; it != end; ++it) { - const String& cur = *it; - if (cur == language || cur == languagePrefix) { - found = true; - break; - } - } - - if (!found) - return false; - } - - // Check wheter arabic form is compatible - return isCompatibleArabicForm(identifier, chars, startPosition, endPosition); -} - -} - -#endif diff --git a/Source/WebCore/platform/graphics/SVGGlyph.h b/Source/WebCore/platform/graphics/SVGGlyph.h deleted file mode 100644 index 272811bb6..000000000 --- a/Source/WebCore/platform/graphics/SVGGlyph.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2007 Eric Seidel <eric@webkit.org> - * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) 2008 Rob Buis <buis@kde.org> - * Copyright (C) Research In Motion Limited 2011. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef SVGGlyph_h -#define SVGGlyph_h - -#if ENABLE(SVG_FONTS) -#include "Glyph.h" -#include "Path.h" - -#include <limits> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -// Describe a glyph from a SVG Font. -struct SVGGlyph { - enum Orientation { - Vertical, - Horizontal, - Both - }; - - // SVG Font depends on exactly this order. - enum ArabicForm { - None = 0, - Isolated, - Terminal, - Initial, - Medial - }; - - SVGGlyph() - : isPartOfLigature(false) - , orientation(Both) - , arabicForm(None) - , priority(0) - , tableEntry(0) - , unicodeStringLength(0) - , horizontalAdvanceX(0) - , verticalOriginX(0) - , verticalOriginY(0) - , verticalAdvanceY(0) - { - } - - // Used to mark our float properties as "to be inherited from SVGFontData" - static float inheritedValue() - { - static float s_inheritedValue = std::numeric_limits<float>::infinity(); - return s_inheritedValue; - } - - bool operator==(const SVGGlyph& other) const - { - return isPartOfLigature == other.isPartOfLigature - && orientation == other.orientation - && arabicForm == other.arabicForm - && tableEntry == other.tableEntry - && unicodeStringLength == other.unicodeStringLength - && glyphName == other.glyphName - && horizontalAdvanceX == other.horizontalAdvanceX - && verticalOriginX == other.verticalOriginX - && verticalOriginY == other.verticalOriginY - && verticalAdvanceY == other.verticalAdvanceY - && languages == other.languages; - } - - bool isPartOfLigature : 1; - - unsigned orientation : 2; // Orientation - unsigned arabicForm : 3; // ArabicForm - int priority; - Glyph tableEntry; - size_t unicodeStringLength; - String glyphName; - - float horizontalAdvanceX; - float verticalOriginX; - float verticalOriginY; - float verticalAdvanceY; - - Path pathData; - Vector<String> languages; -}; - -Vector<SVGGlyph::ArabicForm> charactersWithArabicForm(const String& input, bool rtl); -bool isCompatibleGlyph(const SVGGlyph&, bool isVerticalText, const String& language, const Vector<SVGGlyph::ArabicForm>&, unsigned startPosition, unsigned endPosition); - -} // namespace WebCore - -#endif // ENABLE(SVG_FONTS) -#endif diff --git a/Source/WebCore/platform/graphics/SegmentedFontData.cpp b/Source/WebCore/platform/graphics/SegmentedFontData.cpp deleted file mode 100644 index efb20a8c1..000000000 --- a/Source/WebCore/platform/graphics/SegmentedFontData.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SegmentedFontData.h" - -#include "SimpleFontData.h" -#include <wtf/Assertions.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -SegmentedFontData::~SegmentedFontData() -{ - GlyphPageTreeNode::pruneTreeCustomFontData(this); -} - -const SimpleFontData* SegmentedFontData::fontDataForCharacter(UChar32 c) const -{ - Vector<FontDataRange>::const_iterator end = m_ranges.end(); - for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->from() <= c && it->to() >= c) - return it->fontData().get(); - } - return m_ranges[0].fontData().get(); -} - -bool SegmentedFontData::containsCharacter(UChar32 c) const -{ - Vector<FontDataRange>::const_iterator end = m_ranges.end(); - for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (c >= it->from() && c <= it->to()) - return true; - } - return false; -} - -bool SegmentedFontData::containsCharacters(const UChar* characters, int length) const -{ - UChar32 c; - for (int i = 0; i < length; ) { - U16_NEXT(characters, i, length, c) - if (!containsCharacter(c)) - return false; - } - return true; -} - -bool SegmentedFontData::isCustomFont() const -{ - // All segmented fonts are custom fonts. - return true; -} - -bool SegmentedFontData::isLoading() const -{ - Vector<FontDataRange>::const_iterator end = m_ranges.end(); - for (Vector<FontDataRange>::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->fontData()->isLoading()) - return true; - } - return false; -} - -bool SegmentedFontData::isSegmented() const -{ - return true; -} - -#ifndef NDEBUG -String SegmentedFontData::description() const -{ - return "[segmented font]"; -} -#endif - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/SegmentedFontData.h b/Source/WebCore/platform/graphics/SegmentedFontData.h deleted file mode 100644 index 45d7d158c..000000000 --- a/Source/WebCore/platform/graphics/SegmentedFontData.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SegmentedFontData_h -#define SegmentedFontData_h - -#include "FontData.h" -#include <wtf/Vector.h> - -namespace WebCore { - -class SimpleFontData; - -struct FontDataRange { - FontDataRange(UChar32 from, UChar32 to, PassRefPtr<SimpleFontData> fontData) - : m_from(from) - , m_to(to) - , m_fontData(fontData) - { - } - - UChar32 from() const { return m_from; } - UChar32 to() const { return m_to; } - PassRefPtr<SimpleFontData> fontData() const { return m_fontData; } - -private: - UChar32 m_from; - UChar32 m_to; - RefPtr<SimpleFontData> m_fontData; -}; - -class SegmentedFontData : public FontData { -public: - static PassRefPtr<SegmentedFontData> create() { return adoptRef(new SegmentedFontData); } - - virtual ~SegmentedFontData(); - - void appendRange(const FontDataRange& range) { m_ranges.append(range); } - unsigned numRanges() const { return m_ranges.size(); } - const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; } - -#ifndef NDEBUG - virtual String description() const; -#endif - -private: - SegmentedFontData() { } - - virtual const SimpleFontData* fontDataForCharacter(UChar32) const; - virtual bool containsCharacters(const UChar*, int length) const; - - virtual bool isCustomFont() const; - virtual bool isLoading() const; - virtual bool isSegmented() const; - - bool containsCharacter(UChar32) const; - - Vector<FontDataRange, 1> m_ranges; -}; - -} // namespace WebCore - -#endif // SegmentedFontData_h diff --git a/Source/WebCore/platform/graphics/ShadowBlur.cpp b/Source/WebCore/platform/graphics/ShadowBlur.cpp index 82b180a46..f17b3ffe7 100644 --- a/Source/WebCore/platform/graphics/ShadowBlur.cpp +++ b/Source/WebCore/platform/graphics/ShadowBlur.cpp @@ -17,7 +17,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -36,6 +36,7 @@ #include "ImageBuffer.h" #include "Timer.h" #include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Noncopyable.h> namespace WebCore { @@ -57,7 +58,7 @@ class ScratchBuffer { WTF_MAKE_FAST_ALLOCATED; public: ScratchBuffer() - : m_purgeTimer(this, &ScratchBuffer::timerFired) + : m_purgeTimer(*this, &ScratchBuffer::clearScratchBuffer) , m_lastWasInset(false) #if !ASSERT_DISABLED , m_bufferInUse(false) @@ -79,19 +80,20 @@ public: IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height())); clearScratchBuffer(); - m_imageBuffer = ImageBuffer::create(roundedSize, 1); + + // ShadowBlur is not used with accelerated drawing, so it's OK to make an unconditionally unaccelerated buffer. + m_imageBuffer = ImageBuffer::create(roundedSize, Unaccelerated, 1); return m_imageBuffer.get(); } - bool setCachedShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedRect::Radii& radii, const FloatSize& layerSize) + bool setCachedShadowValues(const FloatSize& radius, const Color& color, const FloatRect& shadowRect, const FloatRoundedRect::Radii& radii, const FloatSize& layerSize) { - if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastShadowRect == shadowRect && m_lastRadii == radii && m_lastLayerSize == layerSize) + if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastShadowRect == shadowRect && m_lastRadii == radii && m_lastLayerSize == layerSize) return false; m_lastWasInset = false; m_lastRadius = radius; m_lastColor = color; - m_lastColorSpace = colorSpace; m_lastShadowRect = shadowRect; m_lastRadii = radii; m_lastLayerSize = layerSize; @@ -99,16 +101,15 @@ public: return true; } - bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedRect::Radii& radii) + bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, const FloatRect& bounds, const FloatRect& shadowRect, const FloatRoundedRect::Radii& radii) { - if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii) + if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii) return false; m_lastWasInset = true; m_lastInsetBounds = bounds; m_lastRadius = radius; m_lastColor = color; - m_lastColorSpace = colorSpace; m_lastShadowRect = shadowRect; m_lastRadii = radii; @@ -127,14 +128,9 @@ public: m_purgeTimer.startOneShot(scratchBufferPurgeInterval); } - static ScratchBuffer& shared(); + static ScratchBuffer& singleton(); private: - void timerFired(Timer<ScratchBuffer>*) - { - clearScratchBuffer(); - } - void clearScratchBuffer() { m_imageBuffer = nullptr; @@ -143,13 +139,12 @@ private: } std::unique_ptr<ImageBuffer> m_imageBuffer; - Timer<ScratchBuffer> m_purgeTimer; + Timer m_purgeTimer; FloatRect m_lastInsetBounds; FloatRect m_lastShadowRect; - RoundedRect::Radii m_lastRadii; + FloatRoundedRect::Radii m_lastRadii; Color m_lastColor; - ColorSpace m_lastColorSpace; FloatSize m_lastRadius; bool m_lastWasInset; FloatSize m_lastLayerSize; @@ -159,9 +154,9 @@ private: #endif }; -ScratchBuffer& ScratchBuffer::shared() +ScratchBuffer& ScratchBuffer::singleton() { - DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ()); + static NeverDestroyed<ScratchBuffer> scratchBuffer; return scratchBuffer; } @@ -174,9 +169,8 @@ static float radiusToLegacyRadius(float radius) } #endif -ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace) +ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color) : m_color(color) - , m_colorSpace(colorSpace) , m_blurRadius(radius) , m_offset(offset) , m_layerImage(0) @@ -187,7 +181,6 @@ ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const C ShadowBlur::ShadowBlur(const GraphicsContextState& state) : m_color(state.shadowColor) - , m_colorSpace(state.shadowColorSpace) , m_blurRadius(state.shadowBlur, state.shadowBlur) , m_offset(state.shadowOffset) , m_layerImage(0) @@ -209,12 +202,11 @@ ShadowBlur::ShadowBlur() { } -void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace, bool ignoreTransforms) +void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, bool ignoreTransforms) { m_blurRadius = radius; m_offset = offset; m_color = color; - m_colorSpace = colorSpace; m_shadowsIgnoreTransforms = ignoreTransforms; updateShadowBlurValues(); @@ -226,7 +218,7 @@ void ShadowBlur::updateShadowBlurValues() m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128)); // The type of shadow is decided by the blur radius, shadow offset, and shadow color. - if (!m_color.isValid() || !m_color.alpha()) { + if (!m_color.isVisible()) { // Can't paint the shadow with invalid or invisible color. m_type = NoShadow; } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) { @@ -373,12 +365,12 @@ void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, i } } -void ShadowBlur::adjustBlurRadius(GraphicsContext* context) +void ShadowBlur::adjustBlurRadius(GraphicsContext& context) { if (!m_shadowsIgnoreTransforms) return; - AffineTransform transform = context->getCTM(); + AffineTransform transform = context.getCTM(); m_blurRadius.scale(1 / static_cast<float>(transform.xScale()), 1 / static_cast<float>(transform.yScale())); } @@ -396,7 +388,7 @@ IntSize ShadowBlur::blurredEdgeSize() const return edgeSize; } -IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect) +IntSize ShadowBlur::calculateLayerBoundingRect(GraphicsContext& context, const FloatRect& shadowedRect, const IntRect& clipRect) { IntSize edgeSize = blurredEdgeSize(); @@ -404,11 +396,11 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F FloatRect layerRect; IntSize inflation; - const AffineTransform transform = context->getCTM(); + const AffineTransform transform = context.getCTM(); if (m_shadowsIgnoreTransforms && !transform.isIdentity()) { FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect)); transformedPolygon.move(m_offset); - layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox(); + layerRect = transform.inverse().value_or(AffineTransform()).mapQuad(transformedPolygon).boundingBox(); } else { layerRect = shadowedRect; layerRect.move(m_offset); @@ -426,7 +418,7 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F if (!clipRect.contains(enclosingIntRect(layerRect))) { // If we are totally outside the clip region, we aren't painting at all. if (intersection(layerRect, clipRect).isEmpty()) - return IntRect(); + return IntSize(); IntRect inflatedClip = clipRect; // Pixels at the edges can be affected by pixels outside the buffer, @@ -459,30 +451,30 @@ IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const F float translationY = -shadowedRect.y() + inflation.height() - fabsf(clippedOut.height()); m_layerContextTranslation = FloatSize(translationX, translationY); - return enclosingIntRect(layerRect); + return expandedIntSize(layerRect.size()); } -void ShadowBlur::drawShadowBuffer(GraphicsContext* graphicsContext) +void ShadowBlur::drawShadowBuffer(GraphicsContext& graphicsContext) { if (!m_layerImage) return; - GraphicsContextStateSaver stateSaver(*graphicsContext); + GraphicsContextStateSaver stateSaver(graphicsContext); IntSize bufferSize = m_layerImage->internalSize(); if (bufferSize != m_layerSize) { // The rect passed to clipToImageBuffer() has to be the size of the entire buffer, // but we may not have cleared it all, so clip to the filled part first. - graphicsContext->clip(FloatRect(m_layerOrigin, m_layerSize)); + graphicsContext.clip(FloatRect(m_layerOrigin, m_layerSize)); } - graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize)); - graphicsContext->setFillColor(m_color, m_colorSpace); + graphicsContext.clipToImageBuffer(*m_layerImage, FloatRect(m_layerOrigin, bufferSize)); + graphicsContext.setFillColor(m_color); - graphicsContext->clearShadow(); - graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size())); + graphicsContext.clearShadow(); + graphicsContext.fillRect(FloatRect(m_layerOrigin, m_sourceRect.size())); } -static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const RoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice) +static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const FloatRoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice) { leftSlice = twiceRadius.width() + std::max(radii.topLeft().width(), radii.bottomLeft().width()); rightSlice = twiceRadius.width() + std::max(radii.topRight().width(), radii.bottomRight().width()); @@ -491,7 +483,7 @@ static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const Rounded bottomSlice = twiceRadius.height() + std::max(radii.bottomLeft().height(), radii.bottomRight().height()); } -IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const RoundedRect::Radii& radii) const +IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const FloatRoundedRect::Radii& radii) const { const int templateSideLength = 1; @@ -509,134 +501,136 @@ IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const RoundedRect templateSideLength + topSlice + bottomSlice); } -void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii) +void ShadowBlur::drawRectShadow(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect) { - IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds()); - if (layerRect.isEmpty()) + IntSize layerSize = calculateLayerBoundingRect(graphicsContext, shadowedRect.rect(), graphicsContext.clipBounds()); + if (layerSize.isEmpty()) return; adjustBlurRadius(graphicsContext); // drawRectShadowWithTiling does not work with rotations. // https://bugs.webkit.org/show_bug.cgi?id=45042 - if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) { - drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect); + if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) { + drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerSize); return; } IntSize edgeSize = blurredEdgeSize(); - IntSize templateSize = this->templateSize(edgeSize, radii); + IntSize templateSize = this->templateSize(edgeSize, shadowedRect.radii()); + const FloatRect& rect = shadowedRect.rect(); - if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height() + if (templateSize.width() > rect.width() || templateSize.height() > rect.height() || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) { - drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect); + drawRectShadowWithoutTiling(graphicsContext, shadowedRect, layerSize); return; } - drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize, edgeSize); + drawRectShadowWithTiling(graphicsContext, shadowedRect, templateSize, edgeSize); } -void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii) +void ShadowBlur::drawInsetShadow(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect) { - IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds()); - if (layerRect.isEmpty()) + IntSize layerSize = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext.clipBounds()); + if (layerSize.isEmpty()) return; adjustBlurRadius(graphicsContext); // drawInsetShadowWithTiling does not work with rotations. // https://bugs.webkit.org/show_bug.cgi?id=45042 - if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) { - drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect); + if (!graphicsContext.getCTM().preservesAxisAlignment() || m_type != BlurShadow) { + drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerSize); return; } IntSize edgeSize = blurredEdgeSize(); - IntSize templateSize = this->templateSize(edgeSize, holeRadii); + IntSize templateSize = this->templateSize(edgeSize, holeRect.radii()); + const FloatRect& hRect = holeRect.rect(); - if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height() - || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) { - drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect); + if (templateSize.width() > hRect.width() || templateSize.height() > hRect.height() + || (templateSize.width() * templateSize.height() > hRect.width() * hRect.height())) { + drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, layerSize); return; } - drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize, edgeSize); + drawInsetShadowWithTiling(graphicsContext, rect, holeRect, templateSize, edgeSize); } -void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntRect& layerRect) +void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& layerSize) { - m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size()); + m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerSize); if (!m_layerImage) return; - FloatRect bufferRelativeShadowedRect = shadowedRect; + FloatRect bufferRelativeShadowedRect = shadowedRect.rect(); bufferRelativeShadowedRect.move(m_layerContextTranslation); // Only redraw in the scratch buffer if its cached contents don't match our needs - bool redrawNeeded = ScratchBuffer::shared().setCachedShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii, m_layerSize); + bool redrawNeeded = ScratchBuffer::singleton().setCachedShadowValues(m_blurRadius, Color::black, bufferRelativeShadowedRect, shadowedRect.radii(), m_layerSize); if (redrawNeeded) { - GraphicsContext* shadowContext = m_layerImage->context(); - GraphicsContextStateSaver stateSaver(*shadowContext); + GraphicsContext& shadowContext = m_layerImage->context(); + GraphicsContextStateSaver stateSaver(shadowContext); // Add a pixel to avoid later edge aliasing when rotated. - shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); - shadowContext->translate(m_layerContextTranslation); - shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); - if (radii.isZero()) - shadowContext->fillRect(shadowedRect); + shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); + shadowContext.translate(m_layerContextTranslation); + shadowContext.setFillColor(Color::black); + if (shadowedRect.radii().isZero()) + shadowContext.fillRect(shadowedRect.rect()); else { Path path; - path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); - shadowContext->fillPath(path); + path.addRoundedRect(shadowedRect); + shadowContext.fillPath(path); } - blurShadowBuffer(expandedIntSize(m_layerSize)); + blurShadowBuffer(layerSize); } drawShadowBuffer(graphicsContext); - m_layerImage = 0; - ScratchBuffer::shared().scheduleScratchBufferPurge(); + m_layerImage = nullptr; + ScratchBuffer::singleton().scheduleScratchBufferPurge(); } -void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii, const IntRect& layerRect) +void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& layerSize) { - m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size()); + m_layerImage = ScratchBuffer::singleton().getScratchBuffer(layerSize); if (!m_layerImage) return; FloatRect bufferRelativeRect = rect; bufferRelativeRect.move(m_layerContextTranslation); - FloatRect bufferRelativeHoleRect = holeRect; + FloatRect bufferRelativeHoleRect = holeRect.rect(); bufferRelativeHoleRect.move(m_layerContextTranslation); // Only redraw in the scratch buffer if its cached contents don't match our needs - bool redrawNeeded = ScratchBuffer::shared().setCachedInsetShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii); + bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, Color::black, bufferRelativeRect, bufferRelativeHoleRect, holeRect.radii()); if (redrawNeeded) { - GraphicsContext* shadowContext = m_layerImage->context(); - GraphicsContextStateSaver stateSaver(*shadowContext); + GraphicsContext& shadowContext = m_layerImage->context(); + GraphicsContextStateSaver stateSaver(shadowContext); // Add a pixel to avoid later edge aliasing when rotated. - shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); - shadowContext->translate(m_layerContextTranslation); + shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); + shadowContext.translate(m_layerContextTranslation); Path path; path.addRect(rect); - if (holeRadii.isZero()) - path.addRect(holeRect); + if (holeRect.radii().isZero()) + path.addRect(holeRect.rect()); else - path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight()); + path.addRoundedRect(holeRect); - shadowContext->setFillRule(RULE_EVENODD); - shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); - shadowContext->fillPath(path); + shadowContext.setFillRule(RULE_EVENODD); + shadowContext.setFillColor(Color::black); + shadowContext.fillPath(path); - blurShadowBuffer(expandedIntSize(m_layerSize)); + blurShadowBuffer(layerSize); } drawShadowBuffer(graphicsContext); - m_layerImage = 0; - ScratchBuffer::shared().scheduleScratchBufferPurge(); + m_layerImage = nullptr; + ScratchBuffer::singleton().scheduleScratchBufferPurge(); } /* @@ -671,9 +665,9 @@ void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, the shadow. */ -void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize) +void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext& graphicsContext, const FloatRect& rect, const FloatRoundedRect& holeRect, const IntSize& templateSize, const IntSize& edgeSize) { - m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize); + m_layerImage = ScratchBuffer::singleton().getScratchBuffer(templateSize); if (!m_layerImage) return; @@ -682,36 +676,36 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, con FloatRect templateHole = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height()); // Only redraw in the scratch buffer if its cached contents don't match our needs - bool redrawNeeded = ScratchBuffer::shared().setCachedInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii); + bool redrawNeeded = ScratchBuffer::singleton().setCachedInsetShadowValues(m_blurRadius, m_color, templateBounds, templateHole, holeRect.radii()); if (redrawNeeded) { // Draw shadow into a new ImageBuffer. - GraphicsContext* shadowContext = m_layerImage->context(); - GraphicsContextStateSaver shadowStateSaver(*shadowContext); - shadowContext->clearRect(templateBounds); - shadowContext->setFillRule(RULE_EVENODD); - shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + GraphicsContext& shadowContext = m_layerImage->context(); + GraphicsContextStateSaver shadowStateSaver(shadowContext); + shadowContext.clearRect(templateBounds); + shadowContext.setFillRule(RULE_EVENODD); + shadowContext.setFillColor(Color::black); Path path; path.addRect(templateBounds); - if (radii.isZero()) + if (holeRect.radii().isZero()) path.addRect(templateHole); else - path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); + path.addRoundedRect(FloatRoundedRect(templateHole, holeRect.radii())); - shadowContext->fillPath(path); + shadowContext.fillPath(path); blurAndColorShadowBuffer(templateSize); } FloatSize offset = m_offset; if (shadowsIgnoreTransforms()) { - AffineTransform transform = graphicsContext->getCTM(); + AffineTransform transform = graphicsContext.getCTM(); offset.scale(1 / transform.xScale(), 1 / transform.yScale()); } FloatRect boundingRect = rect; boundingRect.move(offset); - FloatRect destHoleRect = holeRect; + FloatRect destHoleRect = holeRect.rect(); destHoleRect.move(offset); FloatRect destHoleBounds = destHoleRect; destHoleBounds.inflateX(edgeSize.width()); @@ -723,65 +717,66 @@ void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, con exteriorPath.addRect(destHoleBounds); { - GraphicsContextStateSaver fillStateSaver(*graphicsContext); - graphicsContext->setFillRule(RULE_EVENODD); - graphicsContext->setFillColor(m_color, m_colorSpace); - graphicsContext->clearShadow(); - graphicsContext->fillPath(exteriorPath); + GraphicsContextStateSaver fillStateSaver(graphicsContext); + graphicsContext.setFillRule(RULE_EVENODD); + graphicsContext.setFillColor(m_color); + graphicsContext.clearShadow(); + graphicsContext.fillPath(exteriorPath); } - drawLayerPieces(graphicsContext, destHoleBounds, radii, edgeSize, templateSize, InnerShadow); + drawLayerPieces(graphicsContext, destHoleBounds, holeRect.radii(), edgeSize, templateSize, InnerShadow); - m_layerImage = 0; - ScratchBuffer::shared().scheduleScratchBufferPurge(); + m_layerImage = nullptr; + ScratchBuffer::singleton().scheduleScratchBufferPurge(); } -void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize) +void ShadowBlur::drawRectShadowWithTiling(GraphicsContext& graphicsContext, const FloatRoundedRect& shadowedRect, const IntSize& templateSize, const IntSize& edgeSize) { - m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize); + auto& scratchBuffer = ScratchBuffer::singleton(); + m_layerImage = scratchBuffer.getScratchBuffer(templateSize); if (!m_layerImage) return; FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height()); // Only redraw in the scratch buffer if its cached contents don't match our needs - bool redrawNeeded = ScratchBuffer::shared().setCachedShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, radii, m_layerSize); + bool redrawNeeded = scratchBuffer.setCachedShadowValues(m_blurRadius, m_color, templateShadow, shadowedRect.radii(), m_layerSize); if (redrawNeeded) { // Draw shadow into the ImageBuffer. - GraphicsContext* shadowContext = m_layerImage->context(); - GraphicsContextStateSaver shadowStateSaver(*shadowContext); + GraphicsContext& shadowContext = m_layerImage->context(); + GraphicsContextStateSaver shadowStateSaver(shadowContext); - shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height())); - shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + shadowContext.clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height())); + shadowContext.setFillColor(Color::black); - if (radii.isZero()) - shadowContext->fillRect(templateShadow); + if (shadowedRect.radii().isZero()) + shadowContext.fillRect(templateShadow); else { Path path; - path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); - shadowContext->fillPath(path); + path.addRoundedRect(FloatRoundedRect(templateShadow, shadowedRect.radii())); + shadowContext.fillPath(path); } blurAndColorShadowBuffer(templateSize); } FloatSize offset = m_offset; if (shadowsIgnoreTransforms()) { - AffineTransform transform = graphicsContext->getCTM(); + AffineTransform transform = graphicsContext.getCTM(); offset.scale(1 / transform.xScale(), 1 / transform.yScale()); } - FloatRect shadowBounds = shadowedRect; + FloatRect shadowBounds = shadowedRect.rect(); shadowBounds.move(offset); shadowBounds.inflateX(edgeSize.width()); shadowBounds.inflateY(edgeSize.height()); - drawLayerPieces(graphicsContext, shadowBounds, radii, edgeSize, templateSize, OuterShadow); + drawLayerPieces(graphicsContext, shadowBounds, shadowedRect.radii(), edgeSize, templateSize, OuterShadow); - m_layerImage = 0; - ScratchBuffer::shared().scheduleScratchBufferPurge(); + m_layerImage = nullptr; + ScratchBuffer::singleton().scheduleScratchBufferPurge(); } -void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction) +void ShadowBlur::drawLayerPieces(GraphicsContext& graphicsContext, const FloatRect& shadowBounds, const FloatRoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction) { const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2); @@ -797,65 +792,65 @@ void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRe if (direction == OuterShadow) { FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight); if (!shadowInterior.isEmpty()) { - GraphicsContextStateSaver stateSaver(*graphicsContext); - graphicsContext->setFillColor(m_color, m_colorSpace); - graphicsContext->clearShadow(); - graphicsContext->fillRect(shadowInterior); + GraphicsContextStateSaver stateSaver(graphicsContext); + graphicsContext.setFillColor(m_color); + graphicsContext.clearShadow(); + graphicsContext.fillRect(shadowInterior); } } - GraphicsContextStateSaver stateSaver(*graphicsContext); - graphicsContext->setFillColor(m_color, m_colorSpace); - graphicsContext->clearShadow(); + GraphicsContextStateSaver stateSaver(graphicsContext); + graphicsContext.setFillColor(m_color); + graphicsContext.clearShadow(); // Note that drawing the ImageBuffer is faster than creating a Image and drawing that, // because ImageBuffer::draw() knows that it doesn't have to copy the image bits. FloatRect centerRect(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight); - centerRect = graphicsContext->roundToDevicePixels(centerRect); + centerRect = graphicsContext.roundToDevicePixels(centerRect); // Top side. FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice); FloatRect destRect = FloatRect(centerRect.x(), centerRect.y() - topSlice, centerRect.width(), topSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Draw the bottom side. tileRect.setY(templateSize.height() - bottomSlice); tileRect.setHeight(bottomSlice); destRect.setY(centerRect.maxY()); destRect.setHeight(bottomSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Left side. tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength); destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y(), leftSlice, centerRect.height()); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Right side. tileRect.setX(templateSize.width() - rightSlice); tileRect.setWidth(rightSlice); destRect.setX(centerRect.maxX()); destRect.setWidth(rightSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Top left corner. tileRect = FloatRect(0, 0, leftSlice, topSlice); destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y() - topSlice, leftSlice, topSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Top right corner. tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice); destRect = FloatRect(centerRect.maxX(), centerRect.y() - topSlice, rightSlice, topSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Bottom right corner. tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice); destRect = FloatRect(centerRect.maxX(), centerRect.maxY(), rightSlice, bottomSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); // Bottom left corner. tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice); destRect = FloatRect(centerRect.x() - leftSlice, centerRect.maxY(), leftSlice, bottomSlice); - graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + graphicsContext.drawImageBuffer(*m_layerImage, destRect, tileRect); } @@ -875,49 +870,50 @@ void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize) blurShadowBuffer(templateSize); // Mask the image with the shadow color. - GraphicsContext* shadowContext = m_layerImage->context(); - GraphicsContextStateSaver stateSaver(*shadowContext); - shadowContext->setCompositeOperation(CompositeSourceIn); - shadowContext->setFillColor(m_color, m_colorSpace); - shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height())); + GraphicsContext& shadowContext = m_layerImage->context(); + GraphicsContextStateSaver stateSaver(shadowContext); + shadowContext.setCompositeOperation(CompositeSourceIn); + shadowContext.setFillColor(m_color); + shadowContext.fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height())); } -GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext *context, const FloatRect& layerArea) +GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext& context, const FloatRect& layerArea) { adjustBlurRadius(context); - IntRect layerRect = calculateLayerBoundingRect(context, layerArea, context->clipBounds()); + IntSize layerSize = calculateLayerBoundingRect(context, layerArea, context.clipBounds()); - if (layerRect.isEmpty()) - return 0; + if (layerSize.isEmpty()) + return nullptr; // We reset the scratch buffer values here, because the buffer will no longer contain // data from any previous rectangle or inset shadows drawn via the tiling path. - ScratchBuffer::shared().setCachedShadowValues(FloatSize(), Color::black, ColorSpaceDeviceRGB, IntRect(), RoundedRect::Radii(), m_layerSize); - m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size()); + auto& scratchBuffer = ScratchBuffer::singleton(); + scratchBuffer.setCachedShadowValues(FloatSize(), Color::black, IntRect(), FloatRoundedRect::Radii(), m_layerSize); + m_layerImage = scratchBuffer.getScratchBuffer(layerSize); - GraphicsContext* shadowContext = m_layerImage->context(); - shadowContext->save(); + GraphicsContext& shadowContext = m_layerImage->context(); + shadowContext.save(); // Add a pixel to avoid later edge aliasing when rotated. - shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); + shadowContext.clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); - shadowContext->translate(m_layerContextTranslation); - return shadowContext; + shadowContext.translate(m_layerContextTranslation); + return &shadowContext; } -void ShadowBlur::endShadowLayer(GraphicsContext* context) +void ShadowBlur::endShadowLayer(GraphicsContext& context) { - m_layerImage->context()->restore(); + m_layerImage->context().restore(); blurAndColorShadowBuffer(expandedIntSize(m_layerSize)); - GraphicsContextStateSaver stateSave(*context); + GraphicsContextStateSaver stateSave(context); - context->clearShadow(); - context->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, roundedIntPoint(m_layerOrigin), IntRect(0, 0, m_layerSize.width(), m_layerSize.height()), context->compositeOperation()); + context.clearShadow(); + context.drawImageBuffer(*m_layerImage, FloatRect(roundedIntPoint(m_layerOrigin), m_layerSize), FloatRect(FloatPoint(), m_layerSize), context.compositeOperation()); - m_layerImage = 0; - ScratchBuffer::shared().scheduleScratchBufferPurge(); + m_layerImage = nullptr; + ScratchBuffer::singleton().scheduleScratchBufferPurge(); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ShadowBlur.h b/Source/WebCore/platform/graphics/ShadowBlur.h index 32ff60b6c..34869496a 100644 --- a/Source/WebCore/platform/graphics/ShadowBlur.h +++ b/Source/WebCore/platform/graphics/ShadowBlur.h @@ -16,7 +16,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,9 +30,8 @@ #define ShadowBlur_h #include "Color.h" -#include "ColorSpace.h" #include "FloatRect.h" -#include "RoundedRect.h" +#include "FloatRoundedRect.h" #include <wtf/Noncopyable.h> namespace WebCore { @@ -51,20 +50,20 @@ public: BlurShadow }; - ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color&, ColorSpace); + ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color&); ShadowBlur(const GraphicsContextState&); ShadowBlur(); - void setShadowValues(const FloatSize&, const FloatSize& , const Color&, ColorSpace, bool ignoreTransforms = false); + void setShadowValues(const FloatSize&, const FloatSize& , const Color&, bool ignoreTransforms = false); void setShadowsIgnoreTransforms(bool ignoreTransforms) { m_shadowsIgnoreTransforms = ignoreTransforms; } bool shadowsIgnoreTransforms() const { return m_shadowsIgnoreTransforms; } - GraphicsContext* beginShadowLayer(GraphicsContext*, const FloatRect& layerArea); - void endShadowLayer(GraphicsContext*); + GraphicsContext* beginShadowLayer(GraphicsContext&, const FloatRect& layerArea); + void endShadowLayer(GraphicsContext&); - void drawRectShadow(GraphicsContext*, const FloatRect&, const RoundedRect::Radii&); - void drawInsetShadow(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii); + void drawRectShadow(GraphicsContext&, const FloatRoundedRect&); + void drawInsetShadow(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect); void blurLayerImage(unsigned char*, const IntSize&, int stride); @@ -75,25 +74,25 @@ public: private: void updateShadowBlurValues(); - void drawShadowBuffer(GraphicsContext*); + void drawShadowBuffer(GraphicsContext&); - void adjustBlurRadius(GraphicsContext*); + void adjustBlurRadius(GraphicsContext&); enum ShadowDirection { OuterShadow, InnerShadow }; - IntRect calculateLayerBoundingRect(GraphicsContext*, const FloatRect& layerArea, const IntRect& clipRect); - IntSize templateSize(const IntSize& blurredEdgeSize, const RoundedRect::Radii&) const; + IntSize calculateLayerBoundingRect(GraphicsContext&, const FloatRect& layerArea, const IntRect& clipRect); + IntSize templateSize(const IntSize& blurredEdgeSize, const FloatRoundedRect::Radii&) const; - void drawRectShadowWithoutTiling(GraphicsContext*, const FloatRect&, const RoundedRect::Radii&, const IntRect& layerRect); - void drawRectShadowWithTiling(GraphicsContext*, const FloatRect&, const RoundedRect::Radii&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize); + void drawRectShadowWithoutTiling(GraphicsContext&, const FloatRoundedRect&, const IntSize& layerSize); + void drawRectShadowWithTiling(GraphicsContext&, const FloatRoundedRect&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize); - void drawInsetShadowWithoutTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedRect::Radii&, const IntRect& layerRect); - void drawInsetShadowWithTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedRect::Radii&, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize); + void drawInsetShadowWithoutTiling(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect, const IntSize& layerSize); + void drawInsetShadowWithTiling(GraphicsContext&, const FloatRect&, const FloatRoundedRect& holeRect, const IntSize& shadowTemplateSize, const IntSize& blurredEdgeSize); - void drawLayerPieces(GraphicsContext*, const FloatRect& shadowBounds, const RoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, ShadowDirection); + void drawLayerPieces(GraphicsContext&, const FloatRect& shadowBounds, const FloatRoundedRect::Radii&, const IntSize& roundedRadius, const IntSize& templateSize, ShadowDirection); void blurShadowBuffer(const IntSize& templateSize); void blurAndColorShadowBuffer(const IntSize& templateSize); @@ -104,7 +103,6 @@ private: ShadowType m_type; Color m_color; - ColorSpace m_colorSpace; FloatSize m_blurRadius; FloatSize m_offset; diff --git a/Source/WebCore/platform/graphics/SimpleFontData.cpp b/Source/WebCore/platform/graphics/SimpleFontData.cpp deleted file mode 100644 index b0627ad24..000000000 --- a/Source/WebCore/platform/graphics/SimpleFontData.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2006 Alexey Proskuryakov - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SimpleFontData.h" - -#include "Font.h" -#include "FontCache.h" -#include <wtf/MathExtras.h> - -#if ENABLE(OPENTYPE_VERTICAL) -#include "OpenTypeVerticalData.h" -#endif - -namespace WebCore { - -const float smallCapsFontSizeMultiplier = 0.7f; -const float emphasisMarkFontSizeMultiplier = 0.5f; - -SimpleFontData::SimpleFontData(const FontPlatformData& platformData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback) - : m_maxCharWidth(-1) - , m_avgCharWidth(-1) - , m_platformData(platformData) - , m_treatAsFixedPitch(false) - , m_isCustomFont(isCustomFont) - , m_isLoading(isLoading) - , m_isTextOrientationFallback(isTextOrientationFallback) - , m_isBrokenIdeographFallback(false) -#if ENABLE(OPENTYPE_VERTICAL) - , m_verticalData(0) -#endif - , m_hasVerticalGlyphs(false) -{ - platformInit(); - platformGlyphInit(); - platformCharWidthInit(); -#if ENABLE(OPENTYPE_VERTICAL) - if (platformData.orientation() == Vertical && !isTextOrientationFallback) { - m_verticalData = platformData.verticalData(); - m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics(); - } -#endif -} - -SimpleFontData::SimpleFontData(std::unique_ptr<AdditionalFontData> fontData, float fontSize, bool syntheticBold, bool syntheticItalic) - : m_platformData(FontPlatformData(fontSize, syntheticBold, syntheticItalic)) - , m_fontData(std::move(fontData)) - , m_treatAsFixedPitch(false) - , m_isCustomFont(true) - , m_isLoading(false) - , m_isTextOrientationFallback(false) - , m_isBrokenIdeographFallback(false) -#if ENABLE(OPENTYPE_VERTICAL) - , m_verticalData(0) -#endif - , m_hasVerticalGlyphs(false) -#if PLATFORM(IOS) - , m_shouldNotBeUsedForArabic(false) -#endif -{ - m_fontData->initializeFontData(this, fontSize); -} - -// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font. -void SimpleFontData::initCharWidths() -{ - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - - // Treat the width of a '0' as the avgCharWidth. - if (m_avgCharWidth <= 0.f && glyphPageZero) { - static const UChar32 digitZeroChar = '0'; - Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter(digitZeroChar).glyph; - if (digitZeroGlyph) - m_avgCharWidth = widthForGlyph(digitZeroGlyph); - } - - // If we can't retrieve the width of a '0', fall back to the x height. - if (m_avgCharWidth <= 0.f) - m_avgCharWidth = m_fontMetrics.xHeight(); - - if (m_maxCharWidth <= 0.f) - m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); -} - -void SimpleFontData::platformGlyphInit() -{ - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - if (!glyphPageZero) { - LOG_ERROR("Failed to get glyph page zero."); - m_spaceGlyph = 0; - m_spaceWidth = 0; - m_zeroGlyph = 0; - m_adjustedSpaceWidth = 0; - determinePitch(); - m_zeroWidthSpaceGlyph = 0; - m_missingGlyphData.fontData = this; - m_missingGlyphData.glyph = 0; - return; - } - - m_zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph; - - // Nasty hack to determine if we should round or ceil space widths. - // If the font is monospace or fake monospace we ceil to ensure that - // every character and the space are the same width. Otherwise we round. - m_spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph; - float width = widthForGlyph(m_spaceGlyph); - m_spaceWidth = width; - m_zeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph; - m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph)); - determinePitch(); - m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width); - - // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. - // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. - // See <http://bugs.webkit.org/show_bug.cgi?id=13178> - // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, - // are mapped to the ZERO WIDTH SPACE glyph. - if (m_zeroWidthSpaceGlyph == m_spaceGlyph) { - m_zeroWidthSpaceGlyph = 0; - LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width will not be overridden."); - } - - m_missingGlyphData.fontData = this; - m_missingGlyphData.glyph = 0; -} - -SimpleFontData::~SimpleFontData() -{ - if (!m_fontData) - platformDestroy(); - - if (isCustomFont()) - GlyphPageTreeNode::pruneTreeCustomFontData(this); - else - GlyphPageTreeNode::pruneTreeFontData(this); -} - -const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const -{ - return this; -} - -Glyph SimpleFontData::glyphForCharacter(UChar32 character) const -{ - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(this, character / GlyphPage::size); - return node->page() ? node->page()->glyphAt(character % GlyphPage::size) : 0; -} - -bool SimpleFontData::isSegmented() const -{ - return false; -} - -PassRefPtr<SimpleFontData> SimpleFontData::verticalRightOrientationFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->verticalRightOrientation) { - FontPlatformData verticalRightPlatformData(m_platformData); - verticalRightPlatformData.setOrientation(Horizontal); - m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, isCustomFont(), false, true); - } - return m_derivedFontData->verticalRightOrientation; -} - -PassRefPtr<SimpleFontData> SimpleFontData::uprightOrientationFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->uprightOrientation) - m_derivedFontData->uprightOrientation = create(m_platformData, isCustomFont(), false, true); - return m_derivedFontData->uprightOrientation; -} - -PassRefPtr<SimpleFontData> SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->smallCaps) - m_derivedFontData->smallCaps = createScaledFontData(fontDescription, smallCapsFontSizeMultiplier); - - return m_derivedFontData->smallCaps; -} - -PassRefPtr<SimpleFontData> SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->emphasisMark) - m_derivedFontData->emphasisMark = createScaledFontData(fontDescription, emphasisMarkFontSizeMultiplier); - - return m_derivedFontData->emphasisMark; -} - -PassRefPtr<SimpleFontData> SimpleFontData::brokenIdeographFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->brokenIdeograph) { - m_derivedFontData->brokenIdeograph = create(m_platformData, isCustomFont(), false); - m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true; - } - return m_derivedFontData->brokenIdeograph; -} - -PassRefPtr<SimpleFontData> SimpleFontData::nonSyntheticItalicFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->nonSyntheticItalic) { - FontPlatformData nonSyntheticItalicFontPlatformData(m_platformData); -#if PLATFORM(MAC) - nonSyntheticItalicFontPlatformData.m_syntheticOblique = false; -#endif - m_derivedFontData->nonSyntheticItalic = create(nonSyntheticItalicFontPlatformData, isCustomFont(), false, true); - } - return m_derivedFontData->nonSyntheticItalic; -} - -#ifndef NDEBUG -String SimpleFontData::description() const -{ - if (isSVGFont()) - return "[SVG font]"; - if (isCustomFont()) - return "[custom font]"; - - return platformData().description(); -} -#endif - -PassOwnPtr<SimpleFontData::DerivedFontData> SimpleFontData::DerivedFontData::create(bool forCustomFont) -{ - return adoptPtr(new DerivedFontData(forCustomFont)); -} - -SimpleFontData::DerivedFontData::~DerivedFontData() -{ - if (!forCustomFont) - return; - - if (smallCaps) - GlyphPageTreeNode::pruneTreeCustomFontData(smallCaps.get()); - if (emphasisMark) - GlyphPageTreeNode::pruneTreeCustomFontData(emphasisMark.get()); - if (brokenIdeograph) - GlyphPageTreeNode::pruneTreeCustomFontData(brokenIdeograph.get()); - if (verticalRightOrientation) - GlyphPageTreeNode::pruneTreeCustomFontData(verticalRightOrientation.get()); - if (uprightOrientation) - GlyphPageTreeNode::pruneTreeCustomFontData(uprightOrientation.get()); -#if PLATFORM(MAC) - if (compositeFontReferences) { - CFDictionaryRef dictionary = CFDictionaryRef(compositeFontReferences.get()); - CFIndex count = CFDictionaryGetCount(dictionary); - if (count > 0) { - Vector<SimpleFontData*, 2> stash(count); - SimpleFontData** fonts = stash.data(); - CFDictionaryGetKeysAndValues(dictionary, 0, (const void **)fonts); - while (count-- > 0 && *fonts) { - RefPtr<SimpleFontData> afont = adoptRef(*fonts++); - GlyphPageTreeNode::pruneTreeCustomFontData(afont.get()); - } - } - } -#endif -} - -PassRefPtr<SimpleFontData> SimpleFontData::createScaledFontData(const FontDescription& fontDescription, float scaleFactor) const -{ - // FIXME: Support scaled fonts that used AdditionalFontData. - if (m_fontData) - return 0; - - return platformCreateScaledFontData(fontDescription, scaleFactor); -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/SimpleFontData.h b/Source/WebCore/platform/graphics/SimpleFontData.h deleted file mode 100644 index ffdde1754..000000000 --- a/Source/WebCore/platform/graphics/SimpleFontData.h +++ /dev/null @@ -1,373 +0,0 @@ -/* - * This file is part of the internal font implementation. - * - * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2007-2008 Torch Mobile, Inc. - * - * 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 SimpleFontData_h -#define SimpleFontData_h - -#include "FontBaseline.h" -#include "FontData.h" -#include "FontMetrics.h" -#include "FontPlatformData.h" -#include "FloatRect.h" -#include "GlyphBuffer.h" -#include "GlyphMetricsMap.h" -#include "GlyphPageTreeNode.h" -#if ENABLE(OPENTYPE_VERTICAL) -#include "OpenTypeVerticalData.h" -#endif -#include "TypesettingFeatures.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/text/StringHash.h> - -#if PLATFORM(MAC) -#include "WebCoreSystemInterface.h" -#endif - -#if PLATFORM(MAC) -#include <wtf/RetainPtr.h> -#endif - -#if (PLATFORM(WIN) && !OS(WINCE)) -#include <usp10.h> -#endif - -#if USE(CAIRO) -#include <cairo.h> -#endif - -namespace WebCore { - -class FontDescription; -class SharedBuffer; -struct WidthIterator; - -enum FontDataVariant { AutoVariant, NormalVariant, SmallCapsVariant, EmphasisMarkVariant, BrokenIdeographVariant }; -enum Pitch { UnknownPitch, FixedPitch, VariablePitch }; - -class SimpleFontData final : public FontData { -public: - class AdditionalFontData { - WTF_MAKE_FAST_ALLOCATED; - public: - virtual ~AdditionalFontData() { } - - virtual void initializeFontData(SimpleFontData*, float fontSize) = 0; - virtual float widthForSVGGlyph(Glyph, float fontSize) const = 0; - virtual bool fillSVGGlyphPage(GlyphPage*, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData*) const = 0; - virtual bool applySVGGlyphSelection(WidthIterator&, GlyphData&, bool mirror, int currentCharacter, unsigned& advanceLength) const = 0; - }; - - // Used to create platform fonts. - static PassRefPtr<SimpleFontData> create(const FontPlatformData& platformData, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false) - { - return adoptRef(new SimpleFontData(platformData, isCustomFont, isLoading, isTextOrientationFallback)); - } - - // Used to create SVG Fonts. - static PassRefPtr<SimpleFontData> create(std::unique_ptr<AdditionalFontData> fontData, float fontSize, bool syntheticBold, bool syntheticItalic) - { - return adoptRef(new SimpleFontData(std::move(fontData), fontSize, syntheticBold, syntheticItalic)); - } - - virtual ~SimpleFontData(); - - static const SimpleFontData* systemFallback() { return reinterpret_cast<const SimpleFontData*>(-1); } - - const FontPlatformData& platformData() const { return m_platformData; } -#if ENABLE(OPENTYPE_VERTICAL) - const OpenTypeVerticalData* verticalData() const { return m_verticalData.get(); } -#endif - - PassRefPtr<SimpleFontData> smallCapsFontData(const FontDescription&) const; - PassRefPtr<SimpleFontData> emphasisMarkFontData(const FontDescription&) const; - PassRefPtr<SimpleFontData> brokenIdeographFontData() const; - PassRefPtr<SimpleFontData> nonSyntheticItalicFontData() const; - - PassRefPtr<SimpleFontData> variantFontData(const FontDescription& description, FontDataVariant variant) const - { - switch (variant) { - case SmallCapsVariant: - return smallCapsFontData(description); - case EmphasisMarkVariant: - return emphasisMarkFontData(description); - case BrokenIdeographVariant: - return brokenIdeographFontData(); - case AutoVariant: - case NormalVariant: - break; - } - ASSERT_NOT_REACHED(); - return const_cast<SimpleFontData*>(this); - } - - PassRefPtr<SimpleFontData> verticalRightOrientationFontData() const; - PassRefPtr<SimpleFontData> uprightOrientationFontData() const; - - bool hasVerticalGlyphs() const { return m_hasVerticalGlyphs; } - bool isTextOrientationFallback() const { return m_isTextOrientationFallback; } - - FontMetrics& fontMetrics() { return m_fontMetrics; } - const FontMetrics& fontMetrics() const { return m_fontMetrics; } - float sizePerUnit() const { return platformData().size() / (fontMetrics().unitsPerEm() ? fontMetrics().unitsPerEm() : 1); } - - float maxCharWidth() const { return m_maxCharWidth; } - void setMaxCharWidth(float maxCharWidth) { m_maxCharWidth = maxCharWidth; } - - float avgCharWidth() const { return m_avgCharWidth; } - void setAvgCharWidth(float avgCharWidth) { m_avgCharWidth = avgCharWidth; } - - FloatRect boundsForGlyph(Glyph) const; - float widthForGlyph(Glyph glyph) const; - FloatRect platformBoundsForGlyph(Glyph) const; - float platformWidthForGlyph(Glyph) const; - - float spaceWidth() const { return m_spaceWidth; } - float adjustedSpaceWidth() const { return m_adjustedSpaceWidth; } - void setSpaceWidth(float spaceWidth) { m_spaceWidth = spaceWidth; } - -#if USE(CG) || USE(CAIRO) - float syntheticBoldOffset() const { return m_syntheticBoldOffset; } -#endif - - Glyph spaceGlyph() const { return m_spaceGlyph; } - void setSpaceGlyph(Glyph spaceGlyph) { m_spaceGlyph = spaceGlyph; } - Glyph zeroWidthSpaceGlyph() const { return m_zeroWidthSpaceGlyph; } - void setZeroWidthSpaceGlyph(Glyph spaceGlyph) { m_zeroWidthSpaceGlyph = spaceGlyph; } - bool isZeroWidthSpaceGlyph(Glyph glyph) const { return glyph == m_zeroWidthSpaceGlyph && glyph; } - Glyph zeroGlyph() const { return m_zeroGlyph; } - void setZeroGlyph(Glyph zeroGlyph) { m_zeroGlyph = zeroGlyph; } - - virtual const SimpleFontData* fontDataForCharacter(UChar32) const override; - virtual bool containsCharacters(const UChar*, int length) const override; - - Glyph glyphForCharacter(UChar32) const; - - void determinePitch(); - Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; } - - AdditionalFontData* fontData() const { return m_fontData.get(); } - bool isSVGFont() const { return m_fontData != nullptr; } - - virtual bool isCustomFont() const override { return m_isCustomFont; } - virtual bool isLoading() const override { return m_isLoading; } - virtual bool isSegmented() const override; - - const GlyphData& missingGlyphData() const { return m_missingGlyphData; } - void setMissingGlyphData(const GlyphData& glyphData) { m_missingGlyphData = glyphData; } - -#ifndef NDEBUG - virtual String description() const override; -#endif - -#if USE(APPKIT) - const SimpleFontData* getCompositeFontReferenceFontData(NSFont *key) const; - NSFont* getNSFont() const { return m_platformData.font(); } -#endif - -#if PLATFORM(IOS) - CTFontRef getCTFont() const { return m_platformData.font(); } - bool shouldNotBeUsedForArabic() const { return m_shouldNotBeUsedForArabic; }; -#endif -#if PLATFORM(MAC) - CFDictionaryRef getCFStringAttributes(TypesettingFeatures, FontOrientation) const; -#endif - -#if PLATFORM(MAC) || USE(HARFBUZZ) - bool canRenderCombiningCharacterSequence(const UChar*, size_t) const; -#endif - - bool applyTransforms(GlyphBufferGlyph* glyphs, GlyphBufferAdvance* advances, size_t glyphCount, TypesettingFeatures typesettingFeatures) const - { - if (isSVGFont()) - return false; -#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080) - wkCTFontTransformOptions options = (typesettingFeatures & Kerning ? wkCTFontTransformApplyPositioning : 0) | (typesettingFeatures & Ligatures ? wkCTFontTransformApplyShaping : 0); - return wkCTFontTransformGlyphs(m_platformData.ctFont(), glyphs, reinterpret_cast<CGSize*>(advances), glyphCount, options); -#else - UNUSED_PARAM(glyphs); - UNUSED_PARAM(advances); - UNUSED_PARAM(glyphCount); - UNUSED_PARAM(typesettingFeatures); - return false; -#endif - } - -#if PLATFORM(WIN) - bool isSystemFont() const { return m_isSystemFont; } -#if !OS(WINCE) // disable unused members to save space - SCRIPT_FONTPROPERTIES* scriptFontProperties() const; - SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } -#endif - static void setShouldApplyMacAscentHack(bool); - static bool shouldApplyMacAscentHack(); - static float ascentConsideringMacAscentHack(const WCHAR*, float ascent, float descent); -#endif - -private: - SimpleFontData(const FontPlatformData&, bool isCustomFont = false, bool isLoading = false, bool isTextOrientationFallback = false); - - SimpleFontData(std::unique_ptr<AdditionalFontData>, float fontSize, bool syntheticBold, bool syntheticItalic); - - void platformInit(); - void platformGlyphInit(); - void platformCharWidthInit(); - void platformDestroy(); - - void initCharWidths(); - - PassRefPtr<SimpleFontData> createScaledFontData(const FontDescription&, float scaleFactor) const; - PassRefPtr<SimpleFontData> platformCreateScaledFontData(const FontDescription&, float scaleFactor) const; - -#if (PLATFORM(WIN) && !OS(WINCE)) - void initGDIFont(); - void platformCommonDestroy(); - FloatRect boundsForGDIGlyph(Glyph glyph) const; - float widthForGDIGlyph(Glyph glyph) const; -#endif - - FontMetrics m_fontMetrics; - float m_maxCharWidth; - float m_avgCharWidth; - - FontPlatformData m_platformData; - std::unique_ptr<AdditionalFontData> m_fontData; - - mutable OwnPtr<GlyphMetricsMap<FloatRect>> m_glyphToBoundsMap; - mutable GlyphMetricsMap<float> m_glyphToWidthMap; - - bool m_treatAsFixedPitch; - bool m_isCustomFont; // Whether or not we are custom font loaded via @font-face - bool m_isLoading; // Whether or not this custom font is still in the act of loading. - - bool m_isTextOrientationFallback; - bool m_isBrokenIdeographFallback; -#if ENABLE(OPENTYPE_VERTICAL) - RefPtr<OpenTypeVerticalData> m_verticalData; -#endif - bool m_hasVerticalGlyphs; - - Glyph m_spaceGlyph; - float m_spaceWidth; - Glyph m_zeroGlyph; - float m_adjustedSpaceWidth; - - Glyph m_zeroWidthSpaceGlyph; - - GlyphData m_missingGlyphData; - - struct DerivedFontData { - static PassOwnPtr<DerivedFontData> create(bool forCustomFont); - ~DerivedFontData(); - - bool forCustomFont; - RefPtr<SimpleFontData> smallCaps; - RefPtr<SimpleFontData> emphasisMark; - RefPtr<SimpleFontData> brokenIdeograph; - RefPtr<SimpleFontData> verticalRightOrientation; - RefPtr<SimpleFontData> uprightOrientation; - RefPtr<SimpleFontData> nonSyntheticItalic; -#if PLATFORM(MAC) - mutable RetainPtr<CFMutableDictionaryRef> compositeFontReferences; -#endif - - private: - DerivedFontData(bool custom) - : forCustomFont(custom) - { - } - }; - - mutable OwnPtr<DerivedFontData> m_derivedFontData; - -#if USE(CG) || USE(CAIRO) - float m_syntheticBoldOffset; -#endif - -#if PLATFORM(MAC) - mutable HashMap<unsigned, RetainPtr<CFDictionaryRef>> m_CFStringAttributes; -#endif - -#if PLATFORM(MAC) || USE(HARFBUZZ) - mutable OwnPtr<HashMap<String, bool>> m_combiningCharacterSequenceSupport; -#endif - -#if PLATFORM(WIN) - bool m_isSystemFont; -#if !OS(WINCE) // disable unused members to save space - mutable SCRIPT_CACHE m_scriptCache; - mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; -#endif -#endif -#if PLATFORM(IOS) - bool m_shouldNotBeUsedForArabic; -#endif -}; - -ALWAYS_INLINE FloatRect SimpleFontData::boundsForGlyph(Glyph glyph) const -{ - if (isZeroWidthSpaceGlyph(glyph)) - return FloatRect(); - - FloatRect bounds; - if (m_glyphToBoundsMap) { - bounds = m_glyphToBoundsMap->metricsForGlyph(glyph); - if (bounds.width() != cGlyphSizeUnknown) - return bounds; - } - - bounds = platformBoundsForGlyph(glyph); - if (!m_glyphToBoundsMap) - m_glyphToBoundsMap = adoptPtr(new GlyphMetricsMap<FloatRect>); - m_glyphToBoundsMap->setMetricsForGlyph(glyph, bounds); - return bounds; -} - -ALWAYS_INLINE float SimpleFontData::widthForGlyph(Glyph glyph) const -{ - if (isZeroWidthSpaceGlyph(glyph)) - return 0; - - float width = m_glyphToWidthMap.metricsForGlyph(glyph); - if (width != cGlyphSizeUnknown) - return width; - - if (m_fontData) - width = m_fontData->widthForSVGGlyph(glyph, m_platformData.size()); -#if ENABLE(OPENTYPE_VERTICAL) - else if (m_verticalData) -#if USE(CG) || USE(CAIRO) - width = m_verticalData->advanceHeight(this, glyph) + m_syntheticBoldOffset; -#else - width = m_verticalData->advanceHeight(this, glyph); -#endif -#endif - else - width = platformWidthForGlyph(glyph); - - m_glyphToWidthMap.setMetricsForGlyph(glyph, width); - return width; -} - -} // namespace WebCore -#endif // SimpleFontData_h diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.h b/Source/WebCore/platform/graphics/SourceBufferPrivate.h index ad27d3602..7ac5dd275 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.h +++ b/Source/WebCore/platform/graphics/SourceBufferPrivate.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2013 Google Inc. All rights reserved. - * Copyright (C) 2013 Orange + * Copyright (C) 2013-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,35 +29,41 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MediaSourceGStreamer_h -#define MediaSourceGStreamer_h +#pragma once -#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) -#include "MediaSource.h" -#include "WebKitMediaSourceGStreamer.h" +#if ENABLE(MEDIA_SOURCE) + +#include "MediaPlayer.h" namespace WebCore { -class MediaSourceGStreamer final : public MediaSourcePrivate { +class MediaSample; +class SourceBufferPrivateClient; + +class SourceBufferPrivate : public RefCounted<SourceBufferPrivate> { public: - static void open(PassRefPtr<HTMLMediaSource>, WebKitMediaSrc*); - ~MediaSourceGStreamer(); - AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&); - double duration() { return m_duration; } - void setDuration(double); - void markEndOfStream(EndOfStreamStatus); - void unmarkEndOfStream(); - MediaPlayer::ReadyState readyState() const { return m_readyState; } - void setReadyState(MediaPlayer::ReadyState readyState) { m_readyState = readyState; } - -private: - RefPtr<MediaSourceClientGstreamer> m_client; - MediaSourceGStreamer(WebKitMediaSrc*); - double m_duration; - MediaPlayer::ReadyState m_readyState; + virtual ~SourceBufferPrivate() { } + + virtual void setClient(SourceBufferPrivateClient*) = 0; + + virtual void append(const unsigned char* data, unsigned length) = 0; + virtual void abort() = 0; + virtual void resetParserState() = 0; + virtual void removedFromMediaSource() = 0; + + virtual MediaPlayer::ReadyState readyState() const = 0; + virtual void setReadyState(MediaPlayer::ReadyState) = 0; + + virtual void flush(const AtomicString&) { } + virtual void enqueueSample(Ref<MediaSample>&&, const AtomicString&) { } + virtual bool isReadyForMoreSamples(const AtomicString&) { return false; } + virtual void setActive(bool) { } + virtual void stopAskingForMoreSamples(const AtomicString&) { } + virtual void notifyClientWhenReadyForMoreSamples(const AtomicString&) { } + + virtual Vector<String> enqueuedSamplesForTrackID(const AtomicString&) { return { }; } }; } #endif -#endif diff --git a/Source/WebCore/platform/graphics/SourceBufferPrivateClient.h b/Source/WebCore/platform/graphics/SourceBufferPrivateClient.h new file mode 100644 index 000000000..18087d5e3 --- /dev/null +++ b/Source/WebCore/platform/graphics/SourceBufferPrivateClient.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013-2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(MEDIA_SOURCE) + +#include <wtf/MediaTime.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AudioTrackPrivate; +class InbandTextTrackPrivate; +class MediaSample; +class MediaDescription; +class VideoTrackPrivate; + +class SourceBufferPrivateClient { +public: + virtual ~SourceBufferPrivateClient() { } + + struct InitializationSegment { + MediaTime duration; + + struct AudioTrackInformation { + RefPtr<MediaDescription> description; + RefPtr<AudioTrackPrivate> track; + }; + Vector<AudioTrackInformation> audioTracks; + + struct VideoTrackInformation { + RefPtr<MediaDescription> description; + RefPtr<VideoTrackPrivate> track; + }; + Vector<VideoTrackInformation> videoTracks; + + struct TextTrackInformation { + RefPtr<MediaDescription> description; + RefPtr<InbandTextTrackPrivate> track; + }; + Vector<TextTrackInformation> textTracks; + }; + virtual void sourceBufferPrivateDidReceiveInitializationSegment(const InitializationSegment&) = 0; + virtual void sourceBufferPrivateDidReceiveSample(MediaSample&) = 0; + virtual bool sourceBufferPrivateHasAudio() const = 0; + virtual bool sourceBufferPrivateHasVideo() const = 0; + + virtual void sourceBufferPrivateDidBecomeReadyForMoreSamples(const AtomicString& trackID) = 0; + + virtual MediaTime sourceBufferPrivateFastSeekTimeForMediaTime(const MediaTime& time, const MediaTime&, const MediaTime&) { return time; } + virtual void sourceBufferPrivateSeekToTime(const MediaTime&) { }; + + enum AppendResult { AppendSucceeded, ReadStreamFailed, ParsingFailed }; + virtual void sourceBufferPrivateAppendComplete(AppendResult) = 0; + virtual void sourceBufferPrivateDidReceiveRenderingError(int errorCode) = 0; +}; + +} + +#endif // ENABLE(MEDIA_SOURCE) diff --git a/Source/WebCore/platform/graphics/SpringSolver.h b/Source/WebCore/platform/graphics/SpringSolver.h new file mode 100644 index 000000000..e7ae9bcfa --- /dev/null +++ b/Source/WebCore/platform/graphics/SpringSolver.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +class SpringSolver { +public: + SpringSolver(double mass, double stiffness, double damping, double initialVelocity) + { + m_w0 = std::sqrt(stiffness / mass); + m_zeta = damping / (2 * std::sqrt(stiffness * mass)); + + if (m_zeta < 1) { + // Under-damped. + m_wd = m_w0 * std::sqrt(1 - m_zeta * m_zeta); + m_A = 1; + m_B = (m_zeta * m_w0 + -initialVelocity) / m_wd; + } else { + // Critically damped (ignoring over-damped case for now). + m_A = 1; + m_B = -initialVelocity + m_w0; + } + } + + double solve(double t) + { + if (m_zeta < 1) { + // Under-damped + t = std::exp(-t * m_zeta * m_w0) * (m_A * std::cos(m_wd * t) + m_B * std::sin(m_wd * t)); + } else { + // Critically damped (ignoring over-damped case for now). + t = (m_A + m_B * t) * std::exp(-t * m_w0); + } + + // Map range from [1..0] to [0..1]. + return 1 - t; + } + +private: + double m_w0; + double m_zeta; + double m_wd; + double m_A; + double m_B; +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/StringTruncator.cpp b/Source/WebCore/platform/graphics/StringTruncator.cpp index 72f80c03d..85a3ad29a 100644 --- a/Source/WebCore/platform/graphics/StringTruncator.cpp +++ b/Source/WebCore/platform/graphics/StringTruncator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -29,11 +29,13 @@ #include "config.h" #include "StringTruncator.h" -#include "Font.h" -#include "TextBreakIterator.h" +#include "FontCascade.h" +#include <wtf/text/TextBreakIterator.h> #include "TextRun.h" +#include <unicode/ubrk.h> #include <wtf/Assertions.h> #include <wtf/Vector.h> +#include <wtf/text/StringView.h> #include <wtf/unicode/CharacterNames.h> namespace WebCore { @@ -42,19 +44,19 @@ namespace WebCore { typedef unsigned TruncationFunction(const String&, unsigned length, unsigned keepCount, UChar* buffer, bool shouldInsertEllipsis); -static inline int textBreakAtOrPreceding(TextBreakIterator* it, int offset) +static inline int textBreakAtOrPreceding(UBreakIterator* it, int offset) { - if (isTextBreak(it, offset)) + if (ubrk_isBoundary(it, offset)) return offset; - int result = textBreakPreceding(it, offset); - return result == TextBreakDone ? 0 : result; + int result = ubrk_preceding(it, offset); + return result == UBRK_DONE ? 0 : result; } -static inline int boundedTextBreakFollowing(TextBreakIterator* it, int offset, int length) +static inline int boundedTextBreakFollowing(UBreakIterator* it, int offset, int length) { - int result = textBreakFollowing(it, offset); - return result == TextBreakDone ? length : result; + int result = ubrk_following(it, offset); + return result == UBRK_DONE ? length : result; } static unsigned centerTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer, bool shouldInsertEllipsis) @@ -69,6 +71,8 @@ static unsigned centerTruncateToBuffer(const String& string, unsigned length, un #if PLATFORM(IOS) // FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard. + // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize + // it to handle all whitespace, not just "space". // Strip single character before ellipsis character, when that character is preceded by a space if (omitStart > 1 && string[omitStart - 1] != space && omitStart > 2 && string[omitStart - 2] == space) @@ -86,15 +90,13 @@ static unsigned centerTruncateToBuffer(const String& string, unsigned length, un ++omitEnd; #endif - unsigned truncatedLength = shouldInsertEllipsis ? omitStart + 1 + (length - omitEnd) : length - (omitEnd - omitStart); + unsigned truncatedLength = omitStart + shouldInsertEllipsis + (length - omitEnd); ASSERT(truncatedLength <= length); - memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * omitStart); - if (shouldInsertEllipsis) { - buffer[omitStart] = horizontalEllipsis; - memcpy(&buffer[omitStart + 1], &string.deprecatedCharacters()[omitEnd], sizeof(UChar) * (length - omitEnd)); - } else - memcpy(&buffer[omitStart], &string.deprecatedCharacters()[omitEnd], sizeof(UChar) * (length - omitEnd)); + StringView(string).substring(0, omitStart).getCharactersWithUpconvert(buffer); + if (shouldInsertEllipsis) + buffer[omitStart++] = horizontalEllipsis; + StringView(string).substring(omitEnd, length - omitEnd).getCharactersWithUpconvert(&buffer[omitStart]); return truncatedLength; } @@ -105,6 +107,8 @@ static unsigned rightTruncateToBuffer(const String& string, unsigned length, uns #if PLATFORM(IOS) // FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard. + // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize + // it to handle all whitespace, not just "space". // Strip single character before ellipsis character, when that character is preceded by a space if (keepCount > 1 && string[keepCount - 1] != space && keepCount > 2 && string[keepCount - 2] == space) @@ -119,7 +123,7 @@ static unsigned rightTruncateToBuffer(const String& string, unsigned length, uns unsigned keepLength = textBreakAtOrPreceding(it, keepCount); unsigned truncatedLength = shouldInsertEllipsis ? keepLength + 1 : keepLength; - memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * keepLength); + StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer); if (shouldInsertEllipsis) buffer[keepLength] = horizontalEllipsis; @@ -133,7 +137,7 @@ static unsigned rightClipToCharacterBuffer(const String& string, unsigned length NonSharedCharacterBreakIterator it(StringView(string).substring(0, length)); unsigned keepLength = textBreakAtOrPreceding(it, keepCount); - memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * keepLength); + StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer); return keepLength; } @@ -143,16 +147,20 @@ static unsigned rightClipToWordBuffer(const String& string, unsigned length, uns ASSERT(keepCount < length); ASSERT(keepCount < STRING_BUFFER_SIZE); - TextBreakIterator* it = wordBreakIterator(StringView(string).substring(0, length)); + UBreakIterator* it = wordBreakIterator(StringView(string).substring(0, length)); unsigned keepLength = textBreakAtOrPreceding(it, keepCount); - memcpy(buffer, string.deprecatedCharacters(), sizeof(UChar) * keepLength); + StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer); #if PLATFORM(IOS) // FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard. + // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize + // it to handle all whitespace, not just "space". + // Motivated by <rdar://problem/7439327> truncation should not include a trailing space - while ((keepLength > 0) && (string[keepLength - 1] == space)) + while (keepLength && string[keepLength - 1] == space) --keepLength; #endif + return keepLength; } @@ -165,7 +173,7 @@ static unsigned leftTruncateToBuffer(const String& string, unsigned length, unsi NonSharedCharacterBreakIterator it(string); unsigned adjustedStartIndex = startIndex; - startIndex = boundedTextBreakFollowing(it, startIndex, length - startIndex); + boundedTextBreakFollowing(it, startIndex, length - startIndex); // Strip single character after ellipsis character, when that character is preceded by a space if (adjustedStartIndex < length && string[adjustedStartIndex] != space @@ -178,22 +186,20 @@ static unsigned leftTruncateToBuffer(const String& string, unsigned length, unsi if (shouldInsertEllipsis) { buffer[0] = horizontalEllipsis; - memcpy(&buffer[1], &string.deprecatedCharacters()[adjustedStartIndex], sizeof(UChar) * (length - adjustedStartIndex + 1)); + StringView(string).substring(adjustedStartIndex, length - adjustedStartIndex + 1).getCharactersWithUpconvert(&buffer[1]); return length - adjustedStartIndex + 1; } - memcpy(&buffer[0], &string.deprecatedCharacters()[adjustedStartIndex], sizeof(UChar) * (length - adjustedStartIndex + 1)); + StringView(string).substring(adjustedStartIndex, length - adjustedStartIndex + 1).getCharactersWithUpconvert(&buffer[0]); return length - adjustedStartIndex; } -static float stringWidth(const Font& renderer, const UChar* characters, unsigned length, bool disableRoundingHacks) +static float stringWidth(const FontCascade& renderer, const UChar* characters, unsigned length) { - TextRun run(characters, length); - if (disableRoundingHacks) - run.disableRoundingHacks(); + TextRun run(StringView(characters, length)); return renderer.width(run); } -static String truncateString(const String& string, float maxWidth, const Font& font, TruncationFunction truncateToBuffer, bool disableRoundingHacks, float* resultWidth = nullptr, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false) +static String truncateString(const String& string, float maxWidth, const FontCascade& font, TruncationFunction truncateToBuffer, float* resultWidth = nullptr, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false) { if (string.isEmpty()) return string; @@ -203,7 +209,7 @@ static String truncateString(const String& string, float maxWidth, const Font& f ASSERT(maxWidth >= 0); - float currentEllipsisWidth = shouldInsertEllipsis ? stringWidth(font, &horizontalEllipsis, 1, disableRoundingHacks) : customTruncationElementWidth; + float currentEllipsisWidth = shouldInsertEllipsis ? stringWidth(font, &horizontalEllipsis, 1) : customTruncationElementWidth; UChar stringBuffer[STRING_BUFFER_SIZE]; unsigned truncatedLength; @@ -218,11 +224,11 @@ static String truncateString(const String& string, float maxWidth, const Font& f truncatedLength = centerTruncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis); } else { keepCount = length; - memcpy(stringBuffer, string.deprecatedCharacters(), sizeof(UChar) * length); + StringView(string).getCharactersWithUpconvert(stringBuffer); truncatedLength = length; } - float width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks); + float width = stringWidth(font, stringBuffer, truncatedLength); if (!shouldInsertEllipsis && alwaysTruncate) width += customTruncationElementWidth; if ((width - maxWidth) < 0.0001) { // Ignore rounding errors. @@ -250,11 +256,10 @@ static String truncateString(const String& string, float maxWidth, const Font& f / (widthForSmallestKnownToNotFit - widthForLargestKnownToFit); keepCount = static_cast<unsigned>(maxWidth * ratio); - if (keepCount <= keepCountForLargestKnownToFit) { + if (keepCount <= keepCountForLargestKnownToFit) keepCount = keepCountForLargestKnownToFit + 1; - } else if (keepCount >= keepCountForSmallestKnownToNotFit) { + else if (keepCount >= keepCountForSmallestKnownToNotFit) keepCount = keepCountForSmallestKnownToNotFit - 1; - } ASSERT_WITH_SECURITY_IMPLICATION(keepCount < length); ASSERT(keepCount > 0); @@ -263,7 +268,7 @@ static String truncateString(const String& string, float maxWidth, const Font& f truncatedLength = truncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis); - width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks); + width = stringWidth(font, stringBuffer, truncatedLength); if (!shouldInsertEllipsis) width += customTruncationElementWidth; if (width <= maxWidth) { @@ -289,44 +294,44 @@ static String truncateString(const String& string, float maxWidth, const Font& f return String(stringBuffer, truncatedLength); } -String StringTruncator::centerTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks) +String StringTruncator::centerTruncate(const String& string, float maxWidth, const FontCascade& font) { - return truncateString(string, maxWidth, font, centerTruncateToBuffer, !enableRoundingHacks); + return truncateString(string, maxWidth, font, centerTruncateToBuffer); } -String StringTruncator::rightTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks) +String StringTruncator::rightTruncate(const String& string, float maxWidth, const FontCascade& font) { - return truncateString(string, maxWidth, font, rightTruncateToBuffer, !enableRoundingHacks); + return truncateString(string, maxWidth, font, rightTruncateToBuffer); } -float StringTruncator::width(const String& string, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks) +float StringTruncator::width(const String& string, const FontCascade& font) { - return stringWidth(font, string.deprecatedCharacters(), string.length(), !enableRoundingHacks); + return stringWidth(font, StringView(string).upconvertedCharacters(), string.length()); } -String StringTruncator::centerTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) +String StringTruncator::centerTruncate(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) { - return truncateString(string, maxWidth, font, centerTruncateToBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); + return truncateString(string, maxWidth, font, centerTruncateToBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); } -String StringTruncator::rightTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) +String StringTruncator::rightTruncate(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) { - return truncateString(string, maxWidth, font, rightTruncateToBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); + return truncateString(string, maxWidth, font, rightTruncateToBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); } -String StringTruncator::leftTruncate(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) +String StringTruncator::leftTruncate(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) { - return truncateString(string, maxWidth, font, leftTruncateToBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); + return truncateString(string, maxWidth, font, leftTruncateToBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); } -String StringTruncator::rightClipToCharacter(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) +String StringTruncator::rightClipToCharacter(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth) { - return truncateString(string, maxWidth, font, rightClipToCharacterBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); + return truncateString(string, maxWidth, font, rightClipToCharacterBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth); } -String StringTruncator::rightClipToWord(const String& string, float maxWidth, const Font& font, EnableRoundingHacksOrNot enableRoundingHacks, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth, bool alwaysTruncate) +String StringTruncator::rightClipToWord(const String& string, float maxWidth, const FontCascade& font, float& resultWidth, bool shouldInsertEllipsis, float customTruncationElementWidth, bool alwaysTruncate) { - return truncateString(string, maxWidth, font, rightClipToWordBuffer, !enableRoundingHacks, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth, alwaysTruncate); + return truncateString(string, maxWidth, font, rightClipToWordBuffer, &resultWidth, shouldInsertEllipsis, customTruncationElementWidth, alwaysTruncate); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/StringTruncator.h b/Source/WebCore/platform/graphics/StringTruncator.h index 13d8e14af..edb22eb05 100644 --- a/Source/WebCore/platform/graphics/StringTruncator.h +++ b/Source/WebCore/platform/graphics/StringTruncator.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -33,23 +33,21 @@ namespace WebCore { - class Font; - - class StringTruncator { - public: - enum EnableRoundingHacksOrNot { DisableRoundingHacks, EnableRoundingHacks }; +class FontCascade; - static String centerTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot = DisableRoundingHacks); - static String rightTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot = DisableRoundingHacks); +class StringTruncator { +public: + WEBCORE_EXPORT static String centerTruncate(const String&, float maxWidth, const FontCascade&); + WEBCORE_EXPORT static String rightTruncate(const String&, float maxWidth, const FontCascade&); - static String centerTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); - static String rightTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); - static String leftTruncate(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); - static String rightClipToCharacter(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); - static String rightClipToWord(const String&, float maxWidth, const Font&, EnableRoundingHacksOrNot, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false); + WEBCORE_EXPORT static String centerTruncate(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); + WEBCORE_EXPORT static String rightTruncate(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); + WEBCORE_EXPORT static String leftTruncate(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); + WEBCORE_EXPORT static String rightClipToCharacter(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0); + WEBCORE_EXPORT static String rightClipToWord(const String&, float maxWidth, const FontCascade&, float& resultWidth, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false); - static float width(const String&, const Font&, EnableRoundingHacksOrNot = DisableRoundingHacks); - }; + WEBCORE_EXPORT static float width(const String&, const FontCascade&); +}; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp index 9155e711a..682d98e25 100644 --- a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp +++ b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp @@ -27,11 +27,11 @@ namespace WebCore { -SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator(const UChar* characters, int currentCharacter, int lastCharacter, int endCharacter) +SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator(const UChar* characters, unsigned currentIndex, unsigned lastIndex, unsigned endIndex) : m_characters(characters) - , m_currentCharacter(currentCharacter) - , m_lastCharacter(lastCharacter) - , m_endCharacter(endCharacter) + , m_currentIndex(currentIndex) + , m_lastIndex(lastIndex) + , m_endIndex(endIndex) { } @@ -57,7 +57,7 @@ bool SurrogatePairAwareTextIterator::consumeSlowCase(UChar32& character, unsigne // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) code point before glyph lookup. // Make sure we have another character and it's a low surrogate. - if (m_currentCharacter + 1 >= m_endCharacter) + if (m_currentIndex + 1 >= m_endIndex) return false; UChar low = m_characters[1]; @@ -74,7 +74,7 @@ UChar32 SurrogatePairAwareTextIterator::normalizeVoicingMarks() // According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values static const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; - if (m_currentCharacter + 1 >= m_endCharacter) + if (m_currentIndex + 1 >= m_endIndex) return 0; if (u_getCombiningClass(m_characters[1]) == hiraganaKatakanaVoicingMarksCombiningClass) { diff --git a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h index 85c9694c7..470d14af2 100644 --- a/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h +++ b/Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h @@ -28,13 +28,13 @@ namespace WebCore { class SurrogatePairAwareTextIterator { public: - // The passed in UChar pointer starts at 'currentCharacter'. The iterator operatoes on the range [currentCharacter, lastCharacter]. - // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastCharacter'. - SurrogatePairAwareTextIterator(const UChar*, int currentCharacter, int lastCharacter, int endCharacter); + // The passed in UChar pointer starts at 'currentIndex'. The iterator operatoes on the range [currentIndex, lastIndex]. + // 'endIndex' denotes the maximum length of the UChar array, which might exceed 'lastIndex'. + SurrogatePairAwareTextIterator(const UChar*, unsigned currentIndex, unsigned lastIndex, unsigned endIndex); inline bool consume(UChar32& character, unsigned& clusterLength) { - if (m_currentCharacter >= m_lastCharacter) + if (m_currentIndex >= m_lastIndex) return false; character = *m_characters; @@ -49,10 +49,10 @@ public: void advance(unsigned advanceLength) { m_characters += advanceLength; - m_currentCharacter += advanceLength; + m_currentIndex += advanceLength; } - int currentCharacter() const { return m_currentCharacter; } + unsigned currentIndex() const { return m_currentIndex; } const UChar* characters() const { return m_characters; } private: @@ -60,9 +60,9 @@ private: UChar32 normalizeVoicingMarks(); const UChar* m_characters; - int m_currentCharacter; - int m_lastCharacter; - int m_endCharacter; + unsigned m_currentIndex; + unsigned m_lastIndex; + unsigned m_endIndex; }; } diff --git a/Source/WebCore/platform/graphics/TextRun.cpp b/Source/WebCore/platform/graphics/TextRun.cpp index 7d28b1f0f..884cf9b19 100644 --- a/Source/WebCore/platform/graphics/TextRun.cpp +++ b/Source/WebCore/platform/graphics/TextRun.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,30 +29,15 @@ namespace WebCore { struct ExpectedTextRunSize { - const void* pointer; - int integers[2]; + StringView text; + unsigned integer1; + unsigned integer2; float float1; -#if ENABLE(SVG) float float2; -#endif float float3; - uint32_t bitfields : 10; - unsigned anUnsigned; - RefPtr<TextRun::RenderingContext> renderingContext; + unsigned bitfields : 9; }; COMPILE_ASSERT(sizeof(TextRun) == sizeof(ExpectedTextRunSize), TextRun_is_not_of_expected_size); -bool TextRun::s_allowsRoundingHacks = false; - -void TextRun::setAllowsRoundingHacks(bool allowsRoundingHacks) -{ - s_allowsRoundingHacks = allowsRoundingHacks; -} - -bool TextRun::allowsRoundingHacks() -{ - return s_allowsRoundingHacks; -} - } diff --git a/Source/WebCore/platform/graphics/TextRun.h b/Source/WebCore/platform/graphics/TextRun.h index 213b26372..a80fe7e61 100644 --- a/Source/WebCore/platform/graphics/TextRun.h +++ b/Source/WebCore/platform/graphics/TextRun.h @@ -24,164 +24,76 @@ #ifndef TextRun_h #define TextRun_h -#include "TextDirection.h" +#include "TextFlags.h" +#include "WritingMode.h" #include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> +#include <wtf/text/StringView.h> namespace WebCore { class FloatPoint; class FloatRect; -class Font; +class FontCascade; class GraphicsContext; class GlyphBuffer; -class SimpleFontData; +class GlyphToPathTranslator; +class Font; + struct GlyphData; struct WidthIterator; class TextRun { WTF_MAKE_FAST_ALLOCATED; public: - enum ExpansionBehaviorFlags { - ForbidTrailingExpansion = 0 << 0, - AllowTrailingExpansion = 1 << 0, - ForbidLeadingExpansion = 0 << 1, - AllowLeadingExpansion = 1 << 1, - }; - - typedef unsigned ExpansionBehavior; - - enum RoundingHackFlags { - NoRounding = 0, - RunRounding = 1 << 0, - WordRounding = 1 << 1, - }; - - typedef unsigned RoundingHacks; - -#if ENABLE(8BIT_TEXTRUN) - TextRun(const LChar* c, unsigned len, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true, RoundingHacks roundingHacks = RunRounding | WordRounding) - : m_charactersLength(len) - , m_len(len) - , m_xpos(xpos) -#if ENABLE(SVG) - , m_horizontalGlyphStretch(1) -#endif - , m_expansion(expansion) - , m_expansionBehavior(expansionBehavior) - , m_is8Bit(true) - , m_allowTabs(false) - , m_direction(direction) - , m_directionalOverride(directionalOverride) - , m_characterScanForCodePath(characterScanForCodePath) - , m_applyRunRounding((roundingHacks & RunRounding) && s_allowsRoundingHacks) - , m_applyWordRounding((roundingHacks & WordRounding) && s_allowsRoundingHacks) - , m_disableSpacing(false) - , m_tabSize(0) - { - m_data.characters8 = c; - } -#endif - - TextRun(const UChar* c, unsigned len, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true, RoundingHacks roundingHacks = RunRounding | WordRounding) - : m_charactersLength(len) - , m_len(len) - , m_xpos(xpos) -#if ENABLE(SVG) - , m_horizontalGlyphStretch(1) -#endif - , m_expansion(expansion) - , m_expansionBehavior(expansionBehavior) - , m_is8Bit(false) - , m_allowTabs(false) - , m_direction(direction) - , m_directionalOverride(directionalOverride) - , m_characterScanForCodePath(characterScanForCodePath) - , m_applyRunRounding((roundingHacks & RunRounding) && s_allowsRoundingHacks) - , m_applyWordRounding((roundingHacks & WordRounding) && s_allowsRoundingHacks) - , m_disableSpacing(false) + explicit TextRun(StringView text, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = DefaultExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true) + : m_text(text) + , m_charactersLength(text.length()) , m_tabSize(0) - { - m_data.characters16 = c; - } - - TextRun(const String& s, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true, RoundingHacks roundingHacks = RunRounding | WordRounding) - : m_charactersLength(s.length()) - , m_len(s.length()) , m_xpos(xpos) -#if ENABLE(SVG) , m_horizontalGlyphStretch(1) -#endif , m_expansion(expansion) , m_expansionBehavior(expansionBehavior) , m_allowTabs(false) , m_direction(direction) , m_directionalOverride(directionalOverride) , m_characterScanForCodePath(characterScanForCodePath) - , m_applyRunRounding((roundingHacks & RunRounding) && s_allowsRoundingHacks) - , m_applyWordRounding((roundingHacks & WordRounding) && s_allowsRoundingHacks) , m_disableSpacing(false) - , m_tabSize(0) { -#if ENABLE(8BIT_TEXTRUN) - if (m_charactersLength && s.is8Bit()) { - m_data.characters8 = s.characters8(); - m_is8Bit = true; - } else { - m_data.characters16 = s.deprecatedCharacters(); - m_is8Bit = false; - } -#else - m_data.characters16 = s.deprecatedCharacters(); - m_is8Bit = false; -#endif } TextRun subRun(unsigned startOffset, unsigned length) const { - ASSERT_WITH_SECURITY_IMPLICATION(startOffset < m_len); + ASSERT_WITH_SECURITY_IMPLICATION(startOffset < m_text.length()); TextRun result = *this; -#if ENABLE(8BIT_TEXTRUN) if (is8Bit()) { result.setText(data8(startOffset), length); return result; } -#else - ASSERT(!is8Bit()); -#endif result.setText(data16(startOffset), length); return result; } - UChar operator[](unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); return is8Bit() ? m_data.characters8[i] :m_data.characters16[i]; } - const LChar* data8(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); ASSERT(is8Bit()); return &m_data.characters8[i]; } - const UChar* data16(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); ASSERT(!is8Bit()); return &m_data.characters16[i]; } + UChar operator[](unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_text.length()); return m_text[i]; } + const LChar* data8(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_text.length()); ASSERT(is8Bit()); return &m_text.characters8()[i]; } + const UChar* data16(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_text.length()); ASSERT(!is8Bit()); return &m_text.characters16()[i]; } - const LChar* characters8() const { ASSERT(is8Bit()); return m_data.characters8; } - const UChar* characters16() const { ASSERT(!is8Bit()); return m_data.characters16; } - - bool is8Bit() const { return m_is8Bit; } - int length() const { return m_len; } - int charactersLength() const { return m_charactersLength; } - String string() const - { - if (is8Bit()) - return String(m_data.characters8, m_len); - return String(m_data.characters16, m_len); - } + const LChar* characters8() const { ASSERT(is8Bit()); return m_text.characters8(); } + const UChar* characters16() const { ASSERT(!is8Bit()); return m_text.characters16(); } -#if ENABLE(8BIT_TEXTRUN) - void setText(const LChar* c, unsigned len) { m_data.characters8 = c; m_len = len; m_is8Bit = true;} -#endif - void setText(const UChar* c, unsigned len) { m_data.characters16 = c; m_len = len; m_is8Bit = false;} + bool is8Bit() const { return m_text.is8Bit(); } + unsigned length() const { return m_text.length(); } + unsigned charactersLength() const { return m_charactersLength; } + String string() const { return m_text.toString(); } + + void setText(const LChar* c, unsigned len) { m_text = StringView(c, len); } + void setText(const UChar* c, unsigned len) { m_text = StringView(c, len); } + void setText(StringView stringView) { m_text = stringView; } void setCharactersLength(unsigned charactersLength) { m_charactersLength = charactersLength; } -#if ENABLE(SVG) float horizontalGlyphStretch() const { return m_horizontalGlyphStretch; } void setHorizontalGlyphStretch(float scale) { m_horizontalGlyphStretch = scale; } -#endif bool allowTabs() const { return m_allowTabs; } unsigned tabSize() const { return m_tabSize; } @@ -190,71 +102,40 @@ public: float xPos() const { return m_xpos; } void setXPos(float xPos) { m_xpos = xPos; } float expansion() const { return m_expansion; } - bool allowsLeadingExpansion() const { return m_expansionBehavior & AllowLeadingExpansion; } - bool allowsTrailingExpansion() const { return m_expansionBehavior & AllowTrailingExpansion; } + ExpansionBehavior expansionBehavior() const { return m_expansionBehavior; } TextDirection direction() const { return static_cast<TextDirection>(m_direction); } bool rtl() const { return m_direction == RTL; } bool ltr() const { return m_direction == LTR; } bool directionalOverride() const { return m_directionalOverride; } bool characterScanForCodePath() const { return m_characterScanForCodePath; } - bool applyRunRounding() const { return m_applyRunRounding; } - bool applyWordRounding() const { return m_applyWordRounding; } bool spacingDisabled() const { return m_disableSpacing; } void disableSpacing() { m_disableSpacing = true; } - void disableRoundingHacks() { m_applyRunRounding = m_applyWordRounding = false; } void setDirection(TextDirection direction) { m_direction = direction; } void setDirectionalOverride(bool override) { m_directionalOverride = override; } void setCharacterScanForCodePath(bool scan) { m_characterScanForCodePath = scan; } - - class RenderingContext : public RefCounted<RenderingContext> { - public: - virtual ~RenderingContext() { } - -#if ENABLE(SVG_FONTS) - virtual GlyphData glyphDataForCharacter(const Font&, WidthIterator&, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) = 0; - virtual void drawSVGGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, int from, int to, const FloatPoint&) const = 0; - virtual float floatWidthUsingSVGFont(const Font&, const TextRun&, int& charsConsumed, String& glyphName) const = 0; - virtual bool applySVGKerning(const SimpleFontData*, WidthIterator&, GlyphBuffer*, int from) const = 0; -#endif - }; - - RenderingContext* renderingContext() const { return m_renderingContext.get(); } - void setRenderingContext(PassRefPtr<RenderingContext> context) { m_renderingContext = context; } - - static void setAllowsRoundingHacks(bool); - static bool allowsRoundingHacks(); + StringView text() const { return m_text; } private: - static bool s_allowsRoundingHacks; + StringView m_text; + unsigned m_charactersLength; // Marks the end of the characters buffer. Default equals to length of m_text. - union { - const LChar* characters8; - const UChar* characters16; - } m_data; - unsigned m_charactersLength; // Marks the end of the characters buffer. Default equals to m_len. - unsigned m_len; + unsigned m_tabSize; // m_xpos is the x position relative to the left start of the text line, not relative to the left // start of the containing block. In the case of right alignment or center alignment, left start of - // the text line is not the same as left start of the containing block. + // the text line is not the same as left start of the containing block. This variable is only used + // to calculate the width of \t float m_xpos; -#if ENABLE(SVG) float m_horizontalGlyphStretch; -#endif float m_expansion; - ExpansionBehavior m_expansionBehavior : 2; - unsigned m_is8Bit : 1; + unsigned m_expansionBehavior : 4; unsigned m_allowTabs : 1; unsigned m_direction : 1; unsigned m_directionalOverride : 1; // Was this direction set by an override character. unsigned m_characterScanForCodePath : 1; - unsigned m_applyRunRounding : 1; - unsigned m_applyWordRounding : 1; unsigned m_disableSpacing : 1; - unsigned m_tabSize; - RefPtr<RenderingContext> m_renderingContext; }; inline void TextRun::setTabSize(bool allow, unsigned size) diff --git a/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp b/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp index d178e586f..d06d3a2de 100644 --- a/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp +++ b/Source/WebCore/platform/graphics/TextTrackRepresentation.cpp @@ -29,24 +29,26 @@ #include "TextTrackRepresentation.h" +#include "IntRect.h" + namespace WebCore { class NullTextTrackRepresentation : public TextTrackRepresentation { public: virtual ~NullTextTrackRepresentation() { } - virtual void update() { } -#if USE(ACCELERATED_COMPOSITING) - virtual PlatformLayer* platformLayer() { return 0; } -#endif - virtual void setContentScale(float) { } - virtual IntRect bounds() const { return IntRect(); } + void update() override { } + PlatformLayer* platformLayer() override { return nullptr; } + void setContentScale(float) override { } + IntRect bounds() const override { return IntRect(); } }; -#if !PLATFORM(IOS) -PassOwnPtr<TextTrackRepresentation> TextTrackRepresentation::create(TextTrackRepresentationClient*) +#if !(PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))) + +std::unique_ptr<TextTrackRepresentation> TextTrackRepresentation::create(TextTrackRepresentationClient&) { - return WTF::adoptPtr(new NullTextTrackRepresentation()); + return std::make_unique<NullTextTrackRepresentation>(); } + #endif } diff --git a/Source/WebCore/platform/graphics/TextTrackRepresentation.h b/Source/WebCore/platform/graphics/TextTrackRepresentation.h index 5f844483d..72117048a 100644 --- a/Source/WebCore/platform/graphics/TextTrackRepresentation.h +++ b/Source/WebCore/platform/graphics/TextTrackRepresentation.h @@ -28,13 +28,11 @@ #if ENABLE(VIDEO_TRACK) -#include "IntRect.h" #include "PlatformLayer.h" -#include <wtf/PassOwnPtr.h> +#include <wtf/Forward.h> namespace WebCore { -class GraphicsContext; class Image; class IntRect; @@ -42,20 +40,18 @@ class TextTrackRepresentationClient { public: virtual ~TextTrackRepresentationClient() { } - virtual PassRefPtr<Image> createTextTrackRepresentationImage() = 0; + virtual RefPtr<Image> createTextTrackRepresentationImage() = 0; virtual void textTrackRepresentationBoundsChanged(const IntRect&) = 0; }; class TextTrackRepresentation { public: - static PassOwnPtr<TextTrackRepresentation> create(TextTrackRepresentationClient*); + static std::unique_ptr<TextTrackRepresentation> create(TextTrackRepresentationClient&); virtual ~TextTrackRepresentation() { } virtual void update() = 0; -#if USE(ACCELERATED_COMPOSITING) virtual PlatformLayer* platformLayer() = 0; -#endif virtual void setContentScale(float) = 0; virtual IntRect bounds() const = 0; }; diff --git a/Source/WebCore/platform/graphics/TiledBacking.h b/Source/WebCore/platform/graphics/TiledBacking.h index c6266a6e5..95f7cb8e7 100644 --- a/Source/WebCore/platform/graphics/TiledBacking.h +++ b/Source/WebCore/platform/graphics/TiledBacking.h @@ -23,41 +23,88 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TiledBacking_h -#define TiledBacking_h +#pragma once + +#include <wtf/MonotonicTime.h> +#include <wtf/Optional.h> namespace WebCore { +enum TileSizeMode { + StandardTileSizeMode, + GiantTileSizeMode +}; + +class FloatPoint; +class FloatRect; class IntRect; -#if PLATFORM(MAC) class PlatformCALayer; -#endif enum ScrollingModeIndication { + SynchronousScrollingBecauseOfLackOfScrollingCoordinatorIndication, SynchronousScrollingBecauseOfStyleIndication, SynchronousScrollingBecauseOfEventHandlersIndication, AsyncScrollingIndication }; +struct VelocityData { + double horizontalVelocity; + double verticalVelocity; + double scaleChangeRate; + MonotonicTime lastUpdateTime; + + VelocityData(double horizontal = 0, double vertical = 0, double scaleChange = 0, MonotonicTime updateTime = MonotonicTime()) + : horizontalVelocity(horizontal) + , verticalVelocity(vertical) + , scaleChangeRate(scaleChange) + , lastUpdateTime(updateTime) + { + } + + bool velocityOrScaleIsChanging() const + { + return horizontalVelocity || verticalVelocity || scaleChangeRate; + } +}; + class TiledBacking { public: virtual ~TiledBacking() { } virtual void setVisibleRect(const FloatRect&) = 0; virtual FloatRect visibleRect() const = 0; - virtual bool tilesWouldChangeForVisibleRect(const FloatRect&) const = 0; - virtual void setExposedRect(const FloatRect&) = 0; + // Only used to update the tile coverage map. + virtual void setLayoutViewportRect(std::optional<FloatRect>) = 0; + + virtual void setCoverageRect(const FloatRect&) = 0; + virtual FloatRect coverageRect() const = 0; + virtual bool tilesWouldChangeForCoverageRect(const FloatRect&) const = 0; + + virtual void setTiledScrollingIndicatorPosition(const FloatPoint&) = 0; + virtual void setTopContentInset(float) = 0; + + virtual void setVelocity(const VelocityData&) = 0; + + virtual void setTileSizeUpdateDelayDisabledForTesting(bool) = 0; + + enum { + NotScrollable = 0, + HorizontallyScrollable = 1 << 0, + VerticallyScrollable = 1 << 1 + }; + typedef unsigned Scrollability; + virtual void setScrollability(Scrollability) = 0; virtual void prepopulateRect(const FloatRect&) = 0; virtual void setIsInWindow(bool) = 0; + virtual bool isInWindow() const = 0; enum { CoverageForVisibleArea = 0, CoverageForVerticalScrolling = 1 << 0, CoverageForHorizontalScrolling = 1 << 1, - CoverageForSlowScrolling = 1 << 2, // Indicates that we expect to paint a lot on scrolling. CoverageForScrolling = CoverageForVerticalScrolling | CoverageForHorizontalScrolling }; typedef unsigned TileCoverage; @@ -65,6 +112,11 @@ public: virtual void setTileCoverage(TileCoverage) = 0; virtual TileCoverage tileCoverage() const = 0; + virtual void adjustTileCoverageRect(FloatRect& coverageRect, const FloatSize& newSize, const FloatRect& previousVisibleRect, const FloatRect& currentVisibleRect, float contentsScale) const = 0; + + virtual void willStartLiveResize() = 0; + virtual void didEndLiveResize() = 0; + virtual IntSize tileSize() const = 0; virtual void revalidateTiles() = 0; @@ -73,35 +125,34 @@ public: virtual void setScrollingPerformanceLoggingEnabled(bool) = 0; virtual bool scrollingPerformanceLoggingEnabled() const = 0; - virtual void setAggressivelyRetainsTiles(bool) = 0; - virtual bool aggressivelyRetainsTiles() const = 0; - - virtual void setUnparentsOffscreenTiles(bool) = 0; - virtual bool unparentsOffscreenTiles() const = 0; - virtual double retainedTileBackingStoreMemory() const = 0; - virtual void setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight) = 0; + virtual void setHasMargins(bool marginTop, bool marginBottom, bool marginLeft, bool marginRight) = 0; + virtual void setMarginSize(int) = 0; virtual bool hasMargins() const = 0; + virtual bool hasHorizontalMargins() const = 0; + virtual bool hasVerticalMargins() const = 0; virtual int topMarginHeight() const = 0; virtual int bottomMarginHeight() const = 0; virtual int leftMarginWidth() const = 0; virtual int rightMarginWidth() const = 0; + virtual void setZoomedOutContentsScale(float) = 0; + virtual float zoomedOutContentsScale() const = 0; + // Includes margins. virtual IntRect bounds() const = 0; + virtual IntRect boundsWithoutMargin() const = 0; // Exposed for testing virtual IntRect tileCoverageRect() const = 0; virtual IntRect tileGridExtent() const = 0; virtual void setScrollingModeIndication(ScrollingModeIndication) = 0; -#if PLATFORM(MAC) +#if USE(CA) virtual PlatformCALayer* tiledScrollingIndicatorLayer() = 0; #endif }; } // namespace WebCore - -#endif // TiledBacking_h diff --git a/Source/WebCore/platform/graphics/TrackPrivateBase.h b/Source/WebCore/platform/graphics/TrackPrivateBase.h index 01d23aee9..0b00ca5f6 100644 --- a/Source/WebCore/platform/graphics/TrackPrivateBase.h +++ b/Source/WebCore/platform/graphics/TrackPrivateBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * Copyright (C) 2013 Cable Television Labs, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,33 +25,29 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TrackPrivateBase_h -#define TrackPrivateBase_h - -#include <wtf/Forward.h> -#include <wtf/Noncopyable.h> -#include <wtf/RefCounted.h> -#include <wtf/text/AtomicString.h> +#pragma once #if ENABLE(VIDEO_TRACK) -namespace WebCore { +#include <wtf/MediaTime.h> +#include <wtf/text/AtomicString.h> -class TrackPrivateBase; +namespace WebCore { class TrackPrivateBaseClient { public: virtual ~TrackPrivateBaseClient() { } - virtual void idChanged(TrackPrivateBase*, const String&) = 0; - virtual void labelChanged(TrackPrivateBase*, const String&) = 0; - virtual void languageChanged(TrackPrivateBase*, const String&) = 0; - virtual void willRemove(TrackPrivateBase*) = 0; + virtual void idChanged(const AtomicString&) = 0; + virtual void labelChanged(const AtomicString&) = 0; + virtual void languageChanged(const AtomicString&) = 0; + virtual void willRemove() = 0; }; class TrackPrivateBase : public RefCounted<TrackPrivateBase> { - WTF_MAKE_NONCOPYABLE(TrackPrivateBase); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(TrackPrivateBase); + WTF_MAKE_FAST_ALLOCATED; public: - virtual ~TrackPrivateBase() { } + virtual ~TrackPrivateBase() = default; virtual TrackPrivateBaseClient* client() const = 0; @@ -61,17 +57,18 @@ public: virtual int trackIndex() const { return 0; } + virtual MediaTime startTimeVariance() const { return MediaTime::zeroTime(); } + void willBeRemoved() { - if (TrackPrivateBaseClient* client = this->client()) - client->willRemove(this); + if (auto* client = this->client()) + client->willRemove(); } protected: - TrackPrivateBase() { } + TrackPrivateBase() = default; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/platform/graphics/VideoTrackPrivate.h b/Source/WebCore/platform/graphics/VideoTrackPrivate.h index 917eb9715..f94533567 100644 --- a/Source/WebCore/platform/graphics/VideoTrackPrivate.h +++ b/Source/WebCore/platform/graphics/VideoTrackPrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,31 +23,23 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef VideoTrackPrivate_h -#define VideoTrackPrivate_h - -#include "TrackPrivateBase.h" +#pragma once #if ENABLE(VIDEO_TRACK) -namespace WebCore { +#include "TrackPrivateBase.h" -class VideoTrackPrivate; +namespace WebCore { class VideoTrackPrivateClient : public TrackPrivateBaseClient { public: - virtual void selectedChanged(VideoTrackPrivate*, bool) = 0; + virtual void selectedChanged(bool) = 0; }; class VideoTrackPrivate : public TrackPrivateBase { public: - static PassRefPtr<VideoTrackPrivate> create() - { - return adoptRef(new VideoTrackPrivate()); - } - void setClient(VideoTrackPrivateClient* client) { m_client = client; } - virtual VideoTrackPrivateClient* client() const override { return m_client; } + VideoTrackPrivateClient* client() const override { return m_client; } virtual void setSelected(bool selected) { @@ -55,26 +47,21 @@ public: return; m_selected = selected; if (m_client) - m_client->selectedChanged(this, m_selected); - }; + m_client->selectedChanged(m_selected); + } virtual bool selected() const { return m_selected; } enum Kind { Alternative, Captions, Main, Sign, Subtitles, Commentary, None }; virtual Kind kind() const { return None; } protected: - VideoTrackPrivate() - : m_client(0) - , m_selected(false) - { - } + VideoTrackPrivate() = default; private: - VideoTrackPrivateClient* m_client; - bool m_selected; + VideoTrackPrivateClient* m_client { nullptr }; + bool m_selected { false }; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp index 051871ac6..c2323cf04 100644 --- a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp +++ b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp @@ -30,27 +30,32 @@ #include "SharedBuffer.h" #include <wtf/ByteOrder.h> +#if USE(WOFF2) +#include "woff2_common.h" +#include "woff2_dec.h" +#endif + namespace WebCore { -static bool readUInt32(SharedBuffer* buffer, size_t& offset, uint32_t& value) +static bool readUInt32(SharedBuffer& buffer, size_t& offset, uint32_t& value) { - ASSERT_ARG(offset, offset <= buffer->size()); - if (buffer->size() - offset < sizeof(value)) + ASSERT_ARG(offset, offset <= buffer.size()); + if (buffer.size() - offset < sizeof(value)) return false; - value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer->data() + offset)); + value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer.data() + offset)); offset += sizeof(value); return true; } -static bool readUInt16(SharedBuffer* buffer, size_t& offset, uint16_t& value) +static bool readUInt16(SharedBuffer& buffer, size_t& offset, uint16_t& value) { - ASSERT_ARG(offset, offset <= buffer->size()); - if (buffer->size() - offset < sizeof(value)) + ASSERT_ARG(offset, offset <= buffer.size()); + if (buffer.size() - offset < sizeof(value)) return false; - value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer->data() + offset)); + value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer.data() + offset)); offset += sizeof(value); return true; @@ -70,15 +75,58 @@ static bool writeUInt16(Vector<char>& vector, uint16_t value) static const uint32_t woffSignature = 0x774f4646; /* 'wOFF' */ -bool isWOFF(SharedBuffer* buffer) +bool isWOFF(SharedBuffer& buffer) { size_t offset = 0; uint32_t signature; - return readUInt32(buffer, offset, signature) && signature == woffSignature; + if (!readUInt32(buffer, offset, signature)) + return false; + +#if USE(WOFF2) + return signature == woffSignature || signature == woff2::kWoff2Signature; +#else + return signature == woffSignature; +#endif } -bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) +#if USE(WOFF2) +class WOFF2VectorOut : public woff2::WOFF2Out { +public: + WOFF2VectorOut(Vector<char>& vector) + : m_vector(vector) + { } + + bool Write(const void* data, size_t n) override + { + if (!m_vector.tryReserveCapacity(m_vector.size() + n)) + return false; + m_vector.append(static_cast<const char*>(data), n); + return true; + } + + bool Write(const void* data, size_t offset, size_t n) override + { + if (!m_vector.tryReserveCapacity(offset + n)) + return false; + if (offset + n > m_vector.size()) + m_vector.resize(offset + n); + m_vector.remove(offset, n); + m_vector.insert(offset, static_cast<const char*>(data), n); + return true; + } + + size_t Size() override + { + return m_vector.size(); + } + +private: + Vector<char>& m_vector; +}; +#endif + +bool convertWOFFToSfnt(SharedBuffer& woff, Vector<char>& sfnt) { ASSERT_ARG(sfnt, sfnt.isEmpty()); @@ -86,7 +134,26 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) // Read the WOFF header. uint32_t signature; - if (!readUInt32(woff, offset, signature) || signature != woffSignature) { + if (!readUInt32(woff, offset, signature)) { + ASSERT_NOT_REACHED(); + return false; + } + +#if USE(WOFF2) + if (signature == woff2::kWoff2Signature) { + const uint8_t* woffData = reinterpret_cast_ptr<const uint8_t*>(woff.data()); + const size_t woffSize = woff.size(); + const size_t sfntSize = woff2::ComputeWOFF2FinalSize(woffData, woffSize); + + if (!sfnt.tryReserveCapacity(sfntSize)) + return false; + + WOFF2VectorOut out(sfnt); + return woff2::ConvertWOFF2ToTTF(woffData, woffSize, &out); + } +#endif + + if (signature != woffSignature) { ASSERT_NOT_REACHED(); return false; } @@ -96,7 +163,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) return false; uint32_t length; - if (!readUInt32(woff, offset, length) || length != woff->size()) + if (!readUInt32(woff, offset, length) || length != woff.size()) return false; uint16_t numTables; @@ -114,7 +181,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) if (!readUInt32(woff, offset, totalSfntSize)) return false; - if (woff->size() - offset < sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t)) + if (woff.size() - offset < sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t)) return false; offset += sizeof(uint16_t); // majorVersion @@ -126,7 +193,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) offset += sizeof(uint32_t); // privLength // Check if the WOFF can supply as many tables as it claims it has. - if (woff->size() - offset < numTables * 5 * sizeof(uint32_t)) + if (woff.size() - offset < numTables * 5 * sizeof(uint32_t)) return false; // Write the sfnt offset subtable. @@ -170,7 +237,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) if (!readUInt32(woff, offset, tableCompLength)) return false; - if (tableOffset > woff->size() || tableCompLength > woff->size() - tableOffset) + if (tableOffset > woff.size() || tableCompLength > woff.size() - tableOffset) return false; uint32_t tableOrigLength; @@ -194,7 +261,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) if (tableCompLength == tableOrigLength) { // The table is not compressed. - if (!sfnt.tryAppend(woff->data() + tableOffset, tableCompLength)) + if (!sfnt.tryAppend(woff.data() + tableOffset, tableCompLength)) return false; } else { uLongf destLen = tableOrigLength; @@ -202,7 +269,7 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) return false; Bytef* dest = reinterpret_cast<Bytef*>(sfnt.end()); sfnt.grow(sfnt.size() + tableOrigLength); - if (uncompress(dest, &destLen, reinterpret_cast<const Bytef*>(woff->data() + tableOffset), tableCompLength) != Z_OK) + if (uncompress(dest, &destLen, reinterpret_cast<const Bytef*>(woff.data() + tableOffset), tableCompLength) != Z_OK) return false; if (destLen != tableOrigLength) return false; diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.h b/Source/WebCore/platform/graphics/WOFFFileFormat.h index c6892fde1..44c38245d 100644 --- a/Source/WebCore/platform/graphics/WOFFFileFormat.h +++ b/Source/WebCore/platform/graphics/WOFFFileFormat.h @@ -33,11 +33,11 @@ namespace WebCore { class SharedBuffer; // Returns whether the buffer is a WOFF file. -bool isWOFF(SharedBuffer* buffer); +bool isWOFF(SharedBuffer&); // Returns false if the WOFF file woff is invalid or could not be converted to sfnt (for example, // if conversion ran out of memory). Otherwise returns true and writes the sfnt payload into sfnt. -bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt); +bool convertWOFFToSfnt(SharedBuffer& woff, Vector<char>& sfnt); } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/WidthCache.h b/Source/WebCore/platform/graphics/WidthCache.h index 884d2a2e0..a6b11eace 100644 --- a/Source/WebCore/platform/graphics/WidthCache.h +++ b/Source/WebCore/platform/graphics/WidthCache.h @@ -26,12 +26,12 @@ #ifndef WidthCache_h #define WidthCache_h +#include "MemoryPressureHandler.h" #include "TextRun.h" #include <wtf/Forward.h> #include <wtf/HashFunctions.h> #include <wtf/HashSet.h> -#include <wtf/RefPtr.h> -#include <wtf/StringHasher.h> +#include <wtf/Hasher.h> namespace WebCore { @@ -119,8 +119,25 @@ public: { } + float* add(StringView text, float entry) + { + if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) + return nullptr; + + if (static_cast<unsigned>(text.length()) > SmallStringKey::capacity()) + return nullptr; + + if (m_countdown > 0) { + --m_countdown; + return nullptr; + } + return addSlowCase(text, entry); + } + float* add(const TextRun& run, float entry, bool hasKerningOrLigatures, bool hasWordSpacingOrLetterSpacing, GlyphOverflow* glyphOverflow) { + if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) + return nullptr; // The width cache is not really profitable unless we're doing expensive glyph transformations. if (!hasKerningOrLigatures) return 0; @@ -141,7 +158,7 @@ public: return 0; } - return addSlowCase(run, entry); + return addSlowCase(run.text(), entry); } void clear() @@ -151,23 +168,24 @@ public: } private: - float* addSlowCase(const TextRun& run, float entry) + + float* addSlowCase(StringView text, float entry) { - int length = run.length(); + int length = text.length(); bool isNewEntry; - float *value; + float* value; if (length == 1) { - SingleCharMap::AddResult addResult = m_singleCharMap.add(run[0], entry); + SingleCharMap::AddResult addResult = m_singleCharMap.fastAdd(text[0], entry); isNewEntry = addResult.isNewEntry; value = &addResult.iterator->value; } else { SmallStringKey smallStringKey; - if (run.is8Bit()) - smallStringKey = SmallStringKey(run.characters8(), length); + if (text.is8Bit()) + smallStringKey = SmallStringKey(text.characters8(), length); else - smallStringKey = SmallStringKey(run.characters16(), length); + smallStringKey = SmallStringKey(text.characters16(), length); - Map::AddResult addResult = m_map.add(smallStringKey, entry); + Map::AddResult addResult = m_map.fastAdd(smallStringKey, entry); isNewEntry = addResult.isNewEntry; value = &addResult.iterator->value; } diff --git a/Source/WebCore/platform/graphics/WidthIterator.cpp b/Source/WebCore/platform/graphics/WidthIterator.cpp index 4c44b6e0f..3785e9567 100644 --- a/Source/WebCore/platform/graphics/WidthIterator.cpp +++ b/Source/WebCore/platform/graphics/WidthIterator.cpp @@ -23,9 +23,9 @@ #include "WidthIterator.h" #include "Font.h" +#include "FontCascade.h" #include "GlyphBuffer.h" #include "Latin1TextIterator.h" -#include "SimpleFontData.h" #include "SurrogatePairAwareTextIterator.h" #include <wtf/MathExtras.h> @@ -34,20 +34,17 @@ using namespace Unicode; namespace WebCore { -WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis) +WidthIterator::WidthIterator(const FontCascade* font, const TextRun& run, HashSet<const Font*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis) : m_font(font) , m_run(run) , m_currentCharacter(0) , m_runWidthSoFar(0) - , m_isAfterExpansion(!run.allowsLeadingExpansion()) + , m_isAfterExpansion((run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion) , m_finalRoundingWidth(0) - , m_typesettingFeatures(font->typesettingFeatures()) , m_fallbackFonts(fallbackFonts) , m_accountForGlyphBounds(accountForGlyphBounds) - , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min()) - , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max()) - , m_firstGlyphOverflow(0) - , m_lastGlyphOverflow(0) + , m_enableKerning(font->enableKerning()) + , m_requiresShaping(font->requiresShaping()) , m_forTextEmphasis(forTextEmphasis) { // If the padding is non-zero, count the number of spaces in the run @@ -56,33 +53,13 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const if (!m_expansion) m_expansionPerOpportunity = 0; else { - bool isAfterExpansion = m_isAfterExpansion; - unsigned expansionOpportunityCount = m_run.is8Bit() ? Font::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion) : Font::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion); - if (isAfterExpansion && !m_run.allowsTrailingExpansion()) - expansionOpportunityCount--; + unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, run.expansionBehavior()).first; if (!expansionOpportunityCount) m_expansionPerOpportunity = 0; else m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; } - // Character-index will end up the same or slightly shorter than m_run, so if we reserve that much it will never need to resize. - m_characterIndexOfGlyph.reserveInitialCapacity(m_run.length()); -} - -GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) -{ - ASSERT(m_font); - -#if ENABLE(SVG_FONTS) - if (TextRun::RenderingContext* renderingContext = m_run.renderingContext()) - return renderingContext->glyphDataForCharacter(*m_font, *this, character, mirror, currentCharacter, advanceLength); -#else - UNUSED_PARAM(currentCharacter); - UNUSED_PARAM(advanceLength); -#endif - - return m_font->glyphDataForCharacter(character, mirror); } struct OriginalAdvancesForCharacterTreatedAsSpace { @@ -99,39 +76,43 @@ public: float advanceAtCharacter; }; -typedef Vector<std::pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace; +static inline bool isSoftBankEmoji(UChar32 codepoint) +{ + return codepoint >= 0xE001 && codepoint <= 0xE537; +} + +inline auto WidthIterator::shouldApplyFontTransforms(const GlyphBuffer* glyphBuffer, unsigned lastGlyphCount, UChar32 previousCharacter) const -> TransformsType +{ + if (glyphBuffer && glyphBuffer->size() == (lastGlyphCount + 1) && isSoftBankEmoji(previousCharacter)) + return TransformsType::Forced; + if (m_run.length() <= 1 || !(m_enableKerning || m_requiresShaping)) + return TransformsType::None; + return TransformsType::NotForced; +} -static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const SimpleFontData* fontData, WidthIterator& iterator, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace) +inline float WidthIterator::applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, unsigned& lastGlyphCount, const Font* font, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace& charactersTreatedAsSpace) { - ASSERT(typesettingFeatures & (Kerning | Ligatures)); + ASSERT_UNUSED(previousCharacter, shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter) != WidthIterator::TransformsType::None); if (!glyphBuffer) return 0; - int glyphBufferSize = glyphBuffer->size(); - if (glyphBuffer->size() <= lastGlyphCount + 1) + unsigned glyphBufferSize = glyphBuffer->size(); + if (!force && glyphBufferSize <= lastGlyphCount + 1) { + lastGlyphCount = glyphBufferSize; return 0; + } GlyphBufferAdvance* advances = glyphBuffer->advances(0); float widthDifference = 0; - for (int i = lastGlyphCount; i < glyphBufferSize; ++i) + for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) widthDifference -= advances[i].width(); + ASSERT(lastGlyphCount <= glyphBufferSize); if (!ltr) glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); -#if !ENABLE(SVG_FONTS) - UNUSED_PARAM(iterator); -#else - // We need to handle transforms on SVG fonts internally, since they are rendered internally. - if (fontData->isSVGFont()) { - ASSERT(iterator.run().renderingContext()); - // SVG font ligatures are handled during glyph selection, only kerning remaining. - if (typesettingFeatures & Kerning) - iterator.run().renderingContext()->applySVGKerning(fontData, iterator, glyphBuffer, lastGlyphCount); - } else -#endif - fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures); + font->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, m_enableKerning, m_requiresShaping); if (!ltr) glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); @@ -145,138 +126,202 @@ static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& } charactersTreatedAsSpace.clear(); - for (int i = lastGlyphCount; i < glyphBufferSize; ++i) + for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) widthDifference += advances[i].width(); lastGlyphCount = glyphBufferSize; return widthDifference; } +static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion) +{ + bool expandLeft = ideograph; + bool expandRight = ideograph; + if (treatAsSpace) { + if (ltr) + expandRight = true; + else + expandLeft = true; + } + if (isAfterExpansion) { + if (ltr) + expandLeft = false; + else + expandRight = false; + } + ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion); + ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion); + if (forbidLeadingExpansion) + expandLeft = false; + if (forbidTrailingExpansion) + expandRight = false; + if (forceLeadingExpansion) + expandLeft = true; + if (forceTrailingExpansion) + expandRight = true; + return std::make_pair(expandLeft, expandRight); +} + +static bool characterMustDrawSomething(UChar32 character) +{ + // u_hasBinaryProperty(character, UCHAR_EMOJI) would be better to use, but many OSes which + // WebKit runs on only have ICU version 55.1 or earlier. UCHAR_EMOJI was added in ICU 57. + return character >= 0x1F900 && character <= 0x1F9FF; +} + template <typename TextIterator> inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer) { + // The core logic here needs to match SimpleLineLayout::widthForSimpleText() bool rtl = m_run.rtl(); bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled(); + bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion; + bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion; + bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion; + bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion; float widthSinceLastRounding = m_runWidthSoFar; + float leftoverJustificationWidth = 0; m_runWidthSoFar = floorf(m_runWidthSoFar); widthSinceLastRounding -= m_runWidthSoFar; float lastRoundingWidth = m_finalRoundingWidth; FloatRect bounds; - const SimpleFontData* primaryFont = m_font->primaryFont(); - const SimpleFontData* lastFontData = primaryFont; - int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0; + const Font& primaryFont = m_font->primaryFont(); + const Font* lastFontData = &primaryFont; + unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0; UChar32 character = 0; + UChar32 previousCharacter = 0; unsigned clusterLength = 0; CharactersTreatedAsSpace charactersTreatedAsSpace; + String normalizedSpacesStringCache; + // We are iterating in string order, not glyph order. Compare this to ComplexTextController::adjustGlyphsAndAdvances() while (textIterator.consume(character, clusterLength)) { unsigned advanceLength = clusterLength; - int currentCharacterIndex = textIterator.currentCharacter(); - const GlyphData& glyphData = glyphDataForCharacter(character, rtl, currentCharacterIndex, advanceLength); + int currentCharacter = textIterator.currentIndex(); + const GlyphData& glyphData = m_font->glyphDataForCharacter(character, rtl); Glyph glyph = glyphData.glyph; - const SimpleFontData* fontData = glyphData.fontData; - - ASSERT(fontData); + if (!glyph && !characterMustDrawSomething(character)) { + textIterator.advance(advanceLength); + continue; + } + const Font* font = glyphData.font ? glyphData.font : &m_font->primaryFont(); + ASSERT(font); // Now that we have a glyph and font data, get its width. float width; if (character == '\t' && m_run.allowTabs()) - width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding); + width = m_font->tabWidth(*font, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding); else { - width = fontData->widthForGlyph(glyph); + width = font->widthForGlyph(glyph); -#if ENABLE(SVG) // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text. width *= m_run.horizontalGlyphStretch(); -#endif - - // We special case spaces in two ways when applying word rounding. - // First, we round spaces to an adjusted width in all fonts. - // Second, in fixed-pitch fonts we ensure that all characters that - // match the width of the space character have the same width as the space character. - if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph())) - width = fontData->adjustedSpaceWidth(); } - if (fontData != lastFontData && width) { - if (shouldApplyFontTransforms()) { - m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace); - lastGlyphCount = glyphBuffer->size(); // applyFontTransforms doesn't update when there had been only one glyph. + if (font != lastFontData && width) { + auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter); + if (transformsType != TransformsType::None) { + m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace); + if (glyphBuffer) + glyphBuffer->shrink(lastGlyphCount); } - lastFontData = fontData; - if (m_fallbackFonts && fontData != primaryFont) { + lastFontData = font; + if (m_fallbackFonts && font != &primaryFont) { // FIXME: This does a little extra work that could be avoided if // glyphDataForCharacter() returned whether it chose to use a small caps font. if (!m_font->isSmallCaps() || character == u_toupper(character)) - m_fallbackFonts->add(fontData); + m_fallbackFonts->add(font); else { const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(u_toupper(character), rtl); - if (uppercaseGlyphData.fontData != primaryFont) - m_fallbackFonts->add(uppercaseGlyphData.fontData); + if (uppercaseGlyphData.font != &primaryFont) + m_fallbackFonts->add(uppercaseGlyphData.font); } } } if (hasExtraSpacing) { // Account for letter-spacing. - if (width && m_font->letterSpacing()) + if (width) { width += m_font->letterSpacing(); + width += leftoverJustificationWidth; + leftoverJustificationWidth = 0; + } - static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText(); - bool treatAsSpace = Font::treatAsSpace(character); - if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(character))) { + static bool expandAroundIdeographs = FontCascade::canExpandAroundIdeographsInComplexText(); + bool treatAsSpace = FontCascade::treatAsSpace(character); + bool currentIsLastCharacter = currentCharacter + advanceLength == static_cast<size_t>(m_run.length()); + bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr() + bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr() + bool forbidLeadingExpansion = false; + bool forbidTrailingExpansion = false; + if (runForcesLeadingExpansion) + forceLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter; + if (runForcesTrailingExpansion) + forceTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter; + if (runForbidsLeadingExpansion) + forbidLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter; + if (runForbidsTrailingExpansion) + forbidTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter; + bool ideograph = (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character)); + if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) { // Distribute the run's total expansion evenly over all expansion opportunities in the run. if (m_expansion) { - float previousExpansion = m_expansion; - if (!treatAsSpace && !m_isAfterExpansion) { - // Take the expansion opportunity before this ideograph. - m_expansion -= m_expansionPerOpportunity; - float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); - m_runWidthSoFar += expansionAtThisOpportunity; - if (glyphBuffer) { - if (glyphBuffer->isEmpty()) { - if (m_forTextEmphasis) - glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity); - else - glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity); - m_characterIndexOfGlyph.append(currentCharacterIndex); - } else - glyphBuffer->expandLastAdvance(expansionAtThisOpportunity); + bool expandLeft, expandRight; + std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), m_isAfterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion); + if (expandLeft) { + if (m_run.ltr()) { + // Increase previous width + m_expansion -= m_expansionPerOpportunity; + m_runWidthSoFar += m_expansionPerOpportunity; + if (glyphBuffer) { + if (glyphBuffer->isEmpty()) { + if (m_forTextEmphasis) + glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter); + else + glyphBuffer->add(font->spaceGlyph(), font, m_expansionPerOpportunity, currentCharacter); + } else + glyphBuffer->expandLastAdvance(m_expansionPerOpportunity); + } + } else { + // Increase next width + leftoverJustificationWidth += m_expansionPerOpportunity; + m_isAfterExpansion = true; } - previousExpansion = m_expansion; } - if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length())) - || (m_run.rtl() && textIterator.currentCharacter())) { + if (expandRight) { m_expansion -= m_expansionPerOpportunity; - width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); - m_isAfterExpansion = true; + width += m_expansionPerOpportunity; + if (m_run.ltr()) + m_isAfterExpansion = true; } } else m_isAfterExpansion = false; // Account for word spacing. // We apply additional space between "words" by adding width to the space character. - if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (textIterator.currentCharacter() || character == noBreakSpace) && m_font->wordSpacing()) + if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (currentCharacter || character == noBreakSpace) && m_font->wordSpacing()) width += m_font->wordSpacing(); } else m_isAfterExpansion = false; } - if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(character)) + auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter); + if (transformsType != TransformsType::None && glyphBuffer && FontCascade::treatAsSpace(character)) { charactersTreatedAsSpace.append(std::make_pair(glyphBuffer->size(), OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0, width))); + } if (m_accountForGlyphBounds) { - bounds = fontData->boundsForGlyph(glyph); - if (!textIterator.currentCharacter()) + bounds = font->boundsForGlyph(glyph); + if (!currentCharacter) m_firstGlyphOverflow = std::max<float>(0, -bounds.x()); } - if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character)) + if (m_forTextEmphasis && !FontCascade::canReceiveTextEmphasis(character)) glyph = 0; // Advance past the character we just dealt with. @@ -284,36 +329,10 @@ inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, Glyph float oldWidth = width; - // Force characters that are used to determine word boundaries for the rounding hack - // to be integer width, so following words will start on an integer boundary. - if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) { - width = ceilf(width); - - // Since widthSinceLastRounding can lose precision if we include measurements for - // preceding whitespace, we bypass it here. - m_runWidthSoFar += width; - - // Since this is a rounding hack character, we should have reset this sum on the previous - // iteration. - ASSERT(!widthSinceLastRounding); - } else { - // Check to see if the next character is a "rounding hack character", if so, adjust - // width so that the total run width will be on an integer boundary. - if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters()))) - || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) { - float totalWidth = widthSinceLastRounding + width; - widthSinceLastRounding = ceilf(totalWidth); - width += widthSinceLastRounding - totalWidth; - m_runWidthSoFar += widthSinceLastRounding; - widthSinceLastRounding = 0; - } else - widthSinceLastRounding += width; - } + widthSinceLastRounding += width; - if (glyphBuffer) { - glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width)); - m_characterIndexOfGlyph.append(currentCharacterIndex); - } + if (glyphBuffer) + glyphBuffer->add(glyph, font, (rtl ? oldWidth + lastRoundingWidth : width), currentCharacter); lastRoundingWidth = width - oldWidth; @@ -322,26 +341,38 @@ inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, Glyph m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y()); m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width); } + previousCharacter = character; } - if (shouldApplyFontTransforms()) - m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace); + if (leftoverJustificationWidth) { + if (m_forTextEmphasis) + glyphBuffer->add(lastFontData->zeroWidthSpaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1); + else + glyphBuffer->add(lastFontData->spaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1); + } - unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter; - m_currentCharacter = textIterator.currentCharacter(); + auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter); + if (transformsType != TransformsType::None) { + m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace); + if (glyphBuffer) + glyphBuffer->shrink(lastGlyphCount); + } + + unsigned consumedCharacters = textIterator.currentIndex() - m_currentCharacter; + m_currentCharacter = textIterator.currentIndex(); m_runWidthSoFar += widthSinceLastRounding; m_finalRoundingWidth = lastRoundingWidth; return consumedCharacters; } -unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) +unsigned WidthIterator::advance(unsigned offset, GlyphBuffer* glyphBuffer) { - int length = m_run.length(); + unsigned length = m_run.length(); if (offset > length) offset = length; - if (m_currentCharacter >= static_cast<unsigned>(offset)) + if (m_currentCharacter >= offset) return 0; if (m_run.is8Bit()) { @@ -353,4 +384,15 @@ unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) return advanceInternal(textIterator, glyphBuffer); } +bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer) +{ + unsigned oldSize = glyphBuffer.size(); + advance(m_currentCharacter + 1, &glyphBuffer); + float w = 0; + for (unsigned i = oldSize; i < glyphBuffer.size(); ++i) + w += glyphBuffer.advanceAt(i).width(); + width = w; + return glyphBuffer.size() > oldSize; +} + } diff --git a/Source/WebCore/platform/graphics/WidthIterator.h b/Source/WebCore/platform/graphics/WidthIterator.h index 1344b9ae6..c45fdb8c3 100644 --- a/Source/WebCore/platform/graphics/WidthIterator.h +++ b/Source/WebCore/platform/graphics/WidthIterator.h @@ -22,27 +22,28 @@ #ifndef WidthIterator_h #define WidthIterator_h -#include "Font.h" -#include "SVGGlyph.h" -#include "TextRun.h" +#include <unicode/umachine.h> #include <wtf/HashSet.h> #include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> namespace WebCore { -class Font; +class FontCascade; class GlyphBuffer; -class SimpleFontData; +class Font; class TextRun; struct GlyphData; +struct OriginalAdvancesForCharacterTreatedAsSpace; + +typedef Vector<std::pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace; struct WidthIterator { WTF_MAKE_FAST_ALLOCATED; public: - WidthIterator(const Font*, const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, bool accountForGlyphBounds = false, bool forTextEmphasis = false); + WidthIterator(const FontCascade*, const TextRun&, HashSet<const Font*>* fallbackFonts = 0, bool accountForGlyphBounds = false, bool forTextEmphasis = false); - unsigned advance(int to, GlyphBuffer*); + unsigned advance(unsigned to, GlyphBuffer*); + bool advanceOneCharacter(float& width, GlyphBuffer&); float maxGlyphBoundingBoxY() const { ASSERT(m_accountForGlyphBounds); return m_maxGlyphBoundingBoxY; } float minGlyphBoundingBoxY() const { ASSERT(m_accountForGlyphBounds); return m_minGlyphBoundingBoxY; } @@ -52,25 +53,7 @@ public: const TextRun& run() const { return m_run; } float runWidthSoFar() const { return m_runWidthSoFar; } -#if ENABLE(SVG_FONTS) - String lastGlyphName() const { return m_lastGlyphName; } - void setLastGlyphName(const String& name) { m_lastGlyphName = name; } - Vector<SVGGlyph::ArabicForm>& arabicForms() { return m_arabicForms; } -#endif - - static bool supportsTypesettingFeatures(const Font& font) - { -#if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1080) - if (!font.isPrinterFont()) - return !font.typesettingFeatures(); - - return !(font.typesettingFeatures() & ~(Kerning | Ligatures)); -#else - return !font.typesettingFeatures(); -#endif - } - - const Font* m_font; + const FontCascade* m_font; const TextRun& m_run; @@ -80,29 +63,25 @@ public: float m_expansionPerOpportunity; bool m_isAfterExpansion; float m_finalRoundingWidth; - // An inline capacity of 10 catches around 2/3 of the cases. To catch 90% we would need 32. - Vector<int, 10> m_characterIndexOfGlyph; - -#if ENABLE(SVG_FONTS) - String m_lastGlyphName; - Vector<SVGGlyph::ArabicForm> m_arabicForms; -#endif private: - GlyphData glyphDataForCharacter(UChar32, bool mirror, int currentCharacter, unsigned& advanceLength); + GlyphData glyphDataForCharacter(UChar32, bool mirror); template <typename TextIterator> inline unsigned advanceInternal(TextIterator&, GlyphBuffer*); - bool shouldApplyFontTransforms() const { return m_run.length() > 1 && (m_typesettingFeatures & (Kerning | Ligatures)); } - - TypesettingFeatures m_typesettingFeatures; - HashSet<const SimpleFontData*>* m_fallbackFonts; - bool m_accountForGlyphBounds; - float m_maxGlyphBoundingBoxY; - float m_minGlyphBoundingBoxY; - float m_firstGlyphOverflow; - float m_lastGlyphOverflow; - bool m_forTextEmphasis; + enum class TransformsType { None, Forced, NotForced }; + TransformsType shouldApplyFontTransforms(const GlyphBuffer*, unsigned lastGlyphCount, UChar32 previousCharacter) const; + float applyFontTransforms(GlyphBuffer*, bool ltr, unsigned& lastGlyphCount, const Font*, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace&); + + HashSet<const Font*>* m_fallbackFonts { nullptr }; + bool m_accountForGlyphBounds { false }; + bool m_enableKerning { false }; + bool m_requiresShaping { false }; + bool m_forTextEmphasis { false }; + float m_maxGlyphBoundingBoxY { std::numeric_limits<float>::min() }; + float m_minGlyphBoundingBoxY { std::numeric_limits<float>::max() }; + float m_firstGlyphOverflow { 0 }; + float m_lastGlyphOverflow { 0 }; }; } diff --git a/Source/WebCore/platform/graphics/WindRule.h b/Source/WebCore/platform/graphics/WindRule.h index 8eb77495a..f50834f5f 100644 --- a/Source/WebCore/platform/graphics/WindRule.h +++ b/Source/WebCore/platform/graphics/WindRule.h @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h new file mode 100644 index 000000000..63828c761 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairo.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011,2014 Igalia S.L. + * + * 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 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 + */ + +#ifndef BackingStoreBackendCairo_h +#define BackingStoreBackendCairo_h + +#if USE(CAIRO) + +#include "IntRect.h" +#include "RefPtrCairo.h" +#include <wtf/FastMalloc.h> +#include <wtf/Noncopyable.h> + +typedef struct _cairo_surface cairo_surface_t; + +namespace WebCore { + +class BackingStoreBackendCairo { + WTF_MAKE_NONCOPYABLE(BackingStoreBackendCairo); + WTF_MAKE_FAST_ALLOCATED; +public: + virtual ~BackingStoreBackendCairo() { } + + cairo_surface_t* surface() const { return m_surface.get(); } + const IntSize& size() const { return m_size; } + + virtual void scroll(const IntRect& scrollRect, const IntSize& scrollOffset) = 0; + +protected: + BackingStoreBackendCairo(const IntSize& size) + : m_size(size) + { + } + + RefPtr<cairo_surface_t> m_surface; + IntSize m_size; +}; + + +} // namespace WebCore + +#endif // USE(CAIRO) + +#endif // BackingStoreBackendCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp new file mode 100644 index 000000000..40d06dbf8 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011,2014 Igalia S.L. + * Copyright (C) 2011 Samsung Electronics + * + * 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 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 "BackingStoreBackendCairoImpl.h" + +#if USE(CAIRO) + +#include "CairoUtilities.h" + +namespace WebCore { + +BackingStoreBackendCairoImpl::BackingStoreBackendCairoImpl(cairo_surface_t* surface, const IntSize& size) + : BackingStoreBackendCairo(size) +{ + m_surface = surface; + + // We keep two copies of the surface here, which will double the memory usage, but increase + // scrolling performance since we do not have to keep reallocating a memory region during + // quick scrolling requests. + double xScale, yScale; + cairoSurfaceGetDeviceScale(m_surface.get(), xScale, yScale); + IntSize scaledSize = size; + scaledSize.scale(xScale, yScale); + m_scrollSurface = adoptRef(cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR_ALPHA, scaledSize.width(), scaledSize.height())); +} + +BackingStoreBackendCairoImpl::~BackingStoreBackendCairoImpl() +{ +} + +void BackingStoreBackendCairoImpl::scroll(const IntRect& scrollRect, const IntSize& scrollOffset) +{ + IntRect targetRect = scrollRect; + targetRect.move(scrollOffset); + targetRect.shiftMaxXEdgeTo(targetRect.maxX() - scrollOffset.width()); + targetRect.shiftMaxYEdgeTo(targetRect.maxY() - scrollOffset.height()); + if (targetRect.isEmpty()) + return; + + copyRectFromOneSurfaceToAnother(m_surface.get(), m_scrollSurface.get(), scrollOffset, targetRect); + copyRectFromOneSurfaceToAnother(m_scrollSurface.get(), m_surface.get(), IntSize(), targetRect); +} + +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h new file mode 100644 index 000000000..b1a0149bb --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoImpl.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013,2014 Igalia S.L. + * + * 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 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 + */ + +#ifndef BackingStoreBackendCairoImpl_h +#define BackingStoreBackendCairoImpl_h + +#include "BackingStoreBackendCairo.h" + +#if USE(CAIRO) + +namespace WebCore { + +class BackingStoreBackendCairoImpl final : public BackingStoreBackendCairo { +public: + BackingStoreBackendCairoImpl(cairo_surface_t*, const IntSize&); + virtual ~BackingStoreBackendCairoImpl(); + + void scroll(const IntRect&, const IntSize&) override; + +private: + RefPtr<cairo_surface_t> m_scrollSurface; +}; + +} // namespace WebCore + +#endif // USE(CAIRO) + +#endif // BackingStoreBackendCairoImpl_h diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp new file mode 100644 index 000000000..437dafc82 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011,2014 Igalia S.L. + * + * 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 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 "BackingStoreBackendCairoX11.h" + +#if USE(CAIRO) && PLATFORM(X11) + +#include "CairoUtilities.h" +#include <cairo-xlib.h> + +namespace WebCore { + +BackingStoreBackendCairoX11::BackingStoreBackendCairoX11(unsigned long rootWindowID, Visual* visual, int depth, const IntSize& size, float deviceScaleFactor) + : BackingStoreBackendCairo(size) +{ + IntSize scaledSize = size; + scaledSize.scale(deviceScaleFactor); + + auto* display = downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(); + m_pixmap = XCreatePixmap(display, rootWindowID, scaledSize.width(), scaledSize.height(), depth); + m_gc.reset(XCreateGC(display, m_pixmap.get(), 0, nullptr)); + + m_surface = adoptRef(cairo_xlib_surface_create(display, m_pixmap.get(), visual, scaledSize.width(), scaledSize.height())); + cairoSurfaceSetDeviceScale(m_surface.get(), deviceScaleFactor, deviceScaleFactor); +} + +BackingStoreBackendCairoX11::~BackingStoreBackendCairoX11() +{ + // The pixmap needs to exist when the surface is destroyed, so begin by clearing it. + m_surface = nullptr; +} + +void BackingStoreBackendCairoX11::scroll(const IntRect& scrollRect, const IntSize& scrollOffset) +{ + IntRect targetRect = scrollRect; + targetRect.move(scrollOffset); + targetRect.intersect(scrollRect); + if (targetRect.isEmpty()) + return; + + double xScale, yScale; + cairoSurfaceGetDeviceScale(m_surface.get(), xScale, yScale); + ASSERT(xScale == yScale); + + IntSize scaledScrollOffset = scrollOffset; + targetRect.scale(xScale); + scaledScrollOffset.scale(xScale, yScale); + + cairo_surface_flush(m_surface.get()); + XCopyArea(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(), m_pixmap.get(), m_pixmap.get(), m_gc.get(), + targetRect.x() - scaledScrollOffset.width(), targetRect.y() - scaledScrollOffset.height(), + targetRect.width(), targetRect.height(), targetRect.x(), targetRect.y()); + cairo_surface_mark_dirty_rectangle(m_surface.get(), targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height()); +} + +} // namespace WebCore + +#endif // USE(CAIRO) && PLATFORM(X11) diff --git a/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h new file mode 100644 index 000000000..3441809f5 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/BackingStoreBackendCairoX11.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013,2014 Igalia S.L. + * + * 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 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 + */ + +#ifndef BackingStoreBackendCairoX11_h +#define BackingStoreBackendCairoX11_h + +#include "BackingStoreBackendCairo.h" + +#if USE(CAIRO) && PLATFORM(X11) +#include "XUniquePtr.h" +#include "XUniqueResource.h" + +namespace WebCore { + +class BackingStoreBackendCairoX11 final : public BackingStoreBackendCairo { +public: + BackingStoreBackendCairoX11(unsigned long rootWindowID, Visual*, int depth, const IntSize&, float deviceScaleFactor); + virtual ~BackingStoreBackendCairoX11(); + + void scroll(const IntRect& scrollRect, const IntSize& scrollOffset) override; + +private: + XUniquePixmap m_pixmap; + XUniqueGC m_gc; +}; + +} // namespace WebCore + +#endif // USE(CAIRO) && PLATFORM(X11) + +#endif // GtkWidgetBackingStoreX11_h diff --git a/Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp deleted file mode 100644 index 4611a306a..000000000 --- a/Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "BitmapImage.h" - -#include "CairoUtilities.h" -#include "ImageObserver.h" -#include "PlatformContextCairo.h" -#include "Timer.h" -#include <cairo.h> - -namespace WebCore { - -BitmapImage::BitmapImage(PassRefPtr<cairo_surface_t> nativeImage, ImageObserver* observer) - : Image(observer) - , m_size(cairoSurfaceSize(nativeImage.get())) - , m_currentFrame(0) - , m_repetitionCount(cAnimationNone) - , m_repetitionCountStatus(Unknown) - , m_repetitionsComplete(0) - , m_decodedSize(m_size.width() * m_size.height() * 4) - , m_frameCount(1) - , m_isSolidColor(false) - , m_checkedForSolidColor(false) - , m_animationFinished(true) - , m_allDataReceived(true) - , m_haveSize(true) - , m_sizeAvailable(true) - , m_haveFrameCount(true) -{ - m_frames.grow(1); - m_frames[0].m_hasAlpha = cairo_surface_get_content(nativeImage.get()) != CAIRO_CONTENT_COLOR; - m_frames[0].m_frame = nativeImage; - m_frames[0].m_haveMetadata = true; - - checkForSolidColor(); -} - -void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op, - BlendMode blendMode, ImageOrientationDescription description) -{ - if (!dst.width() || !dst.height() || !src.width() || !src.height()) - return; - - startAnimation(); - - RefPtr<cairo_surface_t> surface = frameAtIndex(m_currentFrame); - if (!surface) // If it's too early we won't have an image yet. - return; - - if (mayFillWithSolidColor()) { - fillWithSolidColor(context, dst, solidColor(), styleColorSpace, op); - return; - } - - context->save(); - - // Set the compositing operation. - if (op == CompositeSourceOver && blendMode == BlendModeNormal && !frameHasAlphaAtIndex(m_currentFrame)) - context->setCompositeOperation(CompositeCopy); - else - context->setCompositeOperation(op, blendMode); - -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) - IntSize scaledSize = cairoSurfaceSize(surface.get()); - FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(src, scaledSize); -#else - FloatRect adjustedSrcRect(src); -#endif - - ImageOrientation frameOrientation(description.imageOrientation()); - if (description.respectImageOrientation() == RespectImageOrientation) - frameOrientation = frameOrientationAtIndex(m_currentFrame); - - FloatRect dstRect = dst; - - if (frameOrientation != DefaultImageOrientation) { - // ImageOrientation expects the origin to be at (0, 0). - context->translate(dstRect.x(), dstRect.y()); - dstRect.setLocation(FloatPoint()); - context->concatCTM(frameOrientation.transformFromDefault(dstRect.size())); - if (frameOrientation.usesWidthAsHeight()) { - // The destination rectangle will have it's width and height already reversed for the orientation of - // the image, as it was needed for page layout, so we need to reverse it back here. - dstRect = FloatRect(dstRect.x(), dstRect.y(), dstRect.height(), dstRect.width()); - } - } - - context->platformContext()->drawSurfaceToContext(surface.get(), dstRect, adjustedSrcRect, context); - - context->restore(); - - if (imageObserver()) - imageObserver()->didDraw(this); -} - -void BitmapImage::checkForSolidColor() -{ - m_isSolidColor = false; - m_checkedForSolidColor = true; - - if (frameCount() > 1) - return; - - RefPtr<cairo_surface_t> surface = frameAtIndex(m_currentFrame); - if (!surface) // If it's too early we won't have an image yet. - return; - - if (cairo_surface_get_type(surface.get()) != CAIRO_SURFACE_TYPE_IMAGE) - return; - - IntSize size = cairoSurfaceSize(surface.get()); - - if (size.width() != 1 || size.height() != 1) - return; - - unsigned* pixelColor = reinterpret_cast_ptr<unsigned*>(cairo_image_surface_get_data(surface.get())); - m_solidColor = colorFromPremultipliedARGB(*pixelColor); - - m_isSolidColor = true; -} - -bool FrameData::clear(bool clearMetadata) -{ - if (clearMetadata) - m_haveMetadata = false; - - if (m_frame) { - m_frame.clear(); - return true; - } - return false; -} - -} // namespace WebCore - diff --git a/Source/WebCore/platform/graphics/TextRenderingMode.h b/Source/WebCore/platform/graphics/cairo/CairoUniquePtr.h index 4f817a4a4..f7a5007d2 100644 --- a/Source/WebCore/platform/graphics/TextRenderingMode.h +++ b/Source/WebCore/platform/graphics/cairo/CairoUniquePtr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2016 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,16 +20,28 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextRenderingMode_h -#define TextRenderingMode_h +#pragma once + +#include <cairo.h> +#include <memory> namespace WebCore { - enum TextRenderingMode { AutoTextRendering, OptimizeSpeed, OptimizeLegibility, GeometricPrecision }; - -} // namespace WebCore +template<typename T> struct CairoPtrDeleter { + void operator()(T* ptr) const = delete; +}; + +template<typename T> +using CairoUniquePtr = std::unique_ptr<T, CairoPtrDeleter<T>>; -#endif // TextRenderingMode_h +template<> struct CairoPtrDeleter<cairo_font_options_t> { + void operator() (cairo_font_options_t* ptr) const + { + cairo_font_options_destroy(ptr); + } +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp index 98207e4b2..8a3ff33d0 100644 --- a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp +++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,16 +27,19 @@ #include "config.h" #include "CairoUtilities.h" +#if USE(CAIRO) + #include "AffineTransform.h" #include "Color.h" #include "FloatPoint.h" #include "FloatRect.h" #include "IntRect.h" -#include "OwnPtrCairo.h" #include "Path.h" #include "PlatformPathCairo.h" #include "RefPtrCairo.h" +#include "Region.h" #include <wtf/Assertions.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Vector.h> #if ENABLE(ACCELERATED_2D_CANVAS) @@ -45,6 +48,14 @@ namespace WebCore { +#if USE(FREETYPE) && !PLATFORM(GTK) +const cairo_font_options_t* getDefaultCairoFontOptions() +{ + static NeverDestroyed<cairo_font_options_t*> options = cairo_font_options_create(); + return options; +} +#endif + void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr) { cairo_set_antialias(dstCr, cairo_get_antialias(srcCr)); @@ -64,15 +75,20 @@ void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr) void setSourceRGBAFromColor(cairo_t* context, const Color& color) { - float red, green, blue, alpha; - color.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(context, red, green, blue, alpha); + if (color.isExtended()) + cairo_set_source_rgba(context, color.asExtended().red(), color.asExtended().green(), color.asExtended().blue(), color.asExtended().alpha()); + else { + float red, green, blue, alpha; + color.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(context, red, green, blue, alpha); + } } void appendPathToCairoContext(cairo_t* to, cairo_t* from) { - OwnPtr<cairo_path_t> cairoPath = adoptPtr(cairo_copy_path(from)); - cairo_append_path(to, cairoPath.get()); + auto cairoPath = cairo_copy_path(from); + cairo_append_path(to, cairoPath); + cairo_path_destroy(cairoPath); } void setPathOnCairoContext(cairo_t* to, cairo_t* from) @@ -101,7 +117,7 @@ void appendRegionToCairoContext(cairo_t* to, const cairo_region_t* region) } } -cairo_operator_t toCairoOperator(CompositeOperator op) +static cairo_operator_t toCairoCompositeOperator(CompositeOperator op) { switch (op) { case CompositeClear: @@ -136,11 +152,12 @@ cairo_operator_t toCairoOperator(CompositeOperator op) return CAIRO_OPERATOR_SOURCE; } } -cairo_operator_t toCairoOperator(BlendMode blendOp) + +cairo_operator_t toCairoOperator(CompositeOperator op, BlendMode blendOp) { switch (blendOp) { case BlendModeNormal: - return CAIRO_OPERATOR_OVER; + return toCairoCompositeOperator(op); case BlendModeMultiply: return CAIRO_OPERATOR_MULTIPLY; case BlendModeScreen: @@ -198,8 +215,46 @@ void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSiz cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + // Due to a limitation in pixman, cairo cannot handle transformation matrices with values bigger than 32768. If the value is + // bigger, cairo is not able to paint anything, and this is the reason for the missing backgrounds reported in + // https://bugs.webkit.org/show_bug.cgi?id=154283. + + // When drawing a pattern there are 2 matrices that can overflow this limitation, and they are the current transformation + // matrix (which translates user space coordinates to coordinates of the output device) and the pattern matrix (which translates + // user space coordinates to pattern coordinates). The overflow happens only in the translation components of the matrices. + + // To avoid the problem in the transformation matrix what we do is remove the translation components of the transformation matrix + // and perform the translation by moving the destination rectangle instead. For this, we get its translation components (which are in + // device coordinates) and divide them by the scale factor to take them to user space coordinates. Then we move the transformation + // matrix by the opposite of that amount (which will zero the translation components of the transformation matrix), and move + // the destination rectangle by the same amount. We also need to apply the same translation to the pattern matrix, so we get the + // same pattern coordinates for the new destination rectangle. + + cairo_matrix_t ctm; + cairo_get_matrix(cr, &ctm); + double dx = 0, dy = 0; + cairo_matrix_transform_point(&ctm, &dx, &dy); + double xScale = 1, yScale = 1; + cairo_matrix_transform_distance(&ctm, &xScale, &yScale); + + dx = dx / xScale; + dy = dy / yScale; + cairo_translate(cr, -dx, -dy); + FloatRect adjustedDestRect(destRect); + adjustedDestRect.move(dx, dy); + + // Regarding the pattern matrix, what we do is reduce the translation component of the matrix taking advantage of the fact that we + // are drawing a repeated pattern. This means that, assuming that (w, h) is the size of the pattern, samplig it at (x, y) is the same + // than sampling it at (x mod w, y mod h), so we transform the translation component of the pattern matrix in that way. + cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform); - cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()}; + // dx and dy are added here as well to compensate the previous translation of the destination rectangle. + double phaseOffsetX = phase.x() + tileRect.x() * patternTransform.a() + dx; + double phaseOffsetY = phase.y() + tileRect.y() * patternTransform.d() + dy; + // this is where we perform the (x mod w, y mod h) metioned above, but with floats instead of integers. + phaseOffsetX -= std::trunc(phaseOffsetX / (tileRect.width() * patternTransform.a())) * tileRect.width() * patternTransform.a(); + phaseOffsetY -= std::trunc(phaseOffsetY / (tileRect.height() * patternTransform.d())) * tileRect.height() * patternTransform.d(); + cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phaseOffsetX, phaseOffsetY}; cairo_matrix_t combined; cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix); cairo_matrix_invert(&combined); @@ -208,13 +263,13 @@ void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSiz cairo_set_operator(cr, op); cairo_set_source(cr, pattern); cairo_pattern_destroy(pattern); - cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height()); + cairo_rectangle(cr, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height()); cairo_fill(cr); cairo_restore(cr); } -PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurface) +RefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurface) { // Cairo doesn't provide a way to copy a cairo_surface_t. // See http://lists.cairographics.org/archives/cairo/2007-June/010877.html @@ -260,6 +315,29 @@ IntSize cairoSurfaceSize(cairo_surface_t* surface) } } +void flipImageSurfaceVertically(cairo_surface_t* surface) +{ + ASSERT(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE); + + IntSize size = cairoSurfaceSize(surface); + ASSERT(!size.isEmpty()); + + int stride = cairo_image_surface_get_stride(surface); + int halfHeight = size.height() / 2; + + uint8_t* source = static_cast<uint8_t*>(cairo_image_surface_get_data(surface)); + std::unique_ptr<uint8_t[]> tmp = std::make_unique<uint8_t[]>(stride); + + for (int i = 0; i < halfHeight; ++i) { + uint8_t* top = source + (i * stride); + uint8_t* bottom = source + ((size.height()-i-1) * stride); + + memcpy(tmp.get(), top, stride); + memcpy(top, bottom, stride); + memcpy(bottom, tmp.get(), stride); + } +} + void cairoSurfaceSetDeviceScale(cairo_surface_t* surface, double xScale, double yScale) { // This function was added pretty much simultaneous to when 1.13 was branched. @@ -271,4 +349,28 @@ void cairoSurfaceSetDeviceScale(cairo_surface_t* surface, double xScale, double ASSERT_UNUSED(yScale, 1 == yScale); #endif } + +void cairoSurfaceGetDeviceScale(cairo_surface_t* surface, double& xScale, double& yScale) +{ +#if HAVE(CAIRO_SURFACE_SET_DEVICE_SCALE) + cairo_surface_get_device_scale(surface, &xScale, &yScale); +#else + UNUSED_PARAM(surface); + xScale = 1; + yScale = 1; +#endif +} + +RefPtr<cairo_region_t> toCairoRegion(const Region& region) +{ + RefPtr<cairo_region_t> cairoRegion = adoptRef(cairo_region_create()); + for (const auto& rect : region.rects()) { + cairo_rectangle_int_t cairoRect = rect; + cairo_region_union_rectangle(cairoRegion.get(), &cairoRect); + } + return cairoRegion; +} + } // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.h b/Source/WebCore/platform/graphics/cairo/CairoUtilities.h index df3680c01..9528e0210 100644 --- a/Source/WebCore/platform/graphics/cairo/CairoUtilities.h +++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.h @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,8 +24,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CairoUtilities_h -#define CairoUtilities_h +#pragma once + +#if USE(CAIRO) #include "GraphicsTypes.h" #include "IntSize.h" @@ -34,6 +35,10 @@ // This function was added pretty much simultaneous to when 1.13 was branched. #define HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR >= 13) +#if USE(FREETYPE) +#include <cairo-ft.h> +#endif + namespace WebCore { class AffineTransform; class Color; @@ -42,6 +47,32 @@ class FloatPoint; class IntSize; class IntRect; class Path; +class Region; + +#if USE(FREETYPE) +class CairoFtFaceLocker { +public: + CairoFtFaceLocker(cairo_scaled_font_t* scaledFont) + : m_scaledFont(scaledFont) + , m_ftFace(cairo_ft_scaled_font_lock_face(scaledFont)) + { + } + + ~CairoFtFaceLocker() + { + if (m_ftFace) + cairo_ft_scaled_font_unlock_face(m_scaledFont); + } + + FT_Face ftFace() const { return m_ftFace; } + +private: + cairo_scaled_font_t* m_scaledFont { nullptr }; + FT_Face m_ftFace { nullptr }; +}; + +const cairo_font_options_t* getDefaultCairoFontOptions(); +#endif void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr); void setSourceRGBAFromColor(cairo_t*, const Color&); @@ -49,18 +80,21 @@ void appendPathToCairoContext(cairo_t* to, cairo_t* from); void setPathOnCairoContext(cairo_t* to, cairo_t* from); void appendWebCorePathToCairoContext(cairo_t* context, const Path& path); void appendRegionToCairoContext(cairo_t*, const cairo_region_t*); -cairo_operator_t toCairoOperator(CompositeOperator op); -cairo_operator_t toCairoOperator(BlendMode blendOp); +cairo_operator_t toCairoOperator(CompositeOperator, BlendMode = BlendModeNormal); void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect); -PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t*); +RefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t*); void copyRectFromCairoSurfaceToContext(cairo_surface_t* from, cairo_t* to, const IntSize& offset, const IntRect&); void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize& offset, const IntRect&, const IntSize& = IntSize(), cairo_operator_t = CAIRO_OPERATOR_OVER); IntSize cairoSurfaceSize(cairo_surface_t*); +void flipImageSurfaceVertically(cairo_surface_t*); void cairoSurfaceSetDeviceScale(cairo_surface_t*, double xScale, double yScale); +void cairoSurfaceGetDeviceScale(cairo_surface_t*, double& xScale, double& yScale); + +RefPtr<cairo_region_t> toCairoRegion(const Region&); } // namespace WebCore -#endif // CairoUtilities_h +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h b/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h index 4eca4e4c5..ececc562d 100644 --- a/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h +++ b/Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.h @@ -21,11 +21,11 @@ * */ -#if USE(CAIRO) - #ifndef DrawErrorUnderline_h #define DrawErrorUnderline_h +#if USE(CAIRO) + #include <cairo.h> // @@ -101,6 +101,6 @@ static inline void drawErrorUnderline(cairo_t* cr, double x, double y, double wi cairo_fill(cr); } -#endif // DrawErrorUnderline_h +#endif // USE(CAIRO) -#endif +#endif // DrawErrorUnderline_h diff --git a/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp b/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp index 9f86f747e..ede90c90f 100644 --- a/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/FloatRectCairo.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,6 +26,8 @@ #include "config.h" #include "FloatRect.h" +#if USE(CAIRO) + #include <cairo.h> namespace WebCore { @@ -43,3 +45,5 @@ FloatRect::operator cairo_rectangle_t() const } } // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/FontCairo.cpp b/Source/WebCore/platform/graphics/cairo/FontCairo.cpp index a09e555d2..a712d0011 100644 --- a/Source/WebCore/platform/graphics/cairo/FontCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/FontCairo.cpp @@ -1,9 +1,10 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2010 Holger Hans Peter Freyther + * Copyright (C) 2014 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -14,10 +15,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,29 +29,32 @@ */ #include "config.h" -#include "Font.h" +#include "FontCascade.h" + +#if USE(CAIRO) #include "AffineTransform.h" #include "CairoUtilities.h" +#include "Font.h" #include "GlyphBuffer.h" #include "Gradient.h" #include "GraphicsContext.h" #include "ImageBuffer.h" #include "Pattern.h" #include "PlatformContextCairo.h" +#include "PlatformPathCairo.h" #include "ShadowBlur.h" -#include "SimpleFontData.h" namespace WebCore { -static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs) +static void drawGlyphsToContext(cairo_t* context, const Font& font, GlyphBufferGlyph* glyphs, unsigned numGlyphs) { cairo_matrix_t originalTransform; - float syntheticBoldOffset = font->syntheticBoldOffset(); + float syntheticBoldOffset = font.syntheticBoldOffset(); if (syntheticBoldOffset) cairo_get_matrix(context, &originalTransform); - cairo_set_scaled_font(context, font->platformData().scaledFont()); + cairo_set_scaled_font(context, font.platformData().scaledFont()); cairo_show_glyphs(context, glyphs, numGlyphs); if (syntheticBoldOffset) { @@ -62,21 +66,21 @@ static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, Gl cairo_set_matrix(context, &originalTransform); } -static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs) +static void drawGlyphsShadow(GraphicsContext& graphicsContext, const FloatPoint& point, const Font& font, GlyphBufferGlyph* glyphs, unsigned numGlyphs) { - ShadowBlur& shadow = graphicsContext->platformContext()->shadowBlur(); + ShadowBlur& shadow = graphicsContext.platformContext()->shadowBlur(); - if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow) + if (!(graphicsContext.textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow) return; - if (!graphicsContext->mustUseShadowBlur()) { + if (!graphicsContext.mustUseShadowBlur()) { // Optimize non-blurry shadows, by just drawing text without the ShadowBlur. - cairo_t* context = graphicsContext->platformContext()->cr(); + cairo_t* context = graphicsContext.platformContext()->cr(); cairo_save(context); - FloatSize shadowOffset(graphicsContext->state().shadowOffset); + FloatSize shadowOffset(graphicsContext.state().shadowOffset); cairo_translate(context, shadowOffset.width(), shadowOffset.height()); - setSourceRGBAFromColor(context, graphicsContext->state().shadowColor); + setSourceRGBAFromColor(context, graphicsContext.state().shadowColor); drawGlyphsToContext(context, font, glyphs, numGlyphs); cairo_restore(context); @@ -84,7 +88,7 @@ static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& } cairo_text_extents_t extents; - cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents); + cairo_scaled_font_glyph_extents(font.platformData().scaledFont(), glyphs, numGlyphs, &extents); FloatRect fontExtentsRect(point.x() + extents.x_bearing, point.y() + extents.y_bearing, extents.width, extents.height); if (GraphicsContext* shadowContext = shadow.beginShadowLayer(graphicsContext, fontExtentsRect)) { @@ -93,29 +97,29 @@ static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& } } -void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const +void FontCascade::drawGlyphs(GraphicsContext& context, const Font& font, const GlyphBuffer& glyphBuffer, + unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode) { - if (!font->platformData().size()) + if (!font.platformData().size()) return; GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); float offset = point.x(); - for (int i = 0; i < numGlyphs; i++) { + for (unsigned i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i).width(); } - PlatformContextCairo* platformContext = context->platformContext(); + PlatformContextCairo* platformContext = context.platformContext(); drawGlyphsShadow(context, point, font, glyphs, numGlyphs); cairo_t* cr = platformContext->cr(); cairo_save(cr); - if (context->textDrawingMode() & TextModeFill) { - platformContext->prepareForFilling(context->state(), PlatformContextCairo::AdjustPatternForGlobalAlpha); + if (context.textDrawingMode() & TextModeFill) { + platformContext->prepareForFilling(context.state(), PlatformContextCairo::AdjustPatternForGlobalAlpha); drawGlyphsToContext(cr, font, glyphs, numGlyphs); } @@ -123,12 +127,12 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons // twice the size of the width of the text we will not ask cairo to stroke // the text as even one single stroke would cover the full wdth of the text. // See https://bugs.webkit.org/show_bug.cgi?id=33759. - if (context->textDrawingMode() & TextModeStroke && context->strokeThickness() < 2 * offset) { - platformContext->prepareForStroking(context->state()); - cairo_set_line_width(cr, context->strokeThickness()); + if (context.textDrawingMode() & TextModeStroke && context.strokeThickness() < 2 * offset) { + platformContext->prepareForStroking(context.state()); + cairo_set_line_width(cr, context.strokeThickness()); // This may disturb the CTM, but we are going to call cairo_restore soon after. - cairo_set_scaled_font(cr, font->platformData().scaledFont()); + cairo_set_scaled_font(cr, font.platformData().scaledFont()); cairo_glyph_path(cr, glyphs, numGlyphs); cairo_stroke(cr); } @@ -136,4 +140,198 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons cairo_restore(cr); } +#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK) +struct GlyphIterationState { + GlyphIterationState(FloatPoint startingPoint, FloatPoint currentPoint, float centerOfLine, float minX, float maxX) + : startingPoint(startingPoint) + , currentPoint(currentPoint) + , centerOfLine(centerOfLine) + , minX(minX) + , maxX(maxX) + { + } + FloatPoint startingPoint; + FloatPoint currentPoint; + float centerOfLine; + float minX; + float maxX; +}; + +static bool findIntersectionPoint(float y, FloatPoint p1, FloatPoint p2, float& x) +{ + x = p1.x() + (y - p1.y()) * (p2.x() - p1.x()) / (p2.y() - p1.y()); + return (p1.y() < y && p2.y() > y) || (p1.y() > y && p2.y() < y); +} + +static void updateX(GlyphIterationState& state, float x) +{ + state.minX = std::min(state.minX, x); + state.maxX = std::max(state.maxX, x); +} + +// This function is called by Path::apply and is therefore invoked for each contour in a glyph. This +// function models each contours as a straight line and calculates the intersections between each +// pseudo-contour and the vertical center of the underline found in GlyphIterationState::centerOfLine. +// It keeps track of the leftmost and rightmost intersection in GlyphIterationState::minX and +// GlyphIterationState::maxX. +static void findPathIntersections(GlyphIterationState& state, const PathElement& element) +{ + bool doIntersection = false; + FloatPoint point = FloatPoint(); + switch (element.type) { + case PathElementMoveToPoint: + state.startingPoint = element.points[0]; + state.currentPoint = element.points[0]; + break; + case PathElementAddLineToPoint: + doIntersection = true; + point = element.points[0]; + break; + case PathElementAddQuadCurveToPoint: + doIntersection = true; + point = element.points[1]; + break; + case PathElementAddCurveToPoint: + doIntersection = true; + point = element.points[2]; + break; + case PathElementCloseSubpath: + doIntersection = true; + point = state.startingPoint; + break; + } + + if (!doIntersection) + return; + + float x; + if (findIntersectionPoint(state.centerOfLine, state.currentPoint, point, x)) + updateX(state, x); + + state.currentPoint = point; } + +class CairoGlyphToPathTranslator final : public GlyphToPathTranslator { +public: + CairoGlyphToPathTranslator(const TextRun& textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin) + : m_index(0) + , m_textRun(textRun) + , m_glyphBuffer(glyphBuffer) + , m_fontData(glyphBuffer.fontAt(m_index)) + , m_translation(AffineTransform().translate(textOrigin.x(), textOrigin.y())) + { + } + + bool containsMorePaths() final { return m_index != m_glyphBuffer.size(); } + Path path() final; + std::pair<float, float> extents() final; + GlyphUnderlineType underlineType() final; + void advance() final; + +private: + unsigned m_index; + const TextRun& m_textRun; + const GlyphBuffer& m_glyphBuffer; + const Font* m_fontData; + AffineTransform m_translation; +}; + +Path CairoGlyphToPathTranslator::path() +{ + Path path; + path.ensurePlatformPath(); + + cairo_glyph_t cairoGlyph = { m_glyphBuffer.glyphAt(m_index), 0, 0 }; + cairo_set_scaled_font(path.platformPath()->context(), m_fontData->platformData().scaledFont()); + cairo_glyph_path(path.platformPath()->context(), &cairoGlyph, 1); + + float syntheticBoldOffset = m_fontData->syntheticBoldOffset(); + if (syntheticBoldOffset) { + cairo_translate(path.platformPath()->context(), syntheticBoldOffset, 0); + cairo_show_glyphs(path.platformPath()->context(), &cairoGlyph, 1); + } + + path.transform(m_translation); + return path; +} + +std::pair<float, float> CairoGlyphToPathTranslator::extents() +{ + FloatPoint beginning = m_translation.mapPoint(FloatPoint()); + FloatSize end = m_translation.mapSize(m_glyphBuffer.advanceAt(m_index)); + return std::make_pair(static_cast<float>(beginning.x()), static_cast<float>(beginning.x() + end.width())); +} + +GlyphToPathTranslator::GlyphUnderlineType CairoGlyphToPathTranslator::underlineType() +{ + return computeUnderlineType(m_textRun, m_glyphBuffer, m_index); +} + +void CairoGlyphToPathTranslator::advance() +{ + GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index); + m_translation = m_translation.translate(advance.width(), advance.height()); + ++m_index; + if (m_index < m_glyphBuffer.size()) + m_fontData = m_glyphBuffer.fontAt(m_index); +} + +DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const +{ + if (isLoadingCustomFonts()) + return DashArray(); + + GlyphBuffer glyphBuffer; + glyphBuffer.saveOffsetsInString(); + float deltaX; + if (codePath(run) != FontCascade::Complex) + deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer); + else + deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer); + + if (!glyphBuffer.size()) + return DashArray(); + + // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778 + FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y()); + CairoGlyphToPathTranslator translator(run, glyphBuffer, origin); + DashArray result; + for (int index = 0; translator.containsMorePaths(); ++index, translator.advance()) { + float centerOfLine = lineExtents.y() + (lineExtents.height() / 2); + GlyphIterationState info = GlyphIterationState(FloatPoint(), FloatPoint(), centerOfLine, lineExtents.x() + lineExtents.width(), lineExtents.x()); + const Font* localFontData = glyphBuffer.fontAt(index); + if (!localFontData) { + // The advances will get all messed up if we do anything other than bail here. + result.clear(); + break; + } + switch (translator.underlineType()) { + case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: { + Path path = translator.path(); + path.apply([&info](const PathElement& pathElement) { + findPathIntersections(info, pathElement); + }); + if (info.minX < info.maxX) { + result.append(info.minX - lineExtents.x()); + result.append(info.maxX - lineExtents.x()); + } + break; + } + case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: { + std::pair<float, float> extents = translator.extents(); + result.append(extents.first - lineExtents.x()); + result.append(extents.second - lineExtents.x()); + break; + } + case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph: + // Nothing to do + break; + } + } + return result; +} +#endif // ENABLE(CSS3_TEXT_DECORATION_SKIP_INK) + +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp b/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp index 0c940c5d6..cc37d7d58 100644 --- a/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp +++ b/Source/WebCore/platform/graphics/cairo/FontCairoHarfbuzzNG.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,48 +25,44 @@ */ #include "config.h" -#include "Font.h" +#include "FontCascade.h" + +#if USE(CAIRO) +#include "Font.h" #include "GraphicsContext.h" #include "HarfBuzzShaper.h" +#include "LayoutRect.h" #include "Logging.h" #include "NotImplemented.h" #include "PlatformContextCairo.h" -#include "SimpleFontData.h" #include <cairo.h> namespace WebCore { -float Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int, int) const +float FontCascade::getGlyphsAndAdvancesForComplexText(const TextRun& run, unsigned, unsigned, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot /* forTextEmphasis */) const { - GlyphBuffer glyphBuffer; HarfBuzzShaper shaper(this, run); - if (shaper.shape(&glyphBuffer)) { - FloatPoint startPoint = point; - float startX = startPoint.x(); - drawGlyphBuffer(context, run, glyphBuffer, startPoint); - return startPoint.x() - startX; + if (!shaper.shape(&glyphBuffer)) { + LOG_ERROR("Shaper couldn't shape glyphBuffer."); + return 0; } - LOG_ERROR("Shaper couldn't shape glyphBuffer."); - return 0; -} -void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const -{ - notImplemented(); + // FIXME: Mac returns an initial advance here. + return 0; } -bool Font::canReturnFallbackFontsForComplexText() +bool FontCascade::canReturnFallbackFontsForComplexText() { return false; } -bool Font::canExpandAroundIdeographsInComplexText() +bool FontCascade::canExpandAroundIdeographsInComplexText() { return false; } -float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const +float FontCascade::floatWidthForComplexText(const TextRun& run, HashSet<const Font*>*, GlyphOverflow*) const { HarfBuzzShaper shaper(this, run); if (shaper.shape()) @@ -75,7 +71,7 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon return 0; } -int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool) const +int FontCascade::offsetForPositionForComplexText(const TextRun& run, float x, bool) const { HarfBuzzShaper shaper(this, run); if (shaper.shape()) @@ -84,13 +80,18 @@ int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool) con return 0; } -FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const +void FontCascade::adjustSelectionRectForComplexText(const TextRun& run, LayoutRect& selectionRect, unsigned from, unsigned to) const { HarfBuzzShaper shaper(this, run); - if (shaper.shape()) - return shaper.selectionRect(point, h, from, to); + if (shaper.shape()) { + // FIXME: This should mimic Mac port. + FloatRect rect = shaper.selectionRect(FloatPoint(selectionRect.location()), selectionRect.height().toInt(), from, to); + selectionRect = LayoutRect(rect); + return; + } LOG_ERROR("Shaper couldn't shape text run."); - return FloatRect(); } -} +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h index 1514526c2..c95ef9b5b 100644 --- a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h @@ -22,9 +22,9 @@ #ifndef FontCustomPlatformData_h #define FontCustomPlatformData_h -#include "FontOrientation.h" -#include "FontRenderingMode.h" -#include "FontWidthVariant.h" +#if USE(CAIRO) + +#include "TextFlags.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -33,6 +33,7 @@ typedef struct _cairo_font_face cairo_font_face_t; namespace WebCore { +class FontDescription; class FontPlatformData; class SharedBuffer; @@ -41,16 +42,17 @@ struct FontCustomPlatformData { public: FontCustomPlatformData(FT_Face, SharedBuffer&); ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(const FontDescription&, bool bold, bool italic); static bool supportsFormat(const String&); private: - FT_Face m_freeTypeFace; cairo_font_face_t* m_fontFace; }; std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffer&); -} +} // namespace WebCore + +#endif // USE(CAIRO) -#endif +#endif // FontCustomPlatformData_h diff --git a/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp b/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp index e0cfe3e65..724d832cc 100644 --- a/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/GradientCairo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,6 +27,8 @@ #include "config.h" #include "Gradient.h" +#if USE(CAIRO) + #include "GraphicsContext.h" #include "PlatformContextCairo.h" #include <cairo.h> @@ -106,4 +108,6 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect) context->restore(); } -} //namespace +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp b/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp index 940265a9a..b4ae79e4a 100644 --- a/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,9 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if USE(CAIRO) + +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" #include "CairoUtilities.h" @@ -38,13 +40,11 @@ #include "PlatformContextCairo.h" #include "RefPtrCairo.h" #include <cairo.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #if PLATFORM(WIN) -#include "GLSLANG/ShaderLang.h" +#include <GLSLANG/ShaderLang.h> #else -#include "ShaderLang.h" +#include <ANGLE/ShaderLang.h> #endif #if USE(OPENGL_ES_2) @@ -54,9 +54,13 @@ #include "OpenGLShims.h" #endif +#if USE(TEXTURE_MAPPER) +#include "TextureMapperGC3DPlatformLayer.h" +#endif + namespace WebCore { -PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attributes, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) +RefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3DAttributes attributes, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) { // This implementation doesn't currently support rendering directly to the HostWindow. if (renderStyle == RenderDirectlyToHostWindow) @@ -73,24 +77,30 @@ PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attri if (!success) return 0; - RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attributes, hostWindow, renderStyle)); - return context.release(); + return adoptRef(new GraphicsContext3D(attributes, hostWindow, renderStyle)); } -GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, HostWindow*, GraphicsContext3D::RenderStyle renderStyle) +GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attributes, HostWindow*, GraphicsContext3D::RenderStyle renderStyle) : m_currentWidth(0) , m_currentHeight(0) - , m_compiler(isGLES2Compliant() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT) , m_attrs(attributes) , m_texture(0) , m_compositorTexture(0) , m_fbo(0) - , m_depthStencilBuffer(0) +#if USE(COORDINATED_GRAPHICS_THREADED) + , m_intermediateTexture(0) +#endif + , m_layerComposited(false) , m_multisampleFBO(0) , m_multisampleDepthStencilBuffer(0) , m_multisampleColorBuffer(0) - , m_private(GraphicsContext3DPrivate::create(this, renderStyle)) { +#if USE(TEXTURE_MAPPER) + m_texmapLayer = std::make_unique<TextureMapperGC3DPlatformLayer>(*this, renderStyle); +#else + m_private = std::make_unique<GraphicsContext3DPrivate>(this, renderStyle); +#endif + makeContextCurrent(); validateAttributes(); @@ -109,9 +119,24 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, H ::glGenFramebuffers(1, &m_fbo); ::glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); - m_state.boundFBO = m_fbo; - if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) - ::glGenRenderbuffers(1, &m_depthStencilBuffer); +#if USE(COORDINATED_GRAPHICS_THREADED) + ::glGenTextures(1, &m_compositorTexture); + ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + ::glGenTextures(1, &m_intermediateTexture); + ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + ::glBindTexture(GL_TEXTURE_2D, 0); +#endif + // Create a multisample FBO. if (m_attrs.antialias) { @@ -121,9 +146,51 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, H ::glGenRenderbuffers(1, &m_multisampleColorBuffer); if (m_attrs.stencil || m_attrs.depth) ::glGenRenderbuffers(1, &m_multisampleDepthStencilBuffer); + } else { + // Bind canvas FBO. + glBindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + m_state.boundFBO = m_fbo; +#if USE(OPENGL_ES_2) + if (m_attrs.depth) + glGenRenderbuffers(1, &m_depthBuffer); + if (m_attrs.stencil) + glGenRenderbuffers(1, &m_stencilBuffer); +#endif + if (m_attrs.stencil || m_attrs.depth) + glGenRenderbuffers(1, &m_depthStencilBuffer); } } +#if !USE(OPENGL_ES_2) + ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + + if (GLContext::current()->version() >= 320) { + m_usingCoreProfile = true; + + // From version 3.2 on we use the OpenGL Core profile, so request that ouput to the shader compiler. + // OpenGL version 3.2 uses GLSL version 1.50. + m_compiler = ANGLEWebKitBridge(SH_GLSL_150_CORE_OUTPUT); + + // From version 3.2 on we use the OpenGL Core profile, and we need a VAO for rendering. + // A VAO could be created and bound by each component using GL rendering (TextureMapper, WebGL, etc). This is + // a simpler solution: the first GraphicsContext3D created on a GLContext will create and bind a VAO for that context. + GC3Dint currentVAO = 0; + getIntegerv(GraphicsContext3D::VERTEX_ARRAY_BINDING, ¤tVAO); + if (!currentVAO) { + m_vao = createVertexArray(); + bindVertexArray(m_vao); + } + } else { + // For lower versions request the compatibility output to the shader compiler. + m_compiler = ANGLEWebKitBridge(SH_GLSL_COMPATIBILITY_OUTPUT); + + // GL_POINT_SPRITE is needed in lower versions. + ::glEnable(GL_POINT_SPRITE); + } +#else + m_compiler = ANGLEWebKitBridge(SH_ESSL_OUTPUT); +#endif + // ANGLE initialization. ShBuiltInResources ANGLEResources; ShInitBuiltInResources(&ANGLEResources); @@ -145,18 +212,18 @@ GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, H m_compiler.setResources(ANGLEResources); -#if !USE(OPENGL_ES_2) - ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - ::glEnable(GL_POINT_SPRITE); -#endif - ::glClearColor(0, 0, 0, 0); } GraphicsContext3D::~GraphicsContext3D() { +#if USE(TEXTURE_MAPPER) + if (m_texmapLayer->renderStyle() == RenderToCurrentGLContext) + return; +#else if (m_private->renderStyle() == RenderToCurrentGLContext) return; +#endif makeContextCurrent(); if (m_texture) @@ -169,11 +236,24 @@ GraphicsContext3D::~GraphicsContext3D() if (m_attrs.stencil || m_attrs.depth) ::glDeleteRenderbuffers(1, &m_multisampleDepthStencilBuffer); ::glDeleteFramebuffers(1, &m_multisampleFBO); - } else { - if (m_attrs.stencil || m_attrs.depth) + } else if (m_attrs.stencil || m_attrs.depth) { +#if USE(OPENGL_ES_2) + if (m_depthBuffer) + glDeleteRenderbuffers(1, &m_depthBuffer); + + if (m_stencilBuffer) + glDeleteRenderbuffers(1, &m_stencilBuffer); +#endif + if (m_depthStencilBuffer) ::glDeleteRenderbuffers(1, &m_depthStencilBuffer); } ::glDeleteFramebuffers(1, &m_fbo); +#if USE(COORDINATED_GRAPHICS_THREADED) + ::glDeleteTextures(1, &m_intermediateTexture); +#endif + + if (m_vao) + deleteVertexArray(m_vao); } GraphicsContext3D::ImageExtractor::~ImageExtractor() @@ -187,17 +267,21 @@ bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool if (!m_image) return false; // We need this to stay in scope because the native image is just a shallow copy of the data. - m_decoder = new ImageSource(premultiplyAlpha ? ImageSource::AlphaPremultiplied : ImageSource::AlphaNotPremultiplied, ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied); + AlphaOption alphaOption = premultiplyAlpha ? AlphaOption::Premultiplied : AlphaOption::NotPremultiplied; + GammaAndColorProfileOption gammaAndColorProfileOption = ignoreGammaAndColorProfile ? GammaAndColorProfileOption::Ignored : GammaAndColorProfileOption::Applied; + m_decoder = new ImageSource(nullptr, alphaOption, gammaAndColorProfileOption); + if (!m_decoder) return false; - ImageSource& decoder = *m_decoder; + ImageSource& decoder = *m_decoder; m_alphaOp = AlphaDoNothing; + if (m_image->data()) { decoder.setData(m_image->data(), true); - if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0)) + if (!decoder.frameCount()) return false; - m_imageSurface = decoder.createFrameAtIndex(0); + m_imageSurface = decoder.createFrameImageAtIndex(0); } else { m_imageSurface = m_image->nativeImageForCurrentFrame(); // 1. For texImage2D with HTMLVideoElment input, assume no PremultiplyAlpha had been applied and the alpha value is 0xFF for each pixel, @@ -209,9 +293,10 @@ bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool // if m_imageSurface is not an image, extract a copy of the surface if (m_imageSurface && cairo_surface_get_type(m_imageSurface.get()) != CAIRO_SURFACE_TYPE_IMAGE) { - RefPtr<cairo_surface_t> tmpSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, m_imageWidth, m_imageHeight)); - copyRectFromOneSurfaceToAnother(m_imageSurface.get(), tmpSurface.get(), IntSize(), IntRect(0, 0, m_imageWidth, m_imageHeight), IntSize(), CAIRO_OPERATOR_SOURCE); - m_imageSurface = tmpSurface.release(); + IntSize surfaceSize = cairoSurfaceSize(m_imageSurface.get()); + auto tmpSurface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, surfaceSize.width(), surfaceSize.height())); + copyRectFromOneSurfaceToAnother(m_imageSurface.get(), tmpSurface.get(), IntSize(), IntRect(IntPoint(), surfaceSize), IntSize(), CAIRO_OPERATOR_SOURCE); + m_imageSurface = WTFMove(tmpSurface); } } @@ -272,24 +357,37 @@ void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imag context->restore(); } -void GraphicsContext3D::setContextLostCallback(PassOwnPtr<ContextLostCallback>) +void GraphicsContext3D::setContextLostCallback(std::unique_ptr<ContextLostCallback>) { } -void GraphicsContext3D::setErrorMessageCallback(PassOwnPtr<ErrorMessageCallback>) +void GraphicsContext3D::setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>) { } bool GraphicsContext3D::makeContextCurrent() { - if (!m_private) - return false; - return m_private->makeContextCurrent(); +#if USE(TEXTURE_MAPPER) + if (m_texmapLayer) + return m_texmapLayer->makeContextCurrent(); +#else + if (m_private) + return m_private->makeContextCurrent(); +#endif + return false; +} + +void GraphicsContext3D::checkGPUStatusIfNecessary() +{ } PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D() { +#if USE(TEXTURE_MAPPER) + return m_texmapLayer->platformContext(); +#else return m_private->platformContext(); +#endif } Platform3DObject GraphicsContext3D::platformTexture() const @@ -306,13 +404,33 @@ bool GraphicsContext3D::isGLES2Compliant() const #endif } -#if USE(ACCELERATED_COMPOSITING) PlatformLayer* GraphicsContext3D::platformLayer() const { +#if USE(TEXTURE_MAPPER) + return m_texmapLayer.get(); +#else return m_private.get(); +#endif +} + +#if PLATFORM(GTK) +Extensions3D& GraphicsContext3D::getExtensions() +{ + if (!m_extensions) { +#if USE(OPENGL_ES_2) + // glGetStringi is not available on GLES2. + m_extensions = std::make_unique<Extensions3DOpenGLES>(this, false); +#else + // From OpenGL 3.2 on we use the Core profile, and there we must use glGetStringi. + m_extensions = std::make_unique<Extensions3DOpenGL>(this, GLContext::current()->version() >= 320); +#endif + } + return *m_extensions; } #endif } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 0fa750396..ac4b4a089 100644 --- a/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2008 Nuanti Ltd. @@ -17,10 +17,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -37,30 +37,29 @@ #include "AffineTransform.h" #include "CairoUtilities.h" +#include "DisplayListRecorder.h" #include "DrawErrorUnderline.h" #include "FloatConversion.h" #include "FloatRect.h" +#include "FloatRoundedRect.h" #include "Font.h" #include "GraphicsContextPlatformPrivateCairo.h" +#include "ImageBuffer.h" #include "IntRect.h" #include "NotImplemented.h" -#include "OwnPtrCairo.h" #include "Path.h" #include "Pattern.h" #include "PlatformContextCairo.h" #include "PlatformPathCairo.h" #include "RefPtrCairo.h" #include "ShadowBlur.h" -#include "SimpleFontData.h" #include "TransformationMatrix.h" #include <cairo.h> #include <math.h> #include <stdio.h> #include <wtf/MathExtras.h> -#if PLATFORM(GTK) -#include <gdk/gdk.h> -#elif PLATFORM(WIN) +#if PLATFORM(WIN) #include <cairo-win32.h> #endif @@ -71,36 +70,31 @@ namespace WebCore { // A helper which quickly fills a rectangle with a simple color fill. static inline void fillRectWithColor(cairo_t* cr, const FloatRect& rect, const Color& color) { - if (!color.alpha() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER) + if (!color.isVisible() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER) return; + setSourceRGBAFromColor(cr, color); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); cairo_fill(cr); } -static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points) -{ - cairo_move_to(context, points[0].x(), points[0].y()); - for (size_t i = 1; i < numPoints; i++) - cairo_line_to(context, points[i].x(), points[i].y()); - cairo_close_path(context); -} - enum PathDrawingStyle { Fill = 1, Stroke = 2, FillAndStroke = Fill + Stroke }; -static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle drawingStyle) +static inline void drawPathShadow(GraphicsContext& context, PathDrawingStyle drawingStyle) { - ShadowBlur& shadow = context->platformContext()->shadowBlur(); + ShadowBlur& shadow = context.platformContext()->shadowBlur(); if (shadow.type() == ShadowBlur::NoShadow) return; // Calculate the extents of the rendered solid paths. - cairo_t* cairoContext = context->platformContext()->cr(); - OwnPtr<cairo_path_t> path = adoptPtr(cairo_copy_path(cairoContext)); + cairo_t* cairoContext = context.platformContext()->cr(); + std::unique_ptr<cairo_path_t, void(*)(cairo_path_t*)> path(cairo_copy_path(cairoContext), [](cairo_path_t* path) { + cairo_path_destroy(path); + }); FloatRect solidFigureExtents; double x0 = 0; @@ -130,14 +124,14 @@ static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle dra if (drawingStyle & Fill) { cairo_save(cairoShadowContext); cairo_append_path(cairoShadowContext, path.get()); - shadowContext->platformContext()->prepareForFilling(context->state(), PlatformContextCairo::NoAdjustment); + shadowContext->platformContext()->prepareForFilling(context.state(), PlatformContextCairo::NoAdjustment); cairo_fill(cairoShadowContext); cairo_restore(cairoShadowContext); } if (drawingStyle & Stroke) { cairo_append_path(cairoShadowContext, path.get()); - shadowContext->platformContext()->prepareForStroking(context->state(), PlatformContextCairo::DoNotPreserveAlpha); + shadowContext->platformContext()->prepareForStroking(context.state(), PlatformContextCairo::DoNotPreserveAlpha); cairo_stroke(cairoShadowContext); } @@ -150,44 +144,45 @@ static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle dra cairo_append_path(cairoContext, path.get()); } -static inline void fillCurrentCairoPath(GraphicsContext* context) +static inline void fillCurrentCairoPath(GraphicsContext& context) { - cairo_t* cr = context->platformContext()->cr(); + cairo_t* cr = context.platformContext()->cr(); cairo_save(cr); - context->platformContext()->prepareForFilling(context->state(), PlatformContextCairo::AdjustPatternForGlobalAlpha); + context.platformContext()->prepareForFilling(context.state(), PlatformContextCairo::AdjustPatternForGlobalAlpha); cairo_fill(cr); cairo_restore(cr); } -static inline void shadowAndFillCurrentCairoPath(GraphicsContext* context) +static inline void shadowAndFillCurrentCairoPath(GraphicsContext& context) { drawPathShadow(context, Fill); fillCurrentCairoPath(context); } -static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext* context) +static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext& context) { drawPathShadow(context, Stroke); - context->platformContext()->prepareForStroking(context->state()); - cairo_stroke(context->platformContext()->cr()); + context.platformContext()->prepareForStroking(context.state()); + cairo_stroke(context.platformContext()->cr()); } GraphicsContext::GraphicsContext(cairo_t* cr) - : m_updatingControlTints(false), - m_transparencyCount(0) { + if (!cr) + return; + m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr)); } void GraphicsContext::platformInit(PlatformContextCairo* platformContext) { + if (!platformContext) + return; + m_data = new GraphicsContextPlatformPrivate(platformContext); - if (platformContext) - m_data->syncContext(platformContext->cr()); - else - setPaintingDisabled(true); + m_data->syncContext(platformContext->cr()); } void GraphicsContext::platformDestroy() @@ -200,6 +195,11 @@ AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const if (paintingDisabled()) return AffineTransform(); + if (isRecording()) { + WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts."); + return AffineTransform(); + } + cairo_t* cr = platformContext()->cr(); cairo_matrix_t m; cairo_get_matrix(cr, &m); @@ -213,28 +213,34 @@ PlatformContextCairo* GraphicsContext::platformContext() const void GraphicsContext::savePlatformState() { + ASSERT(!isRecording()); platformContext()->save(); m_data->save(); } void GraphicsContext::restorePlatformState() { + ASSERT(!isRecording()); platformContext()->restore(); m_data->restore(); platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur), m_state.shadowOffset, m_state.shadowColor, - m_state.shadowColorSpace, m_state.shadowsIgnoreTransforms); } // Draws a filled rectangle with a stroked border. -void GraphicsContext::drawRect(const IntRect& rect) +void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness) { if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->drawRect(rect, borderThickness); + return; + } + ASSERT(!rect.isEmpty()); cairo_t* cr = platformContext()->cr(); @@ -247,108 +253,132 @@ void GraphicsContext::drawRect(const IntRect& rect) FloatRect r(rect); r.inflate(-.5f); cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height()); - cairo_set_line_width(cr, 1.0); + cairo_set_line_width(cr, 1.0); // borderThickness? cairo_stroke(cr); } cairo_restore(cr); } -static double calculateStrokePatternOffset(int distance, int patternWidth) +void GraphicsContext::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation) { - // Example: 80 pixels with a width of 30 pixels. Remainder is 20. - // The maximum pixels of line we could paint will be 50 pixels. - int remainder = distance % patternWidth; - int numSegments = (distance - remainder) / patternWidth; + if (paintingDisabled()) + return; - // Special case 1px dotted borders for speed. - if (patternWidth == 1) - return 1; + if (isRecording()) { + m_displayListRecorder->drawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation); + return; + } - bool evenNumberOfSegments = !(numSegments % 2); - if (remainder) - evenNumberOfSegments = !evenNumberOfSegments; + platformContext()->save(); - if (evenNumberOfSegments) { - if (remainder) - return (patternWidth - remainder) + (remainder / 2); - return patternWidth / 2; + // Set the compositing operation. + if (op == CompositeSourceOver && blendMode == BlendModeNormal) + setCompositeOperation(CompositeCopy); + else + setCompositeOperation(op, blendMode); + + FloatRect dst = destRect; + + if (orientation != DefaultImageOrientation) { + // ImageOrientation expects the origin to be at (0, 0). + translate(dst.x(), dst.y()); + dst.setLocation(FloatPoint()); + concatCTM(orientation.transformFromDefault(dst.size())); + if (orientation.usesWidthAsHeight()) { + // The destination rectangle will have its width and height already reversed for the orientation of + // the image, as it was needed for page layout, so we need to reverse it back here. + dst = FloatRect(dst.x(), dst.y(), dst.height(), dst.width()); + } } - // Odd number of segments. - if (remainder) - return (patternWidth - remainder) / 2.f; - return 0; + platformContext()->drawSurfaceToContext(image.get(), dst, srcRect, *this); + platformContext()->restore(); } -static void drawLineOnCairoContext(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point1, const FloatPoint& point2) +// This is only used to draw borders, so we should not draw shadows. +void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2) { - StrokeStyle style = graphicsContext->strokeStyle(); - if (style == NoStroke) + if (paintingDisabled()) return; - const Color& strokeColor = graphicsContext->strokeColor(); - int strokeThickness = floorf(graphicsContext->strokeThickness()); - if (graphicsContext->strokeThickness() < 1) - strokeThickness = 1; + if (strokeStyle() == NoStroke) + return; - int patternWidth = 0; - if (style == DottedStroke) - patternWidth = strokeThickness; - else if (style == DashedStroke) - patternWidth = 3 * strokeThickness; + if (isRecording()) { + m_displayListRecorder->drawLine(point1, point2); + return; + } - bool isVerticalLine = point1.x() == point2.x(); - FloatPoint point1OnPixelBoundaries = point1; - FloatPoint point2OnPixelBoundaries = point2; - GraphicsContext::adjustLineToPixelBoundaries(point1OnPixelBoundaries, point2OnPixelBoundaries, strokeThickness, style); + const Color& strokeColor = this->strokeColor(); + float thickness = strokeThickness(); + bool isVerticalLine = (point1.x() + thickness == point2.x()); + float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x(); + if (!thickness || !strokeWidth) + return; - if (patternWidth) { - // Do a rect fill of our endpoints. This ensures we always have the - // appearance of being a border. We then draw the actual dotted/dashed line. - FloatRect firstRect(point1OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness)); - FloatRect secondRect(point2OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness)); + cairo_t* cairoContext = platformContext()->cr(); + StrokeStyle strokeStyle = this->strokeStyle(); + float cornerWidth = 0; + bool drawsDashedLine = strokeStyle == DottedStroke || strokeStyle == DashedStroke; + + if (drawsDashedLine) { + cairo_save(cairoContext); + // Figure out end points to ensure we always paint corners. + cornerWidth = dashedLineCornerWidthForStrokeWidth(strokeWidth); if (isVerticalLine) { - firstRect.move(-strokeThickness / 2, -strokeThickness); - secondRect.move(-strokeThickness / 2, 0); + fillRectWithColor(cairoContext, FloatRect(point1.x(), point1.y(), thickness, cornerWidth), strokeColor); + fillRectWithColor(cairoContext, FloatRect(point1.x(), point2.y() - cornerWidth, thickness, cornerWidth), strokeColor); } else { - firstRect.move(-strokeThickness, -strokeThickness / 2); - secondRect.move(0, -strokeThickness / 2); + fillRectWithColor(cairoContext, FloatRect(point1.x(), point1.y(), cornerWidth, thickness), strokeColor); + fillRectWithColor(cairoContext, FloatRect(point2.x() - cornerWidth, point1.y(), cornerWidth, thickness), strokeColor); + } + strokeWidth -= 2 * cornerWidth; + float patternWidth = dashedLinePatternWidthForStrokeWidth(strokeWidth); + // Check if corner drawing sufficiently covers the line. + if (strokeWidth <= patternWidth + 1) { + cairo_restore(cairoContext); + return; } - fillRectWithColor(context, firstRect, strokeColor); - fillRectWithColor(context, secondRect, strokeColor); - int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2 * strokeThickness; - double patternOffset = calculateStrokePatternOffset(distance, patternWidth); - double patternWidthAsDouble = patternWidth; - cairo_set_dash(context, &patternWidthAsDouble, 1, patternOffset); + float patternOffset = dashedLinePatternOffsetForPatternAndStrokeWidth(patternWidth, strokeWidth); + const double dashedLine[2] = { static_cast<double>(patternWidth), static_cast<double>(patternWidth) }; + cairo_set_dash(cairoContext, dashedLine, 2, patternOffset); + } else { + setSourceRGBAFromColor(cairoContext, strokeColor); + if (thickness < 1) + cairo_set_line_width(cairoContext, 1); } - setSourceRGBAFromColor(context, strokeColor); - cairo_set_line_width(context, strokeThickness); - cairo_move_to(context, point1OnPixelBoundaries.x(), point1OnPixelBoundaries.y()); - cairo_line_to(context, point2OnPixelBoundaries.x(), point2OnPixelBoundaries.y()); - cairo_stroke(context); -} -// This is only used to draw borders, so we should not draw shadows. -void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) -{ - if (paintingDisabled()) - return; + auto centeredPoints = centerLineAndCutOffCorners(isVerticalLine, cornerWidth, point1, point2); + auto p1 = centeredPoints[0]; + auto p2 = centeredPoints[1]; - cairo_t* cairoContext = platformContext()->cr(); - cairo_save(cairoContext); - drawLineOnCairoContext(this, cairoContext, point1, point2); - cairo_restore(cairoContext); + if (shouldAntialias()) + cairo_set_antialias(cairoContext, CAIRO_ANTIALIAS_NONE); + + cairo_new_path(cairoContext); + cairo_move_to(cairoContext, p1.x(), p1.y()); + cairo_line_to(cairoContext, p2.x(), p2.y()); + cairo_stroke(cairoContext); + if (drawsDashedLine) + cairo_restore(cairoContext); + if (shouldAntialias()) + cairo_set_antialias(cairoContext, CAIRO_ANTIALIAS_DEFAULT); } // This method is only used to draw the little circles used in lists. -void GraphicsContext::drawEllipse(const IntRect& rect) +void GraphicsContext::drawEllipse(const FloatRect& rect) { if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->drawEllipse(rect); + return; + } + cairo_t* cr = platformContext()->cr(); cairo_save(cr); float yRadius = .5 * rect.height(); @@ -358,7 +388,7 @@ void GraphicsContext::drawEllipse(const IntRect& rect) cairo_arc(cr, 0., 0., 1., 0., 2 * piFloat); cairo_restore(cr); - if (fillColor().alpha()) { + if (fillColor().isVisible()) { setSourceRGBAFromColor(cr, fillColor()); cairo_fill_preserve(cr); } @@ -371,67 +401,19 @@ void GraphicsContext::drawEllipse(const IntRect& rect) cairo_new_path(cr); } -void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) +void GraphicsContext::fillPath(const Path& path) { - if (paintingDisabled()) + if (paintingDisabled() || path.isEmpty()) return; - if (npoints <= 1) + if (isRecording()) { + m_displayListRecorder->fillPath(path); return; - - cairo_t* cr = platformContext()->cr(); - - cairo_save(cr); - cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); - addConvexPolygonToContext(cr, npoints, points); - - if (fillColor().alpha()) { - setSourceRGBAFromColor(cr, fillColor()); - cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); - cairo_fill_preserve(cr); } - if (strokeStyle() != NoStroke) { - setSourceRGBAFromColor(cr, strokeColor()); - cairo_set_line_width(cr, strokeThickness()); - cairo_stroke(cr); - } else - cairo_new_path(cr); - - cairo_restore(cr); -} - -void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) -{ - if (paintingDisabled()) - return; - - if (numPoints <= 1) - return; - - cairo_t* cr = platformContext()->cr(); - - cairo_new_path(cr); - cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); - cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr); - - cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); - cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); - addConvexPolygonToContext(cr, numPoints, points); - cairo_clip(cr); - - cairo_set_antialias(cr, savedAntialiasRule); - cairo_set_fill_rule(cr, savedFillRule); -} - -void GraphicsContext::fillPath(const Path& path) -{ - if (paintingDisabled() || path.isEmpty()) - return; - cairo_t* cr = platformContext()->cr(); setPathOnCairoContext(cr, path.platformPath()->context()); - shadowAndFillCurrentCairoPath(this); + shadowAndFillCurrentCairoPath(*this); } void GraphicsContext::strokePath(const Path& path) @@ -439,9 +421,14 @@ void GraphicsContext::strokePath(const Path& path) if (paintingDisabled() || path.isEmpty()) return; + if (isRecording()) { + m_displayListRecorder->strokePath(path); + return; + } + cairo_t* cr = platformContext()->cr(); setPathOnCairoContext(cr, path.platformPath()->context()); - shadowAndStrokeCurrentCairoPath(this); + shadowAndStrokeCurrentCairoPath(*this); } void GraphicsContext::fillRect(const FloatRect& rect) @@ -449,18 +436,28 @@ void GraphicsContext::fillRect(const FloatRect& rect) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->fillRect(rect); + return; + } + cairo_t* cr = platformContext()->cr(); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); - shadowAndFillCurrentCairoPath(this); + shadowAndFillCurrentCairoPath(*this); } -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace) +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) { if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->fillRect(rect, color); + return; + } + if (hasShadow()) - platformContext()->shadowBlur().drawRectShadow(this, rect, RoundedRect::Radii()); + platformContext()->shadowBlur().drawRectShadow(*this, FloatRoundedRect(rect)); fillRectWithColor(platformContext()->cr(), rect, color); } @@ -470,6 +467,11 @@ void GraphicsContext::clip(const FloatRect& rect) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->clip(rect); + return; + } + cairo_t* cr = platformContext()->cr(); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); @@ -492,15 +494,44 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->clipPath(path, clipRule); + return; + } + cairo_t* cr = platformContext()->cr(); if (!path.isNull()) setPathOnCairoContext(cr, path.platformPath()->context()); + + cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); cairo_clip(cr); + cairo_set_fill_rule(cr, savedFillRule); + + m_data->clip(path); +} + +void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect) +{ + if (paintingDisabled()) + return; + + RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore); + RefPtr<cairo_surface_t> surface = image->nativeImageForCurrentFrame(); + if (surface) + platformContext()->pushImageMask(surface.get(), destRect); } IntRect GraphicsContext::clipBounds() const { + if (paintingDisabled()) + return IntRect(); + + if (isRecording()) { + WTFLogAlways("Getting the clip bounds not yet supported with display lists"); + return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists. + } + double x1, x2, y1, y2; cairo_clip_extents(platformContext()->cr(), &x1, &y1, &x2, &y2); return enclosingIntRect(FloatRect(x1, y1, x2 - x1, y2 - y1)); @@ -510,11 +541,13 @@ static inline void adjustFocusRingColor(Color& color) { #if !PLATFORM(GTK) // Force the alpha to 50%. This matches what the Mac does with outline rings. - color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127)); + color = Color(makeRGBA(color.red(), color.green(), color.blue(), 127)); +#else + UNUSED_PARAM(color); #endif } -static inline void adjustFocusRingLineWidth(int& width) +static inline void adjustFocusRingLineWidth(float& width) { #if PLATFORM(GTK) width = 2; @@ -532,8 +565,11 @@ static inline StrokeStyle focusRingStrokeStyle() #endif } -void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color) +void GraphicsContext::drawFocusRing(const Path& path, float width, float /* offset */, const Color& color) { + if (paintingDisabled()) + return; + // FIXME: We should draw paths that describe a rectangle with rounded corners // so as to be consistent with how we draw rectangular focus rings. Color ringColor = color; @@ -542,109 +578,91 @@ void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset * cairo_t* cr = platformContext()->cr(); cairo_save(cr); + cairo_push_group(cr); appendWebCorePathToCairoContext(cr, path); setSourceRGBAFromColor(cr, ringColor); cairo_set_line_width(cr, width); setPlatformStrokeStyle(focusRingStrokeStyle()); - cairo_stroke(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_stroke_preserve(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); + cairo_fill(cr); + + cairo_pop_group_to_source(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_paint(cr); cairo_restore(cr); } -void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color) +void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float /* offset */, const Color& color) { if (paintingDisabled()) return; - unsigned rectCount = rects.size(); - - cairo_t* cr = platformContext()->cr(); - cairo_save(cr); - cairo_push_group(cr); - cairo_new_path(cr); - + Path path; #if PLATFORM(GTK) -#ifdef GTK_API_VERSION_2 - GdkRegion* reg = gdk_region_new(); -#else - cairo_region_t* reg = cairo_region_create(); -#endif - - for (unsigned i = 0; i < rectCount; i++) { -#ifdef GTK_API_VERSION_2 - GdkRectangle rect = rects[i]; - gdk_region_union_with_rect(reg, &rect); -#else - cairo_rectangle_int_t rect = rects[i]; - cairo_region_union_rectangle(reg, &rect); -#endif - } - gdk_cairo_region(cr, reg); -#ifdef GTK_API_VERSION_2 - gdk_region_destroy(reg); -#else - cairo_region_destroy(reg); -#endif + for (const auto& rect : rects) + path.addRect(rect); #else + unsigned rectCount = rects.size(); int radius = (width - 1) / 2; - Path path; + Path subPath; for (unsigned i = 0; i < rectCount; ++i) { if (i > 0) - path.clear(); - path.addRoundedRect(rects[i], FloatSize(radius, radius)); - appendWebCorePathToCairoContext(cr, path); + subPath.clear(); + subPath.addRoundedRect(rects[i], FloatSize(radius, radius)); + path.addPath(subPath, AffineTransform()); } #endif - Color ringColor = color; - adjustFocusRingColor(ringColor); - adjustFocusRingLineWidth(width); - setSourceRGBAFromColor(cr, ringColor); - cairo_set_line_width(cr, width); - setPlatformStrokeStyle(focusRingStrokeStyle()); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_stroke_preserve(cr); - - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); - cairo_fill(cr); - - cairo_pop_group_to_source(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_paint(cr); - cairo_restore(cr); + drawFocusRing(path, width, 0, color); } -FloatRect GraphicsContext::computeLineBoundsForText(const FloatPoint& origin, float width, bool) +void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines, StrokeStyle) { - return FloatRect(origin, FloatSize(width, strokeThickness())); + DashArray widths; + widths.append(width); + widths.append(0); + drawLinesForText(origin, widths, printing, doubleUnderlines); } -void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing) +void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleUnderlines, StrokeStyle) { if (paintingDisabled()) return; - cairo_t* cairoContext = platformContext()->cr(); - cairo_save(cairoContext); - - // This bumping of <1 stroke thicknesses matches the one in drawLineOnCairoContext. - FloatPoint endPoint(origin + IntSize(width, 0)); - FloatRect lineExtents = computeLineBoundsForText(origin, width, printing); + if (widths.size() <= 0) + return; - ShadowBlur& shadow = platformContext()->shadowBlur(); - if (GraphicsContext* shadowContext = shadow.beginShadowLayer(this, lineExtents)) { - drawLineOnCairoContext(this, shadowContext->platformContext()->cr(), origin, endPoint); - shadow.endShadowLayer(this); + if (isRecording()) { + m_displayListRecorder->drawLinesForText(point, widths, printing, doubleUnderlines, strokeThickness()); + return; } - drawLineOnCairoContext(this, cairoContext, origin, endPoint); - cairo_restore(cairoContext); -} + Color localStrokeColor(strokeColor()); -void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing) -{ + FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, localStrokeColor); + + Vector<FloatRect, 4> dashBounds; + ASSERT(!(widths.size() % 2)); + dashBounds.reserveInitialCapacity(dashBounds.size() / 2); for (size_t i = 0; i < widths.size(); i += 2) - drawLineForText(FloatPoint(point.x() + widths[i], point.y()), widths[i+1] - widths[i], printing); + dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y()), FloatSize(widths[i+1] - widths[i], bounds.height()))); + + if (doubleUnderlines) { + // The space between double underlines is equal to the height of the underline + for (size_t i = 0; i < widths.size(); i += 2) + dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y() + 2 * bounds.height()), FloatSize(widths[i+1] - widths[i], bounds.height()))); + } + + cairo_t* cr = platformContext()->cr(); + cairo_save(cr); + + for (auto& dash : dashBounds) + fillRectWithColor(cr, dash, localStrokeColor); + + cairo_restore(cr); } void GraphicsContext::updateDocumentMarkerResources() @@ -679,6 +697,14 @@ void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode) { + if (paintingDisabled()) + return frect; + + if (isRecording()) { + WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts."); + return frect; + } + FloatRect result; double x = frect.x(); double y = frect.y(); @@ -701,7 +727,7 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingM width = 1; else width = round(width); - if (height > -1 && width < 0) + if (height > -1 && height < 0) height = -1; else if (height > 0 && height < 1) height = 1; @@ -719,18 +745,23 @@ void GraphicsContext::translate(float x, float y) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->translate(x, y); + return; + } + cairo_t* cr = platformContext()->cr(); cairo_translate(cr, x, y); m_data->translate(x, y); } -void GraphicsContext::setPlatformFillColor(const Color&, ColorSpace) +void GraphicsContext::setPlatformFillColor(const Color&) { // Cairo contexts can't hold separate fill and stroke colors // so we set them just before we actually fill or stroke } -void GraphicsContext::setPlatformStrokeColor(const Color&, ColorSpace) +void GraphicsContext::setPlatformStrokeColor(const Color&) { // Cairo contexts can't hold separate fill and stroke colors // so we set them just before we actually fill or stroke @@ -741,17 +772,21 @@ void GraphicsContext::setPlatformStrokeThickness(float strokeThickness) if (paintingDisabled()) return; + ASSERT(!isRecording()); + cairo_set_line_width(platformContext()->cr(), strokeThickness); } void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle) { - static double dashPattern[] = {5.0, 5.0}; - static double dotPattern[] = {1.0, 1.0}; + static const double dashPattern[] = { 5.0, 5.0 }; + static const double dotPattern[] = { 1.0, 1.0 }; if (paintingDisabled()) return; + ASSERT(!isRecording()); + switch (strokeStyle) { case NoStroke: // FIXME: is it the right way to emulate NoStroke? @@ -771,7 +806,7 @@ void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle) } } -void GraphicsContext::setURLForRect(const URL&, const IntRect&) +void GraphicsContext::setURLForRect(const URL&, const FloatRect&) { notImplemented(); } @@ -781,6 +816,11 @@ void GraphicsContext::concatCTM(const AffineTransform& transform) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->concatCTM(transform); + return; + } + cairo_t* cr = platformContext()->cr(); const cairo_matrix_t matrix = cairo_matrix_t(transform); cairo_transform(cr, &matrix); @@ -792,13 +832,18 @@ void GraphicsContext::setCTM(const AffineTransform& transform) if (paintingDisabled()) return; + if (isRecording()) { + WTFLogAlways("GraphicsContext::setCTM() is not compatible with recording contexts."); + return; + } + cairo_t* cr = platformContext()->cr(); const cairo_matrix_t matrix = cairo_matrix_t(transform); cairo_set_matrix(cr, &matrix); m_data->setCTM(transform); } -void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&, ColorSpace) +void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&) { if (paintingDisabled()) return; @@ -813,7 +858,6 @@ void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color cons platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur), m_state.shadowOffset, m_state.shadowColor, - m_state.shadowColorSpace, m_state.shadowsIgnoreTransforms); } @@ -830,6 +874,8 @@ void GraphicsContext::beginPlatformTransparencyLayer(float opacity) if (paintingDisabled()) return; + ASSERT(!isRecording()); + cairo_t* cr = platformContext()->cr(); cairo_push_group(cr); m_data->layers.append(opacity); @@ -840,6 +886,8 @@ void GraphicsContext::endPlatformTransparencyLayer() if (paintingDisabled()) return; + ASSERT(!isRecording()); + cairo_t* cr = platformContext()->cr(); cairo_pop_group_to_source(cr); @@ -857,6 +905,11 @@ void GraphicsContext::clearRect(const FloatRect& rect) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->clearRect(rect); + return; + } + cairo_t* cr = platformContext()->cr(); cairo_save(cr); @@ -871,11 +924,16 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->strokeRect(rect, width); + return; + } + cairo_t* cr = platformContext()->cr(); cairo_save(cr); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); cairo_set_line_width(cr, width); - shadowAndStrokeCurrentCairoPath(this); + shadowAndStrokeCurrentCairoPath(*this); cairo_restore(cr); } @@ -884,6 +942,11 @@ void GraphicsContext::setLineCap(LineCap lineCap) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->setLineCap(lineCap); + return; + } + cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT; switch (lineCap) { case ButtCap: @@ -899,9 +962,29 @@ void GraphicsContext::setLineCap(LineCap lineCap) cairo_set_line_cap(platformContext()->cr(), cairoCap); } +static inline bool isDashArrayAllZero(const DashArray& dashes) +{ + for (auto& dash : dashes) { + if (dash) + return false; + } + return true; +} + void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { - cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset); + if (paintingDisabled()) + return; + + if (isRecording()) { + m_displayListRecorder->setLineDash(dashes, dashOffset); + return; + } + + if (isDashArrayAllZero(dashes)) + cairo_set_dash(platformContext()->cr(), 0, 0, 0); + else + cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset); } void GraphicsContext::setLineJoin(LineJoin lineJoin) @@ -909,6 +992,11 @@ void GraphicsContext::setLineJoin(LineJoin lineJoin) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->setLineJoin(lineJoin); + return; + } + cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER; switch (lineJoin) { case MiterJoin: @@ -929,10 +1017,16 @@ void GraphicsContext::setMiterLimit(float miter) if (paintingDisabled()) return; + if (isRecording()) { + // Maybe this should be part of the state. + m_displayListRecorder->setMiterLimit(miter); + return; + } + cairo_set_miter_limit(platformContext()->cr(), miter); } -void GraphicsContext::setAlpha(float alpha) +void GraphicsContext::setPlatformAlpha(float alpha) { platformContext()->setGlobalAlpha(alpha); } @@ -942,39 +1036,12 @@ void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendM if (paintingDisabled()) return; - cairo_operator_t cairo_op; - if (blendOp == BlendModeNormal) - cairo_op = toCairoOperator(op); - else - cairo_op = toCairoOperator(blendOp); - - cairo_set_operator(platformContext()->cr(), cairo_op); -} - -void GraphicsContext::clip(const Path& path, WindRule windRule) -{ - if (paintingDisabled()) - return; - - cairo_t* cr = platformContext()->cr(); - OwnPtr<cairo_path_t> pathCopy; - if (!path.isNull()) { - pathCopy = adoptPtr(cairo_copy_path(path.platformPath()->context())); - cairo_append_path(cr, pathCopy.get()); - } - cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); - if (windRule == RULE_NONZERO) - cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); - else - cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); - cairo_clip(cr); - cairo_set_fill_rule(cr, savedFillRule); - m_data->clip(path); + cairo_set_operator(platformContext()->cr(), toCairoOperator(op, blendOp)); } void GraphicsContext::canvasClip(const Path& path, WindRule windRule) { - clip(path, windRule); + clipPath(path, windRule); } void GraphicsContext::clipOut(const Path& path) @@ -982,6 +1049,11 @@ void GraphicsContext::clipOut(const Path& path) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->clipOut(path); + return; + } + cairo_t* cr = platformContext()->cr(); double x1, y1, x2, y2; cairo_clip_extents(cr, &x1, &y1, &x2, &y2); @@ -999,6 +1071,11 @@ void GraphicsContext::rotate(float radians) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->rotate(radians); + return; + } + cairo_rotate(platformContext()->cr(), radians); m_data->rotate(radians); } @@ -1008,15 +1085,25 @@ void GraphicsContext::scale(const FloatSize& size) if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->scale(size); + return; + } + cairo_scale(platformContext()->cr(), size.width(), size.height()); m_data->scale(size); } -void GraphicsContext::clipOut(const IntRect& r) +void GraphicsContext::clipOut(const FloatRect& r) { if (paintingDisabled()) return; + if (isRecording()) { + m_displayListRecorder->clipOut(r); + return; + } + cairo_t* cr = platformContext()->cr(); double x1, y1, x2, y2; cairo_clip_extents(cr, &x1, &y1, &x2, &y2); @@ -1028,31 +1115,38 @@ void GraphicsContext::clipOut(const IntRect& r) cairo_set_fill_rule(cr, savedFillRule); } -void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace) +void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color) { if (paintingDisabled()) return; + ASSERT(!isRecording()); + if (hasShadow()) - platformContext()->shadowBlur().drawRectShadow(this, r, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight)); + platformContext()->shadowBlur().drawRectShadow(*this, rect); cairo_t* cr = platformContext()->cr(); cairo_save(cr); Path path; - path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight); + path.addRoundedRect(rect); appendWebCorePathToCairoContext(cr, path); setSourceRGBAFromColor(cr, color); cairo_fill(cr); cairo_restore(cr); } -void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace) +void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color) { if (paintingDisabled() || !color.isValid()) return; + if (isRecording()) { + m_displayListRecorder->fillRectWithRoundedHole(rect, roundedHoleRect, color); + return; + } + if (this->mustUseShadowBlur()) - platformContext()->shadowBlur().drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii()); + platformContext()->shadowBlur().drawInsetShadow(*this, rect, roundedHoleRect); Path path; path.addRect(rect); @@ -1064,57 +1158,57 @@ void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const Rounded cairo_t* cr = platformContext()->cr(); cairo_save(cr); setPathOnCairoContext(platformContext()->cr(), path.platformPath()->context()); - fillCurrentCairoPath(this); + fillCurrentCairoPath(*this); cairo_restore(cr); } -#if PLATFORM(GTK) -void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose) +void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) { - m_data->expose = expose; -} + if (paintingDisabled()) + return; -GdkEventExpose* GraphicsContext::gdkExposeEvent() const -{ - return m_data->expose; -} + if (isRecording()) { + m_displayListRecorder->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode); + return; + } -GdkWindow* GraphicsContext::gdkWindow() const -{ - if (!m_data->expose) - return 0; + RefPtr<cairo_surface_t> surface = image.nativeImageForCurrentFrame(); + if (!surface) // If it's too early we won't have an image yet. + return; - return m_data->expose->window; + cairo_t* cr = platformContext()->cr(); + drawPatternToCairoContext(cr, surface.get(), IntSize(image.size()), tileRect, patternTransform, phase, toCairoOperator(op, blendMode), destRect); } -#endif void GraphicsContext::setPlatformShouldAntialias(bool enable) { if (paintingDisabled()) return; + ASSERT(!isRecording()); + // When true, use the default Cairo backend antialias mode (usually this // enables standard 'grayscale' antialiasing); false to explicitly disable - // antialiasing. This is the same strategy as used in drawConvexPolygon(). + // antialiasing. cairo_set_antialias(platformContext()->cr(), enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); } -void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality) +void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality quality) { - platformContext()->setImageInterpolationQuality(quality); -} + ASSERT(!isRecording()); -InterpolationQuality GraphicsContext::imageInterpolationQuality() const -{ - return platformContext()->imageInterpolationQuality(); + platformContext()->setImageInterpolationQuality(quality); } bool GraphicsContext::isAcceleratedContext() const { + if (isRecording()) + return false; + return cairo_surface_get_type(cairo_get_target(platformContext()->cr())) == CAIRO_SURFACE_TYPE_GL; } -#if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER) +#if ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER) TransformationMatrix GraphicsContext::get3DTransform() const { // FIXME: Can we approximate the transformation better than this? @@ -1130,7 +1224,7 @@ void GraphicsContext::set3DTransform(const TransformationMatrix& transform) { setCTM(transform.toAffineTransform()); } -#endif +#endif // ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER) } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index fc7c8a5d4..b2d25d6a7 100644 --- a/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/Source/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2008 Brent Fulgham <bfulgham@gmail.com> * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,17 +30,15 @@ #include "GraphicsContext.h" +#if USE(CAIRO) + #include "PlatformContextCairo.h" #include "RefPtrCairo.h" #include <cairo.h> #include <math.h> #include <stdio.h> -#include <wtf/MathExtras.h> -#if PLATFORM(GTK) -#include <pango/pango.h> -typedef struct _GdkExposeEvent GdkExposeEvent; -#elif PLATFORM(WIN) +#if PLATFORM(WIN) #include <cairo-win32.h> #endif @@ -50,9 +48,6 @@ class GraphicsContextPlatformPrivate { public: GraphicsContextPlatformPrivate(PlatformContextCairo* newPlatformContext) : platformContext(newPlatformContext) -#if PLATFORM(GTK) - , expose(0) -#endif #if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS)) // NOTE: These may note be needed: review and remove once Cairo implementation is complete , m_hdc(0) @@ -96,9 +91,6 @@ public: PlatformContextCairo* platformContext; Vector<float> layers; -#if PLATFORM(GTK) - GdkEventExpose* expose; -#endif #if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS)) HDC m_hdc; bool m_shouldIncludeChildWindows; @@ -124,4 +116,6 @@ public: } // namespace WebCore +#endif // USE(CAIRO) + #endif // GraphicsContextPlatformPrivateCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 0d97204f4..041373a01 100644 --- a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -13,10 +13,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,6 +29,8 @@ #include "config.h" #include "ImageBuffer.h" +#if USE(CAIRO) + #include "BitmapImage.h" #include "CairoUtilities.h" #include "Color.h" @@ -39,82 +41,201 @@ #include "PlatformContextCairo.h" #include "RefPtrCairo.h" #include <cairo.h> +#include <runtime/JSCInlines.h> +#include <runtime/TypedArrayInlines.h> #include <wtf/Vector.h> #include <wtf/text/Base64.h> #include <wtf/text/WTFString.h> #if ENABLE(ACCELERATED_2D_CANVAS) #include "GLContext.h" -#include "OpenGLShims.h" #include "TextureMapperGL.h" #include <cairo-gl.h> + +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> +#else +#include "OpenGLShims.h" +#endif + +#if USE(COORDINATED_GRAPHICS_THREADED) +#include "TextureMapperPlatformLayerBuffer.h" +#include "TextureMapperPlatformLayerProxy.h" +#endif #endif using namespace std; namespace WebCore { -ImageBufferData::ImageBufferData(const IntSize& size) +ImageBufferData::ImageBufferData(const IntSize& size, RenderingMode renderingMode) : m_platformContext(0) , m_size(size) + , m_renderingMode(renderingMode) #if ENABLE(ACCELERATED_2D_CANVAS) +#if USE(COORDINATED_GRAPHICS_THREADED) + , m_compositorTexture(0) +#endif , m_texture(0) #endif { +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(COORDINATED_GRAPHICS_THREADED) + if (m_renderingMode == RenderingMode::Accelerated) + m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy); +#endif +} + +ImageBufferData::~ImageBufferData() +{ + if (m_renderingMode != Accelerated) + return; + +#if ENABLE(ACCELERATED_2D_CANVAS) + GLContext* previousActiveContext = GLContext::current(); + PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent(); + + if (m_texture) + glDeleteTextures(1, &m_texture); + +#if USE(COORDINATED_GRAPHICS_THREADED) + if (m_compositorTexture) + glDeleteTextures(1, &m_compositorTexture); +#endif + + if (previousActiveContext) + previousActiveContext->makeContextCurrent(); +#endif } #if ENABLE(ACCELERATED_2D_CANVAS) -PassRefPtr<cairo_surface_t> createCairoGLSurface(const IntSize& size, uint32_t& texture) +#if USE(COORDINATED_GRAPHICS_THREADED) +void ImageBufferData::createCompositorBuffer() +{ + auto* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext(); + context->makeContextCurrent(); + + glGenTextures(1, &m_compositorTexture); + glBindTexture(GL_TEXTURE_2D, m_compositorTexture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0 , GL_RGBA, m_size.width(), m_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + m_compositorSurface = adoptRef(cairo_gl_surface_create_for_texture(context->cairoDevice(), CAIRO_CONTENT_COLOR_ALPHA, m_compositorTexture, m_size.width(), m_size.height())); + m_compositorCr = adoptRef(cairo_create(m_compositorSurface.get())); + cairo_set_antialias(m_compositorCr.get(), CAIRO_ANTIALIAS_NONE); +} + +void ImageBufferData::swapBuffersIfNeeded() +{ + GLContext* previousActiveContext = GLContext::current(); + + if (!m_compositorTexture) { + createCompositorBuffer(); + LockHolder holder(m_platformLayerProxy->lock()); + m_platformLayerProxy->pushNextBuffer(std::make_unique<TextureMapperPlatformLayerBuffer>(m_compositorTexture, m_size, TextureMapperGL::ShouldBlend)); + } + + // It would be great if we could just swap the buffers here as we do with webgl, but that breaks the cases + // where one frame uses the content already rendered in the previous frame. So we just copy the content + // into the compositor buffer. + cairo_set_source_surface(m_compositorCr.get(), m_surface.get(), 0, 0); + cairo_set_operator(m_compositorCr.get(), CAIRO_OPERATOR_SOURCE); + cairo_paint(m_compositorCr.get()); + + if (previousActiveContext) + previousActiveContext->makeContextCurrent(); +} +#endif + +void clearSurface(cairo_surface_t* surface) +{ + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) + return; + + RefPtr<cairo_t> cr = adoptRef(cairo_create(surface)); + cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR); + cairo_paint(cr.get()); +} + +void ImageBufferData::createCairoGLSurface() { - GLContext::sharingContext()->makeContextCurrent(); + auto* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext(); + context->makeContextCurrent(); // We must generate the texture ourselves, because there is no Cairo API for extracting it // from a pre-existing surface. - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_2D, m_texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA8, size.width(), size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, m_size.width(), m_size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0); - GLContext* context = GLContext::sharingContext(); cairo_device_t* device = context->cairoDevice(); // Thread-awareness is a huge performance hit on non-Intel drivers. cairo_gl_device_set_thread_aware(device, FALSE); - return adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, texture, size.width(), size.height())); + m_surface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, m_texture, m_size.width(), m_size.height())); + clearSurface(m_surface.get()); } #endif -ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode renderingMode, bool& success) - : m_data(size) - , m_size(size) +ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace, RenderingMode renderingMode, bool& success) + : m_data(IntSize(size), renderingMode) , m_logicalSize(size) + , m_resolutionScale(resolutionScale) { success = false; // Make early return mean error. + + float scaledWidth = ceilf(m_resolutionScale * size.width()); + float scaledHeight = ceilf(m_resolutionScale * size.height()); + + // FIXME: Should we automatically use a lower resolution? + if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize()) + return; + + m_size = IntSize(scaledWidth, scaledHeight); + m_data.m_size = m_size; + if (m_size.isEmpty()) return; #if ENABLE(ACCELERATED_2D_CANVAS) - if (renderingMode == Accelerated) - m_data.m_surface = createCairoGLSurface(size, m_data.m_texture); - else + if (m_data.m_renderingMode == Accelerated) { + m_data.createCairoGLSurface(); + if (!m_data.m_surface || cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS) + m_data.m_renderingMode = Unaccelerated; // If allocation fails, fall back to non-accelerated path. + } + if (m_data.m_renderingMode == Unaccelerated) #else - ASSERT_UNUSED(renderingMode, renderingMode != Accelerated); + ASSERT(m_data.m_renderingMode != Accelerated); #endif - m_data.m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width(), size.height())); + { + static cairo_user_data_key_t s_surfaceDataKey; + + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_size.width()); + auto* surfaceData = fastZeroedMalloc(m_size.height() * stride); + + m_data.m_surface = adoptRef(cairo_image_surface_create_for_data(static_cast<unsigned char*>(surfaceData), CAIRO_FORMAT_ARGB32, m_size.width(), m_size.height(), stride)); + cairo_surface_set_user_data(m_data.m_surface.get(), &s_surfaceDataKey, surfaceData, [](void* data) { fastFree(data); }); + } if (cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS) return; // create will notice we didn't set m_initialized and fail. + cairoSurfaceSetDeviceScale(m_data.m_surface.get(), m_resolutionScale, m_resolutionScale); + RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface.get())); m_data.m_platformContext.setCr(cr.get()); - m_context = adoptPtr(new GraphicsContext(&m_data.m_platformContext)); + m_data.m_context = std::make_unique<GraphicsContext>(&m_data.m_platformContext); success = true; } @@ -122,18 +243,29 @@ ImageBuffer::~ImageBuffer() { } -GraphicsContext* ImageBuffer::context() const +std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context) +{ + return createCompatibleBuffer(size, ColorSpaceSRGB, context); +} + +GraphicsContext& ImageBuffer::context() const +{ + return *m_data.m_context; +} + +RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, ScaleBehavior scaleBehavior) { - return m_context.get(); + return imageBuffer->copyImage(DontCopyBackingStore, scaleBehavior); } -PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const +RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const { + // copyCairoImageSurface inherits surface's device scale factor. if (copyBehavior == CopyBackingStore) return BitmapImage::create(copyCairoImageSurface(m_data.m_surface.get())); // BitmapImage will release the passed in surface on destruction - return BitmapImage::create(m_data.m_surface); + return BitmapImage::create(RefPtr<cairo_surface_t>(m_data.m_surface)); } BackingStoreCopy ImageBuffer::fastCopyImageMode() @@ -141,24 +273,24 @@ BackingStoreCopy ImageBuffer::fastCopyImageMode() return DontCopyBackingStore; } -void ImageBuffer::clip(GraphicsContext* context, const FloatRect& maskRect) const +void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode) { - context->platformContext()->pushImageMask(m_data.m_surface.get(), maskRect); + imageBuffer->draw(destContext, destRect, srcRect, op, blendMode); } -void ImageBuffer::draw(GraphicsContext* destinationContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, - CompositeOperator op, BlendMode blendMode, bool useLowQualityScale) +void ImageBuffer::draw(GraphicsContext& destinationContext, const FloatRect& destRect, const FloatRect& srcRect, + CompositeOperator op, BlendMode blendMode) { - BackingStoreCopy copyMode = destinationContext == context() ? CopyBackingStore : DontCopyBackingStore; + BackingStoreCopy copyMode = &destinationContext == &context() ? CopyBackingStore : DontCopyBackingStore; RefPtr<Image> image = copyImage(copyMode); - destinationContext->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, blendMode, ImageOrientationDescription(), useLowQualityScale); + destinationContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription())); } -void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode) +void ImageBuffer::drawPattern(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode) { - RefPtr<Image> image = copyImage(DontCopyBackingStore); - image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); + if (RefPtr<Image> image = copyImage(DontCopyBackingStore)) + image->drawPattern(context, destRect, srcRect, patternTransform, phase, spacing, op); } void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) @@ -181,10 +313,10 @@ void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) *pixel = premultipliedARGBFromColor(pixelColor); } } - cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_size.width(), m_size.height()); + cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_logicalSize.width(), m_logicalSize.height()); } -PassRefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* surface, IntRect& rect) +RefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* surface, IntRect& rect) { cairo_surface_type_t surfaceType = cairo_surface_get_type(surface); @@ -199,9 +331,11 @@ PassRefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* sur } template <Multiply multiplied> -PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size) +RefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const IntRect& logicalRect, const ImageBufferData& data, const IntSize& size, const IntSize& logicalSize, float resolutionScale) { RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); + if (!result) + return nullptr; if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) result->zeroFill(); @@ -228,13 +362,17 @@ PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBuffe endy = size.height(); int numRows = endy - originy; + // The size of the derived surface is in BackingStoreCoordinateSystem. + // We need to set the device scale for the derived surface from this ImageBuffer. IntRect imageRect(originx, originy, numColumns, numRows); RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(data.m_surface.get(), imageRect); + cairoSurfaceSetDeviceScale(imageSurface.get(), resolutionScale, resolutionScale); originx = imageRect.x(); originy = imageRect.y(); if (imageSurface != data.m_surface.get()) { - IntRect area = intersection(rect, IntRect(0, 0, size.width(), size.height())); - copyRectFromOneSurfaceToAnother(data.m_surface.get(), imageSurface.get(), IntSize(-area.x(), -area.y()), IntRect(IntPoint(), area.size()), IntSize(), CAIRO_OPERATOR_SOURCE); + // This cairo surface operation is done in LogicalCoordinateSystem. + IntRect logicalArea = intersection(logicalRect, IntRect(0, 0, logicalSize.width(), logicalSize.height())); + copyRectFromOneSurfaceToAnother(data.m_surface.get(), imageSurface.get(), IntSize(-logicalArea.x(), -logicalArea.y()), IntRect(IntPoint(), logicalArea.size()), IntSize(), CAIRO_OPERATOR_SOURCE); } unsigned char* dataSrc = cairo_image_surface_get_data(imageSurface.get()); @@ -275,53 +413,91 @@ PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBuffe return result.release(); } -PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const +template<typename Unit> +inline Unit logicalUnit(const Unit& value, ImageBuffer::CoordinateSystem coordinateSystemOfValue, float resolutionScale) { - return getImageData<Unmultiplied>(rect, m_data, m_size); + if (coordinateSystemOfValue == ImageBuffer::LogicalCoordinateSystem || resolutionScale == 1.0) + return value; + Unit result(value); + result.scale(1.0 / resolutionScale); + return result; } -PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const +template<typename Unit> +inline Unit backingStoreUnit(const Unit& value, ImageBuffer::CoordinateSystem coordinateSystemOfValue, float resolutionScale) { - return getImageData<Premultiplied>(rect, m_data, m_size); + if (coordinateSystemOfValue == ImageBuffer::BackingStoreCoordinateSystem || resolutionScale == 1.0) + return value; + Unit result(value); + result.scale(resolutionScale); + return result; } -void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem) +RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const { + IntRect logicalRect = logicalUnit(rect, coordinateSystem, m_resolutionScale); + IntRect backingStoreRect = backingStoreUnit(rect, coordinateSystem, m_resolutionScale); + return getImageData<Unmultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale); +} - ASSERT(sourceRect.width() > 0); - ASSERT(sourceRect.height() > 0); +RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const +{ + IntRect logicalRect = logicalUnit(rect, coordinateSystem, m_resolutionScale); + IntRect backingStoreRect = backingStoreUnit(rect, coordinateSystem, m_resolutionScale); + return getImageData<Premultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale); +} - int originx = sourceRect.x(); - int destx = destPoint.x() + sourceRect.x(); +void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem) +{ + IntRect scaledSourceRect = backingStoreUnit(sourceRect, coordinateSystem, m_resolutionScale); + IntSize scaledSourceSize = backingStoreUnit(sourceSize, coordinateSystem, m_resolutionScale); + IntPoint scaledDestPoint = backingStoreUnit(destPoint, coordinateSystem, m_resolutionScale); + IntRect logicalSourceRect = logicalUnit(sourceRect, coordinateSystem, m_resolutionScale); + IntPoint logicalDestPoint = logicalUnit(destPoint, coordinateSystem, m_resolutionScale); + + ASSERT(scaledSourceRect.width() > 0); + ASSERT(scaledSourceRect.height() > 0); + + int originx = scaledSourceRect.x(); + int destx = scaledDestPoint.x() + scaledSourceRect.x(); + int logicalDestx = logicalDestPoint.x() + logicalSourceRect.x(); ASSERT(destx >= 0); ASSERT(destx < m_size.width()); ASSERT(originx >= 0); - ASSERT(originx <= sourceRect.maxX()); + ASSERT(originx <= scaledSourceRect.maxX()); - int endx = destPoint.x() + sourceRect.maxX(); + int endx = scaledDestPoint.x() + scaledSourceRect.maxX(); + int logicalEndx = logicalDestPoint.x() + logicalSourceRect.maxX(); ASSERT(endx <= m_size.width()); int numColumns = endx - destx; + int logicalNumColumns = logicalEndx - logicalDestx; - int originy = sourceRect.y(); - int desty = destPoint.y() + sourceRect.y(); + int originy = scaledSourceRect.y(); + int desty = scaledDestPoint.y() + scaledSourceRect.y(); + int logicalDesty = logicalDestPoint.y() + logicalSourceRect.y(); ASSERT(desty >= 0); ASSERT(desty < m_size.height()); ASSERT(originy >= 0); - ASSERT(originy <= sourceRect.maxY()); + ASSERT(originy <= scaledSourceRect.maxY()); - int endy = destPoint.y() + sourceRect.maxY(); + int endy = scaledDestPoint.y() + scaledSourceRect.maxY(); + int logicalEndy = logicalDestPoint.y() + logicalSourceRect.maxY(); ASSERT(endy <= m_size.height()); int numRows = endy - desty; + int logicalNumRows = logicalEndy - logicalDesty; + // The size of the derived surface is in BackingStoreCoordinateSystem. + // We need to set the device scale for the derived surface from this ImageBuffer. IntRect imageRect(destx, desty, numColumns, numRows); RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(m_data.m_surface.get(), imageRect); + cairoSurfaceSetDeviceScale(imageSurface.get(), m_resolutionScale, m_resolutionScale); destx = imageRect.x(); desty = imageRect.y(); unsigned char* pixelData = cairo_image_surface_get_data(imageSurface.get()); - unsigned srcBytesPerRow = 4 * sourceSize.width(); + unsigned srcBytesPerRow = 4 * scaledSourceSize.width(); int stride = cairo_image_surface_get_stride(imageSurface.get()); unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; @@ -351,10 +527,13 @@ void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, c srcRows += srcBytesPerRow; } - cairo_surface_mark_dirty_rectangle(imageSurface.get(), destx, desty, numColumns, numRows); + // This cairo surface operation is done in LogicalCoordinateSystem. + cairo_surface_mark_dirty_rectangle(imageSurface.get(), logicalDestx, logicalDesty, logicalNumColumns, logicalNumRows); - if (imageSurface != m_data.m_surface.get()) - copyRectFromOneSurfaceToAnother(imageSurface.get(), m_data.m_surface.get(), IntSize(), IntRect(0, 0, numColumns, numRows), IntSize(destPoint.x() + sourceRect.x(), destPoint.y() + sourceRect.y()), CAIRO_OPERATOR_SOURCE); + if (imageSurface != m_data.m_surface.get()) { + // This cairo surface operation is done in LogicalCoordinateSystem. + copyRectFromOneSurfaceToAnother(imageSurface.get(), m_data.m_surface.get(), IntSize(), IntRect(0, 0, logicalNumColumns, logicalNumRows), IntSize(logicalDestPoint.x() + logicalSourceRect.x(), logicalDestPoint.y() + logicalSourceRect.y()), CAIRO_OPERATOR_SOURCE); + } } #if !PLATFORM(GTK) @@ -372,11 +551,11 @@ static bool encodeImage(cairo_surface_t* image, const String& mimeType, Vector<c return cairo_surface_write_to_png_stream(image, writeFunction, output) == CAIRO_STATUS_SUCCESS; } -String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateSystem) const +String ImageBuffer::toDataURL(const String& mimeType, std::optional<double> quality, CoordinateSystem) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); - cairo_surface_t* image = cairo_get_target(context()->platformContext()->cr()); + cairo_surface_t* image = cairo_get_target(context().platformContext()->cr()); Vector<char> encodedImage; if (!image || !encodeImage(image, mimeType, &encodedImage)) @@ -389,26 +568,20 @@ String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateS } #endif -#if ENABLE(ACCELERATED_2D_CANVAS) -void ImageBufferData::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) +#if ENABLE(ACCELERATED_2D_CANVAS) && !USE(COORDINATED_GRAPHICS_THREADED) +void ImageBufferData::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) { - if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) { - notImplemented(); - return; - } - ASSERT(m_texture); // Cairo may change the active context, so we make sure to change it back after flushing. - GLContext* previousActiveContext = GLContext::getCurrent(); + GLContext* previousActiveContext = GLContext::current(); cairo_surface_flush(m_surface.get()); previousActiveContext->makeContextCurrent(); - static_cast<TextureMapperGL*>(textureMapper)->drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity); + static_cast<TextureMapperGL&>(textureMapper).drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity); } #endif -#if USE(ACCELERATED_COMPOSITING) PlatformLayer* ImageBuffer::platformLayer() const { #if ENABLE(ACCELERATED_2D_CANVAS) @@ -417,6 +590,59 @@ PlatformLayer* ImageBuffer::platformLayer() const #endif return 0; } + +bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum target, Platform3DObject destinationTexture, GC3Denum internalformat, bool premultiplyAlpha, bool flipY) +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + ASSERT_WITH_MESSAGE(m_resolutionScale == 1.0, "Since the HiDPI Canvas feature is removed, the resolution factor here is always 1."); + if (premultiplyAlpha || flipY) + return false; + + if (!m_data.m_texture) + return false; + + GC3Denum bindTextureTarget; + switch (target) { + case GL_TEXTURE_2D: + bindTextureTarget = GL_TEXTURE_2D; + break; + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + bindTextureTarget = GL_TEXTURE_CUBE_MAP; + break; + default: + return false; + } + + cairo_surface_flush(m_data.m_surface.get()); + + std::unique_ptr<GLContext> context = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing()); + context->makeContextCurrent(); + uint32_t fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_data.m_texture, 0); + glBindTexture(bindTextureTarget, destinationTexture); + glCopyTexImage2D(target, 0, internalformat, 0, 0, m_size.width(), m_size.height(), 0); + glBindTexture(bindTextureTarget, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glFlush(); + glDeleteFramebuffers(1, &fbo); + return true; +#else + UNUSED_PARAM(target); + UNUSED_PARAM(destinationTexture); + UNUSED_PARAM(internalformat); + UNUSED_PARAM(premultiplyAlpha); + UNUSED_PARAM(flipY); + return false; #endif +} } // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h b/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h index 72ea4c34f..93d817614 100644 --- a/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h +++ b/Source/WebCore/platform/graphics/cairo/ImageBufferDataCairo.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,12 +23,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef ImageBufferDataCairo_h +#define ImageBufferDataCairo_h + +#if USE(CAIRO) + #include "PlatformContextCairo.h" #include "RefPtrCairo.h" #if ENABLE(ACCELERATED_2D_CANVAS) +#include "PlatformLayer.h" #include "TextureMapper.h" #include "TextureMapperPlatformLayer.h" +#include "TextureMapperPlatformLayerProxy.h" #endif namespace WebCore { @@ -37,20 +44,40 @@ class IntSize; class ImageBufferData #if ENABLE(ACCELERATED_2D_CANVAS) - : public TextureMapperPlatformLayer + : public PlatformLayer #endif { public: - ImageBufferData(const IntSize&); + ImageBufferData(const IntSize&, RenderingMode); + virtual ~ImageBufferData(); RefPtr<cairo_surface_t> m_surface; PlatformContextCairo m_platformContext; + std::unique_ptr<GraphicsContext> m_context; IntSize m_size; + RenderingMode m_renderingMode; #if ENABLE(ACCELERATED_2D_CANVAS) - virtual void paintToTextureMapper(TextureMapper*, const FloatRect& target, const TransformationMatrix&, float opacity); + void createCairoGLSurface(); + +#if USE(COORDINATED_GRAPHICS_THREADED) + RefPtr<TextureMapperPlatformLayerProxy> proxy() const override { return m_platformLayerProxy.copyRef(); } + void swapBuffersIfNeeded() override; + void createCompositorBuffer(); + + RefPtr<TextureMapperPlatformLayerProxy> m_platformLayerProxy; + RefPtr<cairo_surface_t> m_compositorSurface; + uint32_t m_compositorTexture; + RefPtr<cairo_t> m_compositorCr; +#else + virtual void paintToTextureMapper(TextureMapper&, const FloatRect& target, const TransformationMatrix&, float opacity); +#endif uint32_t m_texture; #endif }; } // namespace WebCore + +#endif // USE(CAIRO) + +#endif // ImageBufferDataCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp index 46a39b45b..4e5365958 100644 --- a/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,30 +31,21 @@ #if USE(CAIRO) #include "AffineTransform.h" -#include "CairoUtilities.h" #include "Color.h" #include "GraphicsContext.h" #include "ImageObserver.h" -#include "PlatformContextCairo.h" -#include <cairo.h> -#include <math.h> namespace WebCore { -void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode) +void Image::drawPattern(GraphicsContext& context, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) { - RefPtr<cairo_surface_t> surface = nativeImageForCurrentFrame(); - if (!surface) // If it's too early we won't have an image yet. - return; - - cairo_t* cr = context->platformContext()->cr(); - drawPatternToCairoContext(cr, surface.get(), size(), tileRect, patternTransform, phase, toCairoOperator(op), destRect); + context.drawPattern(*this, destRect, tileRect, patternTransform, phase, spacing, op, blendMode); if (imageObserver()) imageObserver()->didDraw(this); } -} +} // namespace WebCore #endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp b/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp index cef3af905..054bd2ac3 100644 --- a/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/IntRectCairo.cpp @@ -21,6 +21,8 @@ #include "config.h" #include "IntRect.h" +#if USE(CAIRO) + #include <cairo.h> namespace WebCore { @@ -37,4 +39,6 @@ IntRect::operator cairo_rectangle_int_t() const return r; } -} +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp b/Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp new file mode 100644 index 000000000..69b7bb0f8 --- /dev/null +++ b/Source/WebCore/platform/graphics/cairo/NativeImageCairo.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NativeImage.h" + +#if USE(CAIRO) + +#include "CairoUtilities.h" +#include "PlatformContextCairo.h" +#include <cairo.h> + +namespace WebCore { + +IntSize nativeImageSize(const NativeImagePtr& image) +{ + return image ? cairoSurfaceSize(image.get()) : IntSize(); +} + +bool nativeImageHasAlpha(const NativeImagePtr& image) +{ + return !image || cairo_surface_get_content(image.get()) != CAIRO_CONTENT_COLOR; +} + +Color nativeImageSinglePixelSolidColor(const NativeImagePtr& image) +{ + if (!image || nativeImageSize(image) != IntSize(1, 1)) + return Color(); + + if (cairo_surface_get_type(image.get()) != CAIRO_SURFACE_TYPE_IMAGE) + return Color(); + + RGBA32* pixel = reinterpret_cast_ptr<RGBA32*>(cairo_image_surface_get_data(image.get())); + return colorFromPremultipliedARGB(*pixel); +} + +float subsamplingScale(GraphicsContext&, const FloatRect&, const FloatRect&) +{ + return 1; +} + +void drawNativeImage(const NativeImagePtr& image, GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const IntSize&, CompositeOperator op, BlendMode mode, const ImageOrientation& orientation) +{ + context.save(); + + // Set the compositing operation. + if (op == CompositeSourceOver && mode == BlendModeNormal && !nativeImageHasAlpha(image)) + context.setCompositeOperation(CompositeCopy); + else + context.setCompositeOperation(op, mode); + +#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) + IntSize scaledSize = nativeImageSize(image); + FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(srcRect, scaledSize); +#else + FloatRect adjustedSrcRect(srcRect); +#endif + + FloatRect adjustedDestRect = destRect; + + if (orientation != DefaultImageOrientation) { + // ImageOrientation expects the origin to be at (0, 0). + context.translate(destRect.x(), destRect.y()); + adjustedDestRect.setLocation(FloatPoint()); + context.concatCTM(orientation.transformFromDefault(adjustedDestRect.size())); + if (orientation.usesWidthAsHeight()) { + // The destination rectangle will have it's width and height already reversed for the orientation of + // the image, as it was needed for page layout, so we need to reverse it back here. + adjustedDestRect.setSize(adjustedDestRect.size().transposedSize()); + } + } + + context.platformContext()->drawSurfaceToContext(image.get(), adjustedDestRect, adjustedSrcRect, context); + context.restore(); +} + +void clearNativeImageSubimages(const NativeImagePtr&) +{ +} + +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp deleted file mode 100644 index 1594e7b5d..000000000 --- a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2010 Collabora Ltd. - * - * 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 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 "OwnPtrCairo.h" - -#if USE(FREETYPE) -#include <cairo-ft.h> -#include <fontconfig/fcfreetype.h> -#endif - -#include <cairo.h> - -namespace WTF { - -#if USE(FREETYPE) -template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet* ptr) -{ - if (ptr) - FcObjectSetDestroy(ptr); -} - -template <> void deleteOwnedPtr<FcFontSet>(FcFontSet* ptr) -{ - if (ptr) - FcFontSetDestroy(ptr); -} -#endif - -template <> void deleteOwnedPtr<cairo_path_t>(cairo_path_t* ptr) -{ - if (ptr) - cairo_path_destroy(ptr); -} - -} // namespace WTF diff --git a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h b/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h deleted file mode 100644 index 3704b6a17..000000000 --- a/Source/WebCore/platform/graphics/cairo/OwnPtrCairo.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010 Collabora Ltd. - * - * 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 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 - */ - -#ifndef OwnPtrCairo_h -#define OwnPtrCairo_h - -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> - -#if USE(FREETYPE) -typedef struct _FcObjectSet FcObjectSet; -typedef struct _FcFontSet FcFontSet; -#endif - -typedef struct cairo_path cairo_path_t; - -namespace WTF { - -#if USE(FREETYPE) -template <> void deleteOwnedPtr<FcObjectSet>(FcObjectSet*); -template <> void deleteOwnedPtr<FcFontSet>(FcFontSet*); -#endif - -template <> void deleteOwnedPtr<cairo_path_t>(cairo_path_t*); - -} // namespace WTF - -#endif diff --git a/Source/WebCore/platform/graphics/cairo/PathCairo.cpp b/Source/WebCore/platform/graphics/cairo/PathCairo.cpp index 5fac5f0b9..6ab79100c 100644 --- a/Source/WebCore/platform/graphics/cairo/PathCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -26,10 +26,11 @@ #include "config.h" #include "Path.h" +#if USE(CAIRO) + #include "AffineTransform.h" #include "FloatRect.h" #include "GraphicsContext.h" -#include "OwnPtrCairo.h" #include "PlatformPathCairo.h" #include "StrokeStyleApplier.h" #include <cairo.h> @@ -57,8 +58,9 @@ Path::Path(const Path& other) return; cairo_t* cr = ensurePlatformPath()->context(); - OwnPtr<cairo_path_t> pathCopy = adoptPtr(cairo_copy_path(other.platformPath()->context())); - cairo_append_path(cr, pathCopy.get()); + auto pathCopy = cairo_copy_path(other.platformPath()->context()); + cairo_append_path(cr, pathCopy); + cairo_path_destroy(pathCopy); } PlatformPathPtr Path::ensurePlatformPath() @@ -81,8 +83,9 @@ Path& Path::operator=(const Path& other) } else { clear(); cairo_t* cr = ensurePlatformPath()->context(); - OwnPtr<cairo_path_t> pathCopy = adoptPtr(cairo_copy_path(other.platformPath()->context())); - cairo_append_path(cr, pathCopy.get()); + auto pathCopy = cairo_copy_path(other.platformPath()->context()); + cairo_append_path(cr, pathCopy); + cairo_path_destroy(pathCopy); } return *this; @@ -281,6 +284,22 @@ void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) addArc(p, radius, sa, ea, anticlockwise); } +void Path::addEllipse(FloatPoint point, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise) +{ + cairo_t* cr = ensurePlatformPath()->context(); + cairo_save(cr); + cairo_translate(cr, point.x(), point.y()); + cairo_rotate(cr, rotation); + cairo_scale(cr, radiusX, radiusY); + + if (anticlockwise) + cairo_arc_negative(cr, 0, 0, 1, startAngle, endAngle); + else + cairo_arc(cr, 0, 0, 1, startAngle, endAngle); + + cairo_restore(cr); +} + void Path::addEllipse(const FloatRect& rect) { cairo_t* cr = ensurePlatformPath()->context(); @@ -293,6 +312,24 @@ void Path::addEllipse(const FloatRect& rect) cairo_restore(cr); } +void Path::addPath(const Path& path, const AffineTransform& transform) +{ + if (path.isNull()) + return; + + cairo_matrix_t matrix(transform); + if (cairo_matrix_invert(&matrix) != CAIRO_STATUS_SUCCESS) + return; + + cairo_t* cr = path.platformPath()->context(); + cairo_save(cr); + cairo_transform(cr, &matrix); + auto pathCopy = cairo_copy_path(cr); + cairo_restore(cr); + cairo_append_path(ensurePlatformPath()->context(), pathCopy); + cairo_path_destroy(pathCopy); +} + void Path::closeSubpath() { cairo_t* cr = ensurePlatformPath()->context(); @@ -353,13 +390,13 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) return cairo_in_stroke(cr, point.x(), point.y()); } -void Path::apply(void* info, PathApplierFunction function) const +void Path::apply(const PathApplierFunction& function) const { if (isNull()) return; cairo_t* cr = platformPath()->context(); - OwnPtr<cairo_path_t> pathCopy = adoptPtr(cairo_copy_path(cr)); + auto pathCopy = cairo_copy_path(cr); cairo_path_data_t* data; PathElement pelement; FloatPoint points[3]; @@ -371,26 +408,27 @@ void Path::apply(void* info, PathApplierFunction function) const case CAIRO_PATH_MOVE_TO: pelement.type = PathElementMoveToPoint; pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y); - function(info, &pelement); + function(pelement); break; case CAIRO_PATH_LINE_TO: pelement.type = PathElementAddLineToPoint; pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y); - function(info, &pelement); + function(pelement); break; case CAIRO_PATH_CURVE_TO: pelement.type = PathElementAddCurveToPoint; pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y); pelement.points[1] = FloatPoint(data[2].point.x,data[2].point.y); pelement.points[2] = FloatPoint(data[3].point.x,data[3].point.y); - function(info, &pelement); + function(pelement); break; case CAIRO_PATH_CLOSE_PATH: pelement.type = PathElementCloseSubpath; - function(info, &pelement); + function(pelement); break; } } + cairo_path_destroy(pathCopy); } void Path::transform(const AffineTransform& trans) @@ -402,3 +440,5 @@ void Path::transform(const AffineTransform& trans) } } // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp b/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp index 8870d3e03..81bc7ce96 100644 --- a/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/PatternCairo.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,6 +26,8 @@ #include "config.h" #include "Pattern.h" +#if USE(CAIRO) + #include "AffineTransform.h" #include "GraphicsContext.h" #include <cairo.h> @@ -50,4 +52,6 @@ cairo_pattern_t* Pattern::createPlatformPattern(const AffineTransform&) const return pattern; } -} +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp index fc2b2b0fc..65d7eeee9 100644 --- a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.cpp @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,10 +28,11 @@ #include "config.h" #include "PlatformContextCairo.h" +#if USE(CAIRO) + #include "CairoUtilities.h" #include "Gradient.h" #include "GraphicsContext.h" -#include "OwnPtrCairo.h" #include "Pattern.h" #include <cairo.h> @@ -136,9 +137,13 @@ void PlatformContextCairo::pushImageMask(cairo_surface_t* surface, const FloatRe cairo_push_group(m_cr.get()); cairo_set_operator(m_cr.get(), CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(m_cr.get(), currentTarget, 0, 0); - cairo_rectangle(m_cr.get(), rect.x(), rect.y(), rect.width(), rect.height()); + // To avoid the limit of Pixman backend, we need to reduce the size of pattern matrix + // See https://bugs.webkit.org/show_bug.cgi?id=154283 + cairo_set_source_surface(m_cr.get(), currentTarget, rect.x(), rect.y()); + cairo_translate(m_cr.get(), rect.x(), rect.y()); + cairo_rectangle(m_cr.get(), 0, 0, rect.width(), rect.height()); cairo_fill(m_cr.get()); + cairo_translate(m_cr.get(), -rect.x(), -rect.y()); } static void drawPatternToCairoContext(cairo_t* cr, cairo_pattern_t* pattern, const FloatRect& destRect, float alpha) @@ -154,7 +159,7 @@ static void drawPatternToCairoContext(cairo_t* cr, cairo_pattern_t* pattern, con cairo_fill(cr); } -void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const FloatRect& destRect, const FloatRect& originalSrcRect, GraphicsContext* context) +void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const FloatRect& destRect, const FloatRect& originalSrcRect, GraphicsContext& context) { // Avoid invalid cairo matrix with small values. if (std::fabs(destRect.width()) < 0.5f || std::fabs(destRect.height()) < 0.5f) @@ -197,11 +202,11 @@ void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_FAST); break; case InterpolationMedium: - case InterpolationHigh: - cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BILINEAR); - break; case InterpolationDefault: - cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BILINEAR); + cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_GOOD); + break; + case InterpolationHigh: + cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BEST); break; } cairo_pattern_set_extend(pattern.get(), CAIRO_EXTEND_PAD); @@ -215,7 +220,7 @@ void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, leftPadding, topPadding }; cairo_pattern_set_matrix(pattern.get(), &matrix); - ShadowBlur& shadow = context->platformContext()->shadowBlur(); + ShadowBlur& shadow = context.platformContext()->shadowBlur(); if (shadow.type() != ShadowBlur::NoShadow) { if (GraphicsContext* shadowContext = shadow.beginShadowLayer(context, destRect)) { drawPatternToCairoContext(shadowContext->platformContext()->cr(), pattern.get(), destRect, 1); @@ -304,7 +309,7 @@ void PlatformContextCairo::clipForPatternFilling(const GraphicsContextState& sta ASSERT(state.fillPattern); // Hold current cairo path in a variable for restoring it after configuring the pattern clip rectangle. - OwnPtr<cairo_path_t> currentPath = adoptPtr(cairo_copy_path(m_cr.get())); + auto currentPath = cairo_copy_path(m_cr.get()); cairo_new_path(m_cr.get()); // Initialize clipping extent from current cairo clip extents, then shrink if needed according to pattern. @@ -335,7 +340,10 @@ void PlatformContextCairo::clipForPatternFilling(const GraphicsContextState& sta } // Restoring cairo path. - cairo_append_path(m_cr.get(), currentPath.get()); + cairo_append_path(m_cr.get(), currentPath); + cairo_path_destroy(currentPath); } } // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h index d7f1205f7..642a3052c 100644 --- a/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h +++ b/Source/WebCore/platform/graphics/cairo/PlatformContextCairo.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,6 +26,8 @@ #ifndef PlatformContextCairo_h #define PlatformContextCairo_h +#if USE(CAIRO) + #include "GraphicsContext.h" #include "RefPtrCairo.h" #include "ShadowBlur.h" @@ -56,7 +58,7 @@ public: float globalAlpha() const; void pushImageMask(cairo_surface_t*, const FloatRect&); - void drawSurfaceToContext(cairo_surface_t*, const FloatRect& destRect, const FloatRect& srcRect, GraphicsContext*); + void drawSurfaceToContext(cairo_surface_t*, const FloatRect& destRect, const FloatRect& srcRect, GraphicsContext&); void setImageInterpolationQuality(InterpolationQuality); InterpolationQuality imageInterpolationQuality() const; @@ -84,4 +86,6 @@ private: } // namespace WebCore +#endif // USE(CAIRO) + #endif // PlatformContextCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp index 3a7d5128a..14c8d9bcd 100644 --- a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.cpp @@ -20,20 +20,23 @@ #include "config.h" #include "PlatformPathCairo.h" +#if USE(CAIRO) + #include <cairo.h> namespace WebCore { -static cairo_surface_t* getPathSurface() +static cairo_surface_t* pathSurface() { - return cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1); + static cairo_surface_t* s_pathSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1); + return s_pathSurface; } -static cairo_surface_t* gPathSurface = getPathSurface(); - CairoPath::CairoPath() - : m_cr(adoptRef(cairo_create(gPathSurface))) + : m_cr(adoptRef(cairo_create(pathSurface()))) { } -} +} // namespace WebCore + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h index 938b942a4..5f1059446 100644 --- a/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h +++ b/Source/WebCore/platform/graphics/cairo/PlatformPathCairo.h @@ -21,6 +21,8 @@ #ifndef PlatformPathCairo_h #define PlatformPathCairo_h +#if USE(CAIRO) + #include "RefPtrCairo.h" namespace WebCore { @@ -40,4 +42,6 @@ private: } // namespace WebCore +#endif // USE(CAIRO) + #endif // PlatformPathCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp index 0f55f5797..61a0369f5 100644 --- a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.cpp @@ -19,6 +19,8 @@ #include "config.h" #include "RefPtrCairo.h" +#if USE(CAIRO) + #include <cairo.h> #if USE(FREETYPE) @@ -30,89 +32,102 @@ namespace WTF { template<> void refIfNotNull(cairo_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_reference(ptr); } template<> void derefIfNotNull(cairo_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_destroy(ptr); } template<> void refIfNotNull(cairo_surface_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_surface_reference(ptr); } template<> void derefIfNotNull(cairo_surface_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_surface_destroy(ptr); } template<> void refIfNotNull(cairo_font_face_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_font_face_reference(ptr); } template<> void derefIfNotNull(cairo_font_face_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_font_face_destroy(ptr); } template<> void refIfNotNull(cairo_scaled_font_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_scaled_font_reference(ptr); } template<> void derefIfNotNull(cairo_scaled_font_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_scaled_font_destroy(ptr); } template<> void refIfNotNull(cairo_pattern_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_pattern_reference(ptr); } template<> void derefIfNotNull(cairo_pattern_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_pattern_destroy(ptr); } template<> void refIfNotNull(cairo_region_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_region_reference(ptr); } template<> void derefIfNotNull(cairo_region_t* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) cairo_region_destroy(ptr); } #if USE(FREETYPE) template<> void refIfNotNull(FcPattern* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) FcPatternReference(ptr); } template<> void derefIfNotNull(FcPattern* ptr) { - if (LIKELY(ptr != 0)) + if (LIKELY(ptr)) FcPatternDestroy(ptr); } -#endif +template<> void refIfNotNull(FcConfig* ptr) +{ + if (LIKELY(ptr)) + FcConfigReference(ptr); +} +template<> void derefIfNotNull(FcConfig* ptr) +{ + if (LIKELY(ptr)) + FcConfigDestroy(ptr); } +#endif + +} // namespace WTF + +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h index 0e1baf50a..8e92f4e6d 100644 --- a/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h +++ b/Source/WebCore/platform/graphics/cairo/RefPtrCairo.h @@ -20,6 +20,8 @@ #ifndef RefPtrCairo_h #define RefPtrCairo_h +#if USE(CAIRO) + #include <wtf/RefPtr.h> typedef struct _cairo cairo_t; @@ -31,6 +33,7 @@ typedef struct _cairo_region cairo_region_t; #if USE(FREETYPE) typedef struct _FcPattern FcPattern; +typedef struct _FcConfig FcConfig; #endif namespace WTF { @@ -56,8 +59,13 @@ template<> void derefIfNotNull(cairo_region_t*); #if USE(FREETYPE) template<> void refIfNotNull(FcPattern* ptr); template<> void derefIfNotNull(FcPattern* ptr); + +template<> void refIfNotNull(FcConfig* ptr); +template<> void derefIfNotNull(FcConfig* ptr); #endif -} +} // namespace WTF + +#endif // USE(CAIRO) #endif // RefPtrCairo_h diff --git a/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp b/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp index bebc4a321..a5cc372f8 100644 --- a/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp @@ -9,10 +9,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,12 +23,13 @@ */ #include "config.h" -#include "AffineTransform.h" #include "TransformationMatrix.h" +#if USE(CAIRO) + +#include "AffineTransform.h" #include "FloatRect.h" #include "IntRect.h" - #include <cairo.h> namespace WebCore { @@ -61,6 +62,6 @@ AffineTransform::operator cairo_matrix_t() const return m; } -} +} // namespace WebCore -// vim: ts=4 sw=4 et +#endif // USE(CAIRO) diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h index e41ffa216..50ec87961 100644 --- a/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h +++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2012 University of Szeged * Copyright (C) 2012 Gabor Rapcsanyi + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,7 +28,7 @@ #ifndef FEBlendNEON_h #define FEBlendNEON_h -#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#if HAVE(ARM_NEON_INTRINSICS) #include "FEBlend.h" #include <arm_neon.h> @@ -105,6 +106,39 @@ public: } }; +void FEBlend::platformApplySoftware() +{ + FilterEffect* in = inputEffect(0); + FilterEffect* in2 = inputEffect(1); + + Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult(); + if (!dstPixelArray) + return; + + IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); + RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect); + + IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); + RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asPremultipliedImage(effectBDrawingRect); + + unsigned pixelArrayLength = srcPixelArrayA->length(); + ASSERT(pixelArrayLength == srcPixelArrayB->length()); + + if (pixelArrayLength >= 8) { + platformApplyNEON(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength); + return; + } + // If there is just one pixel we expand it to two. + ASSERT(pixelArrayLength > 0); + uint32_t sourceA[2] = {0, 0}; + uint32_t sourceBAndDest[2] = {0, 0}; + + sourceA[0] = reinterpret_cast<uint32_t*>(srcPixelArrayA->data())[0]; + sourceBAndDest[0] = reinterpret_cast<uint32_t*>(srcPixelArrayB->data())[0]; + platformApplyNEON(reinterpret_cast<uint8_t*>(sourceA), reinterpret_cast<uint8_t*>(sourceBAndDest), reinterpret_cast<uint8_t*>(sourceBAndDest), 8); + reinterpret_cast<uint32_t*>(dstPixelArray->data())[0] = sourceBAndDest[0]; +} + void FEBlend::platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* srcPixelArrayB, unsigned char* dstPixelArray, unsigned colorArrayLength) { @@ -129,22 +163,21 @@ void FEBlend::platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* sr uint16x8_t result; switch (m_mode) { - case FEBLEND_MODE_NORMAL: + case BlendModeNormal: result = FEBlendUtilitiesNEON::normal(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne); break; - case FEBLEND_MODE_MULTIPLY: + case BlendModeMultiply: result = FEBlendUtilitiesNEON::multiply(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne); break; - case FEBLEND_MODE_SCREEN: + case BlendModeScreen: result = FEBlendUtilitiesNEON::screen(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne); break; - case FEBLEND_MODE_DARKEN: + case BlendModeDarken: result = FEBlendUtilitiesNEON::darken(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne); break; - case FEBLEND_MODE_LIGHTEN: + case BlendModeLighten: result = FEBlendUtilitiesNEON::lighten(doubblePixelA, doubblePixelB, alphaA, alphaB, sixteenConst255, sixteenConstOne); break; - case FEBLEND_MODE_UNKNOWN: default: result = vdupq_n_u16(0); break; @@ -168,6 +201,6 @@ void FEBlend::platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* sr } // namespace WebCore -#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#endif // HAVE(ARM_NEON_INTRINSICS) #endif // FEBlendNEON_h diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h index 2354dfc80..298f9afa7 100644 --- a/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h +++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h @@ -27,9 +27,10 @@ #ifndef FECompositeArithmeticNEON_h #define FECompositeArithmeticNEON_h -#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#if HAVE(ARM_NEON_INTRINSICS) #include "FEComposite.h" +#include "NEONHelpers.h" #include <arm_neon.h> namespace WebCore { @@ -49,13 +50,8 @@ inline void FEComposite::computeArithmeticPixelsNeon(unsigned char* source, unsi uint32_t* destinationEndPixel = destinationPixel + (pixelArrayLength >> 2); while (destinationPixel < destinationEndPixel) { - uint32x2_t temporary1 = vset_lane_u32(*sourcePixel, temporary1, 0); - uint16x4_t temporary2 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(temporary1))); - float32x4_t sourcePixelAsFloat = vcvtq_f32_u32(vmovl_u16(temporary2)); - - temporary1 = vset_lane_u32(*destinationPixel, temporary1, 0); - temporary2 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(temporary1))); - float32x4_t destinationPixelAsFloat = vcvtq_f32_u32(vmovl_u16(temporary2)); + float32x4_t sourcePixelAsFloat = loadRGBA8AsFloat(sourcePixel); + float32x4_t destinationPixelAsFloat = loadRGBA8AsFloat(destinationPixel); float32x4_t result = vmulq_f32(sourcePixelAsFloat, k2x4); result = vmlaq_f32(result, destinationPixelAsFloat, k3x4); @@ -94,6 +90,6 @@ inline void FEComposite::platformArithmeticNeon(unsigned char* source, unsigned } // namespace WebCore -#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#endif // HAVE(ARM_NEON_INTRINSICS) #endif // FECompositeArithmeticNEON_h diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h index 3779c2ec5..600704ebb 100644 --- a/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h +++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h @@ -27,7 +27,7 @@ #ifndef FEGaussianBlurNEON_h #define FEGaussianBlurNEON_h -#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#if HAVE(ARM_NEON_INTRINSICS) #include "FEGaussianBlur.h" #include "NEONHelpers.h" @@ -73,6 +73,6 @@ inline void boxBlurNEON(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dst } // namespace WebCore -#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#endif // HAVE(ARM_NEON_INTRINSICS) #endif // FEGaussianBlurNEON_h diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp index 789b6aa48..fe44c6275 100644 --- a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp +++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.cpp @@ -27,7 +27,7 @@ #include "config.h" #include "FELightingNEON.h" -#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC) +#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG) namespace WebCore { @@ -500,4 +500,4 @@ int FELighting::getPowerCoefficients(float exponent) } // namespace WebCore -#endif // CPU(ARM_NEON) && COMPILER(GCC) +#endif // CPU(ARM_NEON) && COMPILER(GCC_OR_CLANG) diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h index 1e7140164..41b61e14a 100644 --- a/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h +++ b/Source/WebCore/platform/graphics/cpu/arm/filters/FELightingNEON.h @@ -27,7 +27,7 @@ #ifndef FELightingNEON_h #define FELightingNEON_h -#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC) +#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG) #include "FELighting.h" #include <wtf/ParallelJobs.h> @@ -194,6 +194,6 @@ inline void FELighting::platformApplyNeon(LightingData& data, LightSource::Paint } // namespace WebCore -#endif // CPU(ARM_NEON) && COMPILER(GCC) +#endif // CPU(ARM_NEON) && COMPILER(GCC_OR_CLANG) #endif // FELightingNEON_h diff --git a/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h b/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h index 3708b9d0a..3a13e4a4b 100644 --- a/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h +++ b/Source/WebCore/platform/graphics/cpu/arm/filters/NEONHelpers.h @@ -27,7 +27,7 @@ #ifndef NEONHelpers_h #define NEONHelpers_h -#if ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#if HAVE(ARM_NEON_INTRINSICS) #include <arm_neon.h> @@ -50,6 +50,6 @@ inline void storeFloatAsRGBA8(float32x4_t data, uint32_t* destination) } // namespace WebCore -#endif // ENABLE(FILTERS) && HAVE(ARM_NEON_INTRINSICS) +#endif // HAVE(ARM_NEON_INTRINSICS) #endif // NEONHelpers_h diff --git a/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp new file mode 100644 index 000000000..0b647eff9 --- /dev/null +++ b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PixelBufferConformerCV.h" + +#include "GraphicsContextCG.h" +#include "SoftLinking.h" + +#include "CoreVideoSoftLink.h" + +#if USE(VIDEOTOOLBOX) +SOFT_LINK_FRAMEWORK_OPTIONAL(VideoToolbox) +SOFT_LINK(VideoToolbox, VTPixelBufferConformerCreateWithAttributes, OSStatus, (CFAllocatorRef allocator, CFDictionaryRef attributes, VTPixelBufferConformerRef* conformerOut), (allocator, attributes, conformerOut)); +SOFT_LINK(VideoToolbox, VTPixelBufferConformerIsConformantPixelBuffer, Boolean, (VTPixelBufferConformerRef conformer, CVPixelBufferRef pixBuf), (conformer, pixBuf)) +SOFT_LINK(VideoToolbox, VTPixelBufferConformerCopyConformedPixelBuffer, OSStatus, (VTPixelBufferConformerRef conformer, CVPixelBufferRef sourceBuffer, Boolean ensureModifiable, CVPixelBufferRef* conformedBufferOut), (conformer, sourceBuffer, ensureModifiable, conformedBufferOut)) +#endif + +namespace WebCore { + +PixelBufferConformerCV::PixelBufferConformerCV(CFDictionaryRef attributes) +{ +#if USE(VIDEOTOOLBOX) + VTPixelBufferConformerRef conformer = nullptr; + VTPixelBufferConformerCreateWithAttributes(kCFAllocatorDefault, attributes, &conformer); + ASSERT(conformer); + m_pixelConformer = adoptCF(conformer); +#else + UNUSED_PARAM(attributes); + ASSERT(!attributes); +#endif +} + +static const void* CVPixelBufferGetBytePointerCallback(void* info) +{ + CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info); + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + return CVPixelBufferGetBaseAddress(pixelBuffer); +} + +static void CVPixelBufferReleaseBytePointerCallback(void* info, const void*) +{ + CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info); + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); +} + +static void CVPixelBufferReleaseInfoCallback(void* info) +{ + CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info); + CFRelease(pixelBuffer); +} + +RetainPtr<CGImageRef> PixelBufferConformerCV::createImageFromPixelBuffer(CVPixelBufferRef rawBuffer) +{ + RetainPtr<CVPixelBufferRef> buffer { rawBuffer }; + size_t width = CVPixelBufferGetWidth(buffer.get()); + size_t height = CVPixelBufferGetHeight(buffer.get()); + +#if USE(VIDEOTOOLBOX) + if (!VTPixelBufferConformerIsConformantPixelBuffer(m_pixelConformer.get(), buffer.get())) { + CVPixelBufferRef outputBuffer = nullptr; + OSStatus status = VTPixelBufferConformerCopyConformedPixelBuffer(m_pixelConformer.get(), buffer.get(), false, &outputBuffer); + if (status != noErr || !outputBuffer) + return nullptr; + buffer = adoptCF(outputBuffer); + } +#endif + + CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst; + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(buffer.get()); + size_t byteLength = CVPixelBufferGetDataSize(buffer.get()); + + CFRetain(buffer.get()); // Balanced by CVPixelBufferReleaseInfoCallback in providerCallbacks. + CGDataProviderDirectCallbacks providerCallbacks = { 0, CVPixelBufferGetBytePointerCallback, CVPixelBufferReleaseBytePointerCallback, 0, CVPixelBufferReleaseInfoCallback }; + RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateDirect(buffer.get(), byteLength, &providerCallbacks)); + + return adoptCF(CGImageCreate(width, height, 8, 32, bytesPerRow, sRGBColorSpaceRef(), bitmapInfo, provider.get(), nullptr, false, kCGRenderingIntentDefault)); +} + +} diff --git a/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h new file mode 100644 index 000000000..76ea81b20 --- /dev/null +++ b/Source/WebCore/platform/graphics/cv/PixelBufferConformerCV.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PixelBufferConformerCV_h +#define PixelBufferConformerCV_h + +#include <wtf/RetainPtr.h> + +typedef struct OpaqueVTPixelBufferConformer* VTPixelBufferConformerRef; +typedef struct CGImage* CGImageRef; +typedef struct __CVBuffer *CVPixelBufferRef; + +namespace WebCore { + +class PixelBufferConformerCV { +public: + PixelBufferConformerCV(CFDictionaryRef attributes); + RetainPtr<CGImageRef> createImageFromPixelBuffer(CVPixelBufferRef); + +private: +#if USE(VIDEOTOOLBOX) + RetainPtr<VTPixelBufferConformerRef> m_pixelConformer; +#endif +}; + +} + +#endif // PixelBufferConformerCV_h diff --git a/Source/WebCore/platform/graphics/cv/TextureCacheCV.h b/Source/WebCore/platform/graphics/cv/TextureCacheCV.h new file mode 100644 index 000000000..d796dd76e --- /dev/null +++ b/Source/WebCore/platform/graphics/cv/TextureCacheCV.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextureCacheCV_h +#define TextureCacheCV_h + +#include "GraphicsTypes3D.h" +#include <wtf/Ref.h> +#include <wtf/RetainPtr.h> +#include <wtf/WeakPtr.h> + +typedef struct __CVBuffer* CVImageBufferRef; +typedef CVImageBufferRef CVOpenGLTextureRef; +typedef CVImageBufferRef CVOpenGLESTextureRef; +typedef struct __CVOpenGLTextureCache *CVOpenGLTextureCacheRef; +typedef struct __CVOpenGLESTextureCache *CVOpenGLESTextureCacheRef; + +namespace WebCore { + +class GraphicsContext3D; + +class TextureCacheCV { +public: + static std::unique_ptr<TextureCacheCV> create(GraphicsContext3D&); + +#if PLATFORM(IOS) + typedef CVOpenGLESTextureCacheRef TextureCacheType; + typedef CVOpenGLESTextureRef TextureType; +#else + typedef CVOpenGLTextureCacheRef TextureCacheType; + typedef CVOpenGLTextureRef TextureType; +#endif + + TextureCacheCV(GraphicsContext3D&, RetainPtr<TextureCacheType>&&); + + RetainPtr<TextureType> textureFromImage(CVImageBufferRef, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type); + GraphicsContext3D& context() { return m_context.get(); } + +private: + Ref<GraphicsContext3D> m_context; + RetainPtr<TextureCacheType> m_cache; + WeakPtrFactory<TextureCacheCV> m_weakPtrFactory; +}; + +} + +#endif // TextureCacheCV_h diff --git a/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp new file mode 100644 index 000000000..1d1ea39c8 --- /dev/null +++ b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "VideoTextureCopierCV.h" + +#include "Logging.h" +#include <wtf/NeverDestroyed.h> + +#if PLATFORM(IOS) +#include <OpenGLES/ES3/glext.h> +#endif + +#include "CoreVideoSoftLink.h" + +namespace WebCore { + +VideoTextureCopierCV::VideoTextureCopierCV(GraphicsContext3D& context) + : m_context(context) + , m_readFramebuffer(context.createFramebuffer()) +{ +} + +VideoTextureCopierCV::~VideoTextureCopierCV() +{ + m_context->deleteFramebuffer(m_readFramebuffer); +} + +#if !LOG_DISABLED + +#define STRINGIFY_PAIR(e) e, #e +static std::map<uint32_t, const char*>& enumToStringMap() +{ + static NeverDestroyed<std::map<uint32_t, const char*>> map; + if (map.get().empty()) { + std::map<uint32_t, const char*> stringMap; + map.get().emplace(STRINGIFY_PAIR(GL_RGB)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA)); + map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE_ALPHA)); + map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE)); + map.get().emplace(STRINGIFY_PAIR(GL_ALPHA)); + map.get().emplace(STRINGIFY_PAIR(GL_R8)); + map.get().emplace(STRINGIFY_PAIR(GL_R16F)); + map.get().emplace(STRINGIFY_PAIR(GL_R32F)); + map.get().emplace(STRINGIFY_PAIR(GL_R8UI)); + map.get().emplace(STRINGIFY_PAIR(GL_R8I)); + map.get().emplace(STRINGIFY_PAIR(GL_R16UI)); + map.get().emplace(STRINGIFY_PAIR(GL_R16I)); + map.get().emplace(STRINGIFY_PAIR(GL_R32UI)); + map.get().emplace(STRINGIFY_PAIR(GL_R32I)); + map.get().emplace(STRINGIFY_PAIR(GL_RG8)); + map.get().emplace(STRINGIFY_PAIR(GL_RG16F)); + map.get().emplace(STRINGIFY_PAIR(GL_RG32F)); + map.get().emplace(STRINGIFY_PAIR(GL_RG8UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RG8I)); + map.get().emplace(STRINGIFY_PAIR(GL_RG16UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RG16I)); + map.get().emplace(STRINGIFY_PAIR(GL_RG32UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RG32I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB8)); + map.get().emplace(STRINGIFY_PAIR(GL_SRGB8)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA8)); + map.get().emplace(STRINGIFY_PAIR(GL_SRGB8_ALPHA8)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA4)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB10_A2)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT16)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT24)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT32F)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH24_STENCIL8)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH32F_STENCIL8)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA)); + map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE_ALPHA)); + map.get().emplace(STRINGIFY_PAIR(GL_LUMINANCE)); + map.get().emplace(STRINGIFY_PAIR(GL_ALPHA)); + map.get().emplace(STRINGIFY_PAIR(GL_RED)); + map.get().emplace(STRINGIFY_PAIR(GL_RG_INTEGER)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_STENCIL)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_BYTE)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT_5_6_5)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT_4_4_4_4)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT_5_5_5_1)); + map.get().emplace(STRINGIFY_PAIR(GL_BYTE)); + map.get().emplace(STRINGIFY_PAIR(GL_HALF_FLOAT)); + map.get().emplace(STRINGIFY_PAIR(GL_FLOAT)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_SHORT)); + map.get().emplace(STRINGIFY_PAIR(GL_SHORT)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT)); + map.get().emplace(STRINGIFY_PAIR(GL_INT)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_2_10_10_10_REV)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_24_8)); + map.get().emplace(STRINGIFY_PAIR(GL_FLOAT_32_UNSIGNED_INT_24_8_REV)); + +#if PLATFORM(IOS) + map.get().emplace(STRINGIFY_PAIR(GL_RED_INTEGER)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB_INTEGER)); + map.get().emplace(STRINGIFY_PAIR(GL_RG8_SNORM)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB565)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB8_SNORM)); + map.get().emplace(STRINGIFY_PAIR(GL_R11F_G11F_B10F)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB9_E5)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB16F)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB32F)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB8UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB8I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB16UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB16I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB32UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB32I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA8_SNORM)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA16F)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA32F)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA8UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA8I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB10_A2UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA16UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA16I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA32I)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA32UI)); + map.get().emplace(STRINGIFY_PAIR(GL_RGB5_A1)); + map.get().emplace(STRINGIFY_PAIR(GL_RG)); + map.get().emplace(STRINGIFY_PAIR(GL_RGBA_INTEGER)); + map.get().emplace(STRINGIFY_PAIR(GL_DEPTH_COMPONENT)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_10F_11F_11F_REV)); + map.get().emplace(STRINGIFY_PAIR(GL_UNSIGNED_INT_5_9_9_9_REV)); +#endif + } + return map.get(); +} + +#endif + +bool VideoTextureCopierCV::copyVideoTextureToPlatformTexture(TextureType inputTexture, size_t width, size_t height, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY) +{ + if (flipY || premultiplyAlpha) + return false; + + if (!inputTexture) + return false; + +#if PLATFORM(IOS) + Platform3DObject videoTextureName = CVOpenGLESTextureGetName(inputTexture); + GC3Denum videoTextureTarget = CVOpenGLESTextureGetTarget(inputTexture); +#else + Platform3DObject videoTextureName = CVOpenGLTextureGetName(inputTexture); + GC3Denum videoTextureTarget = CVOpenGLTextureGetTarget(inputTexture); +#endif + + LOG(Media, "VideoTextureCopierCV::copyVideoTextureToPlatformTexture(%p) - internalFormat: %s, format: %s, type: %s", this, enumToStringMap()[internalFormat], enumToStringMap()[format], enumToStringMap()[type]); + + // Save the origial bound texture & framebuffer names so we can re-bind them after copying the video texture. + GC3Dint boundTexture = 0; + GC3Dint boundReadFramebuffer = 0; + m_context->getIntegerv(GraphicsContext3D::TEXTURE_BINDING_2D, &boundTexture); + m_context->getIntegerv(GraphicsContext3D::READ_FRAMEBUFFER_BINDING, &boundReadFramebuffer); + + m_context->bindTexture(videoTextureTarget, videoTextureName); + m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + + // Make that framebuffer the read source from which drawing commands will read voxels. + m_context->bindFramebuffer(GraphicsContext3D::READ_FRAMEBUFFER, m_readFramebuffer); + + // Allocate uninitialized memory for the output texture. + m_context->bindTexture(outputTarget, outputTexture); + m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + m_context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + m_context->texImage2DDirect(outputTarget, level, internalFormat, width, height, 0, format, type, nullptr); + + // Attach the video texture to the framebuffer. + m_context->framebufferTexture2D(GraphicsContext3D::READ_FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, videoTextureTarget, videoTextureName, level); + + GC3Denum status = m_context->checkFramebufferStatus(GraphicsContext3D::READ_FRAMEBUFFER); + if (status != GraphicsContext3D::FRAMEBUFFER_COMPLETE) + return false; + + // Copy texture from the read framebuffer (and thus the video texture) to the output texture. + m_context->copyTexImage2D(outputTarget, level, internalFormat, 0, 0, width, height, 0); + + // Restore the previous texture and framebuffer bindings. + m_context->bindTexture(outputTarget, boundTexture); + m_context->bindFramebuffer(GraphicsContext3D::READ_FRAMEBUFFER, boundReadFramebuffer); + + return !m_context->getError(); +} + + +} diff --git a/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h new file mode 100644 index 000000000..b5f512f89 --- /dev/null +++ b/Source/WebCore/platform/graphics/cv/VideoTextureCopierCV.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VideoTextureCopierCV_h +#define VideoTextureCopierCV_h + +#import "GraphicsContext3D.h" + +typedef struct __CVBuffer* CVImageBufferRef; +typedef CVImageBufferRef CVOpenGLTextureRef; +typedef CVImageBufferRef CVOpenGLESTextureRef; + +namespace WebCore { + +class VideoTextureCopierCV { +public: + VideoTextureCopierCV(GraphicsContext3D&); + ~VideoTextureCopierCV(); + +#if PLATFORM(IOS) + typedef CVOpenGLESTextureRef TextureType; +#else + typedef CVOpenGLTextureRef TextureType; +#endif + + bool copyVideoTextureToPlatformTexture(TextureType, size_t width, size_t height, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY); + + GraphicsContext3D& context() { return m_context.get(); } + +private: + Ref<GraphicsContext3D> m_context; + Platform3DObject m_readFramebuffer; +}; + +} + +#endif // VideoTextureCopierCV_h diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayList.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayList.cpp new file mode 100644 index 000000000..cd9c933d6 --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayList.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DisplayList.h" + +#include "DisplayListItems.h" +#include "Logging.h" +#include "TextStream.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { +namespace DisplayList { + +#if !defined(NDEBUG) || !LOG_DISABLED +WTF::CString DisplayList::description() const +{ + TextStream ts; + ts << *this; + return ts.release().utf8(); +} + +void DisplayList::dump() const +{ + fprintf(stderr, "%s", description().data()); +} +#endif + +void DisplayList::clear() +{ + m_list.clear(); +} + +void DisplayList::removeItemsFromIndex(size_t index) +{ + m_list.resize(index); +} + +bool DisplayList::shouldDumpForFlags(AsTextFlags flags, const Item& item) +{ + switch (item.type()) { + case ItemType::SetState: + if (!(flags & AsTextFlag::IncludesPlatformOperations)) { + const auto& stateItem = downcast<SetState>(item); + // FIXME: for now, only drop the item if the only state-change flags are platform-specific. + if (stateItem.state().m_changeFlags == GraphicsContextState::ShouldSubpixelQuantizeFontsChange) + return false; + + if (stateItem.state().m_changeFlags == GraphicsContextState::ShouldSubpixelQuantizeFontsChange) + return false; + } + break; +#if USE(CG) + case ItemType::ApplyFillPattern: + case ItemType::ApplyStrokePattern: + if (!(flags & AsTextFlag::IncludesPlatformOperations)) + return false; + break; +#endif + default: + break; + } + return true; +} + +String DisplayList::asText(AsTextFlags flags) const +{ + TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect); + for (auto& item : m_list) { + if (!shouldDumpForFlags(flags, item)) + continue; + stream << item; + } + return stream.release(); +} + +void DisplayList::dump(TextStream& ts) const +{ + TextStream::GroupScope group(ts); + ts << "display list"; + + size_t numItems = m_list.size(); + for (size_t i = 0; i < numItems; ++i) { + TextStream::GroupScope scope(ts); + ts << i << " " << m_list[i].get(); + } + ts.startGroup(); + ts << "size in bytes: " << sizeInBytes(); + ts.endGroup(); +} + +size_t DisplayList::sizeInBytes() const +{ + size_t result = 0; + for (auto& ref : m_list) + result += Item::sizeInBytes(ref); + + return result; +} + +} // namespace DisplayList + +TextStream& operator<<(TextStream& ts, const DisplayList::DisplayList& displayList) +{ + displayList.dump(ts); + return ts; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayList.h b/Source/WebCore/platform/graphics/displaylists/DisplayList.h new file mode 100644 index 000000000..da455d25f --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayList.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DisplayList_h +#define DisplayList_h + +#include "DisplayListItems.h" +#include <wtf/FastMalloc.h> +#include <wtf/Noncopyable.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class FloatRect; +class GraphicsContext; +class TextStream; + +namespace DisplayList { + +class Item; + +enum AsTextFlag { + None = 0, + IncludesPlatformOperations = 1 << 0, +}; + +typedef unsigned AsTextFlags; + +class DisplayList { + WTF_MAKE_NONCOPYABLE(DisplayList); WTF_MAKE_FAST_ALLOCATED; + friend class Recorder; + friend class Replayer; +public: + DisplayList() = default; + DisplayList(DisplayList&&) = default; + + DisplayList& operator=(DisplayList&&) = default; + + void dump(TextStream&) const; + + const Vector<Ref<Item>>& list() const { return m_list; } + Item& itemAt(size_t index) + { + ASSERT(index < m_list.size()); + return m_list[index].get(); + } + + void clear(); + void removeItemsFromIndex(size_t); + + size_t itemCount() const { return m_list.size(); } + size_t sizeInBytes() const; + + String asText(AsTextFlags) const; + +#if !defined(NDEBUG) || !LOG_DISABLED + WTF::CString description() const; + void dump() const; +#endif + +private: + Item& append(Ref<Item>&& item) + { + m_list.append(WTFMove(item)); + return m_list.last().get(); + } + + // Less efficient append, only used for tracking replay. + void appendItem(Item& item) + { + m_list.append(item); + } + + static bool shouldDumpForFlags(AsTextFlags, const Item&); + + Vector<Ref<Item>>& list() { return m_list; } + + Vector<Ref<Item>> m_list; +}; + +} // DisplayList + +TextStream& operator<<(TextStream&, const DisplayList::DisplayList&); + +} // WebCore + +using WebCore::DisplayList::DisplayList; + +#endif /* DisplayList_h */ diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp new file mode 100644 index 000000000..0fca123da --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.cpp @@ -0,0 +1,1151 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DisplayListItems.h" + +#include "FontCascade.h" +#include "TextStream.h" + +namespace WebCore { +namespace DisplayList { + +// Should match RenderTheme::platformFocusRingWidth() +static const float platformFocusRingWidth = 3; + +#if !defined(NDEBUG) || !LOG_DISABLED +WTF::CString Item::description() const +{ + TextStream ts; + ts << *this; + return ts.release().utf8(); +} +#endif + +size_t Item::sizeInBytes(const Item& item) +{ + switch (item.type()) { + case ItemType::Save: + return sizeof(downcast<Save>(item)); + case ItemType::Restore: + return sizeof(downcast<Restore>(item)); + case ItemType::Translate: + return sizeof(downcast<Translate>(item)); + case ItemType::Rotate: + return sizeof(downcast<Rotate>(item)); + case ItemType::Scale: + return sizeof(downcast<Scale>(item)); + case ItemType::ConcatenateCTM: + return sizeof(downcast<ConcatenateCTM>(item)); + case ItemType::SetState: + return sizeof(downcast<SetState>(item)); + case ItemType::SetLineCap: + return sizeof(downcast<SetLineCap>(item)); + case ItemType::SetLineDash: + return sizeof(downcast<SetLineDash>(item)); + case ItemType::SetLineJoin: + return sizeof(downcast<SetLineJoin>(item)); + case ItemType::SetMiterLimit: + return sizeof(downcast<SetMiterLimit>(item)); + case ItemType::ClearShadow: + return sizeof(downcast<ClearShadow>(item)); + case ItemType::Clip: + return sizeof(downcast<Clip>(item)); + case ItemType::ClipOut: + return sizeof(downcast<ClipOut>(item)); + case ItemType::ClipOutToPath: + return sizeof(downcast<ClipOutToPath>(item)); + case ItemType::ClipPath: + return sizeof(downcast<ClipPath>(item)); + case ItemType::DrawGlyphs: + return sizeof(downcast<DrawGlyphs>(item)); + case ItemType::DrawImage: + return sizeof(downcast<DrawImage>(item)); + case ItemType::DrawTiledImage: + return sizeof(downcast<DrawTiledImage>(item)); + case ItemType::DrawTiledScaledImage: + return sizeof(downcast<DrawTiledScaledImage>(item)); +#if USE(CG) || USE(CAIRO) + case ItemType::DrawNativeImage: + return sizeof(downcast<DrawNativeImage>(item)); +#endif + case ItemType::DrawPattern: + return sizeof(downcast<DrawPattern>(item)); + case ItemType::DrawRect: + return sizeof(downcast<DrawRect>(item)); + case ItemType::DrawLine: + return sizeof(downcast<DrawLine>(item)); + case ItemType::DrawLinesForText: + return sizeof(downcast<DrawLinesForText>(item)); + case ItemType::DrawLineForDocumentMarker: + return sizeof(downcast<DrawLineForDocumentMarker>(item)); + case ItemType::DrawEllipse: + return sizeof(downcast<DrawEllipse>(item)); + case ItemType::DrawPath: + return sizeof(downcast<DrawPath>(item)); + case ItemType::DrawFocusRingPath: + return sizeof(downcast<DrawFocusRingPath>(item)); + case ItemType::DrawFocusRingRects: + return sizeof(downcast<DrawFocusRingRects>(item)); + case ItemType::FillRect: + return sizeof(downcast<FillRect>(item)); + case ItemType::FillRectWithColor: + return sizeof(downcast<FillRectWithColor>(item)); + case ItemType::FillRectWithGradient: + return sizeof(downcast<FillRectWithGradient>(item)); + case ItemType::FillCompositedRect: + return sizeof(downcast<FillCompositedRect>(item)); + case ItemType::FillRoundedRect: + return sizeof(downcast<FillRoundedRect>(item)); + case ItemType::FillRectWithRoundedHole: + return sizeof(downcast<FillRectWithRoundedHole>(item)); + case ItemType::FillPath: + return sizeof(downcast<FillPath>(item)); + case ItemType::FillEllipse: + return sizeof(downcast<FillEllipse>(item)); + case ItemType::StrokeRect: + return sizeof(downcast<StrokeRect>(item)); + case ItemType::StrokePath: + return sizeof(downcast<StrokePath>(item)); + case ItemType::StrokeEllipse: + return sizeof(downcast<StrokeEllipse>(item)); + case ItemType::ClearRect: + return sizeof(downcast<ClearRect>(item)); + case ItemType::BeginTransparencyLayer: + return sizeof(downcast<BeginTransparencyLayer>(item)); + case ItemType::EndTransparencyLayer: + return sizeof(downcast<EndTransparencyLayer>(item)); +#if USE(CG) + case ItemType::ApplyStrokePattern: + return sizeof(downcast<ApplyStrokePattern>(item)); + case ItemType::ApplyFillPattern: + return sizeof(downcast<ApplyFillPattern>(item)); +#endif + case ItemType::ApplyDeviceScaleFactor: + return sizeof(downcast<ApplyDeviceScaleFactor>(item)); + } + return 0; +} + +static TextStream& operator<<(TextStream& ts, const DrawingItem& item) +{ + ts.startGroup(); + ts << "extent "; + if (item.extentKnown()) + ts << item.extent(); + else + ts << "unknown"; + + ts.endGroup(); + return ts; +} + +void Save::apply(GraphicsContext& context) const +{ + context.save(); +} + +static TextStream& operator<<(TextStream& ts, const Save& item) +{ + ts.dumpProperty("restore-index", item.restoreIndex()); + return ts; +} + +void Restore::apply(GraphicsContext& context) const +{ + context.restore(); +} + +void Translate::apply(GraphicsContext& context) const +{ + context.translate(m_x, m_y); +} + +static TextStream& operator<<(TextStream& ts, const Translate& item) +{ + ts.dumpProperty("x", item.x()); + ts.dumpProperty("y", item.y()); + + return ts; +} + +void Rotate::apply(GraphicsContext& context) const +{ + context.rotate(m_angle); +} + +static TextStream& operator<<(TextStream& ts, const Rotate& item) +{ + ts.dumpProperty("angle", item.angle()); + + return ts; +} + +void Scale::apply(GraphicsContext& context) const +{ + context.scale(m_size); +} + +static TextStream& operator<<(TextStream& ts, const Scale& item) +{ + ts.dumpProperty("size", item.amount()); + + return ts; +} + +ConcatenateCTM::ConcatenateCTM(const AffineTransform& transform) + : Item(ItemType::ConcatenateCTM) + , m_transform(transform) +{ +} + +void ConcatenateCTM::apply(GraphicsContext& context) const +{ + context.concatCTM(m_transform); +} + +static TextStream& operator<<(TextStream& ts, const ConcatenateCTM& item) +{ + ts.dumpProperty("ctm", item.transform()); + + return ts; +} + +void SetState::apply(GraphicsContext& context) const +{ + m_state.apply(context); +} + +void SetState::accumulate(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) +{ + m_state.accumulate(state, flags); +} + +static TextStream& operator<<(TextStream& ts, const SetState& state) +{ + ts << state.state(); + return ts; +} + +void SetLineCap::apply(GraphicsContext& context) const +{ + context.setLineCap(m_lineCap); +} + +static TextStream& operator<<(TextStream& ts, const SetLineCap& lineCap) +{ + ts.dumpProperty("line-cap", lineCap.lineCap()); + return ts; +} + +void SetLineDash::apply(GraphicsContext& context) const +{ + context.setLineDash(m_dashArray, m_dashOffset); +} + +static TextStream& operator<<(TextStream& ts, const SetLineDash& lineDash) +{ + ts.dumpProperty("dash-array", lineDash.dashArray()); + ts.dumpProperty("dash-offset", lineDash.dashOffset()); + return ts; +} + +void SetLineJoin::apply(GraphicsContext& context) const +{ + context.setLineJoin(m_lineJoin); +} + +static TextStream& operator<<(TextStream& ts, const SetLineJoin& lineJoin) +{ + ts.dumpProperty("line-join", lineJoin.lineJoin()); + return ts; +} + +void SetMiterLimit::apply(GraphicsContext& context) const +{ + context.setMiterLimit(m_miterLimit); +} + +static TextStream& operator<<(TextStream& ts, const SetMiterLimit& miterLimit) +{ + ts.dumpProperty("mitre-limit", miterLimit.miterLimit()); + return ts; +} + +void ClearShadow::apply(GraphicsContext& context) const +{ + context.clearShadow(); +} + +void Clip::apply(GraphicsContext& context) const +{ + context.clip(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const Clip& item) +{ + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void ClipOut::apply(GraphicsContext& context) const +{ + context.clipOut(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const ClipOut& item) +{ + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void ClipOutToPath::apply(GraphicsContext& context) const +{ + context.clipOut(m_path); +} + +static TextStream& operator<<(TextStream& ts, const ClipOutToPath& item) +{ + ts.dumpProperty("path", item.path()); + return ts; +} + +void ClipPath::apply(GraphicsContext& context) const +{ + context.clipPath(m_path, m_windRule); +} + +static TextStream& operator<<(TextStream& ts, const ClipPath& item) +{ + ts.dumpProperty("path", item.path()); + ts.dumpProperty("wind-rule", item.windRule()); + return ts; +} + +DrawGlyphs::DrawGlyphs(const Font& font, const GlyphBufferGlyph* glyphs, const GlyphBufferAdvance* advances, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode smoothingMode) + : DrawingItem(ItemType::DrawGlyphs) + , m_font(const_cast<Font&>(font)) + , m_blockLocation(blockLocation) + , m_localAnchor(localAnchor) + , m_smoothingMode(smoothingMode) +{ + m_glyphs.reserveInitialCapacity(count); + m_advances.reserveInitialCapacity(count); + for (unsigned i = 0; i < count; ++i) { + m_glyphs.uncheckedAppend(glyphs[i]); + m_advances.uncheckedAppend(advances[i]); + } + computeBounds(); +} + +inline GlyphBuffer DrawGlyphs::generateGlyphBuffer() const +{ + GlyphBuffer result; + for (size_t i = 0; i < m_glyphs.size(); ++i) { +#if USE(CAIRO) + result.add(m_glyphs[i].index, &m_font.get(), m_advances[i]); +#else + result.add(m_glyphs[i], &m_font.get(), m_advances[i]); +#endif + } + return result; +} + +void DrawGlyphs::apply(GraphicsContext& context) const +{ + FontCascade::drawGlyphs(context, m_font, generateGlyphBuffer(), 0, m_glyphs.size(), anchorPoint(), m_smoothingMode); +} + +void DrawGlyphs::computeBounds() +{ + // FIXME: This code doesn't actually take the extents of the glyphs into consideration. It assumes that + // the glyph lies entirely within its [(ascent + descent), advance] rect. + float ascent = m_font->fontMetrics().floatAscent(); + float descent = m_font->fontMetrics().floatDescent(); + FloatPoint current = toFloatPoint(localAnchor()); + size_t numGlyphs = m_glyphs.size(); + for (size_t i = 0; i < numGlyphs; ++i) { + GlyphBufferAdvance advance = m_advances[i]; + FloatRect glyphRect = FloatRect(current.x(), current.y() - ascent, advance.width(), ascent + descent); + m_bounds.unite(glyphRect); + + current.move(advance.width(), advance.height()); + } +} + +std::optional<FloatRect> DrawGlyphs::localBounds(const GraphicsContext&) const +{ + FloatRect localBounds = m_bounds; + localBounds.move(m_blockLocation.x(), m_blockLocation.y()); + return localBounds; +} + +static TextStream& operator<<(TextStream& ts, const DrawGlyphs& item) +{ + ts << static_cast<const DrawingItem&>(item); + // FIXME: dump more stuff. + ts.dumpProperty("block-location", item.blockLocation()); + ts.dumpProperty("local-anchor", item.localAnchor()); + ts.dumpProperty("anchor-point", item.anchorPoint()); + ts.dumpProperty("length", item.glyphs().size()); + + return ts; +} + +DrawImage::DrawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) + : DrawingItem(ItemType::DrawImage) + , m_image(image) + , m_destination(destination) + , m_source(source) + , m_imagePaintingOptions(imagePaintingOptions) +{ +} + +void DrawImage::apply(GraphicsContext& context) const +{ + context.drawImage(m_image.get(), m_destination, m_source, m_imagePaintingOptions); +} + +static TextStream& operator<<(TextStream& ts, const DrawImage& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("image", item.image()); + ts.dumpProperty("source-rect", item.source()); + ts.dumpProperty("dest-rect", item.destination()); + return ts; +} + +DrawTiledImage::DrawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) + : DrawingItem(ItemType::DrawTiledImage) + , m_image(image) + , m_destination(destination) + , m_source(source) + , m_tileSize(tileSize) + , m_spacing(spacing) + , m_imagePaintingOptions(imagePaintingOptions) +{ +} + +void DrawTiledImage::apply(GraphicsContext& context) const +{ + context.drawTiledImage(m_image.get(), m_destination, m_source, m_tileSize, m_spacing, m_imagePaintingOptions); +} + +static TextStream& operator<<(TextStream& ts, const DrawTiledImage& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("image", item.image()); + ts.dumpProperty("source-point", item.source()); + ts.dumpProperty("dest-rect", item.destination()); + ts.dumpProperty("tile-size", item.tileSize()); + ts.dumpProperty("spacing", item.spacing()); + return ts; +} + +DrawTiledScaledImage::DrawTiledScaledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) + : DrawingItem(ItemType::DrawTiledScaledImage) + , m_image(image) + , m_destination(destination) + , m_source(source) + , m_tileScaleFactor(tileScaleFactor) + , m_hRule(hRule) + , m_vRule(vRule) + , m_imagePaintingOptions(imagePaintingOptions) +{ +} + +void DrawTiledScaledImage::apply(GraphicsContext& context) const +{ + context.drawTiledImage(m_image.get(), m_destination, m_source, m_tileScaleFactor, m_hRule, m_vRule, m_imagePaintingOptions); +} + +static TextStream& operator<<(TextStream& ts, const DrawTiledScaledImage& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("image", item.image()); + ts.dumpProperty("source-rect", item.source()); + ts.dumpProperty("dest-rect", item.destination()); + return ts; +} + +#if USE(CG) || USE(CAIRO) +DrawNativeImage::DrawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation) + : DrawingItem(ItemType::DrawNativeImage) +#if USE(CG) + // FIXME: Need to store an image for Cairo. + , m_image(image) +#endif + , m_imageSize(imageSize) + , m_destination(destRect) + , m_srcRect(srcRect) +#if USE(CG) + , m_op(op) + , m_blendMode(blendMode) +#endif + , m_orientation(orientation) +{ +#if !USE(CG) + UNUSED_PARAM(image); + UNUSED_PARAM(op); + UNUSED_PARAM(blendMode); +#endif +} + +void DrawNativeImage::apply(GraphicsContext& context) const +{ +#if USE(CG) + context.drawNativeImage(m_image, m_imageSize, m_destination, m_srcRect, m_op, m_blendMode, m_orientation); +#else + UNUSED_PARAM(context); +#endif +} + +static TextStream& operator<<(TextStream& ts, const DrawNativeImage& item) +{ + ts << static_cast<const DrawingItem&>(item); + // FIXME: dump more stuff. + ts.dumpProperty("source-rect", item.source()); + ts.dumpProperty("dest-rect", item.destination()); + return ts; +} +#endif + +DrawPattern::DrawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) + : DrawingItem(ItemType::DrawPattern) + , m_image(image) + , m_patternTransform(patternTransform) + , m_tileRect(tileRect) + , m_destination(destRect) + , m_phase(phase) + , m_spacing(spacing) + , m_op(op) + , m_blendMode(blendMode) +{ +} + +void DrawPattern::apply(GraphicsContext& context) const +{ + context.drawPattern(m_image.get(), m_destination, m_tileRect, m_patternTransform, m_phase, m_spacing, m_op, m_blendMode); +} + +static TextStream& operator<<(TextStream& ts, const DrawPattern& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("image", item.image()); + ts.dumpProperty("pattern-transform", item.patternTransform()); + ts.dumpProperty("tile-rect", item.tileRect()); + ts.dumpProperty("dest-rect", item.destRect()); + ts.dumpProperty("phase", item.phase()); + ts.dumpProperty("spacing", item.spacing()); + return ts; +} + +void DrawRect::apply(GraphicsContext& context) const +{ + context.drawRect(m_rect, m_borderThickness); +} + +static TextStream& operator<<(TextStream& ts, const DrawRect& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + ts.dumpProperty("border-thickness", item.borderThickness()); + return ts; +} + +std::optional<FloatRect> DrawLine::localBounds(const GraphicsContext&) const +{ + FloatRect bounds; + bounds.fitToPoints(m_point1, m_point2); + return bounds; +} + +void DrawLine::apply(GraphicsContext& context) const +{ + context.drawLine(m_point1, m_point2); +} + +static TextStream& operator<<(TextStream& ts, const DrawLine& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("point-1", item.point1()); + ts.dumpProperty("point-2", item.point2()); + return ts; +} + +void DrawLinesForText::apply(GraphicsContext& context) const +{ + context.drawLinesForText(point(), m_widths, m_printing, m_doubleLines); +} + +std::optional<FloatRect> DrawLinesForText::localBounds(const GraphicsContext&) const +{ + // This function needs to return a value equal to or enclosing what GraphicsContext::computeLineBoundsAndAntialiasingModeForText() returns. + + if (!m_widths.size()) + return FloatRect(); + + FloatRect result(point(), FloatSize(m_widths.last(), m_strokeWidth)); + result.inflate(1); // Account for pixel snapping. FIXME: This isn't perfect, as it doesn't take the CTM into account. + return result; +} + +static TextStream& operator<<(TextStream& ts, const DrawLinesForText& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("block-location", item.blockLocation()); + ts.dumpProperty("local-anchor", item.localAnchor()); + ts.dumpProperty("point", item.point()); + ts.dumpProperty("double", item.doubleLines()); + ts.dumpProperty("widths", item.widths()); + ts.dumpProperty("is-printing", item.isPrinting()); + ts.dumpProperty("double", item.doubleLines()); + return ts; +} + +void DrawLineForDocumentMarker::apply(GraphicsContext& context) const +{ + context.drawLineForDocumentMarker(m_point, m_width, m_style); +} + +std::optional<FloatRect> DrawLineForDocumentMarker::localBounds(const GraphicsContext&) const +{ + // This function needs to return a value equal to or enclosing what GraphicsContext::drawLineForDocumentMarker() returns. + + FloatRect result(m_point, FloatSize(m_width, cMisspellingLineThickness)); + result.inflate(cMisspellingLineThickness); // Account for "misspelling dot" snapping. + return result; +} + +static TextStream& operator<<(TextStream& ts, const DrawLineForDocumentMarker& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("point", item.point()); + ts.dumpProperty("width", item.width()); + return ts; +} + +void DrawEllipse::apply(GraphicsContext& context) const +{ + context.drawEllipse(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const DrawEllipse& item) +{ + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void DrawPath::apply(GraphicsContext& context) const +{ +#if USE(CG) + context.drawPath(m_path); +#else + UNUSED_PARAM(context); +#endif +} + +static TextStream& operator<<(TextStream& ts, const DrawPath& item) +{ + ts << static_cast<const DrawingItem&>(item); +// ts.dumpProperty("path", item.path()); // FIXME: add logging for paths. + return ts; +} + +void DrawFocusRingPath::apply(GraphicsContext& context) const +{ + context.drawFocusRing(m_path, m_width, m_offset, m_color); +} + +std::optional<FloatRect> DrawFocusRingPath::localBounds(const GraphicsContext&) const +{ + FloatRect result = m_path.fastBoundingRect(); + result.inflate(platformFocusRingWidth); + return result; +} + +static TextStream& operator<<(TextStream& ts, const DrawFocusRingPath& item) +{ + ts << static_cast<const DrawingItem&>(item); +// ts.dumpProperty("path", item.path()); // FIXME: add logging for paths. + ts.dumpProperty("width", item.width()); + ts.dumpProperty("offset", item.offset()); + ts.dumpProperty("color", item.color()); + return ts; +} + +void DrawFocusRingRects::apply(GraphicsContext& context) const +{ + context.drawFocusRing(m_rects, m_width, m_offset, m_color); +} + +std::optional<FloatRect> DrawFocusRingRects::localBounds(const GraphicsContext&) const +{ + FloatRect result; + for (auto& rect : m_rects) + result.unite(rect); + result.inflate(platformFocusRingWidth); + return result; +} + +static TextStream& operator<<(TextStream& ts, const DrawFocusRingRects& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rects", item.rects()); + ts.dumpProperty("width", item.width()); + ts.dumpProperty("offset", item.offset()); + ts.dumpProperty("color", item.color()); + return ts; +} + +void FillRect::apply(GraphicsContext& context) const +{ + context.fillRect(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const FillRect& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void FillRectWithColor::apply(GraphicsContext& context) const +{ + context.fillRect(m_rect, m_color); +} + +static TextStream& operator<<(TextStream& ts, const FillRectWithColor& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + ts.dumpProperty("color", item.color()); + return ts; +} + +void FillRectWithGradient::apply(GraphicsContext& context) const +{ + context.fillRect(m_rect, m_gradient.get()); +} + +static TextStream& operator<<(TextStream& ts, const FillRectWithGradient& item) +{ + ts << static_cast<const DrawingItem&>(item); + // FIXME: log gradient. + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void FillCompositedRect::apply(GraphicsContext& context) const +{ + context.fillRect(m_rect, m_color, m_op, m_blendMode); +} + +static TextStream& operator<<(TextStream& ts, const FillCompositedRect& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + ts.dumpProperty("color", item.color()); + ts.dumpProperty("composite-operation", item.compositeOperator()); + ts.dumpProperty("blend-mode", item.blendMode()); + return ts; +} + +void FillRoundedRect::apply(GraphicsContext& context) const +{ + context.fillRoundedRect(m_rect, m_color, m_blendMode); +} + +static TextStream& operator<<(TextStream& ts, const FillRoundedRect& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.roundedRect()); + ts.dumpProperty("color", item.color()); + ts.dumpProperty("blend-mode", item.blendMode()); + return ts; +} + +void FillRectWithRoundedHole::apply(GraphicsContext& context) const +{ + context.fillRectWithRoundedHole(m_rect, m_roundedHoleRect, m_color); +} + +static TextStream& operator<<(TextStream& ts, const FillRectWithRoundedHole& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + ts.dumpProperty("rounded-hole-rect", item.roundedHoleRect()); + ts.dumpProperty("color", item.color()); + return ts; +} + +void FillPath::apply(GraphicsContext& context) const +{ + context.fillPath(m_path); +} + +static TextStream& operator<<(TextStream& ts, const FillPath& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("path", item.path()); + return ts; +} + +void FillEllipse::apply(GraphicsContext& context) const +{ + context.fillEllipse(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const FillEllipse& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + return ts; +} + +std::optional<FloatRect> StrokeRect::localBounds(const GraphicsContext&) const +{ + FloatRect bounds = m_rect; + bounds.expand(m_lineWidth, m_lineWidth); + return bounds; +} + +void StrokeRect::apply(GraphicsContext& context) const +{ + context.strokeRect(m_rect, m_lineWidth); +} + +static TextStream& operator<<(TextStream& ts, const StrokeRect& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + ts.dumpProperty("line-width", item.lineWidth()); + return ts; +} + +std::optional<FloatRect> StrokePath::localBounds(const GraphicsContext& context) const +{ + // FIXME: Need to take stroke thickness into account correctly, via CGPathByStrokingPath(). + float strokeThickness = context.strokeThickness(); + + FloatRect bounds = m_path.boundingRect(); + bounds.expand(strokeThickness, strokeThickness); + return bounds; +} + +void StrokePath::apply(GraphicsContext& context) const +{ + context.strokePath(m_path); +} + +static TextStream& operator<<(TextStream& ts, const StrokePath& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("path", item.path()); + return ts; +} + +std::optional<FloatRect> StrokeEllipse::localBounds(const GraphicsContext& context) const +{ + float strokeThickness = context.strokeThickness(); + + FloatRect bounds = m_rect; + bounds.expand(strokeThickness, strokeThickness); + return bounds; +} + +void StrokeEllipse::apply(GraphicsContext& context) const +{ + context.strokeEllipse(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const StrokeEllipse& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void ClearRect::apply(GraphicsContext& context) const +{ + context.clearRect(m_rect); +} + +static TextStream& operator<<(TextStream& ts, const ClearRect& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("rect", item.rect()); + return ts; +} + +void BeginTransparencyLayer::apply(GraphicsContext& context) const +{ + context.beginTransparencyLayer(m_opacity); +} + +static TextStream& operator<<(TextStream& ts, const BeginTransparencyLayer& item) +{ + ts << static_cast<const DrawingItem&>(item); + ts.dumpProperty("opacity", item.opacity()); + return ts; +} + +void EndTransparencyLayer::apply(GraphicsContext& context) const +{ + context.endTransparencyLayer(); +} + +#if USE(CG) +void ApplyStrokePattern::apply(GraphicsContext& context) const +{ + context.applyStrokePattern(); +} + +void ApplyFillPattern::apply(GraphicsContext& context) const +{ + context.applyFillPattern(); +} +#endif + +void ApplyDeviceScaleFactor::apply(GraphicsContext& context) const +{ + context.applyDeviceScaleFactor(m_scaleFactor); +} + +static TextStream& operator<<(TextStream& ts, const ApplyDeviceScaleFactor& item) +{ + ts.dumpProperty("scale-factor", item.scaleFactor()); + return ts; +} + +static TextStream& operator<<(TextStream& ts, const ItemType& type) +{ + switch (type) { + case ItemType::Save: ts << "save"; break; + case ItemType::Restore: ts << "restore"; break; + case ItemType::Translate: ts << "translate"; break; + case ItemType::Rotate: ts << "rotate"; break; + case ItemType::Scale: ts << "scale"; break; + case ItemType::ConcatenateCTM: ts << "concatentate-ctm"; break; + case ItemType::SetState: ts << "set-state"; break; + case ItemType::SetLineCap: ts << "set-line-cap"; break; + case ItemType::SetLineDash: ts << "set-line-dash"; break; + case ItemType::SetLineJoin: ts << "set-line-join"; break; + case ItemType::SetMiterLimit: ts << "set-miter-limit"; break; + case ItemType::Clip: ts << "clip"; break; + case ItemType::ClipOut: ts << "clip-out"; break; + case ItemType::ClipOutToPath: ts << "clip-out-to-path"; break; + case ItemType::ClipPath: ts << "clip-path"; break; + case ItemType::DrawGlyphs: ts << "draw-glyphs"; break; + case ItemType::DrawImage: ts << "draw-image"; break; + case ItemType::DrawTiledImage: ts << "draw-tiled-image"; break; + case ItemType::DrawTiledScaledImage: ts << "draw-tiled-scaled-image"; break; +#if USE(CG) || USE(CAIRO) + case ItemType::DrawNativeImage: ts << "draw-native-image"; break; +#endif + case ItemType::DrawPattern: ts << "draw-pattern"; break; + case ItemType::DrawRect: ts << "draw-rect"; break; + case ItemType::DrawLine: ts << "draw-line"; break; + case ItemType::DrawLinesForText: ts << "draw-lines-for-text"; break; + case ItemType::DrawLineForDocumentMarker: ts << "draw-lines-for-document-marker"; break; + case ItemType::DrawEllipse: ts << "draw-ellipse"; break; + case ItemType::DrawPath: ts << "draw-path"; break; + case ItemType::DrawFocusRingPath: ts << "draw-focus-ring-path"; break; + case ItemType::DrawFocusRingRects: ts << "draw-focus-ring-rects"; break; + case ItemType::FillRect: ts << "fill-rect"; break; + case ItemType::FillRectWithColor: ts << "fill-rect-with-color"; break; + case ItemType::FillRectWithGradient: ts << "fill-rect-with-gradient"; break; + case ItemType::FillCompositedRect: ts << "fill-composited-rect"; break; + case ItemType::FillRoundedRect: ts << "fill-rounded-rect"; break; + case ItemType::FillRectWithRoundedHole: ts << "fill-rect-with-rounded-hole"; break; + case ItemType::FillPath: ts << "fill-path"; break; + case ItemType::FillEllipse: ts << "fill-ellipse"; break; + case ItemType::StrokeRect: ts << "stroke-rect"; break; + case ItemType::StrokePath: ts << "stroke-path"; break; + case ItemType::StrokeEllipse: ts << "stroke-ellipse"; break; + case ItemType::ClearRect: ts << "clear-rect"; break; + case ItemType::BeginTransparencyLayer: ts << "begin-transparency-layer"; break; + case ItemType::EndTransparencyLayer: ts << "end-transparency-layer"; break; +#if USE(CG) + case ItemType::ApplyStrokePattern: ts << "apply-stroke-pattern"; break; + case ItemType::ApplyFillPattern: ts << "apply-fill-pattern"; break; +#endif + case ItemType::ApplyDeviceScaleFactor: ts << "apply-device-scale-factor"; break; + case ItemType::ClearShadow: ts << "clear-shadow"; break; + } + return ts; +} + +TextStream& operator<<(TextStream& ts, const Item& item) +{ + TextStream::GroupScope group(ts); + ts << item.type(); + + // FIXME: Make a macro which takes a macro for all these enumeration switches + switch (item.type()) { + case ItemType::Save: + ts << downcast<Save>(item); + break; + case ItemType::Translate: + ts << downcast<Translate>(item); + break; + case ItemType::Rotate: + ts << downcast<Rotate>(item); + break; + case ItemType::Scale: + ts << downcast<Scale>(item); + break; + case ItemType::ConcatenateCTM: + ts << downcast<ConcatenateCTM>(item); + break; + case ItemType::SetState: + ts << downcast<SetState>(item); + break; + case ItemType::SetLineCap: + ts << downcast<SetLineCap>(item); + break; + case ItemType::SetLineDash: + ts << downcast<SetLineDash>(item); + break; + case ItemType::SetLineJoin: + ts << downcast<SetLineJoin>(item); + break; + case ItemType::SetMiterLimit: + ts << downcast<SetMiterLimit>(item); + break; + case ItemType::Clip: + ts << downcast<Clip>(item); + break; + case ItemType::ClipOut: + ts << downcast<ClipOut>(item); + break; + case ItemType::ClipOutToPath: + ts << downcast<ClipOutToPath>(item); + break; + case ItemType::ClipPath: + ts << downcast<ClipPath>(item); + break; + case ItemType::DrawGlyphs: + ts << downcast<DrawGlyphs>(item); + break; + case ItemType::DrawImage: + ts << downcast<DrawImage>(item); + break; + case ItemType::DrawTiledImage: + ts << downcast<DrawTiledImage>(item); + break; + case ItemType::DrawTiledScaledImage: + ts << downcast<DrawTiledScaledImage>(item); + break; +#if USE(CG) || USE(CAIRO) + case ItemType::DrawNativeImage: + ts << downcast<DrawNativeImage>(item); + break; +#endif + case ItemType::DrawPattern: + ts << downcast<DrawPattern>(item); + break; + case ItemType::DrawRect: + ts << downcast<DrawRect>(item); + break; + case ItemType::DrawLine: + ts << downcast<DrawLine>(item); + break; + case ItemType::DrawLinesForText: + ts << downcast<DrawLinesForText>(item); + break; + case ItemType::DrawLineForDocumentMarker: + ts << downcast<DrawLineForDocumentMarker>(item); + break; + case ItemType::DrawEllipse: + ts << downcast<DrawEllipse>(item); + break; + case ItemType::DrawPath: + ts << downcast<DrawPath>(item); + break; + case ItemType::DrawFocusRingPath: + ts << downcast<DrawFocusRingPath>(item); + break; + case ItemType::DrawFocusRingRects: + ts << downcast<DrawFocusRingRects>(item); + break; + case ItemType::FillRect: + ts << downcast<FillRect>(item); + break; + case ItemType::FillRectWithColor: + ts << downcast<FillRectWithColor>(item); + break; + case ItemType::FillRectWithGradient: + ts << downcast<FillRectWithGradient>(item); + break; + case ItemType::FillCompositedRect: + ts << downcast<FillCompositedRect>(item); + break; + case ItemType::FillRoundedRect: + ts << downcast<FillRoundedRect>(item); + break; + case ItemType::FillRectWithRoundedHole: + ts << downcast<FillRectWithRoundedHole>(item); + break; + case ItemType::FillPath: + ts << downcast<FillPath>(item); + break; + case ItemType::FillEllipse: + ts << downcast<FillEllipse>(item); + break; + case ItemType::StrokeRect: + ts << downcast<StrokeRect>(item); + break; + case ItemType::StrokePath: + ts << downcast<StrokePath>(item); + break; + case ItemType::StrokeEllipse: + ts << downcast<StrokeEllipse>(item); + break; + case ItemType::ClearRect: + ts << downcast<ClearRect>(item); + break; + case ItemType::BeginTransparencyLayer: + ts << downcast<BeginTransparencyLayer>(item); + break; + case ItemType::ApplyDeviceScaleFactor: + ts << downcast<ApplyDeviceScaleFactor>(item); + break; + + // Items with no additional data. + case ItemType::Restore: + case ItemType::EndTransparencyLayer: +#if USE(CG) + case ItemType::ApplyStrokePattern: + case ItemType::ApplyFillPattern: +#endif + case ItemType::ClearShadow: + break; + } + return ts; +} + +} +} diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListItems.h b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.h new file mode 100644 index 000000000..8158d3d28 --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayListItems.h @@ -0,0 +1,1387 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DisplayListItems_h +#define DisplayListItems_h + +#include "FloatPoint.h" +#include "FloatRect.h" +#include "FloatRoundedRect.h" +#include "Font.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "Image.h" +#include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> + +#if USE(CG) +#include "GraphicsContextPlatformPrivateCG.h" +#endif + +namespace WebCore { + +class TextStream; +struct ImagePaintingOptions; + +namespace DisplayList { + +enum class ItemType { + Save, + Restore, + Translate, + Rotate, + Scale, + ConcatenateCTM, + SetState, + SetLineCap, + SetLineDash, + SetLineJoin, + SetMiterLimit, + ClearShadow, + Clip, + ClipOut, + ClipOutToPath, + ClipPath, + DrawGlyphs, + DrawImage, + DrawTiledImage, + DrawTiledScaledImage, +#if USE(CG) || USE(CAIRO) + DrawNativeImage, +#endif + DrawPattern, + DrawRect, + DrawLine, + DrawLinesForText, + DrawLineForDocumentMarker, + DrawEllipse, + DrawPath, + DrawFocusRingPath, + DrawFocusRingRects, + FillRect, + FillRectWithColor, + FillRectWithGradient, + FillCompositedRect, + FillRoundedRect, + FillRectWithRoundedHole, + FillPath, + FillEllipse, + StrokeRect, + StrokePath, + StrokeEllipse, + ClearRect, + BeginTransparencyLayer, + EndTransparencyLayer, +#if USE(CG) + ApplyStrokePattern, // FIXME: should not be a recorded item. + ApplyFillPattern, // FIXME: should not be a recorded item. +#endif + ApplyDeviceScaleFactor, +}; + +class Item : public RefCounted<Item> { +public: + Item() = delete; + + Item(ItemType type) + : m_type(type) + { + } + + virtual ~Item() { } + + ItemType type() const + { + return m_type; + } + + virtual void apply(GraphicsContext&) const = 0; + + static constexpr bool isDisplayListItem = true; + + virtual bool isDrawingItem() const { return false; } + + // A state item is one preserved by Save/Restore. + bool isStateItem() const + { + return isStateItemType(m_type); + } + + static bool isStateItemType(ItemType itemType) + { + switch (itemType) { + case ItemType:: Translate: + case ItemType:: Rotate: + case ItemType:: Scale: + case ItemType:: ConcatenateCTM: + case ItemType:: SetState: + case ItemType:: SetLineCap: + case ItemType:: SetLineDash: + case ItemType:: SetLineJoin: + case ItemType:: SetMiterLimit: + case ItemType:: ClearShadow: + return true; + default: + return false; + } + return false; + } + +#if !defined(NDEBUG) || !LOG_DISABLED + WTF::CString description() const; +#endif + static size_t sizeInBytes(const Item&); + +private: + ItemType m_type; +}; + +class DrawingItem : public Item { +public: + DrawingItem(ItemType type) + : Item(type) + { + } + + void setExtent(const FloatRect& r) { m_extent = r; } + const FloatRect& extent() const { return m_extent.value(); } + + bool extentKnown() const { return static_cast<bool>(m_extent); } + + // Return bounds of this drawing operation in local coordinates. + // Does not include effets of transform, shadow etc in the state. + virtual std::optional<FloatRect> localBounds(const GraphicsContext&) const { return std::nullopt; } + +private: + bool isDrawingItem() const override { return true; } + + std::optional<FloatRect> m_extent; // In base coordinates, taking shadows and transforms into account. +}; + +class Save : public Item { +public: + static Ref<Save> create() + { + return adoptRef(*new Save); + } + + // Index in the display list of the corresponding Restore item. 0 if unmatched. + size_t restoreIndex() const { return m_restoreIndex; } + void setRestoreIndex(size_t index) { m_restoreIndex = index; } + +private: + Save() + : Item(ItemType::Save) + { + } + + void apply(GraphicsContext&) const override; + + size_t m_restoreIndex { 0 }; +}; + +class Restore : public Item { +public: + static Ref<Restore> create() + { + return adoptRef(*new Restore); + } + +private: + Restore() + : Item(ItemType::Restore) + { + } + + void apply(GraphicsContext&) const override; +}; + +class Translate : public Item { +public: + static Ref<Translate> create(float x, float y) + { + return adoptRef(*new Translate(x, y)); + } + + float x() const { return m_x; } + float y() const { return m_y; } + +private: + Translate(float x, float y) + : Item(ItemType::Translate) + , m_x(x) + , m_y(y) + { + } + + void apply(GraphicsContext&) const override; + + float m_x; + float m_y; +}; + +class Rotate : public Item { +public: + static Ref<Rotate> create(float angleInRadians) + { + return adoptRef(*new Rotate(angleInRadians)); + } + + float angle() const { return m_angle; } + +private: + Rotate(float angle) + : Item(ItemType::Rotate) + , m_angle(angle) + { + } + + void apply(GraphicsContext&) const override; + + float m_angle; // In radians. +}; + +class Scale : public Item { +public: + static Ref<Scale> create(const FloatSize& size) + { + return adoptRef(*new Scale(size)); + } + + const FloatSize& amount() const { return m_size; } + +private: + Scale(const FloatSize& size) + : Item(ItemType::Scale) + , m_size(size) + { + } + + void apply(GraphicsContext&) const override; + + FloatSize m_size; +}; + +class ConcatenateCTM : public Item { +public: + static Ref<ConcatenateCTM> create(const AffineTransform& matrix) + { + return adoptRef(*new ConcatenateCTM(matrix)); + } + + const AffineTransform& transform() const { return m_transform; } + +private: + ConcatenateCTM(const AffineTransform&); + + void apply(GraphicsContext&) const override; + + AffineTransform m_transform; +}; + +class SetState : public Item { +public: + static Ref<SetState> create(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) + { + return adoptRef(*new SetState(state, flags)); + } + + const GraphicsContextStateChange& state() const { return m_state; } + + void accumulate(const GraphicsContextState&, GraphicsContextState::StateChangeFlags); + + void accumulate(GraphicsContextState&) const; + + static void applyState(GraphicsContext&, const GraphicsContextState&, GraphicsContextState::StateChangeFlags); + + static void dumpStateChanges(TextStream&, const GraphicsContextState&, GraphicsContextState::StateChangeFlags); +private: + SetState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) + : Item(ItemType::SetState) + , m_state(state, flags) + { + } + + void apply(GraphicsContext&) const override; + + GraphicsContextStateChange m_state; +}; + +class SetLineCap : public Item { +public: + static Ref<SetLineCap> create(LineCap lineCap) + { + return adoptRef(*new SetLineCap(lineCap)); + } + + LineCap lineCap() const { return m_lineCap; } + +private: + SetLineCap(LineCap lineCap) + : Item(ItemType::SetLineCap) + , m_lineCap(lineCap) + { + } + + void apply(GraphicsContext&) const override; + + LineCap m_lineCap; +}; + +class SetLineDash : public Item { +public: + static Ref<SetLineDash> create(const DashArray& dashArray, float dashOffset) + { + return adoptRef(*new SetLineDash(dashArray, dashOffset)); + } + + const DashArray& dashArray() const { return m_dashArray; } + float dashOffset() const { return m_dashOffset; } + +private: + SetLineDash(const DashArray& dashArray, float dashOffset) + : Item(ItemType::SetLineDash) + , m_dashArray(dashArray) + , m_dashOffset(dashOffset) + { + } + + void apply(GraphicsContext&) const override; + + DashArray m_dashArray; + float m_dashOffset; +}; + +class SetLineJoin : public Item { +public: + static Ref<SetLineJoin> create(LineJoin lineJoin) + { + return adoptRef(*new SetLineJoin(lineJoin)); + } + + LineJoin lineJoin() const { return m_lineJoin; } + +private: + SetLineJoin(LineJoin lineJoin) + : Item(ItemType::SetLineJoin) + , m_lineJoin(lineJoin) + { + } + + void apply(GraphicsContext&) const override; + + LineJoin m_lineJoin; +}; + +class SetMiterLimit : public Item { +public: + static Ref<SetMiterLimit> create(float limit) + { + return adoptRef(*new SetMiterLimit(limit)); + } + + float miterLimit() const { return m_miterLimit; } + +private: + SetMiterLimit(float miterLimit) + : Item(ItemType::SetMiterLimit) + , m_miterLimit(miterLimit) + { + } + + void apply(GraphicsContext&) const override; + + float m_miterLimit; +}; + +class ClearShadow : public Item { +public: + static Ref<ClearShadow> create() + { + return adoptRef(*new ClearShadow); + } + +private: + ClearShadow() + : Item(ItemType::ClearShadow) + { + } + + void apply(GraphicsContext&) const override; +}; + +// FIXME: treat as DrawingItem? +class Clip : public Item { +public: + static Ref<Clip> create(const FloatRect& rect) + { + return adoptRef(*new Clip(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + Clip(const FloatRect& rect) + : Item(ItemType::Clip) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + + FloatRect m_rect; +}; + +class ClipOut : public Item { +public: + static Ref<ClipOut> create(const FloatRect& rect) + { + return adoptRef(*new ClipOut(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + ClipOut(const FloatRect& rect) + : Item(ItemType::ClipOut) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + + FloatRect m_rect; +}; + +class ClipOutToPath : public Item { +public: + static Ref<ClipOutToPath> create(const Path& path) + { + return adoptRef(*new ClipOutToPath(path)); + } + + const Path& path() const { return m_path; } + +private: + ClipOutToPath(const Path& path) + : Item(ItemType::ClipOutToPath) + , m_path(path) + { + } + + void apply(GraphicsContext&) const override; + + const Path m_path; +}; + +class ClipPath : public Item { +public: + static Ref<ClipPath> create(const Path& path, WindRule windRule) + { + return adoptRef(*new ClipPath(path, windRule)); + } + + const Path& path() const { return m_path; } + WindRule windRule() const { return m_windRule; } + +private: + ClipPath(const Path& path, WindRule windRule) + : Item(ItemType::ClipPath) + , m_path(path) + , m_windRule(windRule) + { + } + + void apply(GraphicsContext&) const override; + + const Path m_path; + WindRule m_windRule; +}; + +class DrawGlyphs : public DrawingItem { +public: + static Ref<DrawGlyphs> create(const Font& font, const GlyphBufferGlyph* glyphs, const GlyphBufferAdvance* advances, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode smoothingMode) + { + return adoptRef(*new DrawGlyphs(font, glyphs, advances, count, blockLocation, localAnchor, smoothingMode)); + } + + const FloatPoint& blockLocation() const { return m_blockLocation; } + void setBlockLocation(const FloatPoint& blockLocation) { m_blockLocation = blockLocation; } + + const FloatSize& localAnchor() const { return m_localAnchor; } + + FloatPoint anchorPoint() const { return m_blockLocation + m_localAnchor; } + + const Vector<GlyphBufferGlyph, 128>& glyphs() const { return m_glyphs; } + +private: + DrawGlyphs(const Font&, const GlyphBufferGlyph*, const GlyphBufferAdvance*, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode); + + void computeBounds(); + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + GlyphBuffer generateGlyphBuffer() const; + + Ref<Font> m_font; + Vector<GlyphBufferGlyph, 128> m_glyphs; + Vector<GlyphBufferAdvance, 128> m_advances; + FloatRect m_bounds; + FloatPoint m_blockLocation; + FloatSize m_localAnchor; + FontSmoothingMode m_smoothingMode; +}; + +class DrawImage : public DrawingItem { +public: + static Ref<DrawImage> create(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) + { + return adoptRef(*new DrawImage(image, destination, source, imagePaintingOptions)); + } + + const Image& image() const { return m_image.get(); } + FloatRect source() const { return m_source; } + FloatRect destination() const { return m_destination; } + +private: + DrawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&); + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; } + + mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable. + FloatRect m_destination; + FloatRect m_source; + ImagePaintingOptions m_imagePaintingOptions; +}; + +class DrawTiledImage : public DrawingItem { +public: + static Ref<DrawTiledImage> create(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) + { + return adoptRef(*new DrawTiledImage(image, destination, source, tileSize, spacing, imagePaintingOptions)); + } + + const Image& image() const { return m_image.get(); } + FloatPoint source() const { return m_source; } + FloatRect destination() const { return m_destination; } + + FloatSize tileSize() const { return m_tileSize; } + FloatSize spacing() const { return m_spacing; } + +private: + DrawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&); + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; } + + mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable. + FloatRect m_destination; + FloatPoint m_source; + FloatSize m_tileSize; + FloatSize m_spacing; + ImagePaintingOptions m_imagePaintingOptions; +}; + +class DrawTiledScaledImage : public DrawingItem { +public: + static Ref<DrawTiledScaledImage> create(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) + { + return adoptRef(*new DrawTiledScaledImage(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions)); + } + + const Image& image() const { return m_image.get(); } + FloatRect source() const { return m_source; } + FloatRect destination() const { return m_destination; } + +private: + DrawTiledScaledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&); + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; } + + mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable. + FloatRect m_destination; + FloatRect m_source; + FloatSize m_tileScaleFactor; + Image::TileRule m_hRule; + Image::TileRule m_vRule; + ImagePaintingOptions m_imagePaintingOptions; +}; + +#if USE(CG) || USE(CAIRO) +class DrawNativeImage : public DrawingItem { +public: + static Ref<DrawNativeImage> create(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation) + { + return adoptRef(*new DrawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation)); + } + + FloatRect source() const { return m_srcRect; } + FloatRect destination() const { return m_destination; } + +private: + DrawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation); + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; } + +#if USE(CG) + RetainPtr<CGImageRef> m_image; +#endif + FloatSize m_imageSize; + FloatRect m_destination; + FloatRect m_srcRect; +#if USE(CG) + CompositeOperator m_op; + BlendMode m_blendMode; +#endif + ImageOrientation m_orientation; +}; +#endif + +class DrawPattern : public DrawingItem { +public: + static Ref<DrawPattern> create(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) + { + return adoptRef(*new DrawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode)); + } + + const Image& image() const { return m_image.get(); } + const AffineTransform& patternTransform() const { return m_patternTransform; } + FloatRect tileRect() const { return m_tileRect; } + FloatRect destRect() const { return m_destination; } + FloatPoint phase() const { return m_phase; } + FloatSize spacing() const { return m_spacing; } + +private: + DrawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal); + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; } + + mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable. + AffineTransform m_patternTransform; + FloatRect m_tileRect; + FloatRect m_destination; + FloatPoint m_phase; + FloatSize m_spacing; + CompositeOperator m_op; + BlendMode m_blendMode; +}; + +// Is DrawingItem because the size of the transparency layer is implicitly the clip bounds. +class BeginTransparencyLayer : public DrawingItem { +public: + static Ref<BeginTransparencyLayer> create(float opacity) + { + return adoptRef(*new BeginTransparencyLayer(opacity)); + } + + float opacity() const { return m_opacity; } + +private: + BeginTransparencyLayer(float opacity) + : DrawingItem(ItemType::BeginTransparencyLayer) + , m_opacity(opacity) + { + } + + void apply(GraphicsContext&) const override; + + float m_opacity; +}; + +class EndTransparencyLayer : public DrawingItem { +public: + static Ref<EndTransparencyLayer> create() + { + return adoptRef(*new EndTransparencyLayer); + } + +private: + EndTransparencyLayer() + : DrawingItem(ItemType::EndTransparencyLayer) + { + } + + void apply(GraphicsContext&) const override; +}; + +class DrawRect : public DrawingItem { +public: + static Ref<DrawRect> create(const FloatRect& rect, float borderThickness) + { + return adoptRef(*new DrawRect(rect, borderThickness)); + } + + FloatRect rect() const { return m_rect; } + float borderThickness() const { return m_borderThickness; } + +private: + DrawRect(const FloatRect& rect, float borderThickness) + : DrawingItem(ItemType::DrawRect) + , m_rect(rect) + , m_borderThickness(borderThickness) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; + float m_borderThickness; +}; + +class DrawLine : public DrawingItem { +public: + static Ref<DrawLine> create(const FloatPoint& point1, const FloatPoint& point2) + { + return adoptRef(*new DrawLine(point1, point2)); + } + + FloatPoint point1() const { return m_point1; } + FloatPoint point2() const { return m_point2; } + +private: + DrawLine(const FloatPoint& point1, const FloatPoint& point2) + : DrawingItem(ItemType::DrawLine) + , m_point1(point1) + , m_point2(point2) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + FloatPoint m_point1; + FloatPoint m_point2; +}; + +class DrawLinesForText : public DrawingItem { +public: + static Ref<DrawLinesForText> create(const FloatPoint& blockLocation, const FloatSize& localAnchor, const DashArray& widths, bool printing, bool doubleLines, float strokeWidth) + { + return adoptRef(*new DrawLinesForText(blockLocation, localAnchor, widths, printing, doubleLines, strokeWidth)); + } + + void setBlockLocation(const FloatPoint& blockLocation) { m_blockLocation = blockLocation; } + const FloatPoint& blockLocation() const { return m_blockLocation; } + const FloatSize& localAnchor() const { return m_localAnchor; } + FloatPoint point() const { return m_blockLocation + m_localAnchor; } + const DashArray& widths() const { return m_widths; } + bool isPrinting() const { return m_printing; } + bool doubleLines() const { return m_doubleLines; } + +private: + DrawLinesForText(const FloatPoint& blockLocation, const FloatSize& localAnchor, const DashArray& widths, bool printing, bool doubleLines, float strokeWidth) + : DrawingItem(ItemType::DrawLinesForText) + , m_blockLocation(blockLocation) + , m_localAnchor(localAnchor) + , m_widths(widths) + , m_strokeWidth(strokeWidth) + , m_printing(printing) + , m_doubleLines(doubleLines) + { + } + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + FloatPoint m_blockLocation; + FloatSize m_localAnchor; + DashArray m_widths; + float m_strokeWidth; + bool m_printing; + bool m_doubleLines; +}; + +class DrawLineForDocumentMarker : public DrawingItem { +public: + static Ref<DrawLineForDocumentMarker> create(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style) + { + return adoptRef(*new DrawLineForDocumentMarker(point, width, style)); + } + + FloatPoint point() const { return m_point; } + float width() const { return m_width; } + +private: + DrawLineForDocumentMarker(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style) + : DrawingItem(ItemType::DrawLineForDocumentMarker) + , m_point(point) + , m_width(width) + , m_style(style) + { + } + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + FloatPoint m_point; + float m_width; + GraphicsContext::DocumentMarkerLineStyle m_style; +}; + +class DrawEllipse : public DrawingItem { +public: + static Ref<DrawEllipse> create(const FloatRect& rect) + { + return adoptRef(*new DrawEllipse(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + DrawEllipse(const FloatRect& rect) + : DrawingItem(ItemType::DrawEllipse) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; +}; + +class DrawPath : public DrawingItem { +public: + static Ref<DrawPath> create(const Path& path) + { + return adoptRef(*new DrawPath(path)); + } + + const Path& path() const { return m_path; } + +private: + DrawPath(const Path& path) + : DrawingItem(ItemType::DrawPath) + , m_path(path) + { + } + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_path.fastBoundingRect(); } + + const Path m_path; +}; + +class DrawFocusRingPath : public DrawingItem { +public: + static Ref<DrawFocusRingPath> create(const Path& path, int width, int offset, const Color& color) + { + return adoptRef(*new DrawFocusRingPath(path, width, offset, color)); + } + + const Path& path() const { return m_path; } + int width() const { return m_width; } + int offset() const { return m_offset; } + const Color& color() const { return m_color; } + +private: + DrawFocusRingPath(const Path& path, int width, int offset, const Color& color) + : DrawingItem(ItemType::DrawFocusRingPath) + , m_path(path) + , m_width(width) + , m_offset(offset) + , m_color(color) + { + } + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + const Path m_path; + int m_width; + int m_offset; + Color m_color; +}; + +class DrawFocusRingRects : public DrawingItem { +public: + static Ref<DrawFocusRingRects> create(const Vector<FloatRect>& rects, int width, int offset, const Color& color) + { + return adoptRef(*new DrawFocusRingRects(rects, width, offset, color)); + } + + const Vector<FloatRect> rects() const { return m_rects; } + int width() const { return m_width; } + int offset() const { return m_offset; } + const Color& color() const { return m_color; } + +private: + DrawFocusRingRects(const Vector<FloatRect>& rects, int width, int offset, const Color& color) + : DrawingItem(ItemType::DrawFocusRingRects) + , m_rects(rects) + , m_width(width) + , m_offset(offset) + , m_color(color) + { + } + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + Vector<FloatRect> m_rects; + int m_width; + int m_offset; + Color m_color; +}; + +class FillRect : public DrawingItem { +public: + static Ref<FillRect> create(const FloatRect& rect) + { + return adoptRef(*new FillRect(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + FillRect(const FloatRect& rect) + : DrawingItem(ItemType::FillRect) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; +}; + +// FIXME: Make these inherit from FillRect proper. +class FillRectWithColor : public DrawingItem { +public: + static Ref<FillRectWithColor> create(const FloatRect& rect, const Color& color) + { + return adoptRef(*new FillRectWithColor(rect, color)); + } + + FloatRect rect() const { return m_rect; } + const Color& color() const { return m_color; } + +private: + FillRectWithColor(const FloatRect& rect, const Color& color) + : DrawingItem(ItemType::FillRectWithColor) + , m_rect(rect) + , m_color(color) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; + Color m_color; +}; + +class FillRectWithGradient : public DrawingItem { +public: + static Ref<FillRectWithGradient> create(const FloatRect& rect, Gradient& gradient) + { + return adoptRef(*new FillRectWithGradient(rect, gradient)); + } + + FloatRect rect() const { return m_rect; } + +private: + FillRectWithGradient(const FloatRect& rect, Gradient& gradient) + : DrawingItem(ItemType::FillRectWithGradient) + , m_rect(rect) + , m_gradient(gradient) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; + mutable Ref<Gradient> m_gradient; // FIXME: Make this not mutable +}; + +class FillCompositedRect : public DrawingItem { +public: + static Ref<FillCompositedRect> create(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode) + { + return adoptRef(*new FillCompositedRect(rect, color, op, blendMode)); + } + + FloatRect rect() const { return m_rect; } + const Color& color() const { return m_color; } + CompositeOperator compositeOperator() const { return m_op; } + BlendMode blendMode() const { return m_blendMode; } + +private: + FillCompositedRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode) + : DrawingItem(ItemType::FillCompositedRect) + , m_rect(rect) + , m_color(color) + , m_op(op) + , m_blendMode(blendMode) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; + Color m_color; + CompositeOperator m_op; + BlendMode m_blendMode; +}; + +class FillRoundedRect : public DrawingItem { +public: + static Ref<FillRoundedRect> create(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode) + { + return adoptRef(*new FillRoundedRect(rect, color, blendMode)); + } + + const FloatRoundedRect& roundedRect() const { return m_rect; } + const Color& color() const { return m_color; } + BlendMode blendMode() const { return m_blendMode; } + +private: + FillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode) + : DrawingItem(ItemType::FillRoundedRect) + , m_rect(rect) + , m_color(color) + , m_blendMode(blendMode) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect.rect(); } + + FloatRoundedRect m_rect; + Color m_color; + BlendMode m_blendMode; +}; + +class FillRectWithRoundedHole : public DrawingItem { +public: + static Ref<FillRectWithRoundedHole> create(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color) + { + return adoptRef(*new FillRectWithRoundedHole(rect, roundedHoleRect, color)); + } + + const FloatRect& rect() const { return m_rect; } + const FloatRoundedRect& roundedHoleRect() const { return m_roundedHoleRect; } + const Color& color() const { return m_color; } + +private: + FillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color) + : DrawingItem(ItemType::FillRectWithRoundedHole) + , m_rect(rect) + , m_roundedHoleRect(roundedHoleRect) + , m_color(color) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; + FloatRoundedRect m_roundedHoleRect; + Color m_color; +}; + +class FillPath : public DrawingItem { +public: + static Ref<FillPath> create(const Path& path) + { + return adoptRef(*new FillPath(path)); + } + + const Path& path() const { return m_path; } + +private: + FillPath(const Path& path) + : DrawingItem(ItemType::FillPath) + , m_path(path) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_path.fastBoundingRect(); } + + const Path m_path; +}; + +class FillEllipse : public DrawingItem { +public: + static Ref<FillEllipse> create(const FloatRect& rect) + { + return adoptRef(*new FillEllipse(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + FillEllipse(const FloatRect& rect) + : DrawingItem(ItemType::FillEllipse) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; +}; + +class StrokeRect : public DrawingItem { +public: + static Ref<StrokeRect> create(const FloatRect& rect, float lineWidth) + { + return adoptRef(*new StrokeRect(rect, lineWidth)); + } + + FloatRect rect() const { return m_rect; } + float lineWidth() const { return m_lineWidth; } + +private: + StrokeRect(const FloatRect& rect, float lineWidth) + : DrawingItem(ItemType::StrokeRect) + , m_rect(rect) + , m_lineWidth(lineWidth) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + FloatRect m_rect; + float m_lineWidth; +}; + +class StrokePath : public DrawingItem { +public: + static Ref<StrokePath> create(const Path& path) + { + return adoptRef(*new StrokePath(path)); + } + + const Path& path() const { return m_path; } + +private: + StrokePath(const Path& path) + : DrawingItem(ItemType::StrokePath) + , m_path(path) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + const Path m_path; + FloatPoint m_blockLocation; +}; + +class StrokeEllipse : public DrawingItem { +public: + static Ref<StrokeEllipse> create(const FloatRect& rect) + { + return adoptRef(*new StrokeEllipse(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + StrokeEllipse(const FloatRect& rect) + : DrawingItem(ItemType::StrokeEllipse) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override; + + FloatRect m_rect; +}; + +class ClearRect : public DrawingItem { +public: + static Ref<ClearRect> create(const FloatRect& rect) + { + return adoptRef(*new ClearRect(rect)); + } + + FloatRect rect() const { return m_rect; } + +private: + ClearRect(const FloatRect& rect) + : DrawingItem(ItemType::ClearRect) + , m_rect(rect) + { + } + + void apply(GraphicsContext&) const override; + std::optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; } + + FloatRect m_rect; +}; + +#if USE(CG) +class ApplyStrokePattern : public Item { +public: + static Ref<ApplyStrokePattern> create() + { + return adoptRef(*new ApplyStrokePattern); + } + +private: + ApplyStrokePattern() + : Item(ItemType::ApplyStrokePattern) + { + } + + void apply(GraphicsContext&) const override; +}; + +class ApplyFillPattern : public Item { +public: + static Ref<ApplyFillPattern> create() + { + return adoptRef(*new ApplyFillPattern); + } + +private: + ApplyFillPattern() + : Item(ItemType::ApplyFillPattern) + { + } + + void apply(GraphicsContext&) const override; +}; +#endif + +class ApplyDeviceScaleFactor : public Item { +public: + static Ref<ApplyDeviceScaleFactor> create(float scaleFactor) + { + return adoptRef(*new ApplyDeviceScaleFactor(scaleFactor)); + } + + float scaleFactor() const { return m_scaleFactor; } + +private: + ApplyDeviceScaleFactor(float scaleFactor) + : Item(ItemType::ApplyDeviceScaleFactor) + , m_scaleFactor(scaleFactor) + { + } + + void apply(GraphicsContext&) const override; + + float m_scaleFactor; +}; + +TextStream& operator<<(TextStream&, const Item&); + +} // namespace DisplayList +} // namespace WebCore + + +#define SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_DRAWINGITEM(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DisplayList::ToValueTypeName) \ + static bool isType(const WebCore::DisplayList::Item& object) { return object.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() + +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_DRAWINGITEM(DrawingItem, isDrawingItem()) + +#define SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ToValueTypeName) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DisplayList::ToValueTypeName) \ + static bool isType(const WebCore::DisplayList::Item& item) { return item.type() == WebCore::DisplayList::ItemType::ToValueTypeName; } \ +SPECIALIZE_TYPE_TRAITS_END() + +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Save) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Restore) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Translate) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Rotate) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Scale) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ConcatenateCTM) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetState) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineCap) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineDash) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineJoin) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetMiterLimit) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Clip) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipOut) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipOutToPath) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipPath) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawGlyphs) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawImage) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawTiledImage) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawTiledScaledImage) +#if USE(CG) || USE(CAIRO) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawNativeImage) +#endif +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawPattern) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawRect) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLine) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLinesForText) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLineForDocumentMarker) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawEllipse) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawPath) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawFocusRingPath) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawFocusRingRects) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRect) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithColor) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithGradient) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillCompositedRect) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRoundedRect) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithRoundedHole) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillPath) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillEllipse) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokeRect) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokePath) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokeEllipse) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClearRect) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(BeginTransparencyLayer) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(EndTransparencyLayer) +#if USE(CG) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyStrokePattern) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyFillPattern) +#endif +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyDeviceScaleFactor) +SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClearShadow) + +#endif // DisplayListItems_h diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp new file mode 100644 index 000000000..6d6853345 --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DisplayListRecorder.h" + +#include "DisplayList.h" +#include "DisplayListItems.h" +#include "GraphicsContext.h" +#include "Logging.h" +#include "TextStream.h" +#include <wtf/MathExtras.h> + +namespace WebCore { +namespace DisplayList { + +Recorder::Recorder(GraphicsContext& context, DisplayList& displayList, const FloatRect& initialClip, const AffineTransform& baseCTM) + : m_graphicsContext(context) + , m_displayList(displayList) +{ + LOG_WITH_STREAM(DisplayLists, stream << "\nRecording with clip " << initialClip); + m_graphicsContext.setDisplayListRecorder(this); + m_stateStack.append(ContextState(baseCTM, initialClip)); +} + +Recorder::~Recorder() +{ + ASSERT(m_stateStack.size() == 1); // If this fires, it indicates mismatched save/restore. + LOG(DisplayLists, "Recorded display list:\n%s", m_displayList.description().data()); +} + +void Recorder::willAppendItem(const Item& item) +{ + if (item.isDrawingItem() +#if USE(CG) + || item.type() == ItemType::ApplyStrokePattern || item.type() == ItemType::ApplyStrokePattern +#endif + ) { + GraphicsContextStateChange& stateChanges = currentState().stateChange; + GraphicsContextState::StateChangeFlags changesFromLastState = stateChanges.changesFromState(currentState().lastDrawingState); + if (changesFromLastState) { + LOG_WITH_STREAM(DisplayLists, stream << "pre-drawing, saving state " << GraphicsContextStateChange(stateChanges.m_state, changesFromLastState)); + m_displayList.append(SetState::create(stateChanges.m_state, changesFromLastState)); + stateChanges.m_changeFlags = 0; + currentState().lastDrawingState = stateChanges.m_state; + } + currentState().wasUsedForDrawing = true; + } +} + +void Recorder::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) +{ + currentState().stateChange.accumulate(state, flags); +} + +void Recorder::clearShadow() +{ + appendItem(ClearShadow::create()); +} + +void Recorder::setLineCap(LineCap lineCap) +{ + appendItem(SetLineCap::create(lineCap)); +} + +void Recorder::setLineDash(const DashArray& dashArray, float dashOffset) +{ + appendItem(SetLineDash::create(dashArray, dashOffset)); +} + +void Recorder::setLineJoin(LineJoin lineJoin) +{ + appendItem(SetLineJoin::create(lineJoin)); +} + +void Recorder::setMiterLimit(float miterLimit) +{ + appendItem(SetMiterLimit::create(miterLimit)); +} + +void Recorder::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& startPoint, FontSmoothingMode smoothingMode) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawGlyphs::create(font, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs, FloatPoint(), toFloatSize(startPoint), smoothingMode))); + updateItemExtent(newItem); +} + +void Recorder::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawImage::create(image, destination, source, imagePaintingOptions))); + updateItemExtent(newItem); +} + +void Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledImage::create(image, destination, source, tileSize, spacing, imagePaintingOptions))); + updateItemExtent(newItem); +} + +#if USE(CG) || USE(CAIRO) +void Recorder::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawNativeImage::create(image, imageSize, destRect, srcRect, op, blendMode, orientation))); + updateItemExtent(newItem); +} +#endif + +void Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledScaledImage::create(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions))); + updateItemExtent(newItem); +} + +void Recorder::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPattern::create(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode))); + updateItemExtent(newItem); +} + +void Recorder::save() +{ + appendItem(Save::create()); + m_stateStack.append(m_stateStack.last().cloneForSave(m_displayList.itemCount() - 1)); +} + +void Recorder::restore() +{ + if (!m_stateStack.size()) + return; + + bool stateUsedForDrawing = currentState().wasUsedForDrawing; + size_t saveIndex = currentState().saveItemIndex; + + m_stateStack.removeLast(); + // Have to avoid eliding nested Save/Restore when a descendant state contains drawing items. + currentState().wasUsedForDrawing |= stateUsedForDrawing; + + if (!stateUsedForDrawing && saveIndex) { + // This Save/Restore didn't contain any drawing items. Roll back to just before the last save. + m_displayList.removeItemsFromIndex(saveIndex); + return; + } + + appendItem(Restore::create()); + + if (saveIndex) { + Save& saveItem = downcast<Save>(m_displayList.itemAt(saveIndex)); + saveItem.setRestoreIndex(m_displayList.itemCount() - 1); + } +} + +void Recorder::translate(float x, float y) +{ + currentState().translate(x, y); + appendItem(Translate::create(x, y)); +} + +void Recorder::rotate(float angleInRadians) +{ + currentState().rotate(angleInRadians); + appendItem(Rotate::create(angleInRadians)); +} + +void Recorder::scale(const FloatSize& size) +{ + currentState().scale(size); + appendItem(Scale::create(size)); +} + +void Recorder::concatCTM(const AffineTransform& transform) +{ + currentState().concatCTM(transform); + appendItem(ConcatenateCTM::create(transform)); +} + +void Recorder::beginTransparencyLayer(float opacity) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(BeginTransparencyLayer::create(opacity))); + updateItemExtent(newItem); +} + +void Recorder::endTransparencyLayer() +{ + appendItem(EndTransparencyLayer::create()); +} + +void Recorder::drawRect(const FloatRect& rect, float borderThickness) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawRect::create(rect, borderThickness))); + updateItemExtent(newItem); +} + +void Recorder::drawLine(const FloatPoint& point1, const FloatPoint& point2) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLine::create(point1, point2))); + updateItemExtent(newItem); +} + +void Recorder::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, float strokeThickness) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLinesForText::create(FloatPoint(), toFloatSize(point), widths, printing, doubleLines, strokeThickness))); + updateItemExtent(newItem); +} + +void Recorder::drawLineForDocumentMarker(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLineForDocumentMarker::create(point, width, style))); + updateItemExtent(newItem); +} + +void Recorder::drawEllipse(const FloatRect& rect) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawEllipse::create(rect))); + updateItemExtent(newItem); +} + +void Recorder::drawPath(const Path& path) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPath::create(path))); + updateItemExtent(newItem); +} + +void Recorder::drawFocusRing(const Path& path, int width, int offset, const Color& color) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingPath::create(path, width, offset, color))); + updateItemExtent(newItem); +} + +void Recorder::drawFocusRing(const Vector<FloatRect>& rects, int width, int offset, const Color& color) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingRects::create(rects, width, offset, color))); + updateItemExtent(newItem); +} + +void Recorder::fillRect(const FloatRect& rect) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRect::create(rect))); + updateItemExtent(newItem); +} + +void Recorder::fillRect(const FloatRect& rect, const Color& color) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithColor::create(rect, color))); + updateItemExtent(newItem); +} + +void Recorder::fillRect(const FloatRect& rect, Gradient& gradient) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithGradient::create(rect, gradient))); + updateItemExtent(newItem); +} + +void Recorder::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillCompositedRect::create(rect, color, op, blendMode))); + updateItemExtent(newItem); +} + +void Recorder::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRoundedRect::create(rect, color, blendMode))); + updateItemExtent(newItem); +} + +void Recorder::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithRoundedHole::create(rect, roundedHoleRect, color))); + updateItemExtent(newItem); +} + +void Recorder::fillPath(const Path& path) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillPath::create(path))); + updateItemExtent(newItem); +} + +void Recorder::fillEllipse(const FloatRect& rect) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillEllipse::create(rect))); + updateItemExtent(newItem); +} + +void Recorder::strokeRect(const FloatRect& rect, float lineWidth) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeRect::create(rect, lineWidth))); + updateItemExtent(newItem); +} + +void Recorder::strokePath(const Path& path) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokePath::create(path))); + updateItemExtent(newItem); +} + +void Recorder::strokeEllipse(const FloatRect& rect) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeEllipse::create(rect))); + updateItemExtent(newItem); +} + +void Recorder::clearRect(const FloatRect& rect) +{ + DrawingItem& newItem = downcast<DrawingItem>(appendItem(ClearRect::create(rect))); + updateItemExtent(newItem); +} + +#if USE(CG) +void Recorder::applyStrokePattern() +{ + appendItem(ApplyStrokePattern::create()); +} + +void Recorder::applyFillPattern() +{ + appendItem(ApplyFillPattern::create()); +} +#endif + +void Recorder::clip(const FloatRect& rect) +{ + currentState().clipBounds.intersect(rect); + appendItem(Clip::create(rect)); +} + +void Recorder::clipOut(const FloatRect& rect) +{ + appendItem(ClipOut::create(rect)); +} + +void Recorder::clipOut(const Path& path) +{ + appendItem(ClipOutToPath::create(path)); +} + +void Recorder::clipPath(const Path& path, WindRule windRule) +{ + currentState().clipBounds.intersect(path.fastBoundingRect()); + appendItem(ClipPath::create(path, windRule)); +} + +void Recorder::applyDeviceScaleFactor(float deviceScaleFactor) +{ + // FIXME: this changes the baseCTM, which will invalidate all of our cached extents. + // Assert that it's only called early on? + appendItem(ApplyDeviceScaleFactor::create(deviceScaleFactor)); +} + +Item& Recorder::appendItem(Ref<Item>&& item) +{ + willAppendItem(item.get()); + return m_displayList.append(WTFMove(item)); +} + +void Recorder::updateItemExtent(DrawingItem& item) const +{ + if (std::optional<FloatRect> rect = item.localBounds(m_graphicsContext)) + item.setExtent(extentFromLocalBounds(rect.value())); +} + +// FIXME: share with ShadowData +static inline float shadowPaintingExtent(float blurRadius) +{ + // Blurring uses a Gaussian function whose std. deviation is m_radius/2, and which in theory + // extends to infinity. In 8-bit contexts, however, rounding causes the effect to become + // undetectable at around 1.4x the radius. + const float radiusExtentMultiplier = 1.4; + return ceilf(blurRadius * radiusExtentMultiplier); +} + +FloatRect Recorder::extentFromLocalBounds(const FloatRect& rect) const +{ + FloatRect bounds = rect; + const ContextState& state = currentState(); + + FloatSize shadowOffset; + float shadowRadius; + Color shadowColor; + if (m_graphicsContext.getShadow(shadowOffset, shadowRadius, shadowColor)) { + FloatRect shadowExtent= bounds; + shadowExtent.move(shadowOffset); + shadowExtent.inflate(shadowPaintingExtent(shadowRadius)); + bounds.unite(shadowExtent); + } + + FloatRect clippedExtent = intersection(state.clipBounds, bounds); + return state.ctm.mapRect(clippedExtent); +} + +const Recorder::ContextState& Recorder::currentState() const +{ + ASSERT(m_stateStack.size()); + return m_stateStack.last(); +} + +Recorder::ContextState& Recorder::currentState() +{ + ASSERT(m_stateStack.size()); + return m_stateStack.last(); +} + +const AffineTransform& Recorder::ctm() const +{ + return currentState().ctm; +} + +const FloatRect& Recorder::clipBounds() const +{ + return currentState().clipBounds; +} + +void Recorder::ContextState::translate(float x, float y) +{ + ctm.translate(x, y); + clipBounds.move(-x, -y); +} + +void Recorder::ContextState::rotate(float angleInRadians) +{ + double angleInDegrees = rad2deg(static_cast<double>(angleInRadians)); + ctm.rotate(angleInDegrees); + + AffineTransform rotation; + rotation.rotate(angleInDegrees); + + if (std::optional<AffineTransform> inverse = rotation.inverse()) + clipBounds = inverse.value().mapRect(clipBounds); +} + +void Recorder::ContextState::scale(const FloatSize& size) +{ + ctm.scale(size); + clipBounds.scale(1 / size.width(), 1 / size.height()); +} + +void Recorder::ContextState::concatCTM(const AffineTransform& matrix) +{ + ctm *= matrix; + + if (std::optional<AffineTransform> inverse = matrix.inverse()) + clipBounds = inverse.value().mapRect(clipBounds); +} + +} // namespace DisplayList +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h new file mode 100644 index 000000000..95c626fd6 --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayListRecorder.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DisplayListRecorder_h +#define DisplayListRecorder_h + +#include "DisplayList.h" +#include "GraphicsContext.h" // For InterpolationQuality. +#include "Image.h" // For Image::TileRule. +#include "TextFlags.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class FloatPoint; +class FloatRect; +class GlyphBuffer; +class FloatPoint; +class Font; +class Image; + +struct GraphicsContextState; +struct ImagePaintingOptions; + +namespace DisplayList { + +class DrawingItem; + +class Recorder { + WTF_MAKE_NONCOPYABLE(Recorder); +public: + Recorder(GraphicsContext&, DisplayList&, const FloatRect& initialClip, const AffineTransform&); + ~Recorder(); + + void updateState(const GraphicsContextState&, GraphicsContextState::StateChangeFlags); + void clearShadow(); + + void setLineCap(LineCap); + void setLineDash(const DashArray&, float dashOffset); + void setLineJoin(LineJoin); + void setMiterLimit(float); + + void fillRect(const FloatRect&); + void fillRect(const FloatRect&, const Color&); + void fillRect(const FloatRect&, Gradient&); + void fillRect(const FloatRect&, const Color&, CompositeOperator, BlendMode); + void fillRoundedRect(const FloatRoundedRect&, const Color&, BlendMode); + void fillRectWithRoundedHole(const FloatRect&, const FloatRoundedRect& roundedHoleRect, const Color&); + void fillPath(const Path&); + void fillEllipse(const FloatRect&); + void strokeRect(const FloatRect&, float lineWidth); + void strokePath(const Path&); + void strokeEllipse(const FloatRect&); + void clearRect(const FloatRect&); + +#if USE(CG) + void applyStrokePattern(); + void applyFillPattern(); +#endif + + void drawGlyphs(const Font&, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint& anchorPoint, FontSmoothingMode); + + void drawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&); + void drawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&); + void drawTiledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&); +#if USE(CG) || USE(CAIRO) + void drawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation); +#endif + void drawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal); + + void drawRect(const FloatRect&, float borderThickness); + void drawLine(const FloatPoint&, const FloatPoint&); + void drawLinesForText(const FloatPoint&, const DashArray& widths, bool printing, bool doubleLines, float strokeThickness); + void drawLineForDocumentMarker(const FloatPoint&, float width, GraphicsContext::DocumentMarkerLineStyle); + void drawEllipse(const FloatRect&); + void drawPath(const Path&); + + void drawFocusRing(const Path&, int width, int offset, const Color&); + void drawFocusRing(const Vector<FloatRect>&, int width, int offset, const Color&); + + void save(); + void restore(); + + void translate(float x, float y); + void rotate(float angleInRadians); + void scale(const FloatSize&); + void concatCTM(const AffineTransform&); + + void beginTransparencyLayer(float opacity); + void endTransparencyLayer(); + + void clip(const FloatRect&); + void clipOut(const FloatRect&); + void clipOut(const Path&); + void clipPath(const Path&, WindRule); + + void applyDeviceScaleFactor(float); + + size_t itemCount() const { return m_displayList.itemCount(); } + +private: + Item& appendItem(Ref<Item>&&); + void willAppendItem(const Item&); + + FloatRect extentFromLocalBounds(const FloatRect&) const; + void updateItemExtent(DrawingItem&) const; + + const AffineTransform& ctm() const; + const FloatRect& clipBounds() const; + + struct ContextState { + AffineTransform ctm; + FloatRect clipBounds; + GraphicsContextStateChange stateChange; + GraphicsContextState lastDrawingState; + bool wasUsedForDrawing { false }; + size_t saveItemIndex { 0 }; + + ContextState(const AffineTransform& transform, const FloatRect& clip) + : ctm(transform) + , clipBounds(clip) + { + } + + ContextState cloneForSave(size_t saveIndex) const + { + ContextState state(ctm, clipBounds); + state.stateChange = stateChange; + state.lastDrawingState = lastDrawingState; + state.saveItemIndex = saveIndex; + return state; + } + + void translate(float x, float y); + void rotate(float angleInRadians); + void scale(const FloatSize&); + void concatCTM(const AffineTransform&); + }; + + const ContextState& currentState() const; + ContextState& currentState(); + + GraphicsContext& m_graphicsContext; + DisplayList& m_displayList; + + Vector<ContextState, 32> m_stateStack; +}; + +} +} + +#endif // DisplayListRecorder_h diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp new file mode 100644 index 000000000..8cc915403 --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DisplayListReplayer.h" + +#include "DisplayListItems.h" +#include "GraphicsContext.h" +#include "Logging.h" +#include "TextStream.h" + +namespace WebCore { +namespace DisplayList { + +Replayer::Replayer(GraphicsContext& context, const DisplayList& displayList) + : m_displayList(displayList) + , m_context(context) +{ +} + +Replayer::~Replayer() +{ +} + +std::unique_ptr<DisplayList> Replayer::replay(const FloatRect& initialClip, bool trackReplayList) +{ + LOG_WITH_STREAM(DisplayLists, stream << "\nReplaying with clip " << initialClip); + UNUSED_PARAM(initialClip); + + std::unique_ptr<DisplayList> replayList; + if (UNLIKELY(trackReplayList)) + replayList = std::make_unique<DisplayList>(); + + size_t numItems = m_displayList.itemCount(); + for (size_t i = 0; i < numItems; ++i) { + auto& item = m_displayList.list()[i].get(); + + if (is<DrawingItem>(item)) { + const DrawingItem& drawingItem = downcast<DrawingItem>(item); + if (drawingItem.extentKnown() && !drawingItem.extent().intersects(initialClip)) { + LOG_WITH_STREAM(DisplayLists, stream << "skipping " << i << " " << item); + continue; + } + } + + LOG_WITH_STREAM(DisplayLists, stream << "applying " << i << " " << item); + item.apply(m_context); + + if (UNLIKELY(trackReplayList)) + replayList->appendItem(const_cast<Item&>(item)); + } + + return replayList; +} + +} // namespace DisplayList +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h new file mode 100644 index 000000000..136c15ba3 --- /dev/null +++ b/Source/WebCore/platform/graphics/displaylists/DisplayListReplayer.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DisplayListReplayer_h +#define DisplayListReplayer_h + +#include "DisplayList.h" +#include <wtf/Noncopyable.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class FloatRect; +class GraphicsContext; + +namespace DisplayList { + +class Replayer { + WTF_MAKE_NONCOPYABLE(Replayer); +public: + Replayer(GraphicsContext&, const DisplayList&); + ~Replayer(); + + std::unique_ptr<DisplayList> replay(const FloatRect& initialClip, bool trackReplayList = false); + +private: + const DisplayList& m_displayList; + GraphicsContext& m_context; +}; + +} +} + +#endif // DisplayListReplayer_h diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp index 329131601..a086efa98 100644 --- a/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp +++ b/Source/WebCore/platform/graphics/egl/GLContextEGL.cpp @@ -22,7 +22,8 @@ #if USE(EGL) #include "GraphicsContext3D.h" -#include <wtf/OwnPtr.h> +#include "PlatformDisplay.h" +#include <EGL/egl.h> #if USE(CAIRO) #include <cairo.h> @@ -45,30 +46,6 @@ namespace WebCore { -static EGLDisplay gSharedEGLDisplay = EGL_NO_DISPLAY; - -#if USE(OPENGL_ES_2) -static const EGLenum gGLAPI = EGL_OPENGL_ES_API; -#else -static const EGLenum gGLAPI = EGL_OPENGL_API; -#endif - -static EGLDisplay sharedEGLDisplay() -{ - static bool initialized = false; - if (!initialized) { - initialized = true; -#if PLATFORM(X11) - gSharedEGLDisplay = eglGetDisplay(GLContext::sharedX11Display()); -#else - gSharedEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); -#endif - if (gSharedEGLDisplay != EGL_NO_DISPLAY && (!eglInitialize(gSharedEGLDisplay, 0, 0) || !eglBindAPI(gGLAPI))) - gSharedEGLDisplay = EGL_NO_DISPLAY; - } - return gSharedEGLDisplay; -} - static const EGLint gContextAttributes[] = { #if USE(OPENGL_ES_2) EGL_CONTEXT_CLIENT_VERSION, 2, @@ -76,7 +53,13 @@ static const EGLint gContextAttributes[] = { EGL_NONE }; -static bool getEGLConfig(EGLConfig* config, GLContextEGL::EGLSurfaceType surfaceType) +#if USE(OPENGL_ES_2) +static const EGLenum gEGLAPIVersion = EGL_OPENGL_ES_API; +#else +static const EGLenum gEGLAPIVersion = EGL_OPENGL_API; +#endif + +bool GLContextEGL::getEGLConfig(EGLDisplay display, EGLConfig* config, EGLSurfaceType surfaceType) { EGLint attributeList[] = { #if USE(OPENGL_ES_2) @@ -101,45 +84,52 @@ static bool getEGLConfig(EGLConfig* config, GLContextEGL::EGLSurfaceType surface attributeList[13] = EGL_PIXMAP_BIT; break; case GLContextEGL::WindowSurface: + case GLContextEGL::Surfaceless: attributeList[13] = EGL_WINDOW_BIT; break; } EGLint numberConfigsReturned; - return eglChooseConfig(sharedEGLDisplay(), attributeList, config, 1, &numberConfigsReturned) && numberConfigsReturned; + return eglChooseConfig(display, attributeList, config, 1, &numberConfigsReturned) && numberConfigsReturned; } -PassOwnPtr<GLContextEGL> GLContextEGL::createWindowContext(EGLNativeWindowType window, GLContext* sharingContext) +std::unique_ptr<GLContextEGL> GLContextEGL::createWindowContext(GLNativeWindowType window, PlatformDisplay& platformDisplay, EGLContext sharingContext) { - EGLContext eglSharingContext = sharingContext ? static_cast<GLContextEGL*>(sharingContext)->m_context : 0; - - EGLDisplay display = sharedEGLDisplay(); - if (display == EGL_NO_DISPLAY) - return nullptr; - + EGLDisplay display = platformDisplay.eglDisplay(); EGLConfig config; - if (!getEGLConfig(&config, WindowSurface)) + if (!getEGLConfig(display, &config, WindowSurface)) return nullptr; - EGLContext context = eglCreateContext(display, config, eglSharingContext, gContextAttributes); + EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes); if (context == EGL_NO_CONTEXT) return nullptr; - EGLSurface surface = eglCreateWindowSurface(display, config, window, 0); - if (surface == EGL_NO_SURFACE) + EGLSurface surface = EGL_NO_SURFACE; +#if PLATFORM(GTK) +#if PLATFORM(X11) + if (platformDisplay.type() == PlatformDisplay::Type::X11) + surface = createWindowSurfaceX11(display, config, window); +#endif +#if PLATFORM(WAYLAND) + if (platformDisplay.type() == PlatformDisplay::Type::Wayland) + surface = createWindowSurfaceWayland(display, config, window); +#endif +#else + surface = eglCreateWindowSurface(display, config, static_cast<EGLNativeWindowType>(window), nullptr); +#endif + if (surface == EGL_NO_SURFACE) { + eglDestroyContext(display, context); return nullptr; + } - return adoptPtr(new GLContextEGL(context, surface, WindowSurface)); + return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WindowSurface)); } -PassOwnPtr<GLContextEGL> GLContextEGL::createPbufferContext(EGLContext sharingContext) +std::unique_ptr<GLContextEGL> GLContextEGL::createPbufferContext(PlatformDisplay& platformDisplay, EGLContext sharingContext) { - EGLDisplay display = sharedEGLDisplay(); - if (display == EGL_NO_DISPLAY) - return nullptr; - + EGLDisplay display = platformDisplay.eglDisplay(); EGLConfig config; - if (!getEGLConfig(&config, PbufferSurface)) + if (!getEGLConfig(display, &config, PbufferSurface)) return nullptr; EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes); @@ -153,78 +143,91 @@ PassOwnPtr<GLContextEGL> GLContextEGL::createPbufferContext(EGLContext sharingCo return nullptr; } - return adoptPtr(new GLContextEGL(context, surface, PbufferSurface)); + return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, PbufferSurface)); } -PassOwnPtr<GLContextEGL> GLContextEGL::createPixmapContext(EGLContext sharingContext) +std::unique_ptr<GLContextEGL> GLContextEGL::createSurfacelessContext(PlatformDisplay& platformDisplay, EGLContext sharingContext) { -#if PLATFORM(X11) - EGLDisplay display = sharedEGLDisplay(); + EGLDisplay display = platformDisplay.eglDisplay(); if (display == EGL_NO_DISPLAY) return nullptr; + const char* extensions = eglQueryString(display, EGL_EXTENSIONS); + if (!GLContext::isExtensionSupported(extensions, "EGL_KHR_surfaceless_context") && !GLContext::isExtensionSupported(extensions, "EGL_KHR_surfaceless_opengl")) + return nullptr; + EGLConfig config; - if (!getEGLConfig(&config, PixmapSurface)) + if (!getEGLConfig(display, &config, Surfaceless)) return nullptr; EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes); if (context == EGL_NO_CONTEXT) return nullptr; - EGLint depth; - if (!eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depth)) - return nullptr; + return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, EGL_NO_SURFACE, Surfaceless)); +} - Pixmap pixmap = XCreatePixmap(sharedX11Display(), DefaultRootWindow(sharedX11Display()), 1, 1, depth); - if (!pixmap) +std::unique_ptr<GLContextEGL> GLContextEGL::createContext(GLNativeWindowType window, PlatformDisplay& platformDisplay) +{ + if (platformDisplay.eglDisplay() == EGL_NO_DISPLAY) return nullptr; - EGLSurface surface = eglCreatePixmapSurface(display, config, pixmap, 0); - - if (surface == EGL_NO_SURFACE) + if (eglBindAPI(gEGLAPIVersion) == EGL_FALSE) return nullptr; - return adoptPtr(new GLContextEGL(context, surface, PixmapSurface)); -#else - return nullptr; + EGLContext eglSharingContext = platformDisplay.sharingGLContext() ? static_cast<GLContextEGL*>(platformDisplay.sharingGLContext())->m_context : EGL_NO_CONTEXT; + auto context = window ? createWindowContext(window, platformDisplay, eglSharingContext) : nullptr; + if (!context) + context = createSurfacelessContext(platformDisplay, eglSharingContext); + if (!context) { +#if PLATFORM(X11) + if (platformDisplay.type() == PlatformDisplay::Type::X11) + context = createPixmapContext(platformDisplay, eglSharingContext); +#endif +#if PLATFORM(WAYLAND) + if (platformDisplay.type() == PlatformDisplay::Type::Wayland) + context = createWaylandContext(platformDisplay, eglSharingContext); #endif + } + if (!context) + context = createPbufferContext(platformDisplay, eglSharingContext); + + return context; } -PassOwnPtr<GLContextEGL> GLContextEGL::createContext(EGLNativeWindowType window, GLContext* sharingContext) +std::unique_ptr<GLContextEGL> GLContextEGL::createSharingContext(PlatformDisplay& platformDisplay) { - if (!sharedEGLDisplay()) + if (platformDisplay.eglDisplay() == EGL_NO_DISPLAY) return nullptr; - static bool initialized = false; - static bool success = true; - if (!initialized) { -#if !USE(OPENGL_ES_2) - success = initializeOpenGLShims(); -#endif - initialized = true; - } - if (!success) + if (eglBindAPI(gEGLAPIVersion) == EGL_FALSE) return nullptr; - EGLContext eglSharingContext = sharingContext ? static_cast<GLContextEGL*>(sharingContext)->m_context : 0; - OwnPtr<GLContextEGL> context = window ? createWindowContext(window, sharingContext) : nullptr; + auto context = createSurfacelessContext(platformDisplay); + if (!context) { +#if PLATFORM(X11) + if (platformDisplay.type() == PlatformDisplay::Type::X11) + context = createPixmapContext(platformDisplay); +#endif +#if PLATFORM(WAYLAND) + if (platformDisplay.type() == PlatformDisplay::Type::Wayland) + context = createWaylandContext(platformDisplay); +#endif + } if (!context) - context = createPixmapContext(eglSharingContext); + context = createPbufferContext(platformDisplay); - if (!context) - context = createPbufferContext(eglSharingContext); - - return context.release(); + return context; } -GLContextEGL::GLContextEGL(EGLContext context, EGLSurface surface, EGLSurfaceType type) - : m_context(context) +GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, EGLSurfaceType type) + : GLContext(display) + , m_context(context) , m_surface(surface) , m_type(type) -#if USE(CAIRO) - , m_cairoDevice(0) -#endif { + ASSERT(type != PixmapSurface); + ASSERT(type == Surfaceless || surface != EGL_NO_SURFACE); } GLContextEGL::~GLContextEGL() @@ -234,7 +237,7 @@ GLContextEGL::~GLContextEGL() cairo_device_destroy(m_cairoDevice); #endif - EGLDisplay display = sharedEGLDisplay(); + EGLDisplay display = m_display.eglDisplay(); if (m_context) { glBindFramebuffer(GL_FRAMEBUFFER, 0); eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -243,6 +246,10 @@ GLContextEGL::~GLContextEGL() if (m_surface) eglDestroySurface(display, m_surface); + +#if PLATFORM(WAYLAND) + destroyWaylandWindow(); +#endif } bool GLContextEGL::canRenderToDefaultFramebuffer() @@ -255,9 +262,10 @@ IntSize GLContextEGL::defaultFrameBufferSize() if (!canRenderToDefaultFramebuffer()) return IntSize(); + EGLDisplay display = m_display.eglDisplay(); EGLint width, height; - if (!eglQuerySurface(sharedEGLDisplay(), m_surface, EGL_WIDTH, &width) - || !eglQuerySurface(sharedEGLDisplay(), m_surface, EGL_HEIGHT, &height)) + if (!eglQuerySurface(display, m_surface, EGL_WIDTH, &width) + || !eglQuerySurface(display, m_surface, EGL_HEIGHT, &height)) return IntSize(); return IntSize(width, height); @@ -265,19 +273,19 @@ IntSize GLContextEGL::defaultFrameBufferSize() bool GLContextEGL::makeContextCurrent() { - ASSERT(m_context && m_surface); + ASSERT(m_context); GLContext::makeContextCurrent(); if (eglGetCurrentContext() == m_context) return true; - return eglMakeCurrent(sharedEGLDisplay(), m_surface, m_surface, m_context); + return eglMakeCurrent(m_display.eglDisplay(), m_surface, m_surface, m_context); } void GLContextEGL::swapBuffers() { ASSERT(m_surface); - eglSwapBuffers(sharedEGLDisplay(), m_surface); + eglSwapBuffers(m_display.eglDisplay(), m_surface); } void GLContextEGL::waitNative() @@ -285,6 +293,12 @@ void GLContextEGL::waitNative() eglWaitNative(EGL_CORE_NATIVE_ENGINE); } +void GLContextEGL::swapInterval(int interval) +{ + ASSERT(m_surface); + eglSwapInterval(m_display.eglDisplay(), interval); +} + #if USE(CAIRO) cairo_device_t* GLContextEGL::cairoDevice() { @@ -292,14 +306,14 @@ cairo_device_t* GLContextEGL::cairoDevice() return m_cairoDevice; #if ENABLE(ACCELERATED_2D_CANVAS) - m_cairoDevice = cairo_egl_device_create(sharedEGLDisplay(), m_context); + m_cairoDevice = cairo_egl_device_create(m_display.eglDisplay(), m_context); #endif return m_cairoDevice; } #endif -#if ENABLE(WEBGL) +#if ENABLE(GRAPHICS_CONTEXT_3D) PlatformGraphicsContext3D GLContextEGL::platformContext() { return m_context; diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGL.h b/Source/WebCore/platform/graphics/egl/GLContextEGL.h index c887e0beb..e5a33597d 100644 --- a/Source/WebCore/platform/graphics/egl/GLContextEGL.h +++ b/Source/WebCore/platform/graphics/egl/GLContextEGL.h @@ -17,57 +17,92 @@ * Boston, MA 02110-1301 USA */ -#ifndef GLContextEGL_h -#define GLContextEGL_h +#pragma once #if USE(EGL) #include "GLContext.h" -#include <EGL/egl.h> +#if PLATFORM(X11) +#include "XUniqueResource.h" +#endif + +#if PLATFORM(WAYLAND) +#include "WlUniquePtr.h" +struct wl_egl_window; +#endif + +typedef void *EGLConfig; +typedef void *EGLContext; +typedef void *EGLDisplay; +typedef void *EGLSurface; namespace WebCore { -class GLContextEGL : public GLContext { +class GLContextEGL final : public GLContext { WTF_MAKE_NONCOPYABLE(GLContextEGL); public: - enum EGLSurfaceType { PbufferSurface, WindowSurface, PixmapSurface }; - static PassOwnPtr<GLContextEGL> createContext(EGLNativeWindowType, GLContext* sharingContext = 0); - static PassOwnPtr<GLContextEGL> createWindowContext(EGLNativeWindowType, GLContext* sharingContext); + static std::unique_ptr<GLContextEGL> createContext(GLNativeWindowType, PlatformDisplay&); + static std::unique_ptr<GLContextEGL> createSharingContext(PlatformDisplay&); virtual ~GLContextEGL(); - virtual bool makeContextCurrent(); - virtual void swapBuffers(); - virtual void waitNative(); - virtual bool canRenderToDefaultFramebuffer(); - virtual IntSize defaultFrameBufferSize(); + +private: + bool makeContextCurrent() override; + void swapBuffers() override; + void waitNative() override; + bool canRenderToDefaultFramebuffer() override; + IntSize defaultFrameBufferSize() override; + void swapInterval(int) override; #if USE(CAIRO) - virtual cairo_device_t* cairoDevice(); + cairo_device_t* cairoDevice() override; #endif + bool isEGLContext() const override { return true; } -#if ENABLE(WEBGL) - virtual PlatformGraphicsContext3D platformContext(); +#if ENABLE(GRAPHICS_CONTEXT_3D) + PlatformGraphicsContext3D platformContext() override; #endif -private: - static PassOwnPtr<GLContextEGL> createPbufferContext(EGLContext sharingContext); - static PassOwnPtr<GLContextEGL> createPixmapContext(EGLContext sharingContext); + enum EGLSurfaceType { PbufferSurface, WindowSurface, PixmapSurface, Surfaceless }; - static void addActiveContext(GLContextEGL*); - static void cleanupSharedEGLDisplay(void); + GLContextEGL(PlatformDisplay&, EGLContext, EGLSurface, EGLSurfaceType); +#if PLATFORM(X11) + GLContextEGL(PlatformDisplay&, EGLContext, EGLSurface, XUniquePixmap&&); +#endif +#if PLATFORM(WAYLAND) + GLContextEGL(PlatformDisplay&, EGLContext, EGLSurface, WlUniquePtr<struct wl_surface>&&, struct wl_egl_window*); + void destroyWaylandWindow(); +#endif + + static std::unique_ptr<GLContextEGL> createWindowContext(GLNativeWindowType, PlatformDisplay&, EGLContext sharingContext = nullptr); + static std::unique_ptr<GLContextEGL> createPbufferContext(PlatformDisplay&, EGLContext sharingContext = nullptr); + static std::unique_ptr<GLContextEGL> createSurfacelessContext(PlatformDisplay&, EGLContext sharingContext = nullptr); +#if PLATFORM(X11) + static std::unique_ptr<GLContextEGL> createPixmapContext(PlatformDisplay&, EGLContext sharingContext = nullptr); + static EGLSurface createWindowSurfaceX11(EGLDisplay, EGLConfig, GLNativeWindowType); +#endif +#if PLATFORM(WAYLAND) + static std::unique_ptr<GLContextEGL> createWaylandContext(PlatformDisplay&, EGLContext sharingContext = nullptr); + static EGLSurface createWindowSurfaceWayland(EGLDisplay, EGLConfig, GLNativeWindowType); +#endif - GLContextEGL(EGLContext, EGLSurface, EGLSurfaceType); + static bool getEGLConfig(EGLDisplay, EGLConfig*, EGLSurfaceType); - EGLContext m_context; - EGLSurface m_surface; + EGLContext m_context { nullptr }; + EGLSurface m_surface { nullptr }; EGLSurfaceType m_type; +#if PLATFORM(X11) + XUniquePixmap m_pixmap; +#endif +#if PLATFORM(WAYLAND) + WlUniquePtr<struct wl_surface> m_wlSurface; + struct wl_egl_window* m_wlWindow { nullptr }; +#endif #if USE(CAIRO) - cairo_device_t* m_cairoDevice; + cairo_device_t* m_cairoDevice { nullptr }; #endif }; } // namespace WebCore #endif // USE(EGL) - -#endif // GLContextEGL_h diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp new file mode 100644 index 000000000..a0ed56fd9 --- /dev/null +++ b/Source/WebCore/platform/graphics/egl/GLContextEGLWayland.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 Igalia, S.L. + * + * 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 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 "GLContextEGL.h" + +#if USE(EGL) && PLATFORM(WAYLAND) + +#include "PlatformDisplayWayland.h" +// These includes need to be in this order because wayland-egl.h defines WL_EGL_PLATFORM +// and egl.h checks that to decide whether it's Wayland platform. +#include <wayland-egl.h> +#include <EGL/egl.h> + +namespace WebCore { + +GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, WlUniquePtr<struct wl_surface>&& wlSurface, struct wl_egl_window* wlWindow) + : GLContext(display) + , m_context(context) + , m_surface(surface) + , m_type(WindowSurface) + , m_wlSurface(WTFMove(wlSurface)) + , m_wlWindow(wlWindow) +{ +} + +EGLSurface GLContextEGL::createWindowSurfaceWayland(EGLDisplay display, EGLConfig config, GLNativeWindowType window) +{ + return eglCreateWindowSurface(display, config, reinterpret_cast<EGLNativeWindowType>(window), nullptr); +} + +std::unique_ptr<GLContextEGL> GLContextEGL::createWaylandContext(PlatformDisplay& platformDisplay, EGLContext sharingContext) +{ + EGLDisplay display = platformDisplay.eglDisplay(); + EGLConfig config; + if (!getEGLConfig(display, &config, WindowSurface)) + return nullptr; + + static const EGLint contextAttributes[] = { +#if USE(OPENGL_ES_2) + EGL_CONTEXT_CLIENT_VERSION, 2, +#endif + EGL_NONE + }; + + EGLContext context = eglCreateContext(display, config, sharingContext, contextAttributes); + if (context == EGL_NO_CONTEXT) + return nullptr; + + WlUniquePtr<struct wl_surface> wlSurface(downcast<PlatformDisplayWayland>(platformDisplay).createSurface()); + if (!wlSurface) { + eglDestroyContext(display, context); + return nullptr; + } + + EGLNativeWindowType window = wl_egl_window_create(wlSurface.get(), 1, 1); + EGLSurface surface = eglCreateWindowSurface(display, config, window, 0); + if (surface == EGL_NO_SURFACE) { + eglDestroyContext(display, context); + wl_egl_window_destroy(window); + return nullptr; + } + + return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WTFMove(wlSurface), window)); +} + +void GLContextEGL::destroyWaylandWindow() +{ + if (m_wlWindow) + wl_egl_window_destroy(m_wlWindow); +} + +} // namespace WebCore + +#endif // USE(EGL) && PLATFORM(WAYLAND) diff --git a/Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp b/Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp new file mode 100644 index 000000000..8fdd4cad3 --- /dev/null +++ b/Source/WebCore/platform/graphics/egl/GLContextEGLX11.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 Igalia, S.L. + * + * 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 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 "GLContextEGL.h" + +#if USE(EGL) && PLATFORM(X11) +#include "PlatformDisplayX11.h" +#include "XErrorTrapper.h" +#include "XUniquePtr.h" +#include <EGL/egl.h> +#include <X11/Xlib.h> + +namespace WebCore { + +GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, XUniquePixmap&& pixmap) + : GLContext(display) + , m_context(context) + , m_surface(surface) + , m_type(PixmapSurface) + , m_pixmap(WTFMove(pixmap)) +{ +} + +EGLSurface GLContextEGL::createWindowSurfaceX11(EGLDisplay display, EGLConfig config, GLNativeWindowType window) +{ + return eglCreateWindowSurface(display, config, static_cast<EGLNativeWindowType>(window), nullptr); +} + +std::unique_ptr<GLContextEGL> GLContextEGL::createPixmapContext(PlatformDisplay& platformDisplay, EGLContext sharingContext) +{ + EGLDisplay display = platformDisplay.eglDisplay(); + EGLConfig config; + if (!getEGLConfig(display, &config, PixmapSurface)) + return nullptr; + + static const EGLint contextAttributes[] = { +#if USE(OPENGL_ES_2) + EGL_CONTEXT_CLIENT_VERSION, 2, +#endif + EGL_NONE + }; + EGLContext context = eglCreateContext(display, config, sharingContext, contextAttributes); + if (context == EGL_NO_CONTEXT) + return nullptr; + + EGLint visualId; + if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualId)) { + eglDestroyContext(display, context); + return nullptr; + } + + Display* x11Display = downcast<PlatformDisplayX11>(platformDisplay).native(); + + XVisualInfo visualInfo; + visualInfo.visualid = visualId; + int numVisuals = 0; + XUniquePtr<XVisualInfo> visualInfoList(XGetVisualInfo(x11Display, VisualIDMask, &visualInfo, &numVisuals)); + if (!visualInfoList || !numVisuals) { + eglDestroyContext(display, context); + return nullptr; + } + + // We are using VisualIDMask so there must be only one. + ASSERT(numVisuals == 1); + XUniquePixmap pixmap = XCreatePixmap(x11Display, DefaultRootWindow(x11Display), 1, 1, visualInfoList.get()[0].depth); + if (!pixmap) { + eglDestroyContext(display, context); + return nullptr; + } + + // Some drivers fail to create the surface producing BadDrawable X error and the default XError handler normally aborts. + // However, if the X error is ignored, eglCreatePixmapSurface() ends up returning a surface and we can continue creating + // the context. Since this is an offscreen context, it doesn't matter if the pixmap used is not valid because we never do + // swap buffers. So, we use a custom XError handler here that ignores BadDrawable errors and only warns about any other + // errors without aborting in any case. + XErrorTrapper trapper(x11Display, XErrorTrapper::Policy::Warn, { BadDrawable }); + EGLSurface surface = eglCreatePixmapSurface(display, config, reinterpret_cast<EGLNativePixmapType>(pixmap.get()), 0); + if (surface == EGL_NO_SURFACE) { + eglDestroyContext(display, context); + return nullptr; + } + + return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WTFMove(pixmap))); +} + +} // namespace WebCore + +#endif // USE(EGL) && PLATFORM(X11) diff --git a/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp b/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp index 164f36a1b..4bc76704e 100644 --- a/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp +++ b/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp @@ -29,8 +29,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "DistantLightSource.h" #include "TextStream.h" @@ -76,5 +74,3 @@ TextStream& DistantLightSource::externalRepresentation(TextStream& ts) const } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/DistantLightSource.h b/Source/WebCore/platform/graphics/filters/DistantLightSource.h index bbe224b84..663abaebb 100644 --- a/Source/WebCore/platform/graphics/filters/DistantLightSource.h +++ b/Source/WebCore/platform/graphics/filters/DistantLightSource.h @@ -23,28 +23,28 @@ #ifndef DistantLightSource_h #define DistantLightSource_h -#if ENABLE(FILTERS) #include "LightSource.h" +#include <wtf/Ref.h> namespace WebCore { class DistantLightSource : public LightSource { public: - static PassRefPtr<DistantLightSource> create(float azimuth, float elevation) + static Ref<DistantLightSource> create(float azimuth, float elevation) { - return adoptRef(new DistantLightSource(azimuth, elevation)); + return adoptRef(*new DistantLightSource(azimuth, elevation)); } float azimuth() const { return m_azimuth; } float elevation() const { return m_elevation; } - virtual bool setAzimuth(float) override; - virtual bool setElevation(float) override; + bool setAzimuth(float) override; + bool setElevation(float) override; - virtual void initPaintingData(PaintingData&); - virtual void updatePaintingData(PaintingData&, int x, int y, float z); + void initPaintingData(PaintingData&) override; + void updatePaintingData(PaintingData&, int x, int y, float z) override; - virtual TextStream& externalRepresentation(TextStream&) const; + TextStream& externalRepresentation(TextStream&) const override; private: DistantLightSource(float azimuth, float elevation) @@ -60,6 +60,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // DistantLightSource_h diff --git a/Source/WebCore/platform/graphics/filters/FEBlend.cpp b/Source/WebCore/platform/graphics/filters/FEBlend.cpp index 11b707322..ff9111c4b 100644 --- a/Source/WebCore/platform/graphics/filters/FEBlend.cpp +++ b/Source/WebCore/platform/graphics/filters/FEBlend.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,11 +23,9 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEBlend.h" -#include "FEBlendNEON.h" +#include "FEBlendNEON.h" #include "Filter.h" #include "FloatPoint.h" #include "GraphicsContext.h" @@ -34,27 +33,25 @@ #include <runtime/Uint8ClampedArray.h> -typedef unsigned char (*BlendType)(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB); - namespace WebCore { -FEBlend::FEBlend(Filter* filter, BlendModeType mode) +FEBlend::FEBlend(Filter& filter, BlendMode mode) : FilterEffect(filter) , m_mode(mode) { } -PassRefPtr<FEBlend> FEBlend::create(Filter* filter, BlendModeType mode) +Ref<FEBlend> FEBlend::create(Filter& filter, BlendMode mode) { - return adoptRef(new FEBlend(filter, mode)); + return adoptRef(*new FEBlend(filter, mode)); } -BlendModeType FEBlend::blendMode() const +BlendMode FEBlend::blendMode() const { return m_mode; } -bool FEBlend::setBlendMode(BlendModeType mode) +bool FEBlend::setBlendMode(BlendMode mode) { if (m_mode == mode) return false; @@ -62,158 +59,40 @@ bool FEBlend::setBlendMode(BlendModeType mode) return true; } -inline unsigned char feBlendNormal(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char) -{ - return fastDivideBy255((255 - alphaA) * colorB + colorA * 255); -} - -inline unsigned char feBlendMultiply(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB) -{ - return fastDivideBy255((255 - alphaA) * colorB + (255 - alphaB + colorB) * colorA); -} - -inline unsigned char feBlendScreen(unsigned char colorA, unsigned char colorB, unsigned char, unsigned char) -{ - return fastDivideBy255((colorB + colorA) * 255 - colorA * colorB); -} - -inline unsigned char feBlendDarken(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB) -{ - return fastDivideBy255(std::min((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255)); -} - -inline unsigned char feBlendLighten(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB) -{ - return fastDivideBy255(std::max((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255)); -} - -inline unsigned char feBlendUnknown(unsigned char, unsigned char, unsigned char, unsigned char) -{ - return 0; -} - -template<BlendType BlendFunction> -static void platformApply(unsigned char* sourcePixelA, unsigned char* sourcePixelB, - unsigned char* destinationPixel, unsigned pixelArrayLength) -{ - unsigned len = pixelArrayLength / 4; - for (unsigned pixelOffset = 0; pixelOffset < len; pixelOffset++) { - unsigned char alphaA = sourcePixelA[3]; - unsigned char alphaB = sourcePixelB[3]; - destinationPixel[0] = BlendFunction(sourcePixelA[0], sourcePixelB[0], alphaA, alphaB); - destinationPixel[1] = BlendFunction(sourcePixelA[1], sourcePixelB[1], alphaA, alphaB); - destinationPixel[2] = BlendFunction(sourcePixelA[2], sourcePixelB[2], alphaA, alphaB); - destinationPixel[3] = 255 - fastDivideBy255((255 - alphaA) * (255 - alphaB)); - sourcePixelA += 4; - sourcePixelB += 4; - destinationPixel += 4; - } -} - -void FEBlend::platformApplyGeneric(unsigned char* sourcePixelA, unsigned char* sourcePixelB, - unsigned char* destinationPixel, unsigned pixelArrayLength) -{ - switch (m_mode) { - case FEBLEND_MODE_NORMAL: - platformApply<feBlendNormal>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength); - break; - case FEBLEND_MODE_MULTIPLY: - platformApply<feBlendMultiply>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength); - break; - case FEBLEND_MODE_SCREEN: - platformApply<feBlendScreen>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength); - break; - case FEBLEND_MODE_DARKEN: - platformApply<feBlendDarken>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength); - break; - case FEBLEND_MODE_LIGHTEN: - platformApply<feBlendLighten>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength); - break; - case FEBLEND_MODE_UNKNOWN: - platformApply<feBlendUnknown>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength); - break; - } -} - +#if !HAVE(ARM_NEON_INTRINSICS) void FEBlend::platformApplySoftware() { FilterEffect* in = inputEffect(0); FilterEffect* in2 = inputEffect(1); - ASSERT(m_mode > FEBLEND_MODE_UNKNOWN); - ASSERT(m_mode <= FEBLEND_MODE_LIGHTEN); - - Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult(); - if (!dstPixelArray) + ImageBuffer* resultImage = createImageBufferResult(); + if (!resultImage) return; + GraphicsContext& filterContext = resultImage->context(); - IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); - RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect); - - IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); - RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asPremultipliedImage(effectBDrawingRect); - - unsigned pixelArrayLength = srcPixelArrayA->length(); - ASSERT(pixelArrayLength == srcPixelArrayB->length()); - -#if HAVE(ARM_NEON_INTRINSICS) - if (pixelArrayLength >= 8) - platformApplyNEON(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength); - else { // If there is just one pixel we expand it to two. - ASSERT(pixelArrayLength > 0); - uint32_t sourceA[2] = {0, 0}; - uint32_t sourceBAndDest[2] = {0, 0}; + ImageBuffer* imageBuffer = in->asImageBuffer(); + ImageBuffer* imageBuffer2 = in2->asImageBuffer(); + if (!imageBuffer || !imageBuffer2) + return; - sourceA[0] = reinterpret_cast<uint32_t*>(srcPixelArrayA->data())[0]; - sourceBAndDest[0] = reinterpret_cast<uint32_t*>(srcPixelArrayB->data())[0]; - platformApplyNEON(reinterpret_cast<uint8_t*>(sourceA), reinterpret_cast<uint8_t*>(sourceBAndDest), reinterpret_cast<uint8_t*>(sourceBAndDest), 8); - reinterpret_cast<uint32_t*>(dstPixelArray->data())[0] = sourceBAndDest[0]; - } -#else - platformApplyGeneric(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength); -#endif + filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), ImagePaintingOptions(CompositeSourceOver, m_mode)); } +#endif void FEBlend::dump() { } -static TextStream& operator<<(TextStream& ts, const BlendModeType& type) -{ - switch (type) { - case FEBLEND_MODE_UNKNOWN: - ts << "UNKNOWN"; - break; - case FEBLEND_MODE_NORMAL: - ts << "NORMAL"; - break; - case FEBLEND_MODE_MULTIPLY: - ts << "MULTIPLY"; - break; - case FEBLEND_MODE_SCREEN: - ts << "SCREEN"; - break; - case FEBLEND_MODE_DARKEN: - ts << "DARKEN"; - break; - case FEBLEND_MODE_LIGHTEN: - ts << "LIGHTEN"; - break; - } - return ts; -} - TextStream& FEBlend::externalRepresentation(TextStream& ts, int indent) const { writeIndent(ts, indent); ts << "[feBlend"; FilterEffect::externalRepresentation(ts); - ts << " mode=\"" << m_mode << "\"]\n"; + ts << " mode=\"" << (m_mode == BlendModeNormal ? "normal" : compositeOperatorName(CompositeSourceOver, m_mode)) << "\"]\n"; inputEffect(0)->externalRepresentation(ts, indent + 1); inputEffect(1)->externalRepresentation(ts, indent + 1); return ts; } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEBlend.h b/Source/WebCore/platform/graphics/filters/FEBlend.h index 6b00080a8..1c50a77ea 100644 --- a/Source/WebCore/platform/graphics/filters/FEBlend.h +++ b/Source/WebCore/platform/graphics/filters/FEBlend.h @@ -2,6 +2,7 @@ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> * Copyright (C) 2005 Eric Seidel <eric@webkit.org> + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,46 +23,34 @@ #ifndef FEBlend_h #define FEBlend_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" namespace WebCore { -enum BlendModeType { - FEBLEND_MODE_UNKNOWN = 0, - FEBLEND_MODE_NORMAL = 1, - FEBLEND_MODE_MULTIPLY = 2, - FEBLEND_MODE_SCREEN = 3, - FEBLEND_MODE_DARKEN = 4, - FEBLEND_MODE_LIGHTEN = 5 -}; - class FEBlend : public FilterEffect { public: - static PassRefPtr<FEBlend> create(Filter*, BlendModeType); + static Ref<FEBlend> create(Filter&, BlendMode); - BlendModeType blendMode() const; - bool setBlendMode(BlendModeType); + BlendMode blendMode() const; + bool setBlendMode(BlendMode); void platformApplyGeneric(unsigned char* srcPixelArrayA, unsigned char* srcPixelArrayB, unsigned char* dstPixelArray, unsigned colorArrayLength); void platformApplyNEON(unsigned char* srcPixelArrayA, unsigned char* srcPixelArrayB, unsigned char* dstPixelArray, unsigned colorArrayLength); - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEBlend(Filter*, BlendModeType); + FEBlend(Filter&, BlendMode); - BlendModeType m_mode; + BlendMode m_mode; }; } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEBlend_h diff --git a/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp index 1a5504750..2320d52c2 100644 --- a/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp +++ b/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp @@ -21,8 +21,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEColorMatrix.h" #include "Filter.h" @@ -34,16 +32,16 @@ namespace WebCore { -FEColorMatrix::FEColorMatrix(Filter* filter, ColorMatrixType type, const Vector<float>& values) +FEColorMatrix::FEColorMatrix(Filter& filter, ColorMatrixType type, const Vector<float>& values) : FilterEffect(filter) , m_type(type) , m_values(values) { } -PassRefPtr<FEColorMatrix> FEColorMatrix::create(Filter* filter, ColorMatrixType type, const Vector<float>& values) +Ref<FEColorMatrix> FEColorMatrix::create(Filter& filter, ColorMatrixType type, const Vector<float>& values) { - return adoptRef(new FEColorMatrix(filter, type, values)); + return adoptRef(*new FEColorMatrix(filter, type, values)); } ColorMatrixType FEColorMatrix::type() const @@ -149,25 +147,28 @@ void FEColorMatrix::platformApplySoftware() if (!resultImage) return; - resultImage->context()->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); + ImageBuffer* inBuffer = in->asImageBuffer(); + if (inBuffer) + resultImage->context().drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); - IntRect imageRect(IntPoint(), absolutePaintRect().size()); + IntRect imageRect(IntPoint(), resultImage->logicalSize()); RefPtr<Uint8ClampedArray> pixelArray = resultImage->getUnmultipliedImageData(imageRect); + Vector<float> values = normalizedFloats(m_values); switch (m_type) { case FECOLORMATRIX_TYPE_UNKNOWN: break; case FECOLORMATRIX_TYPE_MATRIX: - effectType<FECOLORMATRIX_TYPE_MATRIX>(pixelArray.get(), m_values); + effectType<FECOLORMATRIX_TYPE_MATRIX>(pixelArray.get(), values); break; case FECOLORMATRIX_TYPE_SATURATE: - effectType<FECOLORMATRIX_TYPE_SATURATE>(pixelArray.get(), m_values); + effectType<FECOLORMATRIX_TYPE_SATURATE>(pixelArray.get(), values); break; case FECOLORMATRIX_TYPE_HUEROTATE: - effectType<FECOLORMATRIX_TYPE_HUEROTATE>(pixelArray.get(), m_values); + effectType<FECOLORMATRIX_TYPE_HUEROTATE>(pixelArray.get(), values); break; case FECOLORMATRIX_TYPE_LUMINANCETOALPHA: - effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(pixelArray.get(), m_values); + effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(pixelArray.get(), values); setIsAlphaImage(true); break; } @@ -225,5 +226,3 @@ TextStream& FEColorMatrix::externalRepresentation(TextStream& ts, int indent) co } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEColorMatrix.h b/Source/WebCore/platform/graphics/filters/FEColorMatrix.h index 98905c58e..36d3a2437 100644 --- a/Source/WebCore/platform/graphics/filters/FEColorMatrix.h +++ b/Source/WebCore/platform/graphics/filters/FEColorMatrix.h @@ -22,7 +22,6 @@ #ifndef FEColorMatrix_h #define FEColorMatrix_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -40,7 +39,7 @@ enum ColorMatrixType { class FEColorMatrix : public FilterEffect { public: - static PassRefPtr<FEColorMatrix> create(Filter*, ColorMatrixType, const Vector<float>&); + static Ref<FEColorMatrix> create(Filter&, ColorMatrixType, const Vector<float>&); ColorMatrixType type() const; bool setType(ColorMatrixType); @@ -48,19 +47,16 @@ public: const Vector<float>& values() const; bool setValues(const Vector<float>&); - virtual void platformApplySoftware(); -#if ENABLE(OPENCL) - virtual bool platformApplyOpenCL(); -#endif - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; static inline void calculateSaturateComponents(float* components, float value); static inline void calculateHueRotateComponents(float* components, float value); private: - FEColorMatrix(Filter*, ColorMatrixType, const Vector<float>&); + FEColorMatrix(Filter&, ColorMatrixType, const Vector<float>&); ColorMatrixType m_type; Vector<float> m_values; @@ -97,6 +93,4 @@ inline void FEColorMatrix::calculateHueRotateComponents(float* components, float } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEColorMatrix_h diff --git a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp index 1e36e7956..780398d4f 100644 --- a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp +++ b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp @@ -22,8 +22,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEComponentTransfer.h" #include "Filter.h" @@ -38,7 +36,7 @@ namespace WebCore { typedef void (*TransferType)(unsigned char*, const ComponentTransferFunction&); -FEComponentTransfer::FEComponentTransfer(Filter* filter, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, +FEComponentTransfer::FEComponentTransfer(Filter& filter, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc) : FilterEffect(filter) , m_redFunc(redFunc) @@ -48,10 +46,10 @@ FEComponentTransfer::FEComponentTransfer(Filter* filter, const ComponentTransfer { } -PassRefPtr<FEComponentTransfer> FEComponentTransfer::create(Filter* filter, const ComponentTransferFunction& redFunc, +Ref<FEComponentTransfer> FEComponentTransfer::create(Filter& filter, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc) { - return adoptRef(new FEComponentTransfer(filter, redFunc, greenFunc, blueFunc, alphaFunc)); + return adoptRef(*new FEComponentTransfer(filter, redFunc, greenFunc, blueFunc, alphaFunc)); } ComponentTransferFunction FEComponentTransfer::redFunction() const @@ -247,5 +245,3 @@ TextStream& FEComponentTransfer::externalRepresentation(TextStream& ts, int inde } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h index cf6b7476d..6619755f0 100644 --- a/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h +++ b/Source/WebCore/platform/graphics/filters/FEComponentTransfer.h @@ -22,7 +22,6 @@ #ifndef FEComponentTransfer_h #define FEComponentTransfer_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -63,8 +62,8 @@ struct ComponentTransferFunction { class FEComponentTransfer : public FilterEffect { public: - static PassRefPtr<FEComponentTransfer> create(Filter*, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, - const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc); + static Ref<FEComponentTransfer> create(Filter&, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, + const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc); ComponentTransferFunction redFunction() const; void setRedFunction(const ComponentTransferFunction&); @@ -78,13 +77,13 @@ public: ComponentTransferFunction alphaFunction() const; void setAlphaFunction(const ComponentTransferFunction&); - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEComponentTransfer(Filter*, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, + FEComponentTransfer(Filter&, const ComponentTransferFunction& redFunc, const ComponentTransferFunction& greenFunc, const ComponentTransferFunction& blueFunc, const ComponentTransferFunction& alphaFunc); void getValues(unsigned char rValues[256], unsigned char gValues[256], unsigned char bValues[256], unsigned char aValues[256]); @@ -97,6 +96,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEComponentTransfer_h diff --git a/Source/WebCore/platform/graphics/filters/FEComposite.cpp b/Source/WebCore/platform/graphics/filters/FEComposite.cpp index 07805de9c..94d16285a 100644 --- a/Source/WebCore/platform/graphics/filters/FEComposite.cpp +++ b/Source/WebCore/platform/graphics/filters/FEComposite.cpp @@ -22,8 +22,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEComposite.h" #include "FECompositeArithmeticNEON.h" @@ -35,7 +33,7 @@ namespace WebCore { -FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) +FEComposite::FEComposite(Filter& filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) : FilterEffect(filter) , m_type(type) , m_k1(k1) @@ -45,9 +43,9 @@ FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, flo { } -PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) +Ref<FEComposite> FEComposite::create(Filter& filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) { - return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4)); + return adoptRef(*new FEComposite(filter, type, k1, k2, k3, k4)); } CompositeOperationType FEComposite::operation() const @@ -122,6 +120,13 @@ void FEComposite::correctFilterResultIfNeeded() forceValidPreMultipliedPixels(); } + +static unsigned char clampByte(int c) +{ + unsigned char buff[] = { static_cast<unsigned char>(c), 255, 0 }; + unsigned uc = static_cast<unsigned>(c); + return buff[!!(uc & ~0xff) + !!(uc & ~(~0u >> 1))]; +} template <int b1, int b4> static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength, @@ -143,12 +148,7 @@ static inline void computeArithmeticPixels(unsigned char* source, unsigned char* if (b4) result += scaledK4; - if (result <= 0) - *destination = 0; - else if (result >= 255) - *destination = 255; - else - *destination = result; + *destination = clampByte(result); ++source; ++destination; } @@ -181,6 +181,7 @@ static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsig } } +#if !HAVE(ARM_NEON_INTRINSICS) static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) { float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4; @@ -212,6 +213,7 @@ static inline void arithmeticSoftware(unsigned char* source, unsigned char* dest computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); } } +#endif inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination, float k1, float k2, float k3, float k4) @@ -272,17 +274,17 @@ void FEComposite::platformApplySoftware() ImageBuffer* resultImage = createImageBufferResult(); if (!resultImage) return; - GraphicsContext* filterContext = resultImage->context(); + GraphicsContext& filterContext = resultImage->context(); ImageBuffer* imageBuffer = in->asImageBuffer(); ImageBuffer* imageBuffer2 = in2->asImageBuffer(); - ASSERT(imageBuffer); - ASSERT(imageBuffer2); + if (!imageBuffer || !imageBuffer2) + return; switch (m_type) { case FECOMPOSITE_OPERATOR_OVER: - filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); - filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); break; case FECOMPOSITE_OPERATOR_IN: { // Applies only to the intersected region. @@ -291,25 +293,28 @@ void FEComposite::platformApplySoftware() destinationRect.intersect(absolutePaintRect()); if (destinationRect.isEmpty()) break; - IntRect adjustedDestinationRect = destinationRect - absolutePaintRect().location(); IntRect sourceRect = destinationRect - in->absolutePaintRect().location(); IntRect source2Rect = destinationRect - in2->absolutePaintRect().location(); - filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, adjustedDestinationRect, source2Rect); - filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, adjustedDestinationRect, sourceRect, CompositeSourceIn); + filterContext.drawImageBuffer(*imageBuffer2, adjustedDestinationRect, source2Rect); + filterContext.drawImageBuffer(*imageBuffer, adjustedDestinationRect, sourceRect, CompositeSourceIn); break; } case FECOMPOSITE_OPERATOR_OUT: - filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); - filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut); + filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut); break; case FECOMPOSITE_OPERATOR_ATOP: - filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); - filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop); + filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop); break; case FECOMPOSITE_OPERATOR_XOR: - filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); - filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR); + filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR); + break; + case FECOMPOSITE_OPERATOR_LIGHTER: + filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositePlusLighter); break; default: break; @@ -344,6 +349,9 @@ static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type case FECOMPOSITE_OPERATOR_ARITHMETIC: ts << "ARITHMETIC"; break; + case FECOMPOSITE_OPERATOR_LIGHTER: + ts << "LIGHTER"; + break; } return ts; } @@ -363,5 +371,3 @@ TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) cons } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEComposite.h b/Source/WebCore/platform/graphics/filters/FEComposite.h index 9f78177ea..f515e9500 100644 --- a/Source/WebCore/platform/graphics/filters/FEComposite.h +++ b/Source/WebCore/platform/graphics/filters/FEComposite.h @@ -22,7 +22,6 @@ #ifndef FEComposite_h #define FEComposite_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -37,12 +36,13 @@ enum CompositeOperationType { FECOMPOSITE_OPERATOR_OUT = 3, FECOMPOSITE_OPERATOR_ATOP = 4, FECOMPOSITE_OPERATOR_XOR = 5, - FECOMPOSITE_OPERATOR_ARITHMETIC = 6 + FECOMPOSITE_OPERATOR_ARITHMETIC = 6, + FECOMPOSITE_OPERATOR_LIGHTER = 7 }; class FEComposite : public FilterEffect { public: - static PassRefPtr<FEComposite> create(Filter*, const CompositeOperationType&, float, float, float, float); + static Ref<FEComposite> create(Filter&, const CompositeOperationType&, float, float, float, float); CompositeOperationType operation() const; bool setOperation(CompositeOperationType); @@ -59,20 +59,20 @@ public: float k4() const; bool setK4(float); - virtual void correctFilterResultIfNeeded() override; + void correctFilterResultIfNeeded() override; - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect(); + void determineAbsolutePaintRect() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; protected: - virtual bool requiresValidPreMultipliedPixels() override { return m_type != FECOMPOSITE_OPERATOR_ARITHMETIC; } + bool requiresValidPreMultipliedPixels() override { return m_type != FECOMPOSITE_OPERATOR_ARITHMETIC; } private: - FEComposite(Filter*, const CompositeOperationType&, float, float, float, float); + FEComposite(Filter&, const CompositeOperationType&, float, float, float, float); inline void platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination, float k1, float k2, float k3, float k4); @@ -91,6 +91,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEComposite_h diff --git a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp index 297ce733c..7d4800d65 100644 --- a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp +++ b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp @@ -22,8 +22,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEConvolveMatrix.h" #include "Filter.h" @@ -31,10 +29,11 @@ #include <runtime/Uint8ClampedArray.h> #include <wtf/ParallelJobs.h> +#include <wtf/WorkQueue.h> namespace WebCore { -FEConvolveMatrix::FEConvolveMatrix(Filter* filter, const IntSize& kernelSize, +FEConvolveMatrix::FEConvolveMatrix(Filter& filter, const IntSize& kernelSize, float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode, const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix) : FilterEffect(filter) @@ -51,11 +50,11 @@ FEConvolveMatrix::FEConvolveMatrix(Filter* filter, const IntSize& kernelSize, ASSERT(m_kernelSize.height() > 0); } -PassRefPtr<FEConvolveMatrix> FEConvolveMatrix::create(Filter* filter, const IntSize& kernelSize, +Ref<FEConvolveMatrix> FEConvolveMatrix::create(Filter& filter, const IntSize& kernelSize, float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode, const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix) { - return adoptRef(new FEConvolveMatrix(filter, kernelSize, divisor, bias, targetOffset, edgeMode, kernelUnitLength, + return adoptRef(*new FEConvolveMatrix(filter, kernelSize, divisor, bias, targetOffset, edgeMode, kernelUnitLength, preserveAlpha, kernelMatrix)); } @@ -242,7 +241,7 @@ ALWAYS_INLINE void setDestinationPixels(Uint8ClampedArray* image, int& pixel, fl image->set(pixel++, maxAlpha); } -#if defined(_MSC_VER) && (_MSC_VER >= 1700) +#if COMPILER(MSVC) // Incorrectly diagnosing overwrite of stack in |totals| due to |preserveAlphaValues|. #pragma warning(push) #pragma warning(disable: 4789) @@ -268,7 +267,7 @@ ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintin for (int y = yEnd + 1; y > yStart; --y) { for (int x = clipRight + 1; x > 0; --x) { - int kernelValue = m_kernelMatrix.size() - 1; + int kernelValue = paintingData.kernelMatrix.size() - 1; int kernelPixel = startKernelPixel; int width = m_kernelSize.width(); @@ -279,11 +278,11 @@ ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintin totals[3] = 0; while (kernelValue >= 0) { - totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++)); - totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++)); - totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++)); + totals[0] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++)); + totals[1] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++)); + totals[2] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel++)); if (!preserveAlphaValues) - totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel)); + totals[3] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(kernelPixel)); ++kernelPixel; --kernelValue; if (!--width) { @@ -348,7 +347,7 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in for (int y = height; y > 0; --y) { for (int x = width; x > 0; --x) { - int kernelValue = m_kernelMatrix.size() - 1; + int kernelValue = paintingData.kernelMatrix.size() - 1; int kernelPixelX = startKernelPixelX; int kernelPixelY = startKernelPixelY; int width = m_kernelSize.width(); @@ -362,12 +361,12 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in while (kernelValue >= 0) { int pixelIndex = getPixelValue(paintingData, kernelPixelX, kernelPixelY); if (pixelIndex >= 0) { - totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex)); - totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 1)); - totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 2)); + totals[0] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex)); + totals[1] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 1)); + totals[2] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 2)); } if (!preserveAlphaValues && pixelIndex >= 0) - totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 3)); + totals[3] += paintingData.kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->item(pixelIndex + 3)); ++kernelPixelX; --kernelValue; if (!--width) { @@ -386,7 +385,7 @@ void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, in } } -#if defined(_MSC_VER) && (_MSC_VER >= 1700) +#if COMPILER(MSVC) #pragma warning(pop) // Disable of 4789 #endif @@ -410,11 +409,6 @@ ALWAYS_INLINE void FEConvolveMatrix::setOuterPixels(PaintingData& paintingData, fastSetOuterPixels<false>(paintingData, x1, y1, x2, y2); } -void FEConvolveMatrix::setInteriorPixelsWorker(InteriorPixelParameters* param) -{ - param->filter->setInteriorPixels(*param->paintingData, param->clipRight, param->clipBottom, param->yStart, param->yEnd); -} - void FEConvolveMatrix::platformApplySoftware() { FilterEffect* in = inputEffect(0); @@ -442,55 +436,41 @@ void FEConvolveMatrix::platformApplySoftware() paintingData.width = paintSize.width(); paintingData.height = paintSize.height(); paintingData.bias = m_bias * 255; + paintingData.kernelMatrix = normalizedFloats(m_kernelMatrix); // Drawing fully covered pixels int clipRight = paintSize.width() - m_kernelSize.width(); int clipBottom = paintSize.height() - m_kernelSize.height(); - if (clipRight >= 0 && clipBottom >= 0) { - - int optimalThreadNumber = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension; - if (optimalThreadNumber > 1) { - WTF::ParallelJobs<InteriorPixelParameters> parallelJobs(&WebCore::FEConvolveMatrix::setInteriorPixelsWorker, optimalThreadNumber); - const int numOfThreads = parallelJobs.numberOfJobs(); - - // Split the job into "heightPerThread" jobs but there a few jobs that need to be slightly larger since - // heightPerThread * jobs < total size. These extras are handled by the remainder "jobsWithExtra". - const int heightPerThread = clipBottom / numOfThreads; - const int jobsWithExtra = clipBottom % numOfThreads; - - int startY = 0; - for (int job = 0; job < numOfThreads; ++job) { - InteriorPixelParameters& param = parallelJobs.parameter(job); - param.filter = this; - param.paintingData = &paintingData; - param.clipRight = clipRight; - param.clipBottom = clipBottom; - param.yStart = startY; - startY += job < jobsWithExtra ? heightPerThread + 1 : heightPerThread; - param.yEnd = startY; - } - - parallelJobs.execute(); - } else { - // Fallback to single threaded mode. - setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom); - } - - clipRight += m_targetOffset.x() + 1; - clipBottom += m_targetOffset.y() + 1; - if (m_targetOffset.y() > 0) - setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); - if (clipBottom < paintSize.height()) - setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); - if (m_targetOffset.x() > 0) - setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); - if (clipRight < paintSize.width()) - setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); - } else { + if (clipRight < 0 || clipBottom < 0) { // Rare situation, not optimizied for speed setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height()); + return; } + + if (int iterations = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension) { + int stride = clipBottom / iterations; + int chunkCount = (clipBottom + stride - 1) / stride; + + WorkQueue::concurrentApply(chunkCount, [&](size_t index) { + int yStart = (stride * index); + int yEnd = std::min<int>(stride * (index + 1), clipBottom); + + setInteriorPixels(paintingData, clipRight, clipBottom, yStart, yEnd); + }); + } else + setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom); + + clipRight += m_targetOffset.x() + 1; + clipBottom += m_targetOffset.y() + 1; + if (m_targetOffset.y() > 0) + setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); + if (clipBottom < paintSize.height()) + setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); + if (m_targetOffset.x() > 0) + setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); + if (clipRight < paintSize.width()) + setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); } void FEConvolveMatrix::dump() @@ -534,5 +514,3 @@ TextStream& FEConvolveMatrix::externalRepresentation(TextStream& ts, int indent) } }; // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h index 846e24497..8518c97cc 100644 --- a/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h +++ b/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.h @@ -23,7 +23,6 @@ #ifndef FEConvolveMatrix_h #define FEConvolveMatrix_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "FloatPoint.h" #include "FloatSize.h" @@ -41,7 +40,7 @@ enum EdgeModeType { class FEConvolveMatrix : public FilterEffect { public: - static PassRefPtr<FEConvolveMatrix> create(Filter*, const IntSize&, + static Ref<FEConvolveMatrix> create(Filter&, const IntSize&, float, float, const IntPoint&, EdgeModeType, const FloatPoint&, bool, const Vector<float>&); @@ -69,12 +68,12 @@ public: bool preserveAlpha() const; bool setPreserveAlpha(bool); - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } + void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: @@ -84,9 +83,10 @@ private: int width; int height; float bias; + Vector<float> kernelMatrix; }; - FEConvolveMatrix(Filter*, const IntSize&, float, float, + FEConvolveMatrix(Filter&, const IntSize&, float, float, const IntPoint&, EdgeModeType, const FloatPoint&, bool, const Vector<float>&); template<bool preserveAlphaValues> @@ -104,20 +104,6 @@ private: // Parallelization parts static const int s_minimalRectDimension = (100 * 100); // Empirical data limit for parallel jobs - template<typename Type> - friend class ParallelJobs; - - struct InteriorPixelParameters { - FEConvolveMatrix* filter; - PaintingData* paintingData; - int clipBottom; - int clipRight; - int yStart; - int yEnd; - }; - - static void setInteriorPixelsWorker(InteriorPixelParameters*); - IntSize m_kernelSize; float m_divisor; float m_bias; @@ -130,6 +116,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEConvolveMatrix_h diff --git a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp index dadc2962a..1b4303c98 100644 --- a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp +++ b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp @@ -20,8 +20,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEDiffuseLighting.h" #include "LightSource.h" @@ -29,24 +27,24 @@ namespace WebCore { -FEDiffuseLighting::FEDiffuseLighting(Filter* filter, const Color& lightingColor, float surfaceScale, +FEDiffuseLighting::FEDiffuseLighting(Filter& filter, const Color& lightingColor, float surfaceScale, float diffuseConstant, float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource) : FELighting(filter, DiffuseLighting, lightingColor, surfaceScale, diffuseConstant, 0, 0, kernelUnitLengthX, kernelUnitLengthY, lightSource) { } -PassRefPtr<FEDiffuseLighting> FEDiffuseLighting::create(Filter* filter, const Color& lightingColor, +Ref<FEDiffuseLighting> FEDiffuseLighting::create(Filter& filter, const Color& lightingColor, float surfaceScale, float diffuseConstant, float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource) { - return adoptRef(new FEDiffuseLighting(filter, lightingColor, surfaceScale, diffuseConstant, kernelUnitLengthX, kernelUnitLengthY, lightSource)); + return adoptRef(*new FEDiffuseLighting(filter, lightingColor, surfaceScale, diffuseConstant, kernelUnitLengthX, kernelUnitLengthY, lightSource)); } FEDiffuseLighting::~FEDiffuseLighting() { } -Color FEDiffuseLighting::lightingColor() const +const Color& FEDiffuseLighting::lightingColor() const { return m_lightingColor; } @@ -138,5 +136,3 @@ TextStream& FEDiffuseLighting::externalRepresentation(TextStream& ts, int indent } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h index 5f2065163..5fe2be9f4 100644 --- a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h +++ b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h @@ -22,7 +22,6 @@ #ifndef FEDiffuseLighting_h #define FEDiffuseLighting_h -#if ENABLE(FILTERS) #include "FELighting.h" namespace WebCore { @@ -31,11 +30,11 @@ class LightSource; class FEDiffuseLighting : public FELighting { public: - static PassRefPtr<FEDiffuseLighting> create(Filter*, const Color&, float, float, + static Ref<FEDiffuseLighting> create(Filter&, const Color&, float, float, float, float, PassRefPtr<LightSource>); virtual ~FEDiffuseLighting(); - Color lightingColor() const; + const Color& lightingColor() const; bool setLightingColor(const Color&); float surfaceScale() const; @@ -53,16 +52,14 @@ public: const LightSource* lightSource() const; void setLightSource(PassRefPtr<LightSource>); - virtual void dump(); + void dump() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEDiffuseLighting(Filter*, const Color&, float, float, float, float, PassRefPtr<LightSource>); + FEDiffuseLighting(Filter&, const Color&, float, float, float, float, PassRefPtr<LightSource>); }; } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEDiffuseLighting_h diff --git a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp index dfb9194da..51eb774d4 100644 --- a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp +++ b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp @@ -22,8 +22,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEDisplacementMap.h" #include "Filter.h" @@ -34,7 +32,7 @@ namespace WebCore { -FEDisplacementMap::FEDisplacementMap(Filter* filter, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float scale) +FEDisplacementMap::FEDisplacementMap(Filter& filter, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float scale) : FilterEffect(filter) , m_xChannelSelector(xChannelSelector) , m_yChannelSelector(yChannelSelector) @@ -42,10 +40,10 @@ FEDisplacementMap::FEDisplacementMap(Filter* filter, ChannelSelectorType xChanne { } -PassRefPtr<FEDisplacementMap> FEDisplacementMap::create(Filter* filter, ChannelSelectorType xChannelSelector, +Ref<FEDisplacementMap> FEDisplacementMap::create(Filter& filter, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float scale) { - return adoptRef(new FEDisplacementMap(filter, xChannelSelector, yChannelSelector, scale)); + return adoptRef(*new FEDisplacementMap(filter, xChannelSelector, yChannelSelector, scale)); } ChannelSelectorType FEDisplacementMap::xChannelSelector() const @@ -123,10 +121,10 @@ void FEDisplacementMap::platformApplySoftware() ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length()); - Filter* filter = this->filter(); + Filter& filter = this->filter(); IntSize paintSize = absolutePaintRect().size(); - float scaleX = filter->applyHorizontalScale(m_scale); - float scaleY = filter->applyVerticalScale(m_scale); + float scaleX = filter.applyHorizontalScale(m_scale); + float scaleY = filter.applyVerticalScale(m_scale); float scaleForColorX = scaleX / 255.0; float scaleForColorY = scaleY / 255.0; float scaledOffsetX = 0.5 - scaleX * 0.5; @@ -190,5 +188,3 @@ TextStream& FEDisplacementMap::externalRepresentation(TextStream& ts, int indent } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h index b0ea3d006..f9afbae1a 100644 --- a/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h +++ b/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h @@ -22,7 +22,6 @@ #ifndef FEDisplacementMap_h #define FEDisplacementMap_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" #include <wtf/text/WTFString.h> @@ -39,7 +38,7 @@ enum ChannelSelectorType { class FEDisplacementMap : public FilterEffect { public: - static PassRefPtr<FEDisplacementMap> create(Filter*, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float); + static Ref<FEDisplacementMap> create(Filter&, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float); ChannelSelectorType xChannelSelector() const; bool setXChannelSelector(const ChannelSelectorType); @@ -51,17 +50,17 @@ public: bool setScale(float); void setResultColorSpace(ColorSpace) override; - virtual void transformResultColorSpace(FilterEffect*, const int) override; + void transformResultColorSpace(FilterEffect*, const int) override; - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } + void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEDisplacementMap(Filter*, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float); + FEDisplacementMap(Filter&, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float); ChannelSelectorType m_xChannelSelector; ChannelSelectorType m_yChannelSelector; @@ -70,6 +69,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEDisplacementMap_h diff --git a/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp b/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp index bdc94d2d8..fa0be80df 100644 --- a/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp +++ b/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp @@ -18,11 +18,8 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEDropShadow.h" -#include "ColorSpace.h" #include "FEGaussianBlur.h" #include "Filter.h" #include "GraphicsContext.h" @@ -33,7 +30,7 @@ namespace WebCore { -FEDropShadow::FEDropShadow(Filter* filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity) +FEDropShadow::FEDropShadow(Filter& filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity) : FilterEffect(filter) , m_stdX(stdX) , m_stdY(stdY) @@ -44,28 +41,25 @@ FEDropShadow::FEDropShadow(Filter* filter, float stdX, float stdY, float dx, flo { } -PassRefPtr<FEDropShadow> FEDropShadow::create(Filter* filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity) +Ref<FEDropShadow> FEDropShadow::create(Filter& filter, float stdX, float stdY, float dx, float dy, const Color& shadowColor, float shadowOpacity) { - return adoptRef(new FEDropShadow(filter, stdX, stdY, dx, dy, shadowColor, shadowOpacity)); + return adoptRef(*new FEDropShadow(filter, stdX, stdY, dx, dy, shadowColor, shadowOpacity)); } void FEDropShadow::determineAbsolutePaintRect() { - Filter* filter = this->filter(); - ASSERT(filter); + Filter& filter = this->filter(); FloatRect absolutePaintRect = inputEffect(0)->absolutePaintRect(); FloatRect absoluteOffsetPaintRect(absolutePaintRect); - absoluteOffsetPaintRect.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); + absoluteOffsetPaintRect.move(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy)); absolutePaintRect.unite(absoluteOffsetPaintRect); - - unsigned kernelSizeX = 0; - unsigned kernelSizeY = 0; - FEGaussianBlur::calculateKernelSize(filter, kernelSizeX, kernelSizeY, m_stdX, m_stdY); - + + IntSize kernelSize = FEGaussianBlur::calculateKernelSize(filter, FloatPoint(m_stdX, m_stdY)); + // We take the half kernel size and multiply it with three, because we run box blur three times. - absolutePaintRect.inflateX(3 * kernelSizeX * 0.5f); - absolutePaintRect.inflateY(3 * kernelSizeY * 0.5f); + absolutePaintRect.inflateX(3 * kernelSize.width() * 0.5f); + absolutePaintRect.inflateY(3 * kernelSize.height() * 0.5f); if (clipsToBounds()) absolutePaintRect.intersect(maxEffectRect()); @@ -83,37 +77,39 @@ void FEDropShadow::platformApplySoftware() if (!resultImage) return; - Filter* filter = this->filter(); - FloatSize blurRadius(filter->applyHorizontalScale(m_stdX), filter->applyVerticalScale(m_stdY)); - FloatSize offset(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); + Filter& filter = this->filter(); + FloatSize blurRadius(2 * filter.applyHorizontalScale(m_stdX), 2 * filter.applyVerticalScale(m_stdY)); + blurRadius.scale(filter.filterScale()); + FloatSize offset(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy)); FloatRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect()); FloatRect drawingRegionWithOffset(drawingRegion); drawingRegionWithOffset.move(offset); ImageBuffer* sourceImage = in->asImageBuffer(); - ASSERT(sourceImage); - GraphicsContext* resultContext = resultImage->context(); - ASSERT(resultContext); - resultContext->setAlpha(m_shadowOpacity); - resultContext->drawImageBuffer(sourceImage, ColorSpaceDeviceRGB, drawingRegionWithOffset); - resultContext->setAlpha(1); + if (!sourceImage) + return; - ShadowBlur contextShadow(blurRadius, offset, m_shadowColor, ColorSpaceDeviceRGB); + GraphicsContext& resultContext = resultImage->context(); + resultContext.setAlpha(m_shadowOpacity); + resultContext.drawImageBuffer(*sourceImage, drawingRegionWithOffset); + resultContext.setAlpha(1); + + ShadowBlur contextShadow(blurRadius, offset, m_shadowColor); // TODO: Direct pixel access to ImageBuffer would avoid copying the ImageData. IntRect shadowArea(IntPoint(), resultImage->internalSize()); - RefPtr<Uint8ClampedArray> srcPixelArray = resultImage->getPremultipliedImageData(shadowArea); + RefPtr<Uint8ClampedArray> srcPixelArray = resultImage->getPremultipliedImageData(shadowArea, ImageBuffer::BackingStoreCoordinateSystem); contextShadow.blurLayerImage(srcPixelArray->data(), shadowArea.size(), 4 * shadowArea.size().width()); - resultImage->putByteArray(Premultiplied, srcPixelArray.get(), shadowArea.size(), shadowArea, IntPoint()); + resultImage->putByteArray(Premultiplied, srcPixelArray.get(), shadowArea.size(), shadowArea, IntPoint(), ImageBuffer::BackingStoreCoordinateSystem); - resultContext->setCompositeOperation(CompositeSourceIn); - resultContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), m_shadowColor, ColorSpaceDeviceRGB); - resultContext->setCompositeOperation(CompositeDestinationOver); + resultContext.setCompositeOperation(CompositeSourceIn); + resultContext.fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), m_shadowColor); + resultContext.setCompositeOperation(CompositeDestinationOver); - resultImage->context()->drawImageBuffer(sourceImage, ColorSpaceDeviceRGB, drawingRegion); + resultImage->context().drawImageBuffer(*sourceImage, drawingRegion); } void FEDropShadow::dump() @@ -131,5 +127,3 @@ TextStream& FEDropShadow::externalRepresentation(TextStream& ts, int indent) con } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEDropShadow.h b/Source/WebCore/platform/graphics/filters/FEDropShadow.h index d155b4638..36c9d74b5 100644 --- a/Source/WebCore/platform/graphics/filters/FEDropShadow.h +++ b/Source/WebCore/platform/graphics/filters/FEDropShadow.h @@ -20,7 +20,6 @@ #ifndef FEDropShadow_h #define FEDropShadow_h -#if ENABLE(FILTERS) #include "Color.h" #include "Filter.h" #include "FilterEffect.h" @@ -29,7 +28,7 @@ namespace WebCore { class FEDropShadow : public FilterEffect { public: - static PassRefPtr<FEDropShadow> create(Filter*, float, float, float, float, const Color&, float); + static Ref<FEDropShadow> create(Filter&, float, float, float, float, const Color&, float); float stdDeviationX() const { return m_stdX; } void setStdDeviationX(float stdX) { m_stdX = stdX; } @@ -43,23 +42,21 @@ public: float dy() const { return m_dy; } void setDy(float dy) { m_dy = dy; } - Color shadowColor() const { return m_shadowColor; } + const Color& shadowColor() const { return m_shadowColor; } void setShadowColor(const Color& shadowColor) { m_shadowColor = shadowColor; } float shadowOpacity() const { return m_shadowOpacity; } void setShadowOpacity(float shadowOpacity) { m_shadowOpacity = shadowOpacity; } - static float calculateStdDeviation(float); + void platformApplySoftware() override; + void dump() override; - virtual void platformApplySoftware(); - virtual void dump(); + void determineAbsolutePaintRect() override; - virtual void determineAbsolutePaintRect(); - - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEDropShadow(Filter*, float, float, float, float, const Color&, float); + FEDropShadow(Filter&, float, float, float, float, const Color&, float); float m_stdX; float m_stdY; @@ -71,5 +68,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) #endif // FEDropShadow_h diff --git a/Source/WebCore/platform/graphics/filters/FEFlood.cpp b/Source/WebCore/platform/graphics/filters/FEFlood.cpp index 35bfb839e..f6c408631 100644 --- a/Source/WebCore/platform/graphics/filters/FEFlood.cpp +++ b/Source/WebCore/platform/graphics/filters/FEFlood.cpp @@ -21,8 +21,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEFlood.h" #include "Filter.h" @@ -31,19 +29,19 @@ namespace WebCore { -FEFlood::FEFlood(Filter* filter, const Color& floodColor, float floodOpacity) +FEFlood::FEFlood(Filter& filter, const Color& floodColor, float floodOpacity) : FilterEffect(filter) , m_floodColor(floodColor) , m_floodOpacity(floodOpacity) { } -PassRefPtr<FEFlood> FEFlood::create(Filter* filter, const Color& floodColor, float floodOpacity) +Ref<FEFlood> FEFlood::create(Filter& filter, const Color& floodColor, float floodOpacity) { - return adoptRef(new FEFlood(filter, floodColor, floodOpacity)); + return adoptRef(*new FEFlood(filter, floodColor, floodOpacity)); } -Color FEFlood::floodColor() const +const Color& FEFlood::floodColor() const { return m_floodColor; } @@ -75,8 +73,8 @@ void FEFlood::platformApplySoftware() if (!resultImage) return; - Color color = colorWithOverrideAlpha(floodColor().rgb(), floodOpacity()); - resultImage->context()->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color, ColorSpaceDeviceRGB); + const Color& color = colorWithOverrideAlpha(floodColor().rgb(), floodOpacity()); + resultImage->context().fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color); } void FEFlood::dump() @@ -94,5 +92,3 @@ TextStream& FEFlood::externalRepresentation(TextStream& ts, int indent) const } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEFlood.h b/Source/WebCore/platform/graphics/filters/FEFlood.h index ee93fcf55..ae03b1931 100644 --- a/Source/WebCore/platform/graphics/filters/FEFlood.h +++ b/Source/WebCore/platform/graphics/filters/FEFlood.h @@ -22,7 +22,6 @@ #ifndef FEFlood_h #define FEFlood_h -#if ENABLE(FILTERS) #include "Color.h" #include "Filter.h" #include "FilterEffect.h" @@ -31,10 +30,10 @@ namespace WebCore { class FEFlood : public FilterEffect { public: - static PassRefPtr<FEFlood> create(Filter* filter, const Color&, float); + static Ref<FEFlood> create(Filter&, const Color&, float); - Color floodColor() const; - bool setFloodColor(const Color &); + const Color& floodColor() const; + bool setFloodColor(const Color&); float floodOpacity() const; bool setFloodOpacity(float); @@ -42,22 +41,19 @@ public: #if !USE(CG) // feFlood does not perform color interpolation of any kind, so the result is always in the current // color space regardless of the value of color-interpolation-filters. - void setOperatingColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceDeviceRGB); } - void setResultColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceDeviceRGB); } + void setOperatingColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceSRGB); } + void setResultColorSpace(ColorSpace) override { FilterEffect::setResultColorSpace(ColorSpaceSRGB); } #endif - virtual void platformApplySoftware(); -#if ENABLE(OPENCL) - virtual bool platformApplyOpenCL(); -#endif - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } + void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEFlood(Filter*, const Color&, float); + FEFlood(Filter&, const Color&, float); Color m_floodColor; float m_floodOpacity; @@ -65,6 +61,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEFlood_h diff --git a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp index 5669ba12f..0b3b5caf7 100644 --- a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp +++ b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp @@ -5,6 +5,7 @@ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2010 Igalia, S.L. * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * Copyright (C) 2015-2016 Apple, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,8 +24,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEGaussianBlur.h" #include "FEGaussianBlurNEON.h" @@ -32,7 +31,11 @@ #include "GraphicsContext.h" #include "TextStream.h" -#include <runtime/Operations.h> +#if USE(ACCELERATE) +#include <Accelerate/Accelerate.h> +#endif + +#include <runtime/JSCInlines.h> #include <runtime/TypedArrayInlines.h> #include <runtime/Uint8ClampedArray.h> #include <wtf/MathExtras.h> @@ -43,11 +46,39 @@ static inline float gaussianKernelFactor() return 3 / 4.f * sqrtf(2 * piFloat); } -static const unsigned gMaxKernelSize = 1000; +static const int gMaxKernelSize = 500; namespace WebCore { -FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y, EdgeModeType edgeMode) +inline void kernelPosition(int blurIteration, unsigned& radius, int& deltaLeft, int& deltaRight) +{ + // Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details. + switch (blurIteration) { + case 0: + if (!(radius % 2)) { + deltaLeft = radius / 2 - 1; + deltaRight = radius - deltaLeft; + } else { + deltaLeft = radius / 2; + deltaRight = radius - deltaLeft; + } + break; + case 1: + if (!(radius % 2)) { + deltaLeft++; + deltaRight--; + } + break; + case 2: + if (!(radius % 2)) { + deltaRight++; + radius++; + } + break; + } +} + +FEGaussianBlur::FEGaussianBlur(Filter& filter, float x, float y, EdgeModeType edgeMode) : FilterEffect(filter) , m_stdX(x) , m_stdY(y) @@ -55,9 +86,9 @@ FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y, EdgeModeType ed { } -PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float y, EdgeModeType edgeMode) +Ref<FEGaussianBlur> FEGaussianBlur::create(Filter& filter, float x, float y, EdgeModeType edgeMode) { - return adoptRef(new FEGaussianBlur(filter, x, y, edgeMode)); + return adoptRef(*new FEGaussianBlur(filter, x, y, edgeMode)); } float FEGaussianBlur::stdDeviationX() const @@ -90,86 +121,246 @@ void FEGaussianBlur::setEdgeMode(EdgeModeType edgeMode) m_edgeMode = edgeMode; } -inline void boxBlur(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray, +// This function only operates on Alpha channel. +inline void boxBlurAlphaOnly(const Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray, + unsigned dx, int& dxLeft, int& dxRight, int& stride, int& strideLine, int& effectWidth, int& effectHeight, const int& maxKernelSize) +{ + unsigned char* srcData = srcPixelArray->data(); + unsigned char* dstData = dstPixelArray->data(); + // Memory alignment is: RGBA, zero-index based. + const int channel = 3; + + for (int y = 0; y < effectHeight; ++y) { + int line = y * strideLine; + int sum = 0; + + // Fill the kernel. + for (int i = 0; i < maxKernelSize; ++i) { + unsigned offset = line + i * stride; + unsigned char* srcPtr = srcData + offset; + sum += srcPtr[channel]; + } + + // Blurring. + for (int x = 0; x < effectWidth; ++x) { + unsigned pixelByteOffset = line + x * stride + channel; + unsigned char* dstPtr = dstData + pixelByteOffset; + *dstPtr = static_cast<unsigned char>(sum / dx); + + // Shift kernel. + if (x >= dxLeft) { + unsigned leftOffset = pixelByteOffset - dxLeft * stride; + unsigned char* srcPtr = srcData + leftOffset; + sum -= *srcPtr; + } + + if (x + dxRight < effectWidth) { + unsigned rightOffset = pixelByteOffset + dxRight * stride; + unsigned char* srcPtr = srcData + rightOffset; + sum += *srcPtr; + } + } + } +} + +inline void boxBlur(const Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray, unsigned dx, int dxLeft, int dxRight, int stride, int strideLine, int effectWidth, int effectHeight, bool alphaImage, EdgeModeType edgeMode) { + const int maxKernelSize = std::min(dxRight, effectWidth); + if (alphaImage) { + return boxBlurAlphaOnly(srcPixelArray, dstPixelArray, dx, dxLeft, dxRight, stride, strideLine, + effectWidth, effectHeight, maxKernelSize); + } + + unsigned char* srcData = srcPixelArray->data(); + unsigned char* dstData = dstPixelArray->data(); + + // Concerning the array width/length: it is Element size + Margin + Border. The number of pixels will be + // P = width * height * channels. for (int y = 0; y < effectHeight; ++y) { int line = y * strideLine; - for (int channel = 3; channel >= 0; --channel) { - int sum = 0; - // The code for edgeMode='none' is the common case and highly optimized. - // Furthermore, this code path affects more than just the input area. - if (edgeMode == EDGEMODE_NONE) { - // Fill the kernel - int maxKernelSize = std::min(dxRight, effectWidth); - for (int i = 0; i < maxKernelSize; ++i) - sum += srcPixelArray->item(line + i * stride + channel); - - // Blurring - for (int x = 0; x < effectWidth; ++x) { - int pixelByteOffset = line + x * stride + channel; - dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx)); - // Shift kernel. - if (x >= dxLeft) - sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride); - if (x + dxRight < effectWidth) - sum += srcPixelArray->item(pixelByteOffset + dxRight * stride); + int sumR = 0, sumG = 0, sumB = 0, sumA = 0; + + if (edgeMode == EDGEMODE_NONE) { + // Fill the kernel. + for (int i = 0; i < maxKernelSize; ++i) { + unsigned offset = line + i * stride; + unsigned char* srcPtr = srcData + offset; + sumR += *srcPtr++; + sumG += *srcPtr++; + sumB += *srcPtr++; + sumA += *srcPtr; + } + + // Blurring. + for (int x = 0; x < effectWidth; ++x) { + unsigned pixelByteOffset = line + x * stride; + unsigned char* dstPtr = dstData + pixelByteOffset; + + *dstPtr++ = static_cast<unsigned char>(sumR / dx); + *dstPtr++ = static_cast<unsigned char>(sumG / dx); + *dstPtr++ = static_cast<unsigned char>(sumB / dx); + *dstPtr = static_cast<unsigned char>(sumA / dx); + + // Shift kernel. + if (x >= dxLeft) { + unsigned leftOffset = pixelByteOffset - dxLeft * stride; + unsigned char* srcPtr = srcData + leftOffset; + sumR -= srcPtr[0]; + sumG -= srcPtr[1]; + sumB -= srcPtr[2]; + sumA -= srcPtr[3]; + } + + if (x + dxRight < effectWidth) { + unsigned rightOffset = pixelByteOffset + dxRight * stride; + unsigned char* srcPtr = srcData + rightOffset; + sumR += srcPtr[0]; + sumG += srcPtr[1]; + sumB += srcPtr[2]; + sumA += srcPtr[3]; + } + } + + } else { + // FIXME: Add support for 'wrap' here. + // Get edge values for edgeMode 'duplicate'. + unsigned char* edgeValueLeft = srcData + line; + unsigned char* edgeValueRight = srcData + (line + (effectWidth - 1) * stride); + + // Fill the kernel. + for (int i = dxLeft * -1; i < dxRight; ++i) { + // Is this right for negative values of 'i'? + unsigned offset = line + i * stride; + unsigned char* srcPtr = srcData + offset; + + if (i < 0) { + sumR += edgeValueLeft[0]; + sumG += edgeValueLeft[1]; + sumB += edgeValueLeft[2]; + sumA += edgeValueLeft[3]; + } else if (i >= effectWidth) { + sumR += edgeValueRight[0]; + sumG += edgeValueRight[1]; + sumB += edgeValueRight[2]; + sumA += edgeValueRight[3]; + } else { + sumR += *srcPtr++; + sumG += *srcPtr++; + sumB += *srcPtr++; + sumA += *srcPtr; } - } else { - // FIXME: Add support for 'wrap' here. - // Get edge values for edgeMode 'duplicate'. - int edgeValueLeft = srcPixelArray->item(line + channel); - int edgeValueRight = srcPixelArray->item(line + (effectWidth - 1) * stride + channel); - // Fill the kernel - for (int i = dxLeft * -1; i < dxRight; ++i) { - if (i < 0) - sum += edgeValueLeft; - else if (i >= effectWidth) - sum += edgeValueRight; - else - sum += srcPixelArray->item(line + i * stride + channel); + } + + // Blurring. + for (int x = 0; x < effectWidth; ++x) { + unsigned pixelByteOffset = line + x * stride; + unsigned char* dstPtr = dstData + pixelByteOffset; + + *dstPtr++ = static_cast<unsigned char>(sumR / dx); + *dstPtr++ = static_cast<unsigned char>(sumG / dx); + *dstPtr++ = static_cast<unsigned char>(sumB / dx); + *dstPtr = static_cast<unsigned char>(sumA / dx); + + // Shift kernel. + if (x < dxLeft) { + sumR -= edgeValueLeft[0]; + sumG -= edgeValueLeft[1]; + sumB -= edgeValueLeft[2]; + sumA -= edgeValueLeft[3]; + } else { + unsigned leftOffset = pixelByteOffset - dxLeft * stride; + unsigned char* srcPtr = srcData + leftOffset; + sumR -= srcPtr[0]; + sumG -= srcPtr[1]; + sumB -= srcPtr[2]; + sumA -= srcPtr[3]; } - // Blurring - for (int x = 0; x < effectWidth; ++x) { - int pixelByteOffset = line + x * stride + channel; - dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx)); - // Shift kernel. - if (x < dxLeft) - sum -= edgeValueLeft; - else - sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride); - if (x + dxRight >= effectWidth) - sum += edgeValueRight; - else - sum += srcPixelArray->item(pixelByteOffset + dxRight * stride); + + if (x + dxRight >= effectWidth) { + sumR += edgeValueRight[0]; + sumG += edgeValueRight[1]; + sumB += edgeValueRight[2]; + sumA += edgeValueRight[3]; + } else { + unsigned rightOffset = pixelByteOffset + dxRight * stride; + unsigned char* srcPtr = srcData + rightOffset; + sumR += srcPtr[0]; + sumG += srcPtr[1]; + sumB += srcPtr[2]; + sumA += srcPtr[3]; } } - if (alphaImage) // Source image is black, it just has different alpha values - break; } } } -inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize) +#if USE(ACCELERATE) +inline void accelerateBoxBlur(const Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSize, int stride, int effectWidth, int effectHeight) +{ + if (!src || !src->data() || !dst || !dst->data()) { + ASSERT_NOT_REACHED(); + return; + } + + if (effectWidth <= 0 || effectHeight <= 0 || stride <= 0) { + ASSERT_NOT_REACHED(); + return; + } + + // We must always use an odd radius. + if (kernelSize % 2 != 1) + kernelSize += 1; + + vImage_Buffer effectInBuffer; + effectInBuffer.data = src->data(); + effectInBuffer.width = effectWidth; + effectInBuffer.height = effectHeight; + effectInBuffer.rowBytes = stride; + + vImage_Buffer effectOutBuffer; + effectOutBuffer.data = dst->data(); + effectOutBuffer.width = effectWidth; + effectOutBuffer.height = effectHeight; + effectOutBuffer.rowBytes = stride; + + // Determine the size of a temporary buffer by calling the function first with a special flag. vImage will return + // the size needed, or an error (which are all negative). + size_t tmpBufferSize = vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, 0, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend | kvImageGetTempBufferSize); + if (tmpBufferSize <= 0) + return; + + void* tmpBuffer = fastMalloc(tmpBufferSize); + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend); + WTF::fastFree(tmpBuffer); + + // The final result should be stored in src. + if (dst == src) { + ASSERT(src->length() == dst->length()); + memcpy(dst->data(), src->data(), src->length()); + } +} +#endif + +inline void standardBoxBlur(Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSizeX, unsigned kernelSizeY, int stride, IntSize& paintSize, bool isAlphaImage, EdgeModeType edgeMode) { - int stride = 4 * paintSize.width(); int dxLeft = 0; int dxRight = 0; int dyLeft = 0; int dyRight = 0; - Uint8ClampedArray* src = srcPixelArray; - Uint8ClampedArray* dst = tmpPixelArray; for (int i = 0; i < 3; ++i) { if (kernelSizeX) { kernelPosition(i, kernelSizeX, dxLeft, dxRight); #if HAVE(ARM_NEON_INTRINSICS) - if (!isAlphaImage()) + if (!isAlphaImage) boxBlurNEON(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height()); else - boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true, m_edgeMode); + boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true, edgeMode); #else - boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage(), m_edgeMode); + boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage, edgeMode); #endif std::swap(src, dst); } @@ -177,23 +368,36 @@ inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArra if (kernelSizeY) { kernelPosition(i, kernelSizeY, dyLeft, dyRight); #if HAVE(ARM_NEON_INTRINSICS) - if (!isAlphaImage()) + if (!isAlphaImage) boxBlurNEON(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width()); else - boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true, m_edgeMode); + boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true, edgeMode); #else - boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage(), m_edgeMode); + boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage, edgeMode); #endif std::swap(src, dst); } } - // The final result should be stored in srcPixelArray. - if (dst == srcPixelArray) { + // The final result should be stored in src. + if (dst == src) { ASSERT(src->length() == dst->length()); memcpy(dst->data(), src->data(), src->length()); } +} + +inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize) +{ + int stride = 4 * paintSize.width(); + +#if USE(ACCELERATE) + if (kernelSizeX == kernelSizeY && (m_edgeMode == EDGEMODE_NONE || m_edgeMode == EDGEMODE_DUPLICATE)) { + accelerateBoxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, stride, paintSize.width(), paintSize.height()); + return; + } +#endif + standardBoxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, stride, paintSize, isAlphaImage(), m_edgeMode); } void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters) @@ -205,6 +409,7 @@ void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters) inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize) { +#if !USE(ACCELERATE) int scanline = 4 * paintSize.width(); int extraHeight = 3 * kernelSizeY * 0.5f; int optimalThreadNumber = (paintSize.width() * paintSize.height()) / (s_minimalRectDimension + extraHeight * paintSize.width()); @@ -266,43 +471,43 @@ inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint } // Fallback to single threaded mode. } +#endif // The selection here eventually should happen dynamically on some platforms. platformApplyGeneric(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize); } -void FEGaussianBlur::calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY) +static int clampedToKernelSize(float value) { - ASSERT(stdX >= 0 && stdY >= 0); - - kernelSizeX = 0; - if (stdX) - kernelSizeX = std::max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gaussianKernelFactor() + 0.5f))); - kernelSizeY = 0; - if (stdY) - kernelSizeY = std::max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gaussianKernelFactor() + 0.5f))); + // Limit the kernel size to 500. A bigger radius won't make a big difference for the result image but + // inflates the absolute paint rect too much. This is compatible with Firefox' behavior. + unsigned size = std::max<unsigned>(2, static_cast<unsigned>(floorf(value * gaussianKernelFactor() + 0.5f))); + return clampTo<int>(std::min(size, static_cast<unsigned>(gMaxKernelSize))); +} - // Limit the kernel size to 1000. A bigger radius won't make a big difference for the result image but - // inflates the absolute paint rect to much. This is compatible with Firefox' behavior. - if (kernelSizeX > gMaxKernelSize) - kernelSizeX = gMaxKernelSize; - if (kernelSizeY > gMaxKernelSize) - kernelSizeY = gMaxKernelSize; +IntSize FEGaussianBlur::calculateUnscaledKernelSize(const FloatPoint& stdDeviation) +{ + ASSERT(stdDeviation.x() >= 0 && stdDeviation.y() >= 0); + IntSize kernelSize; + + if (stdDeviation.x()) + kernelSize.setWidth(clampedToKernelSize(stdDeviation.x())); + + if (stdDeviation.y()) + kernelSize.setHeight(clampedToKernelSize(stdDeviation.y())); + + return kernelSize; } -void FEGaussianBlur::calculateKernelSize(Filter* filter, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY) +IntSize FEGaussianBlur::calculateKernelSize(const Filter& filter, const FloatPoint& stdDeviation) { - stdX = filter->applyHorizontalScale(stdX); - stdY = filter->applyVerticalScale(stdY); - - calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdX, stdY); + FloatPoint stdFilterScaled(filter.applyHorizontalScale(stdDeviation.x()), filter.applyVerticalScale(stdDeviation.y())); + return calculateUnscaledKernelSize(stdFilterScaled); } void FEGaussianBlur::determineAbsolutePaintRect() { - unsigned kernelSizeX = 0; - unsigned kernelSizeY = 0; - calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); + IntSize kernelSize = calculateKernelSize(filter(), FloatPoint(m_stdX, m_stdY)); FloatRect absolutePaintRect = inputEffect(0)->absolutePaintRect(); // Edge modes other than 'none' do not inflate the affected paint rect. @@ -312,8 +517,8 @@ void FEGaussianBlur::determineAbsolutePaintRect() } // We take the half kernel size and multiply it with three, because we run box blur three times. - absolutePaintRect.inflateX(3 * kernelSizeX * 0.5f); - absolutePaintRect.inflateY(3 * kernelSizeY * 0.5f); + absolutePaintRect.inflateX(3 * kernelSize.width() * 0.5f); + absolutePaintRect.inflateY(3 * kernelSize.height() * 0.5f); if (clipsToBounds()) absolutePaintRect.intersect(maxEffectRect()); @@ -339,15 +544,18 @@ void FEGaussianBlur::platformApplySoftware() if (!m_stdX && !m_stdY) return; - unsigned kernelSizeX = 0; - unsigned kernelSizeY = 0; - calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); + IntSize kernelSize = calculateKernelSize(filter(), FloatPoint(m_stdX, m_stdY)); + kernelSize.scale(filter().filterScale()); IntSize paintSize = absolutePaintRect().size(); - RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitialized(paintSize.width() * paintSize.height() * 4); - Uint8ClampedArray* tmpPixelArray = tmpImageData.get(); + paintSize.scale(filter().filterScale()); + RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitialized((paintSize.area() * 4).unsafeGet()); + if (!tmpImageData) { + WTFLogAlways("FEGaussianBlur::platformApplySoftware Unable to create buffer. Requested size was %d x %d\n", paintSize.width(), paintSize.height()); + return; + } - platformApply(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize); + platformApply(srcPixelArray, tmpImageData.get(), kernelSize.width(), kernelSize.height(), paintSize); } void FEGaussianBlur::dump() @@ -364,12 +572,4 @@ TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) c return ts; } -float FEGaussianBlur::calculateStdDeviation(float radius) -{ - // Blur radius represents 2/3 times the kernel size, the dest pixel is half of the radius applied 3 times - return std::max((radius * 2 / 3.f - 0.5f) / gaussianKernelFactor(), 0.f); -} - } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h index b666b5955..0748a223d 100644 --- a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h +++ b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.h @@ -22,7 +22,6 @@ #ifndef FEGaussianBlur_h #define FEGaussianBlur_h -#if ENABLE(FILTERS) #include "FEConvolveMatrix.h" #include "Filter.h" #include "FilterEffect.h" @@ -31,7 +30,7 @@ namespace WebCore { class FEGaussianBlur : public FilterEffect { public: - static PassRefPtr<FEGaussianBlur> create(Filter*, float, float, EdgeModeType); + static Ref<FEGaussianBlur> create(Filter&, float, float, EdgeModeType); float stdDeviationX() const; void setStdDeviationX(float); @@ -42,16 +41,14 @@ public: EdgeModeType edgeMode() const; void setEdgeMode(EdgeModeType); - static float calculateStdDeviation(float); + void platformApplySoftware() override; + void dump() override; - virtual void platformApplySoftware(); - virtual void dump(); - - virtual void determineAbsolutePaintRect(); - static void calculateKernelSize(Filter*, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY); - static void calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY); + void determineAbsolutePaintRect() override; + static IntSize calculateKernelSize(const Filter&, const FloatPoint& stdDeviation); + static IntSize calculateUnscaledKernelSize(const FloatPoint& stdDeviation); - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: static const int s_minimalRectDimension = 100 * 100; // Empirical data limit for parallel jobs @@ -71,9 +68,8 @@ private: static void platformApplyWorker(PlatformApplyParameters*); - FEGaussianBlur(Filter*, float, float, EdgeModeType); + FEGaussianBlur(Filter&, float, float, EdgeModeType); - static inline void kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight); inline void platformApply(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize); inline void platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize); @@ -83,36 +79,6 @@ private: EdgeModeType m_edgeMode; }; -inline void FEGaussianBlur::kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight) -{ - // check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details - switch (boxBlur) { - case 0: - if (!(std % 2)) { - dLeft = std / 2 - 1; - dRight = std - dLeft; - } else { - dLeft = std / 2; - dRight = std - dLeft; - } - break; - case 1: - if (!(std % 2)) { - dLeft++; - dRight--; - } - break; - case 2: - if (!(std % 2)) { - dRight++; - std++; - } - break; - } -} - } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEGaussianBlur_h diff --git a/Source/WebCore/platform/graphics/filters/FELighting.cpp b/Source/WebCore/platform/graphics/filters/FELighting.cpp index 1a8180334..2925b0eb6 100644 --- a/Source/WebCore/platform/graphics/filters/FELighting.cpp +++ b/Source/WebCore/platform/graphics/filters/FELighting.cpp @@ -25,8 +25,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FELighting.h" #include "FELightingNEON.h" @@ -34,7 +32,7 @@ namespace WebCore { -FELighting::FELighting(Filter* filter, LightingType lightingType, const Color& lightingColor, float surfaceScale, +FELighting::FELighting(Filter& filter, LightingType lightingType, const Color& lightingColor, float surfaceScale, float diffuseConstant, float specularConstant, float specularExponent, float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource) : FilterEffect(filter) @@ -284,7 +282,7 @@ inline void FELighting::platformApplyGeneric(LightingData& data, LightSource::Pa inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData) { // The selection here eventually should happen dynamically on some platforms. -#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC) +#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG) platformApplyNeon(data, paintingData); #else platformApplyGeneric(data, paintingData); @@ -408,5 +406,3 @@ void FELighting::platformApplySoftware() } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FELighting.h b/Source/WebCore/platform/graphics/filters/FELighting.h index eb79bbdbc..bafbf47c4 100644 --- a/Source/WebCore/platform/graphics/filters/FELighting.h +++ b/Source/WebCore/platform/graphics/filters/FELighting.h @@ -27,7 +27,6 @@ #ifndef FELighting_h #define FELighting_h -#if ENABLE(FILTERS) #include "Color.h" #include "Filter.h" #include "FilterEffect.h" @@ -44,9 +43,9 @@ struct FELightingPaintingDataForNeon; class FELighting : public FilterEffect { public: - virtual void platformApplySoftware(); + void platformApplySoftware() override; - virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } + void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } protected: static const int s_minimalRectDimension = 100 * 100; // Empirical data limit for parallel jobs @@ -89,7 +88,7 @@ protected: static void platformApplyGenericWorker(PlatformApplyGenericParameters*); static void platformApplyNeonWorker(FELightingPaintingDataForNeon*); - FELighting(Filter*, LightingType, const Color&, float, float, float, float, float, float, PassRefPtr<LightSource>); + FELighting(Filter&, LightingType, const Color&, float, float, float, float, float, float, PassRefPtr<LightSource>); bool drawLighting(Uint8ClampedArray*, int, int); inline void inlineSetPixel(int offset, LightingData&, LightSource::PaintingData&, @@ -121,6 +120,4 @@ protected: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FELighting_h diff --git a/Source/WebCore/platform/graphics/filters/FEMerge.cpp b/Source/WebCore/platform/graphics/filters/FEMerge.cpp index 89fb8cef6..32cf254d5 100644 --- a/Source/WebCore/platform/graphics/filters/FEMerge.cpp +++ b/Source/WebCore/platform/graphics/filters/FEMerge.cpp @@ -20,8 +20,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEMerge.h" #include "Filter.h" @@ -30,14 +28,14 @@ namespace WebCore { -FEMerge::FEMerge(Filter* filter) +FEMerge::FEMerge(Filter& filter) : FilterEffect(filter) { } -PassRefPtr<FEMerge> FEMerge::create(Filter* filter) +Ref<FEMerge> FEMerge::create(Filter& filter) { - return adoptRef(new FEMerge(filter)); + return adoptRef(*new FEMerge(filter)); } void FEMerge::platformApplySoftware() @@ -49,10 +47,12 @@ void FEMerge::platformApplySoftware() if (!resultImage) return; - GraphicsContext* filterContext = resultImage->context(); + + GraphicsContext& filterContext = resultImage->context(); for (unsigned i = 0; i < size; ++i) { FilterEffect* in = inputEffect(i); - filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); + if (ImageBuffer* inBuffer = in->asImageBuffer()) + filterContext.drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); } } @@ -74,5 +74,3 @@ TextStream& FEMerge::externalRepresentation(TextStream& ts, int indent) const } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEMerge.h b/Source/WebCore/platform/graphics/filters/FEMerge.h index fb4f4f6cf..620f2205e 100644 --- a/Source/WebCore/platform/graphics/filters/FEMerge.h +++ b/Source/WebCore/platform/graphics/filters/FEMerge.h @@ -22,31 +22,24 @@ #ifndef FEMerge_h #define FEMerge_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" -#include <wtf/Vector.h> namespace WebCore { class FEMerge : public FilterEffect { public: - static PassRefPtr<FEMerge> create(Filter*); + static Ref<FEMerge> create(Filter&); - virtual void platformApplySoftware(); -#if ENABLE(OPENCL) - virtual bool platformApplyOpenCL(); -#endif - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEMerge(Filter*); + FEMerge(Filter&); }; } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEMerge_h diff --git a/Source/WebCore/platform/graphics/filters/FEMorphology.cpp b/Source/WebCore/platform/graphics/filters/FEMorphology.cpp index 6ee8a826d..894fd3d5b 100644 --- a/Source/WebCore/platform/graphics/filters/FEMorphology.cpp +++ b/Source/WebCore/platform/graphics/filters/FEMorphology.cpp @@ -22,8 +22,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEMorphology.h" #include "Filter.h" @@ -35,7 +33,7 @@ namespace WebCore { -FEMorphology::FEMorphology(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY) +FEMorphology::FEMorphology(Filter& filter, MorphologyOperatorType type, float radiusX, float radiusY) : FilterEffect(filter) , m_type(type) , m_radiusX(radiusX) @@ -43,9 +41,9 @@ FEMorphology::FEMorphology(Filter* filter, MorphologyOperatorType type, float ra { } -PassRefPtr<FEMorphology> FEMorphology::create(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY) +Ref<FEMorphology> FEMorphology::create(Filter& filter, MorphologyOperatorType type, float radiusX, float radiusY) { - return adoptRef(new FEMorphology(filter, type, radiusX, radiusY)); + return adoptRef(*new FEMorphology(filter, type, radiusX, radiusY)); } bool FEMorphology::setMorphologyOperator(MorphologyOperatorType type) @@ -75,9 +73,9 @@ bool FEMorphology::setRadiusY(float radiusY) void FEMorphology::determineAbsolutePaintRect() { FloatRect paintRect = inputEffect(0)->absolutePaintRect(); - Filter* filter = this->filter(); - paintRect.inflateX(filter->applyHorizontalScale(m_radiusX)); - paintRect.inflateY(filter->applyVerticalScale(m_radiusY)); + Filter& filter = this->filter(); + paintRect.inflateX(filter.applyHorizontalScale(m_radiusX)); + paintRect.inflateY(filter.applyVerticalScale(m_radiusY)); if (clipsToBounds()) paintRect.intersect(maxEffectRect()); else @@ -227,10 +225,10 @@ void FEMorphology::platformApplySoftware() if (platformApplyDegenerate(dstPixelArray, effectDrawingRect, m_radiusX, m_radiusY)) return; - Filter* filter = this->filter(); + Filter& filter = this->filter(); RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectDrawingRect); - int radiusX = static_cast<int>(floorf(filter->applyHorizontalScale(m_radiusX))); - int radiusY = static_cast<int>(floorf(filter->applyVerticalScale(m_radiusY))); + int radiusX = static_cast<int>(floorf(filter.applyHorizontalScale(m_radiusX))); + int radiusY = static_cast<int>(floorf(filter.applyVerticalScale(m_radiusY))); radiusX = std::min(effectDrawingRect.width() - 1, radiusX); radiusY = std::min(effectDrawingRect.height() - 1, radiusY); @@ -240,10 +238,10 @@ void FEMorphology::platformApplySoftware() PaintingData paintingData; paintingData.srcPixelArray = srcPixelArray.get(); paintingData.dstPixelArray = dstPixelArray; - paintingData.width = effectDrawingRect.width(); - paintingData.height = effectDrawingRect.height(); - paintingData.radiusX = radiusX; - paintingData.radiusY = radiusY; + paintingData.width = ceilf(effectDrawingRect.width() * filter.filterScale()); + paintingData.height = ceilf(effectDrawingRect.height() * filter.filterScale()); + paintingData.radiusX = ceilf(radiusX * filter.filterScale()); + paintingData.radiusY = ceilf(radiusY * filter.filterScale()); platformApply(&paintingData); } @@ -280,5 +278,3 @@ TextStream& FEMorphology::externalRepresentation(TextStream& ts, int indent) con } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEMorphology.h b/Source/WebCore/platform/graphics/filters/FEMorphology.h index f36c41f99..050fc73d5 100644 --- a/Source/WebCore/platform/graphics/filters/FEMorphology.h +++ b/Source/WebCore/platform/graphics/filters/FEMorphology.h @@ -22,7 +22,6 @@ #ifndef FEMorphology_h #define FEMorphology_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -36,7 +35,7 @@ enum MorphologyOperatorType { class FEMorphology : public FilterEffect { public: - static PassRefPtr<FEMorphology> create(Filter*, MorphologyOperatorType, float radiusX, float radiusY); + static Ref<FEMorphology> create(Filter&, MorphologyOperatorType, float radiusX, float radiusY); MorphologyOperatorType morphologyOperator() const { return m_type; } bool setMorphologyOperator(MorphologyOperatorType); @@ -47,12 +46,12 @@ public: float radiusY() const { return m_radiusY; } bool setRadiusY(float); - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect(); + void determineAbsolutePaintRect() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; struct PaintingData { Uint8ClampedArray* srcPixelArray; @@ -77,7 +76,7 @@ public: inline void platformApply(PaintingData*); inline void platformApplyGeneric(PaintingData*, const int yStart, const int yEnd); private: - FEMorphology(Filter*, MorphologyOperatorType, float radiusX, float radiusY); + FEMorphology(Filter&, MorphologyOperatorType, float radiusX, float radiusY); bool platformApplyDegenerate(Uint8ClampedArray* dstPixelArray, const IntRect& imageRect, int radiusX, int radiusY); MorphologyOperatorType m_type; @@ -87,6 +86,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEMorphology_h diff --git a/Source/WebCore/platform/graphics/filters/FEOffset.cpp b/Source/WebCore/platform/graphics/filters/FEOffset.cpp index 45c4cc867..ddef58791 100644 --- a/Source/WebCore/platform/graphics/filters/FEOffset.cpp +++ b/Source/WebCore/platform/graphics/filters/FEOffset.cpp @@ -22,8 +22,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FEOffset.h" #include "Filter.h" @@ -32,16 +30,16 @@ namespace WebCore { -FEOffset::FEOffset(Filter* filter, float dx, float dy) +FEOffset::FEOffset(Filter& filter, float dx, float dy) : FilterEffect(filter) , m_dx(dx) , m_dy(dy) { } -PassRefPtr<FEOffset> FEOffset::create(Filter* filter, float dx, float dy) +Ref<FEOffset> FEOffset::create(Filter& filter, float dx, float dy) { - return adoptRef(new FEOffset(filter, dx, dy)); + return adoptRef(*new FEOffset(filter, dx, dy)); } float FEOffset::dx() const @@ -67,8 +65,8 @@ void FEOffset::setDy(float dy) void FEOffset::determineAbsolutePaintRect() { FloatRect paintRect = inputEffect(0)->absolutePaintRect(); - Filter* filter = this->filter(); - paintRect.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); + Filter& filter = this->filter(); + paintRect.move(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy)); if (clipsToBounds()) paintRect.intersect(maxEffectRect()); else @@ -81,15 +79,16 @@ void FEOffset::platformApplySoftware() FilterEffect* in = inputEffect(0); ImageBuffer* resultImage = createImageBufferResult(); - if (!resultImage) + ImageBuffer* inBuffer = in->asImageBuffer(); + if (!resultImage || !inBuffer) return; setIsAlphaImage(in->isAlphaImage()); FloatRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect()); - Filter* filter = this->filter(); - drawingRegion.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); - resultImage->context()->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegion); + Filter& filter = this->filter(); + drawingRegion.move(filter.applyHorizontalScale(m_dx), filter.applyVerticalScale(m_dy)); + resultImage->context().drawImageBuffer(*inBuffer, drawingRegion); } void FEOffset::dump() @@ -107,5 +106,3 @@ TextStream& FEOffset::externalRepresentation(TextStream& ts, int indent) const } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FEOffset.h b/Source/WebCore/platform/graphics/filters/FEOffset.h index e75febd9c..f196d49e3 100644 --- a/Source/WebCore/platform/graphics/filters/FEOffset.h +++ b/Source/WebCore/platform/graphics/filters/FEOffset.h @@ -22,7 +22,6 @@ #ifndef FEOffset_h #define FEOffset_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -30,7 +29,7 @@ namespace WebCore { class FEOffset : public FilterEffect { public: - static PassRefPtr<FEOffset> create(Filter*, float dx, float dy); + static Ref<FEOffset> create(Filter&, float dx, float dy); float dx() const; void setDx(float); @@ -38,15 +37,15 @@ public: float dy() const; void setDy(float); - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect(); + void determineAbsolutePaintRect() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FEOffset(Filter*, float dx, float dy); + FEOffset(Filter&, float dx, float dy); float m_dx; float m_dy; @@ -54,6 +53,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FEOffset_h diff --git a/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp b/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp index 249914fe3..e2e834f21 100644 --- a/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp +++ b/Source/WebCore/platform/graphics/filters/FESpecularLighting.cpp @@ -20,8 +20,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FESpecularLighting.h" #include "LightSource.h" @@ -29,18 +27,18 @@ namespace WebCore { -FESpecularLighting::FESpecularLighting(Filter* filter, const Color& lightingColor, float surfaceScale, +FESpecularLighting::FESpecularLighting(Filter& filter, const Color& lightingColor, float surfaceScale, float specularConstant, float specularExponent, float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource) : FELighting(filter, SpecularLighting, lightingColor, surfaceScale, 0, specularConstant, specularExponent, kernelUnitLengthX, kernelUnitLengthY, lightSource) { } -PassRefPtr<FESpecularLighting> FESpecularLighting::create(Filter* filter, const Color& lightingColor, +Ref<FESpecularLighting> FESpecularLighting::create(Filter& filter, const Color& lightingColor, float surfaceScale, float specularConstant, float specularExponent, float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource) { - return adoptRef(new FESpecularLighting(filter, lightingColor, surfaceScale, specularConstant, specularExponent, + return adoptRef(*new FESpecularLighting(filter, lightingColor, surfaceScale, specularConstant, specularExponent, kernelUnitLengthX, kernelUnitLengthY, lightSource)); } @@ -48,7 +46,7 @@ FESpecularLighting::~FESpecularLighting() { } -Color FESpecularLighting::lightingColor() const +const Color& FESpecularLighting::lightingColor() const { return m_lightingColor; } @@ -153,5 +151,3 @@ TextStream& FESpecularLighting::externalRepresentation(TextStream& ts, int inden } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FESpecularLighting.h b/Source/WebCore/platform/graphics/filters/FESpecularLighting.h index 9fa3addac..e04f4436a 100644 --- a/Source/WebCore/platform/graphics/filters/FESpecularLighting.h +++ b/Source/WebCore/platform/graphics/filters/FESpecularLighting.h @@ -22,18 +22,17 @@ #ifndef FESpecularLighting_h #define FESpecularLighting_h -#if ENABLE(FILTERS) #include "FELighting.h" namespace WebCore { class FESpecularLighting : public FELighting { public: - static PassRefPtr<FESpecularLighting> create(Filter*, const Color&, float, float, + static Ref<FESpecularLighting> create(Filter&, const Color&, float, float, float, float, float, PassRefPtr<LightSource>); virtual ~FESpecularLighting(); - Color lightingColor() const; + const Color& lightingColor() const; bool setLightingColor(const Color&); float surfaceScale() const; @@ -54,16 +53,14 @@ public: const LightSource* lightSource() const; void setLightSource(PassRefPtr<LightSource>); - virtual void dump(); + void dump() override; - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FESpecularLighting(Filter*, const Color&, float, float, float, float, float, PassRefPtr<LightSource>); + FESpecularLighting(Filter&, const Color&, float, float, float, float, float, PassRefPtr<LightSource>); }; } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FESpecularLighting_h diff --git a/Source/WebCore/platform/graphics/filters/FETile.cpp b/Source/WebCore/platform/graphics/filters/FETile.cpp index 4a6c6a4f9..a0e2e1bf6 100644 --- a/Source/WebCore/platform/graphics/filters/FETile.cpp +++ b/Source/WebCore/platform/graphics/filters/FETile.cpp @@ -19,8 +19,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FETile.h" #include "AffineTransform.h" @@ -33,24 +31,24 @@ namespace WebCore { -FETile::FETile(Filter* filter) +FETile::FETile(Filter& filter) : FilterEffect(filter) { } -PassRefPtr<FETile> FETile::create(Filter* filter) +Ref<FETile> FETile::create(Filter& filter) { - return adoptRef(new FETile(filter)); + return adoptRef(*new FETile(filter)); } void FETile::platformApplySoftware() { // FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise. -#if ENABLE(SVG) FilterEffect* in = inputEffect(0); ImageBuffer* resultImage = createImageBufferResult(); - if (!resultImage) + ImageBuffer* inBuffer = in->asImageBuffer(); + if (!resultImage || !inBuffer) return; setIsAlphaImage(in->isAlphaImage()); @@ -61,28 +59,31 @@ void FETile::platformApplySoftware() FloatPoint inMaxEffectLocation = tileRect.location(); FloatPoint maxEffectLocation = maxEffectRect().location(); if (in->filterEffectType() == FilterEffectTypeSourceInput) { - Filter* filter = this->filter(); - tileRect = filter->filterRegion(); - tileRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); + Filter& filter = this->filter(); + tileRect = filter.filterRegion(); + tileRect.scale(filter.filterResolution().width(), filter.filterResolution().height()); } - std::unique_ptr<ImageBuffer> tileImage; - if (!SVGRenderingContext::createImageBufferForPattern(tileRect, tileRect, tileImage, ColorSpaceDeviceRGB, filter()->renderingMode())) + auto tileImage = SVGRenderingContext::createImageBuffer(tileRect, tileRect, ColorSpaceSRGB, filter().renderingMode()); + if (!tileImage) return; - GraphicsContext* tileImageContext = tileImage->context(); - tileImageContext->translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y()); - tileImageContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, in->absolutePaintRect().location()); + GraphicsContext& tileImageContext = tileImage->context(); + tileImageContext.translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y()); + tileImageContext.drawImageBuffer(*inBuffer, in->absolutePaintRect().location()); + + auto tileImageCopy = ImageBuffer::sinkIntoImage(WTFMove(tileImage)); + if (!tileImageCopy) + return; - RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(CopyBackingStore), true, true); + auto pattern = Pattern::create(WTFMove(tileImageCopy), true, true); AffineTransform patternTransform; patternTransform.translate(inMaxEffectLocation.x() - maxEffectLocation.x(), inMaxEffectLocation.y() - maxEffectLocation.y()); - pattern->setPatternSpaceTransform(patternTransform); - GraphicsContext* filterContext = resultImage->context(); - filterContext->setFillPattern(pattern); - filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size())); -#endif + pattern.get().setPatternSpaceTransform(patternTransform); + GraphicsContext& filterContext = resultImage->context(); + filterContext.setFillPattern(WTFMove(pattern)); + filterContext.fillRect(FloatRect(FloatPoint(), absolutePaintRect().size())); } void FETile::dump() @@ -101,5 +102,3 @@ TextStream& FETile::externalRepresentation(TextStream& ts, int indent) const } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FETile.h b/Source/WebCore/platform/graphics/filters/FETile.h index 633276a3e..c264cefa4 100644 --- a/Source/WebCore/platform/graphics/filters/FETile.h +++ b/Source/WebCore/platform/graphics/filters/FETile.h @@ -22,7 +22,6 @@ #ifndef FETile_h #define FETile_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -30,23 +29,21 @@ namespace WebCore { class FETile : public FilterEffect { public: - static PassRefPtr<FETile> create(Filter* filter); + static Ref<FETile> create(Filter&); - virtual void platformApplySoftware(); - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } + void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } - virtual FilterEffectType filterEffectType() const { return FilterEffectTypeTile; } + FilterEffectType filterEffectType() const override { return FilterEffectTypeTile; } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - FETile(Filter*); + FETile(Filter&); }; } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FETile_h diff --git a/Source/WebCore/platform/graphics/filters/FETurbulence.cpp b/Source/WebCore/platform/graphics/filters/FETurbulence.cpp index a5e8b348a..37d9f6f0e 100644 --- a/Source/WebCore/platform/graphics/filters/FETurbulence.cpp +++ b/Source/WebCore/platform/graphics/filters/FETurbulence.cpp @@ -23,8 +23,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FETurbulence.h" #include "Filter.h" @@ -50,7 +48,7 @@ static const int s_randAmplitude = 16807; // 7**5; primitive root of m static const int s_randQ = 127773; // m / a static const int s_randR = 2836; // m % a -FETurbulence::FETurbulence(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles) +FETurbulence::FETurbulence(Filter& filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles) : FilterEffect(filter) , m_type(type) , m_baseFrequencyX(baseFrequencyX) @@ -61,9 +59,9 @@ FETurbulence::FETurbulence(Filter* filter, TurbulenceType type, float baseFreque { } -PassRefPtr<FETurbulence> FETurbulence::create(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles) +Ref<FETurbulence> FETurbulence::create(Filter& filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles) { - return adoptRef(new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles)); + return adoptRef(*new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles)); } TurbulenceType FETurbulence::type() const @@ -182,8 +180,10 @@ inline void FETurbulence::initPaint(PaintingData& paintingData) for (int i = 0; i < s_blockSize; ++i) { paintingData.latticeSelector[i] = i; gradient = paintingData.gradient[channel][i]; - gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize; - gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize; + do { + gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize; + gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize; + } while (!gradient[0] && !gradient[1]); normalizationFactor = sqrtf(gradient[0] * gradient[0] + gradient[1] * gradient[1]); gradient[0] /= normalizationFactor; gradient[1] /= normalizationFactor; @@ -343,7 +343,7 @@ inline void FETurbulence::fillRegion(Uint8ClampedArray* pixelArray, PaintingData for (int x = 0; x < filterRegion.width(); ++x) { point.setX(point.x() + 1); for (channel = 0; channel < 4; ++channel, ++indexOfPixelChannel) - pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter()->mapAbsolutePointToLocalPoint(point))); + pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter().mapAbsolutePointToLocalPoint(point))); } } } @@ -435,5 +435,3 @@ TextStream& FETurbulence::externalRepresentation(TextStream& ts, int indent) con } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FETurbulence.h b/Source/WebCore/platform/graphics/filters/FETurbulence.h index 84ff11261..4987e3d97 100644 --- a/Source/WebCore/platform/graphics/filters/FETurbulence.h +++ b/Source/WebCore/platform/graphics/filters/FETurbulence.h @@ -24,7 +24,6 @@ #ifndef FETurbulence_h #define FETurbulence_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -38,7 +37,7 @@ enum TurbulenceType { class FETurbulence : public FilterEffect { public: - static PassRefPtr<FETurbulence> create(Filter*, TurbulenceType, float, float, int, float, bool); + static Ref<FETurbulence> create(Filter&, TurbulenceType, float, float, int, float, bool); TurbulenceType type() const; bool setType(TurbulenceType); @@ -60,15 +59,12 @@ public: static void fillRegionWorker(void*); - virtual void platformApplySoftware(); -#if ENABLE(OPENCL) - virtual bool platformApplyOpenCL(); -#endif - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect() { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } + void determineAbsolutePaintRect() override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: static const int s_blockSize = 256; @@ -119,7 +115,7 @@ private: static void fillRegionWorker(FillRegionParameters*); - FETurbulence(Filter*, TurbulenceType, float, float, int, float, bool); + FETurbulence(Filter&, TurbulenceType, float, float, int, float, bool); inline void initPaint(PaintingData&); float noise2D(int channel, PaintingData&, StitchData&, const FloatPoint&); @@ -136,6 +132,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FETurbulence_h diff --git a/Source/WebCore/platform/graphics/filters/Filter.h b/Source/WebCore/platform/graphics/filters/Filter.h index cea0ff2f4..6d344ed87 100644 --- a/Source/WebCore/platform/graphics/filters/Filter.h +++ b/Source/WebCore/platform/graphics/filters/Filter.h @@ -18,35 +18,32 @@ * Boston, MA 02110-1301, USA. */ -#ifndef Filter_h -#define Filter_h +#pragma once -#if ENABLE(FILTERS) -#include "FloatRect.h" #include "FloatSize.h" #include "ImageBuffer.h" -#include <wtf/RefCounted.h> namespace WebCore { -class FilterEffect; - class Filter : public RefCounted<Filter> { public: - Filter(const AffineTransform& absoluteTransform) + Filter(const AffineTransform& absoluteTransform, float filterScale = 1) : m_absoluteTransform(absoluteTransform) - , m_renderingMode(Unaccelerated) + , m_filterScale(filterScale) { } virtual ~Filter() { } - void setSourceImage(std::unique_ptr<ImageBuffer> sourceImage) { m_sourceImage = std::move(sourceImage); } + void setSourceImage(std::unique_ptr<ImageBuffer> sourceImage) { m_sourceImage = WTFMove(sourceImage); } ImageBuffer* sourceImage() { return m_sourceImage.get(); } FloatSize filterResolution() const { return m_filterResolution; } void setFilterResolution(const FloatSize& filterResolution) { m_filterResolution = filterResolution; } + float filterScale() const { return m_filterScale; } + void setFilterScale(float scale) { m_filterScale = scale; } + const AffineTransform& absoluteTransform() const { return m_absoluteTransform; } - FloatPoint mapAbsolutePointToLocalPoint(const FloatPoint& point) const { return m_absoluteTransform.inverse().mapPoint(point); } + FloatPoint mapAbsolutePointToLocalPoint(const FloatPoint& point) const { return m_absoluteTransform.inverse().value_or(AffineTransform()).mapPoint(point); } RenderingMode renderingMode() const { return m_renderingMode; } void setRenderingMode(RenderingMode renderingMode) { m_renderingMode = renderingMode; } @@ -59,18 +56,18 @@ public: virtual FloatRect sourceImageRect() const = 0; virtual FloatRect filterRegion() const = 0; +protected: + explicit Filter(const FloatSize& filterResolution) + : m_filterResolution(filterResolution) + { + } + private: std::unique_ptr<ImageBuffer> m_sourceImage; FloatSize m_filterResolution; AffineTransform m_absoluteTransform; - RenderingMode m_renderingMode; + RenderingMode m_renderingMode { Unaccelerated }; + float m_filterScale { 1 }; }; -#define FILTER_TYPE_CASTS(ToValueTypeName, predicate) \ - TYPE_CASTS_BASE(ToValueTypeName, Filter, filter, filter->predicate, filter.predicate) - } // namespace WebCore - -#endif // ENABLE(FILTERS) - -#endif // Filter_h diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp index 62cc4e6ec..2f742ca17 100644 --- a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp +++ b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) Research In Motion Limited 2010. All rights reserved. * Copyright (C) 2012 University of Szeged + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,14 +22,12 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" #include "ImageBuffer.h" #include "TextStream.h" -#include <runtime/Operations.h> +#include <runtime/JSCInlines.h> #include <runtime/TypedArrayInlines.h> #include <runtime/Uint8ClampedArray.h> @@ -38,7 +37,7 @@ namespace WebCore { -FilterEffect::FilterEffect(Filter* filter) +FilterEffect::FilterEffect(Filter& filter) : m_alphaImage(false) , m_filter(filter) , m_hasX(false) @@ -47,23 +46,14 @@ FilterEffect::FilterEffect(Filter* filter) , m_hasHeight(false) , m_clipsToBounds(true) , m_operatingColorSpace(ColorSpaceLinearRGB) - , m_resultColorSpace(ColorSpaceDeviceRGB) + , m_resultColorSpace(ColorSpaceSRGB) { - ASSERT(m_filter); } FilterEffect::~FilterEffect() { } -inline bool isFilterSizeValid(IntRect rect) -{ - if (rect.width() < 0 || rect.width() > kMaxFilterSize - || rect.height() < 0 || rect.height() > kMaxFilterSize) - return false; - return true; -} - void FilterEffect::determineAbsolutePaintRect() { m_absolutePaintRect = IntRect(); @@ -89,10 +79,16 @@ IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) return IntRect(location, m_absolutePaintRect.size()); } -IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const +FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const { - return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(), - srcRect.y() - m_absolutePaintRect.y()), srcRect.size()); + ASSERT(hasResult()); + + FloatSize scale; + ImageBuffer::clampedSize(m_absolutePaintRect.size(), scale); + + AffineTransform transform; + transform.scale(scale).translate(-m_absolutePaintRect.location()); + return transform.mapRect(srcRect); } FilterEffect* FilterEffect::inputEffect(unsigned number) const @@ -118,24 +114,6 @@ unsigned FilterEffect::totalNumberOfEffectInputs() const return collectEffects(this, allEffects); } -#if ENABLE(OPENCL) -void FilterEffect::applyAll() -{ - if (hasResult()) - return; - FilterContextOpenCL* context = FilterContextOpenCL::context(); - if (context) { - apply(); - if (!context->inError()) - return; - clearResultsRecursive(); - context->destroyContext(); - } - // Software code path. - apply(); -} -#endif - void FilterEffect::apply() { if (hasResult()) @@ -154,7 +132,7 @@ void FilterEffect::apply() determineAbsolutePaintRect(); setResultColorSpace(m_operatingColorSpace); - if (!isFilterSizeValid(m_absolutePaintRect)) + if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size())) return; if (requiresValidPreMultipliedPixels()) { @@ -163,38 +141,9 @@ void FilterEffect::apply() } // Add platform specific apply functions here and return earlier. -#if ENABLE(OPENCL) - if (platformApplyOpenCL()) - return; -#endif platformApplySoftware(); } -#if ENABLE(OPENCL) -// This function will be changed to abstract virtual when all filters are landed. -bool FilterEffect::platformApplyOpenCL() -{ - if (!FilterContextOpenCL::context()) - return false; - - unsigned size = m_inputEffects.size(); - for (unsigned i = 0; i < size; ++i) { - FilterEffect* in = m_inputEffects.at(i).get(); - // Software code path expects that at least one of the following fileds is valid. - if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult) - in->asImageBuffer(); - } - - platformApplySoftware(); - ImageBuffer* sourceImage = asImageBuffer(); - if (sourceImage) { - RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize())); - createOpenCLImageResult(sourceImageData->data()); - } - return true; -} -#endif - void FilterEffect::forceValidPreMultipliedPixels() { // Must operate on pre-multiplied results; other formats cannot have invalid pixels. @@ -248,14 +197,9 @@ void FilterEffect::clearResult() { if (m_imageBufferResult) m_imageBufferResult.reset(); - if (m_unmultipliedImageResult) - m_unmultipliedImageResult.clear(); - if (m_premultipliedImageResult) - m_premultipliedImageResult.clear(); -#if ENABLE(OPENCL) - if (m_openCLImageResult) - m_openCLImageResult.clear(); -#endif + + m_unmultipliedImageResult = nullptr; + m_premultipliedImageResult = nullptr; } void FilterEffect::clearResultsRecursive() @@ -273,14 +217,13 @@ void FilterEffect::clearResultsRecursive() ImageBuffer* FilterEffect::asImageBuffer() { if (!hasResult()) - return 0; + return nullptr; if (m_imageBufferResult) return m_imageBufferResult.get(); -#if ENABLE(OPENCL) - if (m_openCLImageResult) - return openCLImageToImageBuffer(); -#endif - m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_resultColorSpace, m_filter->renderingMode()); + m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace); + if (!m_imageBufferResult) + return nullptr; + IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); if (m_premultipliedImageResult) m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); @@ -289,85 +232,69 @@ ImageBuffer* FilterEffect::asImageBuffer() return m_imageBufferResult.get(); } -#if ENABLE(OPENCL) -ImageBuffer* FilterEffect::openCLImageToImageBuffer() -{ - FilterContextOpenCL* context = FilterContextOpenCL::context(); - ASSERT(context); - - if (context->inError()) - return 0; - - size_t origin[3] = { 0, 0, 0 }; - size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 }; - - RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); - - if (context->isFailed(clFinish(context->commandQueue()))) - return 0; - - if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0))) - return 0; - - m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size()); - IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); - m_imageBufferResult->putByteArray(Unmultiplied, destinationPixelArray.get(), destinationRect.size(), destinationRect, IntPoint()); - - return m_imageBufferResult.get(); -} -#endif - PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect) { - ASSERT(isFilterSizeValid(rect)); - RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); + IntSize scaledSize(rect.size()); + ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize)); + scaledSize.scale(m_filter.filterScale()); + auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet()); copyUnmultipliedImage(imageData.get(), rect); - return imageData.release(); + return WTFMove(imageData); } PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect) { - ASSERT(isFilterSizeValid(rect)); - RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); + IntSize scaledSize(rect.size()); + ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize)); + scaledSize.scale(m_filter.filterScale()); + auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet()); copyPremultipliedImage(imageData.get(), rect); - return imageData.release(); + return WTFMove(imageData); } inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect) { + IntRect scaledRect(rect); + scaledRect.scale(m_filter.filterScale()); + IntSize scaledPaintSize(m_absolutePaintRect.size()); + scaledPaintSize.scale(m_filter.filterScale()); + + if (!source || !destination) + return; + // Initialize the destination to transparent black, if not entirely covered by the source. - if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height()) + if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height()) memset(destination->data(), 0, destination->length()); // Early return if the rect does not intersect with the source. - if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height()) + if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height()) return; - int xOrigin = rect.x(); + int xOrigin = scaledRect.x(); int xDest = 0; if (xOrigin < 0) { xDest = -xOrigin; xOrigin = 0; } - int xEnd = rect.maxX(); - if (xEnd > m_absolutePaintRect.width()) - xEnd = m_absolutePaintRect.width(); + int xEnd = scaledRect.maxX(); + if (xEnd > scaledPaintSize.width()) + xEnd = scaledPaintSize.width(); - int yOrigin = rect.y(); + int yOrigin = scaledRect.y(); int yDest = 0; if (yOrigin < 0) { yDest = -yOrigin; yOrigin = 0; } - int yEnd = rect.maxY(); - if (yEnd > m_absolutePaintRect.height()) - yEnd = m_absolutePaintRect.height(); + int yEnd = scaledRect.maxY(); + if (yEnd > scaledPaintSize.height()) + yEnd = scaledPaintSize.height(); int size = (xEnd - xOrigin) * 4; - int destinationScanline = rect.width() * 4; - int sourceScanline = m_absolutePaintRect.width() * 4; - unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4; - unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4; + int destinationScanline = scaledRect.width() * 4; + int sourceScanline = scaledPaintSize.width() * 4; + unsigned char *destinationPixel = destination->data() + ((yDest * scaledRect.width()) + xDest) * 4; + unsigned char *sourcePixel = source->data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4; while (yOrigin < yEnd) { memcpy(destinationPixel, sourcePixel, size); @@ -386,11 +313,17 @@ void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const I if (m_imageBufferResult) m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); else { - ASSERT(isFilterSizeValid(m_absolutePaintRect)); - m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); + IntSize inputSize(m_absolutePaintRect.size()); + ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize)); + inputSize.scale(m_filter.filterScale()); + m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet()); + if (!m_unmultipliedImageResult) { + WTFLogAlways("FilterEffect::copyUnmultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height()); + return; + } unsigned char* sourceComponent = m_premultipliedImageResult->data(); unsigned char* destinationComponent = m_unmultipliedImageResult->data(); - unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); + unsigned char* end = sourceComponent + (inputSize.area() * 4).unsafeGet(); while (sourceComponent < end) { int alpha = sourceComponent[3]; if (alpha) { @@ -420,11 +353,17 @@ void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const if (m_imageBufferResult) m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); else { - ASSERT(isFilterSizeValid(m_absolutePaintRect)); - m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); + IntSize inputSize(m_absolutePaintRect.size()); + ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize)); + inputSize.scale(m_filter.filterScale()); + m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet()); + if (!m_premultipliedImageResult) { + WTFLogAlways("FilterEffect::copyPremultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height()); + return; + } unsigned char* sourceComponent = m_unmultipliedImageResult->data(); unsigned char* destinationComponent = m_premultipliedImageResult->data(); - unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); + unsigned char* end = sourceComponent + (inputSize.area() * 4).unsafeGet(); while (sourceComponent < end) { int alpha = sourceComponent[3]; destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255; @@ -444,11 +383,13 @@ ImageBuffer* FilterEffect::createImageBufferResult() // Only one result type is allowed. ASSERT(!hasResult()); if (m_absolutePaintRect.isEmpty()) - return 0; - m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_resultColorSpace, m_filter->renderingMode()); + return nullptr; + + FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size()); + m_imageBufferResult = ImageBuffer::create(clampedSize, m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace); if (!m_imageBufferResult) - return 0; - ASSERT(m_imageBufferResult->context()); + return nullptr; + return m_imageBufferResult.get(); } @@ -456,11 +397,13 @@ Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult() { // Only one result type is allowed. ASSERT(!hasResult()); - ASSERT(isFilterSizeValid(m_absolutePaintRect)); - if (m_absolutePaintRect.isEmpty()) - return 0; - m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); + return nullptr; + + IntSize resultSize(m_absolutePaintRect.size()); + ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize)); + resultSize.scale(m_filter.filterScale()); + m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet()); return m_unmultipliedImageResult.get(); } @@ -468,43 +411,15 @@ Uint8ClampedArray* FilterEffect::createPremultipliedImageResult() { // Only one result type is allowed. ASSERT(!hasResult()); - ASSERT(isFilterSizeValid(m_absolutePaintRect)); - if (m_absolutePaintRect.isEmpty()) - return 0; - m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); - return m_premultipliedImageResult.get(); -} - -#if ENABLE(OPENCL) -OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source) -{ - FilterContextOpenCL* context = FilterContextOpenCL::context(); - ASSERT(context); - - if (context->inError()) - return 0; - - ASSERT(!hasResult()); - cl_image_format clImageFormat; - clImageFormat.image_channel_order = CL_RGBA; - clImageFormat.image_channel_data_type = CL_UNORM_INT8; - - int errorCode = 0; -#ifdef CL_API_SUFFIX__VERSION_1_2 - cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0}; - m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0), - &clImageFormat, &imageDescriptor, source, &errorCode); -#else - m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0), - &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode); -#endif - if (context->isFailed(errorCode)) - return 0; + return nullptr; - return m_openCLImageResult; + IntSize resultSize(m_absolutePaintRect.size()); + ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize)); + resultSize.scale(m_filter.filterScale()); + m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet()); + return m_premultipliedImageResult.get(); } -#endif void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace) { @@ -515,30 +430,16 @@ void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace) if (!hasResult() || dstColorSpace == m_resultColorSpace) return; -#if ENABLE(OPENCL) - if (openCLImage()) { - if (m_imageBufferResult) - m_imageBufferResult.clear(); - FilterContextOpenCL* context = FilterContextOpenCL::context(); - ASSERT(context); - context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace); - } else { -#endif - - // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding - // color space transform support for the {pre,un}multiplied arrays. - asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace); - -#if ENABLE(OPENCL) - } -#endif + // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding + // color space transform support for the {pre,un}multiplied arrays. + asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace); m_resultColorSpace = dstColorSpace; if (m_unmultipliedImageResult) - m_unmultipliedImageResult.clear(); + m_unmultipliedImageResult = nullptr; if (m_premultipliedImageResult) - m_premultipliedImageResult.clear(); + m_premultipliedImageResult = nullptr; #endif } @@ -550,5 +451,3 @@ TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.h b/Source/WebCore/platform/graphics/filters/FilterEffect.h index cb3bdc681..ea4ec776f 100644 --- a/Source/WebCore/platform/graphics/filters/FilterEffect.h +++ b/Source/WebCore/platform/graphics/filters/FilterEffect.h @@ -22,24 +22,15 @@ #ifndef FilterEffect_h #define FilterEffect_h -#if ENABLE(FILTERS) #include "ColorSpace.h" #include "FloatRect.h" #include "IntRect.h" - #include <runtime/Uint8ClampedArray.h> - -#include <wtf/HashSet.h> +#include <wtf/MathExtras.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> -#if ENABLE(OPENCL) -#include "FilterContextOpenCL.h" -#endif - -static const float kMaxFilterSize = 5000.0f; - namespace WebCore { class Filter; @@ -91,7 +82,7 @@ public: || m_premultipliedImageResult; } - IntRect drawingRegionOfInputImage(const IntRect&) const; + FloatRect drawingRegionOfInputImage(const IntRect&) const; IntRect requestedRegionOfInputImageData(const IntRect&) const; // Solid black image with different alpha values. @@ -149,7 +140,7 @@ public: FloatRect effectBoundaries() const { return m_effectBoundaries; } void setEffectBoundaries(const FloatRect& effectBoundaries) { m_effectBoundaries = effectBoundaries; } - Filter* filter() { return m_filter; } + Filter& filter() { return m_filter; } bool clipsToBounds() const { return m_clipsToBounds; } void setClipsToBounds(bool value) { m_clipsToBounds = value; } @@ -163,7 +154,7 @@ public: void transformResultColorSpace(ColorSpace); protected: - FilterEffect(Filter*); + FilterEffect(Filter&); ImageBuffer* createImageBufferResult(); Uint8ClampedArray* createUnmultipliedImageResult(); @@ -180,6 +171,14 @@ protected: void forceValidPreMultipliedPixels(); void clipAbsolutePaintRect(); + + static Vector<float> normalizedFloats(const Vector<float>& values) + { + Vector<float> normalizedValues(values.size()); + for (size_t i = 0; i < values.size(); ++i) + normalizedValues[i] = normalizedFloat(values[i]); + return normalizedValues; + } private: std::unique_ptr<ImageBuffer> m_imageBufferResult; @@ -197,7 +196,7 @@ private: // The maximum size of a filter primitive. In SVG this is the primitive subregion in absolute coordinate space. // The absolute paint rect should never be bigger than m_maxEffectRect. FloatRect m_maxEffectRect; - Filter* m_filter; + Filter& m_filter; private: inline void copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect&); @@ -226,6 +225,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // FilterEffect_h diff --git a/Source/WebCore/platform/graphics/filters/FilterOperation.cpp b/Source/WebCore/platform/graphics/filters/FilterOperation.cpp index 7f517c5e8..035e45bb0 100644 --- a/Source/WebCore/platform/graphics/filters/FilterOperation.cpp +++ b/Source/WebCore/platform/graphics/filters/FilterOperation.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,20 +24,27 @@ */ #include "config.h" - -#if ENABLE(CSS_FILTERS) #include "FilterOperation.h" #include "AnimationUtilities.h" - -#if ENABLE(SVG) +#include "CachedResourceLoader.h" #include "CachedSVGDocumentReference.h" -#endif +#include "FilterEffect.h" +#include "SVGURIReference.h" +#include "TextStream.h" namespace WebCore { + +bool DefaultFilterOperation::operator==(const FilterOperation& operation) const +{ + if (!isSameType(operation)) + return false; + + return representedType() == downcast<DefaultFilterOperation>(operation).representedType(); +} -ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const String& fragment, OperationType type) - : FilterOperation(type) +ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const String& fragment) + : FilterOperation(REFERENCE) , m_url(url) , m_fragment(fragment) { @@ -46,15 +53,29 @@ ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const Stri ReferenceFilterOperation::~ReferenceFilterOperation() { } + +bool ReferenceFilterOperation::operator==(const FilterOperation& operation) const +{ + if (!isSameType(operation)) + return false; + + return m_url == downcast<ReferenceFilterOperation>(operation).m_url; +} -#if ENABLE(SVG) -CachedSVGDocumentReference* ReferenceFilterOperation::getOrCreateCachedSVGDocumentReference() +void ReferenceFilterOperation::loadExternalDocumentIfNeeded(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options) { - if (!m_cachedSVGDocumentReference) - m_cachedSVGDocumentReference = std::make_unique<CachedSVGDocumentReference>(m_url); - return m_cachedSVGDocumentReference.get(); + if (m_cachedSVGDocumentReference) + return; + if (!SVGURIReference::isExternalURIReference(m_url, *cachedResourceLoader.document())) + return; + m_cachedSVGDocumentReference = std::make_unique<CachedSVGDocumentReference>(m_url); + m_cachedSVGDocumentReference->load(cachedResourceLoader, options); +} + +void ReferenceFilterOperation::setFilterEffect(PassRefPtr<FilterEffect> filterEffect) +{ + m_filterEffect = filterEffect; } -#endif PassRefPtr<FilterOperation> BasicColorMatrixFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough) { @@ -64,11 +85,19 @@ PassRefPtr<FilterOperation> BasicColorMatrixFilterOperation::blend(const FilterO if (blendToPassthrough) return BasicColorMatrixFilterOperation::create(WebCore::blend(m_amount, passthroughAmount(), progress), m_type); - const BasicColorMatrixFilterOperation* fromOp = static_cast<const BasicColorMatrixFilterOperation*>(from); - double fromAmount = fromOp ? fromOp->amount() : passthroughAmount(); + const BasicColorMatrixFilterOperation* fromOperation = downcast<BasicColorMatrixFilterOperation>(from); + double fromAmount = fromOperation ? fromOperation->amount() : passthroughAmount(); return BasicColorMatrixFilterOperation::create(WebCore::blend(fromAmount, m_amount, progress), m_type); } +inline bool BasicColorMatrixFilterOperation::operator==(const FilterOperation& operation) const +{ + if (!isSameType(operation)) + return false; + const BasicColorMatrixFilterOperation& other = downcast<BasicColorMatrixFilterOperation>(operation); + return m_amount == other.m_amount; +} + double BasicColorMatrixFilterOperation::passthroughAmount() const { switch (m_type) { @@ -92,11 +121,19 @@ PassRefPtr<FilterOperation> BasicComponentTransferFilterOperation::blend(const F if (blendToPassthrough) return BasicComponentTransferFilterOperation::create(WebCore::blend(m_amount, passthroughAmount(), progress), m_type); - const BasicComponentTransferFilterOperation* fromOp = static_cast<const BasicComponentTransferFilterOperation*>(from); - double fromAmount = fromOp ? fromOp->amount() : passthroughAmount(); + const BasicComponentTransferFilterOperation* fromOperation = downcast<BasicComponentTransferFilterOperation>(from); + double fromAmount = fromOperation ? fromOperation->amount() : passthroughAmount(); return BasicComponentTransferFilterOperation::create(WebCore::blend(fromAmount, m_amount, progress), m_type); } +inline bool BasicComponentTransferFilterOperation::operator==(const FilterOperation& operation) const +{ + if (!isSameType(operation)) + return false; + const BasicComponentTransferFilterOperation& other = downcast<BasicComponentTransferFilterOperation>(operation); + return m_amount == other.m_amount; +} + double BasicComponentTransferFilterOperation::passthroughAmount() const { switch (m_type) { @@ -113,7 +150,15 @@ double BasicComponentTransferFilterOperation::passthroughAmount() const return 0; } } - + +bool BlurFilterOperation::operator==(const FilterOperation& operation) const +{ + if (!isSameType(operation)) + return false; + + return m_stdDeviation == downcast<BlurFilterOperation>(operation).stdDeviation(); +} + PassRefPtr<FilterOperation> BlurFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough) { if (from && !from->isSameType(*this)) @@ -122,13 +167,21 @@ PassRefPtr<FilterOperation> BlurFilterOperation::blend(const FilterOperation* fr LengthType lengthType = m_stdDeviation.type(); if (blendToPassthrough) - return BlurFilterOperation::create(Length(lengthType).blend(m_stdDeviation, progress), m_type); + return BlurFilterOperation::create(WebCore::blend(m_stdDeviation, Length(lengthType), progress)); - const BlurFilterOperation* fromOp = static_cast<const BlurFilterOperation*>(from); - Length fromLength = fromOp ? fromOp->m_stdDeviation : Length(lengthType); - return BlurFilterOperation::create(m_stdDeviation.blend(fromLength, progress), m_type); + const BlurFilterOperation* fromOperation = downcast<BlurFilterOperation>(from); + Length fromLength = fromOperation ? fromOperation->m_stdDeviation : Length(lengthType); + return BlurFilterOperation::create(WebCore::blend(fromLength, m_stdDeviation, progress)); } - + +bool DropShadowFilterOperation::operator==(const FilterOperation& operation) const +{ + if (!isSameType(operation)) + return false; + const DropShadowFilterOperation& other = downcast<DropShadowFilterOperation>(operation); + return m_location == other.m_location && m_stdDeviation == other.m_stdDeviation && m_color == other.m_color; +} + PassRefPtr<FilterOperation> DropShadowFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough) { if (from && !from->isSameType(*this)) @@ -138,20 +191,89 @@ PassRefPtr<FilterOperation> DropShadowFilterOperation::blend(const FilterOperati return DropShadowFilterOperation::create( WebCore::blend(m_location, IntPoint(), progress), WebCore::blend(m_stdDeviation, 0, progress), - WebCore::blend(m_color, Color(Color::transparent), progress), - m_type); + WebCore::blend(m_color, Color(Color::transparent), progress)); - const DropShadowFilterOperation* fromOp = static_cast<const DropShadowFilterOperation*>(from); - IntPoint fromLocation = fromOp ? fromOp->location() : IntPoint(); - int fromStdDeviation = fromOp ? fromOp->stdDeviation() : 0; - Color fromColor = fromOp ? fromOp->color() : Color(Color::transparent); + const DropShadowFilterOperation* fromOperation = downcast<DropShadowFilterOperation>(from); + IntPoint fromLocation = fromOperation ? fromOperation->location() : IntPoint(); + int fromStdDeviation = fromOperation ? fromOperation->stdDeviation() : 0; + Color fromColor = fromOperation ? fromOperation->color() : Color(Color::transparent); return DropShadowFilterOperation::create( WebCore::blend(fromLocation, m_location, progress), WebCore::blend(fromStdDeviation, m_stdDeviation, progress), - WebCore::blend(fromColor, m_color, progress), m_type); + WebCore::blend(fromColor, m_color, progress)); } -} // namespace WebCore +TextStream& operator<<(TextStream& ts, const FilterOperation& filter) +{ + switch (filter.type()) { + case FilterOperation::REFERENCE: + ts << "reference"; + break; + case FilterOperation::GRAYSCALE: { + const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter); + ts << "grayscale(" << colorMatrixFilter.amount() << ")"; + break; + } + case FilterOperation::SEPIA: { + const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter); + ts << "sepia(" << colorMatrixFilter.amount() << ")"; + break; + } + case FilterOperation::SATURATE: { + const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter); + ts << "saturate(" << colorMatrixFilter.amount() << ")"; + break; + } + case FilterOperation::HUE_ROTATE: { + const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter); + ts << "hue-rotate(" << colorMatrixFilter.amount() << ")"; + break; + } + case FilterOperation::INVERT: { + const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter); + ts << "invert(" << componentTransferFilter.amount() << ")"; + break; + } + case FilterOperation::OPACITY: { + const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter); + ts << "opacity(" << componentTransferFilter.amount() << ")"; + break; + } + case FilterOperation::BRIGHTNESS: { + const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter); + ts << "brightness(" << componentTransferFilter.amount() << ")"; + break; + } + case FilterOperation::CONTRAST: { + const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter); + ts << "contrast(" << componentTransferFilter.amount() << ")"; + break; + } + case FilterOperation::BLUR: { + const auto& blurFilter = downcast<BlurFilterOperation>(filter); + ts << "blur(" << blurFilter.stdDeviation().value() << ")"; // FIXME: should call floatValueForLength() but that's outisde of platform/. + break; + } + case FilterOperation::DROP_SHADOW: { + const auto& dropShadowFilter = downcast<DropShadowFilterOperation>(filter); + ts << "drop-shadow(" << dropShadowFilter.x() << " " << dropShadowFilter.y() << " " << dropShadowFilter.location() << " "; + ts << dropShadowFilter.color() << ")"; + break; + } + case FilterOperation::PASSTHROUGH: + ts << "passthrough"; + break; + case FilterOperation::DEFAULT: { + const auto& defaultFilter = downcast<DefaultFilterOperation>(filter); + ts << "default type=" << (int)defaultFilter.representedType(); + break; + } + case FilterOperation::NONE: + ts << "none"; + break; + } + return ts; +} -#endif // ENABLE(CSS_FILTERS) +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/filters/FilterOperation.h b/Source/WebCore/platform/graphics/filters/FilterOperation.h index 442982906..4caeb49b0 100644 --- a/Source/WebCore/platform/graphics/filters/FilterOperation.h +++ b/Source/WebCore/platform/graphics/filters/FilterOperation.h @@ -10,31 +10,27 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FilterOperation_h #define FilterOperation_h -#if ENABLE(CSS_FILTERS) - #include "Color.h" -#include "FilterEffect.h" #include "LayoutSize.h" #include "Length.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> #include <wtf/text/WTFString.h> // Annoyingly, wingdi.h #defines this. @@ -46,9 +42,10 @@ namespace WebCore { // CSS Filters -#if ENABLE(SVG) +class CachedResourceLoader; class CachedSVGDocumentReference; -#endif +class FilterEffect; +struct ResourceLoaderOptions; class FilterOperation : public RefCounted<FilterOperation> { public: @@ -65,35 +62,49 @@ public: BLUR, DROP_SHADOW, PASSTHROUGH, + DEFAULT, NONE }; virtual ~FilterOperation() { } + virtual PassRefPtr<FilterOperation> clone() const = 0; + virtual bool operator==(const FilterOperation&) const = 0; bool operator!=(const FilterOperation& o) const { return !(*this == o); } virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, bool /*blendToPassthrough*/ = false) - { + { ASSERT(!blendingNeedsRendererSize()); - return 0; + return 0; } virtual PassRefPtr<FilterOperation> blend(const FilterOperation* /*from*/, double /*progress*/, const LayoutSize&, bool /*blendToPassthrough*/ = false) - { + { ASSERT(blendingNeedsRendererSize()); - return 0; + return 0; + } + + OperationType type() const { return m_type; } + + bool isBasicColorMatrixFilterOperation() const + { + return m_type == GRAYSCALE || m_type == SEPIA || m_type == SATURATE || m_type == HUE_ROTATE; } - virtual OperationType type() const { return m_type; } - virtual bool isSameType(const FilterOperation& o) const { return o.type() == m_type; } - - virtual bool isDefault() const { return false; } + bool isBasicComponentTransferFilterOperation() const + { + return m_type == INVERT || m_type == BRIGHTNESS || m_type == CONTRAST || m_type == OPACITY; + } + + bool isSameType(const FilterOperation& o) const { return o.type() == m_type; } // True if the alpha channel of any pixel can change under this operation. virtual bool affectsOpacity() const { return false; } // True if the the value of one pixel can affect the value of another pixel under this operation, such as blur. virtual bool movesPixels() const { return false; } + // True if the filter should not be allowed to work on content that is not available from this security origin. + virtual bool shouldBeRestrictedBySecurityOrigin() const { return false; } // True if the filter needs the size of the box in order to calculate the animations. virtual bool blendingNeedsRendererSize() const { return false; } @@ -106,25 +117,30 @@ protected: OperationType m_type; }; -class DefaultFilterOperation : public FilterOperation { +class WEBCORE_EXPORT DefaultFilterOperation : public FilterOperation { public: - static PassRefPtr<DefaultFilterOperation> create(OperationType type) + static PassRefPtr<DefaultFilterOperation> create(OperationType representedType) { - return adoptRef(new DefaultFilterOperation(type)); + return adoptRef(new DefaultFilterOperation(representedType)); } -private: - virtual bool operator==(const FilterOperation& o) const override + PassRefPtr<FilterOperation> clone() const override { - return isSameType(o); + return adoptRef(new DefaultFilterOperation(representedType())); } - virtual bool isDefault() const override { return true; } + OperationType representedType() const { return m_representedType; } - DefaultFilterOperation(OperationType type) - : FilterOperation(type) +private: + bool operator==(const FilterOperation&) const override; + + DefaultFilterOperation(OperationType representedType) + : FilterOperation(DEFAULT) + , m_representedType(representedType) { } + + OperationType m_representedType; }; class PassthroughFilterOperation : public FilterOperation { @@ -134,8 +150,13 @@ public: return adoptRef(new PassthroughFilterOperation()); } + PassRefPtr<FilterOperation> clone() const override + { + return adoptRef(new PassthroughFilterOperation()); + } + private: - virtual bool operator==(const FilterOperation& o) const override + bool operator==(const FilterOperation& o) const override { return isSameType(o); } @@ -148,69 +169,69 @@ private: class ReferenceFilterOperation : public FilterOperation { public: - static PassRefPtr<ReferenceFilterOperation> create(const String& url, const String& fragment, OperationType type) + static PassRefPtr<ReferenceFilterOperation> create(const String& url, const String& fragment) { - return adoptRef(new ReferenceFilterOperation(url, fragment, type)); + return adoptRef(new ReferenceFilterOperation(url, fragment)); } virtual ~ReferenceFilterOperation(); - virtual bool affectsOpacity() const override { return true; } - virtual bool movesPixels() const override { return true; } + PassRefPtr<FilterOperation> clone() const override + { + // Reference filters cannot be cloned. + ASSERT_NOT_REACHED(); + return nullptr; + } + + bool affectsOpacity() const override { return true; } + bool movesPixels() const override { return true; } + // FIXME: This only needs to return true for graphs that include ConvolveMatrix, DisplacementMap, Morphology and possibly Lighting. + // https://bugs.webkit.org/show_bug.cgi?id=171753 + bool shouldBeRestrictedBySecurityOrigin() const override { return true; } const String& url() const { return m_url; } const String& fragment() const { return m_fragment; } -#if ENABLE(SVG) + void loadExternalDocumentIfNeeded(CachedResourceLoader&, const ResourceLoaderOptions&); + CachedSVGDocumentReference* cachedSVGDocumentReference() const { return m_cachedSVGDocumentReference.get(); } - CachedSVGDocumentReference* getOrCreateCachedSVGDocumentReference(); -#endif FilterEffect* filterEffect() const { return m_filterEffect.get(); } - void setFilterEffect(PassRefPtr<FilterEffect> filterEffect) { m_filterEffect = filterEffect; } + void setFilterEffect(PassRefPtr<FilterEffect>); private: - ReferenceFilterOperation(const String& url, const String& fragment, OperationType); + ReferenceFilterOperation(const String& url, const String& fragment); - virtual bool operator==(const FilterOperation& o) const override - { - if (!isSameType(o)) - return false; - const ReferenceFilterOperation* other = static_cast<const ReferenceFilterOperation*>(&o); - return m_url == other->m_url; - } + bool operator==(const FilterOperation&) const override; String m_url; String m_fragment; -#if ENABLE(SVG) std::unique_ptr<CachedSVGDocumentReference> m_cachedSVGDocumentReference; -#endif RefPtr<FilterEffect> m_filterEffect; }; // GRAYSCALE, SEPIA, SATURATE and HUE_ROTATE are variations on a basic color matrix effect. // For HUE_ROTATE, the angle of rotation is stored in m_amount. -class BasicColorMatrixFilterOperation : public FilterOperation { +class WEBCORE_EXPORT BasicColorMatrixFilterOperation : public FilterOperation { public: static PassRefPtr<BasicColorMatrixFilterOperation> create(double amount, OperationType type) { return adoptRef(new BasicColorMatrixFilterOperation(amount, type)); } + PassRefPtr<FilterOperation> clone() const override + { + return adoptRef(new BasicColorMatrixFilterOperation(amount(), type())); + } + double amount() const { return m_amount; } - virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; + PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; private: - virtual bool operator==(const FilterOperation& o) const override - { - if (!isSameType(o)) - return false; - const BasicColorMatrixFilterOperation* other = static_cast<const BasicColorMatrixFilterOperation*>(&o); - return m_amount == other->m_amount; - } - + bool operator==(const FilterOperation&) const override; + double passthroughAmount() const; - + BasicColorMatrixFilterOperation(double amount, OperationType type) : FilterOperation(type) , m_amount(amount) @@ -221,27 +242,26 @@ private: }; // INVERT, BRIGHTNESS, CONTRAST and OPACITY are variations on a basic component transfer effect. -class BasicComponentTransferFilterOperation : public FilterOperation { +class WEBCORE_EXPORT BasicComponentTransferFilterOperation : public FilterOperation { public: static PassRefPtr<BasicComponentTransferFilterOperation> create(double amount, OperationType type) { return adoptRef(new BasicComponentTransferFilterOperation(amount, type)); } + PassRefPtr<FilterOperation> clone() const override + { + return adoptRef(new BasicComponentTransferFilterOperation(amount(), type())); + } + double amount() const { return m_amount; } - virtual bool affectsOpacity() const override { return m_type == OPACITY; } + bool affectsOpacity() const override { return m_type == OPACITY; } - virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; + PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; private: - virtual bool operator==(const FilterOperation& o) const override - { - if (!isSameType(o)) - return false; - const BasicComponentTransferFilterOperation* other = static_cast<const BasicComponentTransferFilterOperation*>(&o); - return m_amount == other->m_amount; - } + bool operator==(const FilterOperation&) const override; double passthroughAmount() const; @@ -254,67 +274,65 @@ private: double m_amount; }; -class BlurFilterOperation : public FilterOperation { +class WEBCORE_EXPORT BlurFilterOperation : public FilterOperation { public: - static PassRefPtr<BlurFilterOperation> create(Length stdDeviation, OperationType type) + static PassRefPtr<BlurFilterOperation> create(Length stdDeviation) { - return adoptRef(new BlurFilterOperation(std::move(stdDeviation), type)); + return adoptRef(new BlurFilterOperation(WTFMove(stdDeviation))); + } + + PassRefPtr<FilterOperation> clone() const override + { + return adoptRef(new BlurFilterOperation(stdDeviation())); } const Length& stdDeviation() const { return m_stdDeviation; } - virtual bool affectsOpacity() const override { return true; } - virtual bool movesPixels() const override { return true; } + bool affectsOpacity() const override { return true; } + bool movesPixels() const override { return true; } - virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; + PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; private: - virtual bool operator==(const FilterOperation& o) const override - { - if (!isSameType(o)) - return false; - const BlurFilterOperation* other = static_cast<const BlurFilterOperation*>(&o); - return m_stdDeviation == other->m_stdDeviation; - } + bool operator==(const FilterOperation&) const override; - BlurFilterOperation(Length stdDeviation, OperationType type) - : FilterOperation(type) - , m_stdDeviation(std::move(stdDeviation)) + BlurFilterOperation(Length stdDeviation) + : FilterOperation(BLUR) + , m_stdDeviation(WTFMove(stdDeviation)) { } Length m_stdDeviation; }; -class DropShadowFilterOperation : public FilterOperation { +class WEBCORE_EXPORT DropShadowFilterOperation : public FilterOperation { public: - static PassRefPtr<DropShadowFilterOperation> create(const IntPoint& location, int stdDeviation, Color color, OperationType type) + static PassRefPtr<DropShadowFilterOperation> create(const IntPoint& location, int stdDeviation, const Color& color) { - return adoptRef(new DropShadowFilterOperation(location, stdDeviation, color, type)); + return adoptRef(new DropShadowFilterOperation(location, stdDeviation, color)); + } + + PassRefPtr<FilterOperation> clone() const override + { + return adoptRef(new DropShadowFilterOperation(location(), stdDeviation(), color())); } int x() const { return m_location.x(); } int y() const { return m_location.y(); } IntPoint location() const { return m_location; } int stdDeviation() const { return m_stdDeviation; } - Color color() const { return m_color; } + const Color& color() const { return m_color; } - virtual bool affectsOpacity() const override { return true; } - virtual bool movesPixels() const override { return true; } + bool affectsOpacity() const override { return true; } + bool movesPixels() const override { return true; } - virtual PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; + PassRefPtr<FilterOperation> blend(const FilterOperation* from, double progress, bool blendToPassthrough = false) override; private: - virtual bool operator==(const FilterOperation& o) const override - { - if (!isSameType(o)) - return false; - const DropShadowFilterOperation* other = static_cast<const DropShadowFilterOperation*>(&o); - return m_location == other->m_location && m_stdDeviation == other->m_stdDeviation && m_color == other->m_color; - } + bool operator==(const FilterOperation&) const override; - DropShadowFilterOperation(const IntPoint& location, int stdDeviation, Color color, OperationType type) - : FilterOperation(type) + DropShadowFilterOperation(const IntPoint& location, int stdDeviation, const Color& color) + : FilterOperation(DROP_SHADOW) , m_location(location) , m_stdDeviation(stdDeviation) , m_color(color) @@ -326,13 +344,21 @@ private: Color m_color; }; -#define FILTER_OPERATION_CASTS(ToValueTypeName, predicate) \ - TYPE_CASTS_BASE(ToValueTypeName, FilterOperation, operation, operation->type() == FilterOperation::predicate, operation.type() == FilterOperation::predicate) - -FILTER_OPERATION_CASTS(ReferenceFilterOperation, REFERENCE) +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FilterOperation&); } // namespace WebCore -#endif // ENABLE(CSS_FILTERS) +#define SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \ + static bool isType(const WebCore::FilterOperation& operation) { return operation.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() + +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(DefaultFilterOperation, type() == WebCore::FilterOperation::DEFAULT) +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(PassthroughFilterOperation, type() == WebCore::FilterOperation::PASSTHROUGH) +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(ReferenceFilterOperation, type() == WebCore::FilterOperation::REFERENCE) +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(BasicColorMatrixFilterOperation, isBasicColorMatrixFilterOperation()) +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(BasicComponentTransferFilterOperation, isBasicComponentTransferFilterOperation()) +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(BlurFilterOperation, type() == WebCore::FilterOperation::BLUR) +SPECIALIZE_TYPE_TRAITS_FILTEROPERATION(DropShadowFilterOperation, type() == WebCore::FilterOperation::DROP_SHADOW) #endif // FilterOperation_h diff --git a/Source/WebCore/platform/graphics/filters/FilterOperations.cpp b/Source/WebCore/platform/graphics/filters/FilterOperations.cpp index 4279167cc..a1824f996 100644 --- a/Source/WebCore/platform/graphics/filters/FilterOperations.cpp +++ b/Source/WebCore/platform/graphics/filters/FilterOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,58 +29,39 @@ #include "FEGaussianBlur.h" #include "IntSize.h" #include "LengthFunctions.h" - -#if ENABLE(CSS_FILTERS) +#include "TextStream.h" namespace WebCore { static inline IntSize outsetSizeForBlur(float stdDeviation) { - unsigned kernelSizeX = 0; - unsigned kernelSizeY = 0; - FEGaussianBlur::calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdDeviation, stdDeviation); + auto kernelSize = FEGaussianBlur::calculateUnscaledKernelSize(FloatPoint(stdDeviation, stdDeviation)); - IntSize outset; // We take the half kernel size and multiply it with three, because we run box blur three times. - outset.setWidth(3 * kernelSizeX * 0.5f); - outset.setHeight(3 * kernelSizeY * 0.5f); - - return outset; -} - -FilterOperations::FilterOperations() -{ + return { + 3 * kernelSize.width() / 2, + 3 * kernelSize.height() / 2 + }; } -FilterOperations& FilterOperations::operator=(const FilterOperations& other) +bool FilterOperations::operator==(const FilterOperations& other) const { - m_operations = other.m_operations; - return *this; -} - -bool FilterOperations::operator==(const FilterOperations& o) const -{ - if (m_operations.size() != o.m_operations.size()) + size_t size = m_operations.size(); + if (size != other.m_operations.size()) return false; - - unsigned s = m_operations.size(); - for (unsigned i = 0; i < s; i++) { - if (*m_operations[i] != *o.m_operations[i]) + for (size_t i = 0; i < size; i++) { + if (*m_operations[i] != *other.m_operations[i]) return false; } - return true; } bool FilterOperations::operationsMatch(const FilterOperations& other) const { - size_t numOperations = operations().size(); - // If the sizes of the function lists don't match, the lists don't match - if (numOperations != other.operations().size()) + size_t size = operations().size(); + if (size != other.operations().size()) return false; - - // If the types of each function are not the same, the lists don't match - for (size_t i = 0; i < numOperations; ++i) { + for (size_t i = 0; i < size; ++i) { if (!operations()[i]->isSameType(*other.operations()[i])) return false; } @@ -89,8 +70,8 @@ bool FilterOperations::operationsMatch(const FilterOperations& other) const bool FilterOperations::hasReferenceFilter() const { - for (size_t i = 0; i < m_operations.size(); ++i) { - if (m_operations.at(i)->type() == FilterOperation::REFERENCE) + for (auto& operation : m_operations) { + if (operation->type() == FilterOperation::REFERENCE) return true; } return false; @@ -98,9 +79,9 @@ bool FilterOperations::hasReferenceFilter() const bool FilterOperations::hasOutsets() const { - for (size_t i = 0; i < m_operations.size(); ++i) { - FilterOperation::OperationType operationType = m_operations.at(i).get()->type(); - if (operationType == FilterOperation::BLUR || operationType == FilterOperation::DROP_SHADOW) + for (auto& operation : m_operations) { + auto type = operation->type(); + if (type == FilterOperation::BLUR || type == FilterOperation::DROP_SHADOW) return true; } return false; @@ -109,26 +90,25 @@ bool FilterOperations::hasOutsets() const FilterOutsets FilterOperations::outsets() const { FilterOutsets totalOutsets; - for (size_t i = 0; i < m_operations.size(); ++i) { - FilterOperation* filterOperation = m_operations.at(i).get(); - switch (filterOperation->type()) { + for (auto& operation : m_operations) { + switch (operation->type()) { case FilterOperation::BLUR: { - BlurFilterOperation* blurOperation = static_cast<BlurFilterOperation*>(filterOperation); - float stdDeviation = floatValueForLength(blurOperation->stdDeviation(), 0); + auto& blurOperation = downcast<BlurFilterOperation>(*operation); + float stdDeviation = floatValueForLength(blurOperation.stdDeviation(), 0); IntSize outsetSize = outsetSizeForBlur(stdDeviation); FilterOutsets outsets(outsetSize.height(), outsetSize.width(), outsetSize.height(), outsetSize.width()); totalOutsets += outsets; break; } case FilterOperation::DROP_SHADOW: { - DropShadowFilterOperation* dropShadowOperation = static_cast<DropShadowFilterOperation*>(filterOperation); - IntSize outsetSize = outsetSizeForBlur(dropShadowOperation->stdDeviation()); - FilterOutsets outsets( - std::max(0, outsetSize.height() - dropShadowOperation->y()), - std::max(0, outsetSize.width() + dropShadowOperation->x()), - std::max(0, outsetSize.height() + dropShadowOperation->y()), - std::max(0, outsetSize.width() - dropShadowOperation->x()) - ); + auto& dropShadowOperation = downcast<DropShadowFilterOperation>(*operation); + IntSize outsetSize = outsetSizeForBlur(dropShadowOperation.stdDeviation()); + FilterOutsets outsets { + std::max(0, outsetSize.height() - dropShadowOperation.y()), + std::max(0, outsetSize.width() + dropShadowOperation.x()), + std::max(0, outsetSize.height() + dropShadowOperation.y()), + std::max(0, outsetSize.width() - dropShadowOperation.x()) + }; totalOutsets += outsets; break; } @@ -141,20 +121,43 @@ FilterOutsets FilterOperations::outsets() const bool FilterOperations::hasFilterThatAffectsOpacity() const { - for (size_t i = 0; i < m_operations.size(); ++i) - if (m_operations[i]->affectsOpacity()) + for (auto& operation : m_operations) { + if (operation->affectsOpacity()) return true; + } return false; } bool FilterOperations::hasFilterThatMovesPixels() const { - for (size_t i = 0; i < m_operations.size(); ++i) - if (m_operations[i]->movesPixels()) + for (auto& operation : m_operations) { + if (operation->movesPixels()) return true; + } return false; } -} // namespace WebCore +bool FilterOperations::hasFilterThatShouldBeRestrictedBySecurityOrigin() const +{ + for (auto& operation : m_operations) { + if (operation->shouldBeRestrictedBySecurityOrigin()) + return true; + } + return false; +} -#endif // ENABLE(CSS_FILTERS) +TextStream& operator<<(TextStream& ts, const FilterOperations& filters) +{ + for (size_t i = 0; i < filters.size(); ++i) { + auto filter = filters.at(i); + if (filter) + ts << *filter; + else + ts << "(null)"; + if (i < filters.size() - 1) + ts << " "; + } + return ts; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/filters/FilterOperations.h b/Source/WebCore/platform/graphics/filters/FilterOperations.h index c37998e7e..0752e8d44 100644 --- a/Source/WebCore/platform/graphics/filters/FilterOperations.h +++ b/Source/WebCore/platform/graphics/filters/FilterOperations.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,10 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FilterOperations_h -#define FilterOperations_h - -#if ENABLE(CSS_FILTERS) +#pragma once #include "FilterOperation.h" #include "IntRectExtent.h" @@ -35,33 +32,22 @@ namespace WebCore { -typedef IntRectExtent FilterOutsets; +using FilterOutsets = IntRectExtent; class FilterOperations { WTF_MAKE_FAST_ALLOCATED; public: - FilterOperations(); - FilterOperations(const FilterOperations& other) { *this = other; } - - FilterOperations& operator=(const FilterOperations&); - bool operator==(const FilterOperations&) const; - bool operator!=(const FilterOperations& o) const - { - return !(*this == o); - } - - void clear() - { - m_operations.clear(); - } - + bool operator!=(const FilterOperations& other) const { return !(*this == other); } + + void clear() { m_operations.clear(); } + Vector<RefPtr<FilterOperation>>& operations() { return m_operations; } const Vector<RefPtr<FilterOperation>>& operations() const { return m_operations; } - bool isEmpty() const { return !m_operations.size(); } + bool isEmpty() const { return m_operations.isEmpty(); } size_t size() const { return m_operations.size(); } - const FilterOperation* at(size_t index) const { return index < m_operations.size() ? m_operations.at(index).get() : 0; } + const FilterOperation* at(size_t index) const { return index < m_operations.size() ? m_operations[index].get() : nullptr; } bool operationsMatch(const FilterOperations&) const; @@ -70,14 +56,14 @@ public: bool hasFilterThatAffectsOpacity() const; bool hasFilterThatMovesPixels() const; + bool hasFilterThatShouldBeRestrictedBySecurityOrigin() const; bool hasReferenceFilter() const; + private: Vector<RefPtr<FilterOperation>> m_operations; }; -} // namespace WebCore - -#endif // ENABLE(CSS_FILTERS) +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FilterOperations&); -#endif // FilterOperations_h +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/filters/LightSource.h b/Source/WebCore/platform/graphics/filters/LightSource.h index 7aa270e5f..7677d583e 100644 --- a/Source/WebCore/platform/graphics/filters/LightSource.h +++ b/Source/WebCore/platform/graphics/filters/LightSource.h @@ -24,9 +24,7 @@ #ifndef LightSource_h #define LightSource_h -#if ENABLE(FILTERS) #include "FloatPoint3D.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -91,6 +89,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // LightSource_h diff --git a/Source/WebCore/platform/graphics/filters/PointLightSource.cpp b/Source/WebCore/platform/graphics/filters/PointLightSource.cpp index 207ed8eae..d50ba04dd 100644 --- a/Source/WebCore/platform/graphics/filters/PointLightSource.cpp +++ b/Source/WebCore/platform/graphics/filters/PointLightSource.cpp @@ -29,8 +29,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "PointLightSource.h" #include "TextStream.h" @@ -73,12 +71,6 @@ bool PointLightSource::setZ(float z) return true; } -static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p) -{ - ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z(); - return ts; -} - TextStream& PointLightSource::externalRepresentation(TextStream& ts) const { ts << "[type=POINT-LIGHT] "; @@ -87,5 +79,3 @@ TextStream& PointLightSource::externalRepresentation(TextStream& ts) const } }; // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/PointLightSource.h b/Source/WebCore/platform/graphics/filters/PointLightSource.h index abfacc940..75415aa66 100644 --- a/Source/WebCore/platform/graphics/filters/PointLightSource.h +++ b/Source/WebCore/platform/graphics/filters/PointLightSource.h @@ -23,27 +23,27 @@ #ifndef PointLightSource_h #define PointLightSource_h -#if ENABLE(FILTERS) #include "LightSource.h" +#include <wtf/Ref.h> namespace WebCore { class PointLightSource : public LightSource { public: - static PassRefPtr<PointLightSource> create(const FloatPoint3D& position) + static Ref<PointLightSource> create(const FloatPoint3D& position) { - return adoptRef(new PointLightSource(position)); + return adoptRef(*new PointLightSource(position)); } const FloatPoint3D& position() const { return m_position; } - virtual bool setX(float) override; - virtual bool setY(float) override; - virtual bool setZ(float) override; + bool setX(float) override; + bool setY(float) override; + bool setZ(float) override; - virtual void initPaintingData(PaintingData&); - virtual void updatePaintingData(PaintingData&, int x, int y, float z); + void initPaintingData(PaintingData&) override; + void updatePaintingData(PaintingData&, int x, int y, float z) override; - virtual TextStream& externalRepresentation(TextStream&) const; + TextStream& externalRepresentation(TextStream&) const override; private: PointLightSource(const FloatPoint3D& position) @@ -57,6 +57,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // PointLightSource_h diff --git a/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp b/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp index f286c9562..61402564a 100644 --- a/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp +++ b/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp @@ -18,51 +18,49 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "SourceAlpha.h" #include "Color.h" #include "Filter.h" #include "GraphicsContext.h" #include "TextStream.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/text/WTFString.h> namespace WebCore { -PassRefPtr<SourceAlpha> SourceAlpha::create(Filter* filter) +Ref<SourceAlpha> SourceAlpha::create(FilterEffect& sourceEffect) { - return adoptRef(new SourceAlpha(filter)); + return adoptRef(*new SourceAlpha(sourceEffect)); } const AtomicString& SourceAlpha::effectName() { - DEFINE_STATIC_LOCAL(const AtomicString, s_effectName, ("SourceAlpha", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> s_effectName("SourceAlpha", AtomicString::ConstructFromLiteral); return s_effectName; } void SourceAlpha::determineAbsolutePaintRect() { - Filter* filter = this->filter(); - FloatRect paintRect = filter->sourceImageRect(); - paintRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); - setAbsolutePaintRect(enclosingIntRect(paintRect)); + inputEffect(0)->determineAbsolutePaintRect(); + setAbsolutePaintRect(inputEffect(0)->absolutePaintRect()); } void SourceAlpha::platformApplySoftware() { ImageBuffer* resultImage = createImageBufferResult(); - Filter* filter = this->filter(); - if (!resultImage || !filter->sourceImage()) + if (!resultImage) return; + GraphicsContext& filterContext = resultImage->context(); - setIsAlphaImage(true); + ImageBuffer* imageBuffer = inputEffect(0)->asImageBuffer(); + if (!imageBuffer) + return; FloatRect imageRect(FloatPoint(), absolutePaintRect().size()); - GraphicsContext* filterContext = resultImage->context(); - filterContext->fillRect(imageRect, Color::black, ColorSpaceDeviceRGB); - filterContext->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint(), CompositeDestinationIn); + filterContext.fillRect(imageRect, Color::black); + filterContext.drawImageBuffer(*imageBuffer, IntPoint(), CompositeDestinationIn); } void SourceAlpha::dump() @@ -76,6 +74,11 @@ TextStream& SourceAlpha::externalRepresentation(TextStream& ts, int indent) cons return ts; } -} // namespace WebCore +SourceAlpha::SourceAlpha(FilterEffect& sourceEffect) + : FilterEffect(sourceEffect.filter()) +{ + setOperatingColorSpace(sourceEffect.operatingColorSpace()); + inputEffects().append(&sourceEffect); +} -#endif // ENABLE(FILTERS) +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/filters/SourceAlpha.h b/Source/WebCore/platform/graphics/filters/SourceAlpha.h index b0220ce9e..deef32427 100644 --- a/Source/WebCore/platform/graphics/filters/SourceAlpha.h +++ b/Source/WebCore/platform/graphics/filters/SourceAlpha.h @@ -20,7 +20,6 @@ #ifndef SourceAlpha_h #define SourceAlpha_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -28,31 +27,23 @@ namespace WebCore { class SourceAlpha : public FilterEffect { public: - static PassRefPtr<SourceAlpha> create(Filter*); + static Ref<SourceAlpha> create(FilterEffect&); static const AtomicString& effectName(); - virtual void platformApplySoftware(); -#if ENABLE(OPENCL) - virtual bool platformApplyOpenCL(); -#endif - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect(); + void determineAbsolutePaintRect() override; - virtual FilterEffectType filterEffectType() const { return FilterEffectTypeSourceInput; } + FilterEffectType filterEffectType() const override { return FilterEffectTypeSourceInput; } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - SourceAlpha(Filter* filter) - : FilterEffect(filter) - { - } + explicit SourceAlpha(FilterEffect&); }; } //namespace WebCore -#endif // ENABLE(FILTERS) - #endif // SourceAlpha_h diff --git a/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp b/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp index 5eebf2cee..2313b033e 100644 --- a/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp +++ b/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp @@ -18,45 +18,46 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "SourceGraphic.h" #include "Filter.h" #include "GraphicsContext.h" #include "TextStream.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/text/WTFString.h> namespace WebCore { -PassRefPtr<SourceGraphic> SourceGraphic::create(Filter* filter) +Ref<SourceGraphic> SourceGraphic::create(Filter& filter) { - return adoptRef(new SourceGraphic(filter)); + return adoptRef(*new SourceGraphic(filter)); } const AtomicString& SourceGraphic::effectName() { - DEFINE_STATIC_LOCAL(const AtomicString, s_effectName, ("SourceGraphic", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> s_effectName("SourceGraphic", AtomicString::ConstructFromLiteral); return s_effectName; } void SourceGraphic::determineAbsolutePaintRect() { - Filter* filter = this->filter(); - FloatRect paintRect = filter->sourceImageRect(); - paintRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); + Filter& filter = this->filter(); + FloatRect paintRect = filter.sourceImageRect(); + paintRect.scale(filter.filterResolution().width(), filter.filterResolution().height()); setAbsolutePaintRect(enclosingIntRect(paintRect)); } void SourceGraphic::platformApplySoftware() { + Filter& filter = this->filter(); + ImageBuffer* resultImage = createImageBufferResult(); - Filter* filter = this->filter(); - if (!resultImage || !filter->sourceImage()) + ImageBuffer* sourceImage = filter.sourceImage(); + if (!resultImage || !sourceImage) return; - resultImage->context()->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint()); + resultImage->context().drawImageBuffer(*sourceImage, IntPoint()); } void SourceGraphic::dump() @@ -71,5 +72,3 @@ TextStream& SourceGraphic::externalRepresentation(TextStream& ts, int indent) co } } // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/SourceGraphic.h b/Source/WebCore/platform/graphics/filters/SourceGraphic.h index 4200595fb..3ffd2491b 100644 --- a/Source/WebCore/platform/graphics/filters/SourceGraphic.h +++ b/Source/WebCore/platform/graphics/filters/SourceGraphic.h @@ -21,7 +21,6 @@ #ifndef SourceGraphic_h #define SourceGraphic_h -#if ENABLE(FILTERS) #include "FilterEffect.h" #include "Filter.h" @@ -29,32 +28,27 @@ namespace WebCore { class SourceGraphic : public FilterEffect { public: - static PassRefPtr<SourceGraphic> create(Filter*); + static Ref<SourceGraphic> create(Filter&); static const AtomicString& effectName(); - virtual void platformApplySoftware(); -#if ENABLE(OPENCL) - virtual bool platformApplyOpenCL(); -#endif - virtual void dump(); + void platformApplySoftware() override; + void dump() override; - virtual void determineAbsolutePaintRect(); + void determineAbsolutePaintRect() override; - virtual FilterEffectType filterEffectType() const { return FilterEffectTypeSourceInput; } + FilterEffectType filterEffectType() const override { return FilterEffectTypeSourceInput; } - virtual TextStream& externalRepresentation(TextStream&, int indention) const; + TextStream& externalRepresentation(TextStream&, int indention) const override; private: - SourceGraphic(Filter* filter) + SourceGraphic(Filter& filter) : FilterEffect(filter) { - setOperatingColorSpace(ColorSpaceDeviceRGB); + setOperatingColorSpace(ColorSpaceSRGB); } }; } //namespace WebCore -#endif // ENABLE(FILTERS) - #endif // SourceGraphic_h diff --git a/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp b/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp index 648fcae6d..30c4f61c6 100644 --- a/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp +++ b/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp @@ -30,8 +30,6 @@ */ #include "config.h" - -#if ENABLE(FILTERS) #include "SpotLightSource.h" #include "TextStream.h" @@ -177,12 +175,6 @@ bool SpotLightSource::setLimitingConeAngle(float limitingConeAngle) return true; } -static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p) -{ - ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z(); - return ts; -} - TextStream& SpotLightSource::externalRepresentation(TextStream& ts) const { ts << "[type=SPOT-LIGHT] "; @@ -194,5 +186,3 @@ TextStream& SpotLightSource::externalRepresentation(TextStream& ts) const } }; // namespace WebCore - -#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/SpotLightSource.h b/Source/WebCore/platform/graphics/filters/SpotLightSource.h index ec9c5cae8..bd3ee36f0 100644 --- a/Source/WebCore/platform/graphics/filters/SpotLightSource.h +++ b/Source/WebCore/platform/graphics/filters/SpotLightSource.h @@ -23,17 +23,17 @@ #ifndef SpotLightSource_h #define SpotLightSource_h -#if ENABLE(FILTERS) #include "LightSource.h" +#include <wtf/Ref.h> namespace WebCore { class SpotLightSource : public LightSource { public: - static PassRefPtr<SpotLightSource> create(const FloatPoint3D& position, + static Ref<SpotLightSource> create(const FloatPoint3D& position, const FloatPoint3D& direction, float specularExponent, float limitingConeAngle) { - return adoptRef(new SpotLightSource(position, direction, specularExponent, limitingConeAngle)); + return adoptRef(*new SpotLightSource(position, direction, specularExponent, limitingConeAngle)); } const FloatPoint3D& position() const { return m_position; } @@ -41,20 +41,20 @@ public: float specularExponent() const { return m_specularExponent; } float limitingConeAngle() const { return m_limitingConeAngle; } - virtual bool setX(float) override; - virtual bool setY(float) override; - virtual bool setZ(float) override; - virtual bool setPointsAtX(float) override; - virtual bool setPointsAtY(float) override; - virtual bool setPointsAtZ(float) override; + bool setX(float) override; + bool setY(float) override; + bool setZ(float) override; + bool setPointsAtX(float) override; + bool setPointsAtY(float) override; + bool setPointsAtZ(float) override; - virtual bool setSpecularExponent(float) override; - virtual bool setLimitingConeAngle(float) override; + bool setSpecularExponent(float) override; + bool setLimitingConeAngle(float) override; - virtual void initPaintingData(PaintingData&); - virtual void updatePaintingData(PaintingData&, int x, int y, float z); + void initPaintingData(PaintingData&) override; + void updatePaintingData(PaintingData&, int x, int y, float z) override; - virtual TextStream& externalRepresentation(TextStream&) const; + TextStream& externalRepresentation(TextStream&) const override; private: SpotLightSource(const FloatPoint3D& position, const FloatPoint3D& direction, @@ -76,6 +76,4 @@ private: } // namespace WebCore -#endif // ENABLE(FILTERS) - #endif // SpotLightSource_h diff --git a/Source/WebCore/platform/graphics/freetype/FcUniquePtr.h b/Source/WebCore/platform/graphics/freetype/FcUniquePtr.h new file mode 100644 index 000000000..1450fbda9 --- /dev/null +++ b/Source/WebCore/platform/graphics/freetype/FcUniquePtr.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FcUniquePtr_h +#define FcUniquePtr_h + +#if USE(FREETYPE) + +#include <fontconfig/fontconfig.h> +#include <memory> + +namespace WebCore { + +template<typename T> +struct FcPtrDeleter { + void operator()(T* ptr) const = delete; +}; + +template<typename T> +using FcUniquePtr = std::unique_ptr<T, FcPtrDeleter<T>>; + +template<> struct FcPtrDeleter<FcCharSet> { + void operator()(FcCharSet* ptr) const + { + FcCharSetDestroy(ptr); + } +}; + +template<> struct FcPtrDeleter<FcFontSet> { + void operator()(FcFontSet* ptr) const + { + FcFontSetDestroy(ptr); + } +}; + +template<> struct FcPtrDeleter<FcLangSet> { + void operator()(FcLangSet* ptr) const + { + FcLangSetDestroy(ptr); + } +}; + +template<> struct FcPtrDeleter<FcObjectSet> { + void operator()(FcObjectSet* ptr) const + { + FcObjectSetDestroy(ptr); + } +}; + +} // namespace WebCore + +#endif // USE(FREETYPE) + +#endif // FcUniquePtr_h diff --git a/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp index cd25f7d73..ff3eacf3d 100644 --- a/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp @@ -22,10 +22,10 @@ #include "config.h" #include "FontCache.h" +#include "CairoUtilities.h" +#include "FcUniquePtr.h" #include "Font.h" -#include "OwnPtrCairo.h" #include "RefPtrCairo.h" -#include "SimpleFontData.h" #include "UTF16UChar32Iterator.h" #include <cairo-ft.h> #include <cairo.h> @@ -42,102 +42,130 @@ void FontCache::platformInit() ASSERT_NOT_REACHED(); } -FcPattern* createFontConfigPatternForCharacters(const UChar* characters, int bufferLength) +static RefPtr<FcPattern> createFontConfigPatternForCharacters(const UChar* characters, int bufferLength) { - FcPattern* pattern = FcPatternCreate(); - FcCharSet* fontConfigCharSet = FcCharSetCreate(); + RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); + FcUniquePtr<FcCharSet> fontConfigCharSet(FcCharSetCreate()); UTF16UChar32Iterator iterator(characters, bufferLength); UChar32 character = iterator.next(); while (character != iterator.end()) { - FcCharSetAddChar(fontConfigCharSet, character); + FcCharSetAddChar(fontConfigCharSet.get(), character); character = iterator.next(); } - FcPatternAddCharSet(pattern, FC_CHARSET, fontConfigCharSet); - FcCharSetDestroy(fontConfigCharSet); + FcPatternAddCharSet(pattern.get(), FC_CHARSET, fontConfigCharSet.get()); - FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); + FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue); + FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern); + cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get()); + FcDefaultSubstitute(pattern.get()); return pattern; } -FcPattern* findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern) +static RefPtr<FcPattern> findBestFontGivenFallbacks(const FontPlatformData& fontData, FcPattern* pattern) { - if (!fontData.m_pattern) - return 0; - - if (!fontData.m_fallbacks) { - FcResult fontConfigResult; - fontData.m_fallbacks = FcFontSort(0, fontData.m_pattern.get(), FcTrue, 0, &fontConfigResult); - } - - if (!fontData.m_fallbacks) - return 0; + FcFontSet* fallbacks = fontData.fallbacks(); + if (!fallbacks) + return nullptr; - FcFontSet* sets[] = { fontData.m_fallbacks }; FcResult fontConfigResult; - return FcFontSetMatch(0, sets, 1, pattern, &fontConfigResult); + return FcFontSetMatch(nullptr, &fallbacks, 1, pattern, &fontConfigResult); } -PassRefPtr<SimpleFontData> FontCache::systemFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, bool, const UChar* characters, int length) +RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool, const UChar* characters, unsigned length) { - RefPtr<FcPattern> pattern = adoptRef(createFontConfigPatternForCharacters(characters, length)); + RefPtr<FcPattern> pattern = createFontConfigPatternForCharacters(characters, length); const FontPlatformData& fontData = originalFontData->platformData(); - RefPtr<FcPattern> fallbackPattern = adoptRef(findBestFontGivenFallbacks(fontData, pattern.get())); + RefPtr<FcPattern> fallbackPattern = findBestFontGivenFallbacks(fontData, pattern.get()); if (fallbackPattern) { FontPlatformData alternateFontData(fallbackPattern.get(), description); - return getCachedFontData(&alternateFontData, DoNotRetain); + return fontForPlatformData(alternateFontData); } FcResult fontConfigResult; - RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); + RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult)); if (!resultPattern) - return 0; + return nullptr; FontPlatformData alternateFontData(resultPattern.get(), description); - return getCachedFontData(&alternateFontData, DoNotRetain); + return fontForPlatformData(alternateFontData); +} + +static Vector<String> patternToFamilies(FcPattern& pattern) +{ + char* patternChars = reinterpret_cast<char*>(FcPatternFormat(&pattern, reinterpret_cast<const FcChar8*>("%{family}"))); + String patternString = String::fromUTF8(patternChars); + free(patternChars); + + Vector<String> results; + patternString.split(',', results); + return results; } -PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain) +Vector<String> FontCache::systemFontFamilies() +{ + RefPtr<FcPattern> scalablesOnlyPattern = adoptRef(FcPatternCreate()); + FcPatternAddBool(scalablesOnlyPattern.get(), FC_SCALABLE, FcTrue); + + FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr)); + FcUniquePtr<FcFontSet> fontSet(FcFontList(nullptr, scalablesOnlyPattern.get(), familiesOnly.get())); + + Vector<String> fontFamilies; + for (int i = 0; i < fontSet->nfont; i++) { + FcPattern* pattern = fontSet->fonts[i]; + FcChar8* family = nullptr; + FcPatternGetString(pattern, FC_FAMILY, 0, &family); + if (family) + fontFamilies.appendVector(patternToFamilies(*pattern)); + } + + return fontFamilies; +} + +Ref<Font> FontCache::lastResortFallbackFontForEveryCharacter(const FontDescription& fontDescription) +{ + return lastResortFallbackFont(fontDescription); +} + +Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription) { // We want to return a fallback font here, otherwise the logic preventing FontConfig // matches for non-fallback fonts might return 0. See isFallbackFontAllowed. static AtomicString timesStr("serif"); - return getCachedFontData(fontDescription, timesStr, false, shouldRetain); + if (RefPtr<Font> font = fontForFamily(fontDescription, timesStr)) + return *font; + + // This could be reached due to improperly-installed or misconfigured fontconfig. + RELEASE_ASSERT_NOT_REACHED(); } -void FontCache::getTraitsInFamily(const AtomicString&, Vector<unsigned>&) +Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString&) { + return { }; } -static String getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription& fontDescription, const AtomicString& family) +static String getFamilyNameStringFromFamily(const AtomicString& family) { // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into // the fallback name (like "monospace") that fontconfig understands. if (family.length() && !family.startsWith("-webkit-")) return family.string(); - switch (fontDescription.genericFamily()) { - case FontDescription::StandardFamily: - case FontDescription::SerifFamily: + if (family == standardFamily || family == serifFamily) return "serif"; - case FontDescription::SansSerifFamily: + if (family == sansSerifFamily) return "sans-serif"; - case FontDescription::MonospaceFamily: + if (family == monospaceFamily) return "monospace"; - case FontDescription::CursiveFamily: + if (family == cursiveFamily) return "cursive"; - case FontDescription::FantasyFamily: + if (family == fantasyFamily) return "fantasy"; - case FontDescription::NoFamily: - default: - return ""; - } + return ""; } -int fontWeightToFontconfigWeight(FontWeight weight) +static int fontWeightToFontconfigWeight(FontWeight weight) { switch (weight) { case FontWeight100: @@ -164,13 +192,153 @@ int fontWeightToFontconfigWeight(FontWeight weight) } } -PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +// This is based on Chromium BSD code from Skia (src/ports/SkFontMgr_fontconfig.cpp). It is a +// hack for lack of API in Fontconfig: https://bugs.freedesktop.org/show_bug.cgi?id=19375 +// FIXME: This is horrible. It should be deleted once Fontconfig can do this itself. +enum class AliasStrength { + Weak, + Strong, + Done +}; + +static AliasStrength strengthOfFirstAlias(const FcPattern& original) +{ + // Ideally there would exist a call like + // FcResult FcPatternIsWeak(pattern, object, id, FcBool* isWeak); + // + // However, there is no such call and as of Fc 2.11.0 even FcPatternEquals ignores the weak bit. + // Currently, the only reliable way of finding the weak bit is by its effect on matching. + // The weak bit only affects the matching of FC_FAMILY and FC_POSTSCRIPT_NAME object values. + // A element with the weak bit is scored after FC_LANG, without the weak bit is scored before. + // Note that the weak bit is stored on the element, not on the value it holds. + FcValue value; + FcResult result = FcPatternGet(&original, FC_FAMILY, 0, &value); + if (result != FcResultMatch) + return AliasStrength::Done; + + RefPtr<FcPattern> pattern = adoptRef(FcPatternDuplicate(&original)); + FcBool hasMultipleFamilies = true; + while (hasMultipleFamilies) + hasMultipleFamilies = FcPatternRemove(pattern.get(), FC_FAMILY, 1); + + // Create a font set with two patterns. + // 1. the same FC_FAMILY as pattern and a lang object with only 'nomatchlang'. + // 2. a different FC_FAMILY from pattern and a lang object with only 'matchlang'. + FcUniquePtr<FcFontSet> fontSet(FcFontSetCreate()); + + FcUniquePtr<FcLangSet> strongLangSet(FcLangSetCreate()); + FcLangSetAdd(strongLangSet.get(), reinterpret_cast<const FcChar8*>("nomatchlang")); + // Ownership of this FcPattern will be transferred with FcFontSetAdd. + FcPattern* strong = FcPatternDuplicate(pattern.get()); + FcPatternAddLangSet(strong, FC_LANG, strongLangSet.get()); + + FcUniquePtr<FcLangSet> weakLangSet(FcLangSetCreate()); + FcLangSetAdd(weakLangSet.get(), reinterpret_cast<const FcChar8*>("matchlang")); + // Ownership of this FcPattern will be transferred via FcFontSetAdd. + FcPattern* weak = FcPatternCreate(); + FcPatternAddString(weak, FC_FAMILY, reinterpret_cast<const FcChar8*>("nomatchstring")); + FcPatternAddLangSet(weak, FC_LANG, weakLangSet.get()); + + FcFontSetAdd(fontSet.get(), strong); + FcFontSetAdd(fontSet.get(), weak); + + // Add 'matchlang' to the copy of the pattern. + FcPatternAddLangSet(pattern.get(), FC_LANG, weakLangSet.get()); + + // Run a match against the copy of the pattern. + // If the first element was weak, then we should match the pattern with 'matchlang'. + // If the first element was strong, then we should match the pattern with 'nomatchlang'. + + // Note that this config is only used for FcFontRenderPrepare, which we don't even want. + // However, there appears to be no way to match/sort without it. + RefPtr<FcConfig> config = adoptRef(FcConfigCreate()); + FcFontSet* fontSets[1] = { fontSet.get() }; + RefPtr<FcPattern> match = adoptRef(FcFontSetMatch(config.get(), fontSets, 1, pattern.get(), &result)); + + FcLangSet* matchLangSet; + FcPatternGetLangSet(match.get(), FC_LANG, 0, &matchLangSet); + return FcLangEqual == FcLangSetHasLang(matchLangSet, reinterpret_cast<const FcChar8*>("matchlang")) + ? AliasStrength::Weak : AliasStrength::Strong; +} + +static Vector<String> strongAliasesForFamily(const String& family) +{ + RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); + if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(family.utf8().data()))) + return Vector<String>(); + + FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern); + cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get()); + FcDefaultSubstitute(pattern.get()); + + FcUniquePtr<FcObjectSet> familiesOnly(FcObjectSetBuild(FC_FAMILY, nullptr)); + RefPtr<FcPattern> minimal = adoptRef(FcPatternFilter(pattern.get(), familiesOnly.get())); + + // We really want to match strong (preferred) and same (acceptable) only here. + // If a family name was specified, assume that any weak matches after the last strong match + // are weak (default) and ignore them. + // The reason for is that after substitution the pattern for 'sans-serif' looks like + // "wwwwwwwwwwwwwwswww" where there are many weak but preferred names, followed by defaults. + // So it is possible to have weakly matching but preferred names. + // In aliases, bindings are weak by default, so this is easy and common. + // If no family name was specified, we'll probably only get weak matches, but that's ok. + int lastStrongId = -1; + int numIds = 0; + for (int id = 0; ; ++id) { + AliasStrength result = strengthOfFirstAlias(*minimal); + if (result == AliasStrength::Done) { + numIds = id; + break; + } + if (result == AliasStrength::Strong) + lastStrongId = id; + if (!FcPatternRemove(minimal.get(), FC_FAMILY, 0)) + return Vector<String>(); + } + + // If they were all weak, then leave the pattern alone. + if (lastStrongId < 0) + return Vector<String>(); + + // Remove everything after the last strong. + for (int id = lastStrongId + 1; id < numIds; ++id) { + if (!FcPatternRemove(pattern.get(), FC_FAMILY, lastStrongId + 1)) { + ASSERT_NOT_REACHED(); + return Vector<String>(); + } + } + + return patternToFamilies(*pattern); +} + +static bool areStronglyAliased(const String& familyA, const String& familyB) +{ + for (auto& family : strongAliasesForFamily(familyA)) { + if (family == familyB) + return true; + } + return false; +} + +static inline bool isCommonlyUsedGenericFamily(const String& familyNameString) +{ + return equalLettersIgnoringASCIICase(familyNameString, "sans") + || equalLettersIgnoringASCIICase(familyNameString, "sans-serif") + || equalLettersIgnoringASCIICase(familyNameString, "serif") + || equalLettersIgnoringASCIICase(familyNameString, "monospace") + || equalLettersIgnoringASCIICase(familyNameString, "fantasy") + || equalLettersIgnoringASCIICase(familyNameString, "cursive"); +} + +std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings*, const FontVariantSettings*) { // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) // says that we must find an exact match for font family, slant (italic or oblique can be used) // and font weight (we only match bold/non-bold here). RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); - String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family)); + // Never choose unscalable fonts, as they pixelate when displayed at different sizes. + FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue); + String familyNameString(getFamilyNameStringFromFamily(family)); if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data()))) return nullptr; @@ -183,11 +351,19 @@ PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescrip return nullptr; // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): - - // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback" - // family like "sans," this is the only time we allow Fontconfig to substitute one - // family name for another (i.e. if the fonts are aliased to each other). - FcConfigSubstitute(0, pattern.get(), FcMatchPattern); + // + // We do not normally allow fontconfig to substitute one font family for another, since this + // would break CSS font family fallback: the website should be in control of fallback. During + // normal font matching, the only font family substitution permitted is for generic families + // (sans, serif, monospace) or for strongly-aliased fonts (which are to be treated as + // effectively identical). This is because the font matching step is designed to always find a + // match for the font, which we don't want. + // + // Fontconfig is used in two stages: (1) configuration and (2) matching. During the + // configuration step, before any matching occurs, we allow arbitrary family substitutions, + // since this is an exact matter of respecting the user's font configuration. + FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern); + cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern.get()); FcDefaultSubstitute(pattern.get()); FcChar8* fontConfigFamilyNameAfterConfiguration; @@ -195,7 +371,7 @@ PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescrip String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration)); FcResult fontConfigResult; - RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); + RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult)); if (!resultPattern) // No match. return nullptr; @@ -203,23 +379,26 @@ PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescrip FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); - // If Fontconfig gave use a different font family than the one we requested, we should ignore it - // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if - // this family name is a commonly used generic family. - if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) - && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") - || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") - || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive"))) + // If Fontconfig gave us a different font family than the one we requested, we should ignore it + // and allow WebCore to give us the next font on the CSS fallback list. The exceptions are if + // this family name is a commonly-used generic family, or if the families are strongly-aliased. + // Checking for a strong alias comes last, since it is slow. + if (!equalIgnoringASCIICase(familyNameAfterConfiguration, familyNameAfterMatching) && !isCommonlyUsedGenericFamily(familyNameString) && !areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching)) return nullptr; // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. // If this font doesn't have one of these three encodings, don't select it. - OwnPtr<FontPlatformData> platformData = adoptPtr(new FontPlatformData(resultPattern.get(), fontDescription)); + auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription); if (!platformData->hasCompatibleCharmap()) return nullptr; - return platformData.release(); + return platformData; +} + +const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString&) +{ + return nullAtom; } } diff --git a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp index fdf7a3633..0075343b7 100644 --- a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp @@ -35,10 +35,26 @@ static void releaseCustomFontData(void* data) } FontCustomPlatformData::FontCustomPlatformData(FT_Face freeTypeFace, SharedBuffer& buffer) - : m_freeTypeFace(freeTypeFace) - , m_fontFace(cairo_ft_font_face_create_for_ft_face(freeTypeFace, 0)) + : m_fontFace(cairo_ft_font_face_create_for_ft_face(freeTypeFace, FT_LOAD_FORCE_AUTOHINT)) { - // FIXME Should we be setting some hinting options here? + // FT_LOAD_FORCE_AUTOHINT prohibits use of the font's native hinting. This + // is a safe option for custom fonts because (a) some such fonts may have + // broken hinting, which site admins may not notice if other browsers do not + // use the native hints, and (b) allowing native hints exposes the FreeType + // bytecode interpreter to potentially-malicious input. Treating web fonts + // differently than system fonts is non-ideal, but the result of autohinting + // is always decent, whereas native hints sometimes look terrible, and + // unlike system fonts where Fontconfig may change the hinting settings on a + // per-font basis, the same settings are used for all web fonts. Note that + // Chrome is considering switching from autohinting to native hinting in + // https://code.google.com/p/chromium/issues/detail?id=173207 but this is + // more risk than we want to assume for now. See + // https://bugs.webkit.org/show_bug.cgi?id=140994 before changing this, and + // also consider that (a) the fonts' native hints will all be designed to + // work on Windows, and might not look good at all with FreeType, whereas + // automatic hints will always look decent, and (b) Fontconfig is not + // capable of providing any per-font hinting settings for web fonts, unlike + // for system fonts, so it seems acceptable to treat them differently. buffer.ref(); // This is balanced by the buffer->deref() in releaseCustomFontData. static cairo_user_data_key_t bufferKey; @@ -54,13 +70,12 @@ FontCustomPlatformData::FontCustomPlatformData(FT_Face freeTypeFace, SharedBuffe FontCustomPlatformData::~FontCustomPlatformData() { - // m_freeTypeFace will be destroyed along with m_fontFace. See the constructor. cairo_font_face_destroy(m_fontFace); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& description, bool bold, bool italic) { - return FontPlatformData(m_fontFace, size, bold, italic, orientation); + return FontPlatformData(m_fontFace, description, bold, italic); } std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffer& buffer) @@ -79,7 +94,12 @@ std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffe bool FontCustomPlatformData::supportsFormat(const String& format) { - return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype") || equalIgnoringCase(format, "woff"); + return equalLettersIgnoringASCIICase(format, "truetype") + || equalLettersIgnoringASCIICase(format, "opentype") +#if USE(WOFF2) + || equalLettersIgnoringASCIICase(format, "woff2") +#endif + || equalLettersIgnoringASCIICase(format, "woff"); } } diff --git a/Source/WebCore/platform/graphics/freetype/FontPlatformData.h b/Source/WebCore/platform/graphics/freetype/FontPlatformData.h deleted file mode 100644 index 808ba21ce..000000000 --- a/Source/WebCore/platform/graphics/freetype/FontPlatformData.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * Copyright (C) 2007 Holger Hans Peter Freyther - * Copyright (C) 2007 Pioneer Research Center USA, Inc. - * Copyright (C) 2010 Igalia S.L. - * All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef FontPlatformDataFreeType_h -#define FontPlatformDataFreeType_h - -#include "FontCache.h" -#include "FontDescription.h" -#include "FontOrientation.h" -#include "GlyphBuffer.h" -#include "HarfBuzzFace.h" -#include "OpenTypeVerticalData.h" -#include "RefPtrCairo.h" -#include "SharedBuffer.h" -#include <wtf/Forward.h> -#include <wtf/HashFunctions.h> - -typedef struct _FcFontSet FcFontSet; -class HarfBuzzFace; - -namespace WebCore { - -class FontPlatformData { -public: - FontPlatformData(WTF::HashTableDeletedValueType) - : m_fallbacks(0) - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_scaledFont(hashTableDeletedFontValue()) - , m_orientation(Horizontal) - { } - - FontPlatformData() - : m_fallbacks(0) - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_scaledFont(0) - , m_orientation(Horizontal) - { } - - FontPlatformData(FcPattern*, const FontDescription&); - FontPlatformData(cairo_font_face_t*, float size, bool bold, bool italic, FontOrientation); - FontPlatformData(float size, bool bold, bool italic); - FontPlatformData(const FontPlatformData&); - FontPlatformData(const FontPlatformData&, float size); - - ~FontPlatformData(); - - HarfBuzzFace* harfBuzzFace() const; - - bool isFixedPitch(); - float size() const { return m_size; } - void setSize(float size) { m_size = size; } - bool syntheticBold() const { return m_syntheticBold; } - bool syntheticOblique() const { return m_syntheticOblique; } - bool hasCompatibleCharmap(); - - FontOrientation orientation() const { return m_orientation; } - void setOrientation(FontOrientation); - PassRefPtr<SharedBuffer> openTypeTable(uint32_t table) const; - PassRefPtr<OpenTypeVerticalData> verticalData() const; - - cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } - - unsigned hash() const - { - return PtrHash<cairo_scaled_font_t*>::hash(m_scaledFont); - } - - bool operator==(const FontPlatformData&) const; - FontPlatformData& operator=(const FontPlatformData&); - bool isHashTableDeletedValue() const - { - return m_scaledFont == hashTableDeletedFontValue(); - } - -#ifndef NDEBUG - String description() const; -#endif - - RefPtr<FcPattern> m_pattern; - mutable FcFontSet* m_fallbacks; // Initialized lazily. - float m_size; - bool m_syntheticBold; - bool m_syntheticOblique; - bool m_fixedWidth; - cairo_scaled_font_t* m_scaledFont; - mutable RefPtr<HarfBuzzFace> m_harfBuzzFace; - -private: - void initializeWithFontFace(cairo_font_face_t*, const FontDescription& = FontDescription()); - static cairo_scaled_font_t* hashTableDeletedFontValue() { return reinterpret_cast<cairo_scaled_font_t*>(-1); } - - FontOrientation m_orientation; - cairo_matrix_t m_horizontalOrientationMatrix; -}; - -} - -#endif // FontPlatformDataFreeType_h diff --git a/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp index 7c4d853f8..69d640205 100644 --- a/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2007 Holger Hans Peter Freyther @@ -25,21 +25,21 @@ #include "config.h" #include "FontPlatformData.h" +#include "CairoUniquePtr.h" +#include "CairoUtilities.h" +#include "FontCache.h" #include "FontDescription.h" +#include "SharedBuffer.h" #include <cairo-ft.h> -#include <cairo.h> #include <fontconfig/fcfreetype.h> #include <ft2build.h> #include FT_TRUETYPE_TABLES_H +#include <wtf/MathExtras.h> #include <wtf/text/WTFString.h> -#if !PLATFORM(EFL) -#include <gdk/gdk.h> -#endif - namespace WebCore { -cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder) +static cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder) { switch (fontConfigOrder) { case FC_RGBA_RGB: @@ -57,7 +57,7 @@ cairo_subpixel_order_t convertFontConfigSubpixelOrder(int fontConfigOrder) return CAIRO_SUBPIXEL_ORDER_DEFAULT; } -cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle) +static cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle) { switch (fontConfigStyle) { case FC_HINT_NONE: @@ -72,7 +72,7 @@ cairo_hint_style_t convertFontConfigHintStyle(int fontConfigStyle) return CAIRO_HINT_STYLE_NONE; } -void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcPattern* pattern) +static void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcPattern* pattern) { FcBool booleanResult; int integerResult; @@ -102,46 +102,39 @@ void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcP cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); } -static cairo_font_options_t* getDefaultFontOptions() +static FcPattern* getDefaultFontconfigOptions() { -#if PLATFORM(GTK) - if (GdkScreen* screen = gdk_screen_get_default()) { - const cairo_font_options_t* screenOptions = gdk_screen_get_font_options(screen); - if (screenOptions) - return cairo_font_options_copy(screenOptions); - } -#endif - return cairo_font_options_create(); -} - -static void rotateCairoMatrixForVerticalOrientation(cairo_matrix_t* matrix) -{ - // The resulting transformation matrix for vertical glyphs (V) is a - // combination of rotation (R) and translation (T) applied on the - // horizontal matrix (H). V = H . R . T, where R rotates by -90 degrees - // and T translates by font size towards y axis. - cairo_matrix_rotate(matrix, -piOverTwoDouble); - cairo_matrix_translate(matrix, 0.0, 1.0); + // Get some generic default settings from fontconfig for web fonts. Strategy + // from Behdad Esfahbod in https://code.google.com/p/chromium/issues/detail?id=173207#c35 + // For web fonts, the hint style is overridden in FontCustomPlatformData::FontCustomPlatformData + // so Fontconfig will not affect the hint style, but it may disable hinting completely. + static FcPattern* pattern = nullptr; + static std::once_flag flag; + std::call_once(flag, [](FcPattern*) { + pattern = FcPatternCreate(); + FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern); + FcDefaultSubstitute(pattern); + FcPatternDel(pattern, FC_FAMILY); + FcConfigSubstitute(nullptr, pattern, FcMatchFont); + }, pattern); + return pattern; } FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fontDescription) : m_pattern(pattern) - , m_fallbacks(0) , m_size(fontDescription.computedPixelSize()) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_fixedWidth(false) - , m_scaledFont(0) , m_orientation(fontDescription.orientation()) { + ASSERT(m_pattern); RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(m_pattern.get())); - initializeWithFontFace(fontFace.get(), fontDescription); int spacing; if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO) m_fixedWidth = true; - if (fontDescription.weight() >= FontWeightBold) { + bool descriptionAllowsSyntheticBold = fontDescription.fontSynthesis() & FontSynthesisWeight; + if (descriptionAllowsSyntheticBold && fontDescription.weight() >= FontWeightBold) { // The FC_EMBOLDEN property instructs us to fake the boldness of the font. FcBool fontConfigEmbolden = FcFalse; if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch) @@ -152,36 +145,34 @@ FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fo if (!m_syntheticBold && FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) == FcResultMatch) m_syntheticBold = m_syntheticBold || weight < FC_WEIGHT_DEMIBOLD; } -} -FontPlatformData::FontPlatformData(float size, bool bold, bool italic) - : m_fallbacks(0) - , m_size(size) - , m_syntheticBold(bold) - , m_syntheticOblique(italic) - , m_fixedWidth(false) - , m_scaledFont(0) - , m_orientation(Horizontal) -{ - // We cannot create a scaled font here. + // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic. + int actualFontSlant; + bool descriptionAllowsSyntheticOblique = fontDescription.fontSynthesis() & FontSynthesisStyle; + if (descriptionAllowsSyntheticOblique && fontDescription.italic() + && FcPatternGetInteger(pattern, FC_SLANT, 0, &actualFontSlant) == FcResultMatch) { + m_syntheticOblique = actualFontSlant == FC_SLANT_ROMAN; + } + + buildScaledFont(fontFace.get()); } -FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic, FontOrientation orientation) - : m_fallbacks(0) - , m_size(size) +FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, const FontDescription& description, bool bold, bool italic) + : m_size(description.computedPixelSize()) + , m_orientation(description.orientation()) , m_syntheticBold(bold) , m_syntheticOblique(italic) - , m_fixedWidth(false) - , m_scaledFont(0) - , m_orientation(orientation) { - initializeWithFontFace(fontFace); + buildScaledFont(fontFace); - FT_Face fontConfigFace = cairo_ft_scaled_font_lock_face(m_scaledFont); - if (fontConfigFace) { + CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get()); + if (FT_Face fontConfigFace = cairoFtFaceLocker.ftFace()) m_fixedWidth = fontConfigFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH; - cairo_ft_scaled_font_unlock_face(m_scaledFont); - } +} + +FontPlatformData::FontPlatformData(const FontPlatformData& other) +{ + *this = other; } FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) @@ -191,56 +182,62 @@ FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) return *this; m_size = other.m_size; + m_orientation = other.m_orientation; + m_widthVariant = other.m_widthVariant; + m_textRenderingMode = other.m_textRenderingMode; + m_syntheticBold = other.m_syntheticBold; m_syntheticOblique = other.m_syntheticOblique; + m_isColorBitmapFont = other.m_isColorBitmapFont; + m_isHashTableDeletedValue = other.m_isHashTableDeletedValue; + m_isSystemFont = other.m_isSystemFont; + m_fixedWidth = other.m_fixedWidth; m_pattern = other.m_pattern; - m_orientation = other.m_orientation; - m_horizontalOrientationMatrix = other.m_horizontalOrientationMatrix; - - if (m_fallbacks) { - FcFontSetDestroy(m_fallbacks); - // This will be re-created on demand. - m_fallbacks = 0; - } - if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue()) - cairo_scaled_font_destroy(m_scaledFont); - m_scaledFont = cairo_scaled_font_reference(other.m_scaledFont); + // This will be re-created on demand. + m_fallbacks = nullptr; + m_scaledFont = other.m_scaledFont; m_harfBuzzFace = other.m_harfBuzzFace; return *this; } -FontPlatformData::FontPlatformData(const FontPlatformData& other) - : m_fallbacks(0) - , m_scaledFont(0) - , m_harfBuzzFace(other.m_harfBuzzFace) +FontPlatformData::~FontPlatformData() { - *this = other; } -FontPlatformData::FontPlatformData(const FontPlatformData& other, float size) - : m_harfBuzzFace(other.m_harfBuzzFace) +FontPlatformData FontPlatformData::cloneWithOrientation(const FontPlatformData& source, FontOrientation orientation) { - *this = other; - - // We need to reinitialize the instance, because the difference in size - // necessitates a new scaled font instance. - m_size = size; - initializeWithFontFace(cairo_scaled_font_get_font_face(m_scaledFont)); + FontPlatformData copy(source); + if (copy.m_scaledFont && copy.m_orientation != orientation) { + copy.m_orientation = orientation; + copy.buildScaledFont(cairo_scaled_font_get_font_face(copy.m_scaledFont.get())); + } + return copy; } -FontPlatformData::~FontPlatformData() +FontPlatformData FontPlatformData::cloneWithSyntheticOblique(const FontPlatformData& source, bool syntheticOblique) { - if (m_fallbacks) { - FcFontSetDestroy(m_fallbacks); - m_fallbacks = 0; + FontPlatformData copy(source); + if (copy.m_syntheticOblique != syntheticOblique) { + copy.m_syntheticOblique = syntheticOblique; + ASSERT(copy.m_scaledFont.get()); + copy.buildScaledFont(cairo_scaled_font_get_font_face(copy.m_scaledFont.get())); } + return copy; +} - if (m_scaledFont && m_scaledFont != hashTableDeletedFontValue()) - cairo_scaled_font_destroy(m_scaledFont); +FontPlatformData FontPlatformData::cloneWithSize(const FontPlatformData& source, float size) +{ + FontPlatformData copy(source); + copy.m_size = size; + // We need to reinitialize the instance, because the difference in size + // necessitates a new scaled font instance. + ASSERT(copy.m_scaledFont.get()); + copy.buildScaledFont(cairo_scaled_font_get_font_face(copy.m_scaledFont.get())); + return copy; } HarfBuzzFace* FontPlatformData::harfBuzzFace() const @@ -251,12 +248,29 @@ HarfBuzzFace* FontPlatformData::harfBuzzFace() const return m_harfBuzzFace.get(); } -bool FontPlatformData::isFixedPitch() +FcFontSet* FontPlatformData::fallbacks() const +{ + if (m_fallbacks) + return m_fallbacks.get(); + + if (m_pattern) { + FcResult fontConfigResult; + m_fallbacks.reset(FcFontSort(nullptr, m_pattern.get(), FcTrue, nullptr, &fontConfigResult)); + } + return m_fallbacks.get(); +} + +bool FontPlatformData::isFixedPitch() const { return m_fixedWidth; } -bool FontPlatformData::operator==(const FontPlatformData& other) const +unsigned FontPlatformData::hash() const +{ + return PtrHash<cairo_scaled_font_t*>::hash(m_scaledFont.get()); +} + +bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const { // FcPatternEqual does not support null pointers as arguments. if ((m_pattern && !other.m_pattern) @@ -264,11 +278,7 @@ bool FontPlatformData::operator==(const FontPlatformData& other) const || (m_pattern != other.m_pattern && !FcPatternEqual(m_pattern.get(), other.m_pattern.get()))) return false; - return m_scaledFont == other.m_scaledFont - && m_size == other.m_size - && m_syntheticOblique == other.m_syntheticOblique - && m_orientation == other.m_orientation - && m_syntheticBold == other.m_syntheticBold; + return m_scaledFont == other.m_scaledFont; } #ifndef NDEBUG @@ -278,127 +288,87 @@ String FontPlatformData::description() const } #endif -void FontPlatformData::initializeWithFontFace(cairo_font_face_t* fontFace, const FontDescription& fontDescription) +void FontPlatformData::buildScaledFont(cairo_font_face_t* fontFace) { - cairo_font_options_t* options = getDefaultFontOptions(); + CairoUniquePtr<cairo_font_options_t> options(cairo_font_options_copy(getDefaultCairoFontOptions())); + FcPattern* optionsPattern = m_pattern ? m_pattern.get() : getDefaultFontconfigOptions(); + setCairoFontOptionsFromFontConfigPattern(options.get(), optionsPattern); cairo_matrix_t ctm; cairo_matrix_init_identity(&ctm); - // Scaling a font with width zero size leads to a failed cairo_scaled_font_t instantiations. - // Instead we scale we scale the font to a very tiny size and just abort rendering later on. - float realSize = m_size ? m_size : 1; - + // FontConfig may return a list of transformation matrices with the pattern, for instance, + // for fonts that are oblique. We use that to initialize the cairo font matrix. cairo_matrix_t fontMatrix; - if (!m_pattern) - cairo_matrix_init_scale(&fontMatrix, realSize, realSize); - else { - setCairoFontOptionsFromFontConfigPattern(options, m_pattern.get()); - - // FontConfig may return a list of transformation matrices with the pattern, for instance, - // for fonts that are oblique. We use that to initialize the cairo font matrix. - FcMatrix fontConfigMatrix, *tempFontConfigMatrix; - FcMatrixInit(&fontConfigMatrix); - - // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them. - for (int i = 0; FcPatternGetMatrix(m_pattern.get(), FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++) - FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix); - cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx, - -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0); - - // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic. - int actualFontSlant; - if (fontDescription.italic() && FcPatternGetInteger(m_pattern.get(), FC_SLANT, 0, &actualFontSlant) == FcResultMatch) - m_syntheticOblique = actualFontSlant == FC_SLANT_ROMAN; - - // The matrix from FontConfig does not include the scale. - cairo_matrix_scale(&fontMatrix, realSize, realSize); - } + FcMatrix fontConfigMatrix, *tempFontConfigMatrix; + FcMatrixInit(&fontConfigMatrix); + + // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them. + for (int i = 0; FcPatternGetMatrix(optionsPattern, FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++) + FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix); + cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx, + -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0); + + // The matrix from FontConfig does not include the scale. Scaling a font with width zero size leads + // to a failed cairo_scaled_font_t instantiations. Instead we scale we scale the font to a very tiny + // size and just abort rendering later on. + float realSize = m_size ? m_size : 1; + cairo_matrix_scale(&fontMatrix, realSize, realSize); if (syntheticOblique()) { static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90); - cairo_matrix_t skew = {1, 0, syntheticObliqueSkew, 1, 0, 0}; - cairo_matrix_multiply(&fontMatrix, &skew, &fontMatrix); + static const cairo_matrix_t skew = {1, 0, syntheticObliqueSkew, 1, 0, 0}; + static const cairo_matrix_t verticalSkew = {1, -syntheticObliqueSkew, 0, 1, 0, 0}; + cairo_matrix_multiply(&fontMatrix, m_orientation == Vertical ? &verticalSkew : &skew, &fontMatrix); } - m_horizontalOrientationMatrix = fontMatrix; - if (m_orientation == Vertical) - rotateCairoMatrixForVerticalOrientation(&fontMatrix); - - m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); - cairo_font_options_destroy(options); -} + if (m_orientation == Vertical) { + // The resulting transformation matrix for vertical glyphs (V) is a + // combination of rotation (R) and translation (T) applied on the + // horizontal matrix (H). V = H . R . T, where R rotates by -90 degrees + // and T translates by font size towards y axis. + cairo_matrix_rotate(&fontMatrix, -piOverTwoDouble); + cairo_matrix_translate(&fontMatrix, 0.0, 1.0); + } -bool FontPlatformData::hasCompatibleCharmap() -{ - ASSERT(m_scaledFont); - FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont); - bool hasCompatibleCharmap = !(FT_Select_Charmap(freeTypeFace, ft_encoding_unicode) - && FT_Select_Charmap(freeTypeFace, ft_encoding_symbol) - && FT_Select_Charmap(freeTypeFace, ft_encoding_apple_roman)); - cairo_ft_scaled_font_unlock_face(m_scaledFont); - return hasCompatibleCharmap; + m_scaledFont = adoptRef(cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options.get())); } -PassRefPtr<OpenTypeVerticalData> FontPlatformData::verticalData() const +bool FontPlatformData::hasCompatibleCharmap() const { - ASSERT(hash()); - return fontCache()->getVerticalData(String::number(hash()), *this); + ASSERT(m_scaledFont.get()); + CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get()); + FT_Face freeTypeFace = cairoFtFaceLocker.ftFace(); + if (!freeTypeFace) + return false; + return !(FT_Select_Charmap(freeTypeFace, ft_encoding_unicode) + && FT_Select_Charmap(freeTypeFace, ft_encoding_symbol) + && FT_Select_Charmap(freeTypeFace, ft_encoding_apple_roman)); } -PassRefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const +RefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const { - FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_scaledFont); + CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get()); + FT_Face freeTypeFace = cairoFtFaceLocker.ftFace(); if (!freeTypeFace) - return 0; + return nullptr; FT_ULong tableSize = 0; // Tag bytes need to be reversed because OT_MAKE_TAG uses big-endian order. uint32_t tag = FT_MAKE_TAG((table & 0xff), (table & 0xff00) >> 8, (table & 0xff0000) >> 16, table >> 24); if (FT_Load_Sfnt_Table(freeTypeFace, tag, 0, 0, &tableSize)) - return 0; + return nullptr; RefPtr<SharedBuffer> buffer = SharedBuffer::create(tableSize); - FT_ULong expectedTableSize = tableSize; if (buffer->size() != tableSize) - return 0; + return nullptr; + FT_ULong expectedTableSize = tableSize; FT_Error error = FT_Load_Sfnt_Table(freeTypeFace, tag, 0, reinterpret_cast<FT_Byte*>(const_cast<char*>(buffer->data())), &tableSize); if (error || tableSize != expectedTableSize) - return 0; - - cairo_ft_scaled_font_unlock_face(m_scaledFont); + return nullptr; - return buffer.release(); + return buffer; } -void FontPlatformData::setOrientation(FontOrientation orientation) -{ - ASSERT(m_scaledFont); - - if (!m_scaledFont || (m_orientation == orientation)) - return; - - cairo_matrix_t transformationMatrix; - cairo_matrix_init_identity(&transformationMatrix); - - cairo_matrix_t fontMatrix; - cairo_scaled_font_get_font_matrix(m_scaledFont, &fontMatrix); - - cairo_font_options_t* options = getDefaultFontOptions(); - - // In case of vertical orientation, rotate the transformation matrix. - // Otherwise restore the horizontal orientation matrix. - if (orientation == Vertical) - rotateCairoMatrixForVerticalOrientation(&fontMatrix); - else - fontMatrix = m_horizontalOrientationMatrix; - - cairo_font_face_t* fontFace = cairo_scaled_font_get_font_face(m_scaledFont); - cairo_scaled_font_destroy(m_scaledFont); - m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &transformationMatrix, options); - cairo_font_options_destroy(options); - m_orientation = orientation; -} - -} +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp b/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp index d156d8c83..caa4b8de7 100644 --- a/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/GlyphPageTreeNodeFreeType.cpp @@ -12,7 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -29,9 +29,10 @@ */ #include "config.h" -#include "GlyphPageTreeNode.h" +#include "GlyphPage.h" -#include "SimpleFontData.h" +#include "CairoUtilities.h" +#include "Font.h" #include "UTF16UChar32Iterator.h" #include <cairo-ft.h> #include <cairo.h> @@ -39,32 +40,33 @@ namespace WebCore { -bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +bool GlyphPage::fill(UChar* buffer, unsigned bufferLength) { - cairo_scaled_font_t* scaledFont = fontData->platformData().scaledFont(); + const Font& font = this->font(); + cairo_scaled_font_t* scaledFont = font.platformData().scaledFont(); ASSERT(scaledFont); - FT_Face face = cairo_ft_scaled_font_lock_face(scaledFont); + CairoFtFaceLocker cairoFtFaceLocker(scaledFont); + FT_Face face = cairoFtFaceLocker.ftFace(); if (!face) return false; bool haveGlyphs = false; UTF16UChar32Iterator iterator(buffer, bufferLength); - for (unsigned i = 0; i < length; i++) { + for (unsigned i = 0; i < GlyphPage::size; i++) { UChar32 character = iterator.next(); if (character == iterator.end()) break; Glyph glyph = FcFreeTypeCharIndex(face, character); if (!glyph) - setGlyphDataForIndex(offset + i, 0, 0); + setGlyphForIndex(i, 0); else { - setGlyphDataForIndex(offset + i, glyph, fontData); + setGlyphForIndex(i, glyph); haveGlyphs = true; } } - cairo_ft_scaled_font_unlock_face(scaledFont); return haveGlyphs; } diff --git a/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp index 80c4042fa..d528c7bd6 100644 --- a/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2007 Holger Hans Peter Freyther @@ -14,7 +14,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,11 +31,11 @@ */ #include "config.h" -#include "SimpleFontData.h" +#include "Font.h" +#include "CairoUtilities.h" #include "FloatConversion.h" #include "FloatRect.h" -#include "Font.h" #include "FontCache.h" #include "FontDescription.h" #include "GlyphBuffer.h" @@ -49,13 +49,12 @@ #include FT_TRUETYPE_TAGS_H #include <unicode/normlzr.h> #include <wtf/MathExtras.h> -#include <wtf/unicode/Unicode.h> namespace WebCore { -void SimpleFontData::platformInit() +void Font::platformInit() { - if (!m_platformData.m_size) + if (!m_platformData.size()) return; ASSERT(m_platformData.scaledFont()); @@ -64,17 +63,34 @@ void SimpleFontData::platformInit() float ascent = narrowPrecisionToFloat(fontExtents.ascent); float descent = narrowPrecisionToFloat(fontExtents.descent); + float capHeight = narrowPrecisionToFloat(fontExtents.height); float lineGap = narrowPrecisionToFloat(fontExtents.height - fontExtents.ascent - fontExtents.descent); + { + CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont()); + + // If the USE_TYPO_METRICS flag is set in the OS/2 table then we use typo metrics instead. + FT_Face freeTypeFace = cairoFtFaceLocker.ftFace(); + TT_OS2* OS2Table = freeTypeFace ? static_cast<TT_OS2*>(FT_Get_Sfnt_Table(freeTypeFace, ft_sfnt_os2)) : nullptr; + if (OS2Table) { + const FT_Short kUseTypoMetricsMask = 1 << 7; + if (OS2Table->fsSelection & kUseTypoMetricsMask) { + // FT_Size_Metrics::y_scale is in 16.16 fixed point format. + // Its (fractional) value is a factor that converts vertical metrics from design units to units of 1/64 pixels. + double yscale = (freeTypeFace->size->metrics.y_scale / 65536.0) / 64.0; + ascent = narrowPrecisionToFloat(yscale * OS2Table->sTypoAscender); + descent = -narrowPrecisionToFloat(yscale * OS2Table->sTypoDescender); + lineGap = narrowPrecisionToFloat(yscale * OS2Table->sTypoLineGap); + } + } + } + m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); + m_fontMetrics.setCapHeight(capHeight); -#if PLATFORM(EFL) - m_fontMetrics.setLineSpacing(ascent + descent + lineGap); -#else // Match CoreGraphics metrics. m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); -#endif m_fontMetrics.setLineGap(lineGap); cairo_text_extents_t textExtents; @@ -85,63 +101,39 @@ void SimpleFontData::platformInit() m_spaceWidth = narrowPrecisionToFloat((platformData().orientation() == Horizontal) ? textExtents.x_advance : -textExtents.y_advance); if ((platformData().orientation() == Vertical) && !isTextOrientationFallback()) { - FT_Face freeTypeFace = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont()); + CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont()); + FT_Face freeTypeFace = cairoFtFaceLocker.ftFace(); m_fontMetrics.setUnitsPerEm(freeTypeFace->units_per_EM); - cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont()); } m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; } -void SimpleFontData::platformCharWidthInit() +void Font::platformCharWidthInit() { m_avgCharWidth = 0.f; m_maxCharWidth = 0.f; initCharWidths(); } -void SimpleFontData::platformDestroy() -{ -} - -PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const +RefPtr<Font> Font::platformCreateScaledFont(const FontDescription& fontDescription, float scaleFactor) const { ASSERT(m_platformData.scaledFont()); - return SimpleFontData::create(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()), - scaleFactor * fontDescription.computedSize(), + FontDescription scaledFontDescription = fontDescription; + scaledFontDescription.setComputedSize(scaleFactor * fontDescription.computedSize()); + return Font::create(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()), + scaledFontDescription, m_platformData.syntheticBold(), - m_platformData.syntheticOblique(), - fontDescription.orientation()), + m_platformData.syntheticOblique()), isCustomFont(), false); } -bool SimpleFontData::containsCharacters(const UChar* characters, int bufferLength) const -{ - ASSERT(m_platformData.scaledFont()); - FT_Face face = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont()); - if (!face) - return false; - - UTF16UChar32Iterator iterator(characters, bufferLength); - UChar32 character = iterator.next(); - while (character != iterator.end()) { - if (!FcFreeTypeCharIndex(face, character)) { - cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont()); - return false; - } - character = iterator.next(); - } - - cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont()); - return true; -} - -void SimpleFontData::determinePitch() +void Font::determinePitch() { m_treatAsFixedPitch = m_platformData.isFixedPitch(); } -FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const +FloatRect Font::platformBoundsForGlyph(Glyph glyph) const { if (!m_platformData.size()) return FloatRect(); @@ -156,7 +148,7 @@ FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const return FloatRect(); } -float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +float Font::platformWidthForGlyph(Glyph glyph) const { if (!m_platformData.size()) return 0; @@ -172,10 +164,10 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const } #if USE(HARFBUZZ) -bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const +bool Font::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const { if (!m_combiningCharacterSequenceSupport) - m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>); + m_combiningCharacterSequenceSupport = std::make_unique<HashMap<String, bool>>(); WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false); if (!addResult.isNewEntry) @@ -188,14 +180,14 @@ bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length)) return false; - FT_Face face = cairo_ft_scaled_font_lock_face(m_platformData.scaledFont()); + CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont()); + FT_Face face = cairoFtFaceLocker.ftFace(); if (!face) return false; if (FcFreeTypeCharIndex(face, normalizedCharacters[0])) addResult.iterator->value = true; - cairo_ft_scaled_font_unlock_face(m_platformData.scaledFont()); return addResult.iterator->value; } #endif diff --git a/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp b/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp index 176f7722d..451b914ba 100644 --- a/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp +++ b/Source/WebCore/platform/graphics/glx/GLContextGLX.cpp @@ -22,9 +22,10 @@ #if USE(GLX) #include "GraphicsContext3D.h" #include "OpenGLShims.h" +#include "PlatformDisplayX11.h" +#include "XErrorTrapper.h" #include <GL/glx.h> #include <cairo.h> -#include <wtf/OwnPtr.h> #if ENABLE(ACCELERATED_2D_CANVAS) #include <cairo-gl.h> @@ -32,36 +33,130 @@ namespace WebCore { -PassOwnPtr<GLContextGLX> GLContextGLX::createWindowContext(XID window, GLContext* sharingContext) +#if !defined(PFNGLXSWAPINTERVALSGIPROC) +typedef int (*PFNGLXSWAPINTERVALSGIPROC) (int); +#endif +#if !defined(PFNGLXCREATECONTEXTATTRIBSARBPROC) +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +#endif + +static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; +static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; + +static bool hasSGISwapControlExtension(Display* display) { - Display* display = sharedX11Display(); + static bool initialized = false; + if (initialized) + return !!glXSwapIntervalSGI; + + initialized = true; + if (!GLContext::isExtensionSupported(glXQueryExtensionsString(display, 0), "GLX_SGI_swap_control")) + return false; + + glXSwapIntervalSGI = reinterpret_cast<PFNGLXSWAPINTERVALSGIPROC>(glXGetProcAddress(reinterpret_cast<const unsigned char*>("glXSwapIntervalSGI"))); + return !!glXSwapIntervalSGI; +} + +static bool hasGLXARBCreateContextExtension(Display* display) +{ + static bool initialized = false; + if (initialized) + return !!glXCreateContextAttribsARB; + + initialized = true; + if (!GLContext::isExtensionSupported(glXQueryExtensionsString(display, 0), "GLX_ARB_create_context")) + return false; + + glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(reinterpret_cast<const unsigned char*>("glXCreateContextAttribsARB"))); + return !!glXCreateContextAttribsARB; +} + +static GLXContext createGLXARBContext(Display* display, GLXFBConfig config, GLXContext sharingContext) +{ + // We want to create a context with version >= 3.2 core profile, cause that ensures that the i965 driver won't + // use the software renderer. If that doesn't work, we will use whatever version available. Unfortunately, + // there's no way to know whether glXCreateContextAttribsARB can provide an OpenGL version >= 3.2 until + // we actually call it and check the return value. To make things more fun, if a version >= 3.2 cannot be + // provided, glXCreateContextAttribsARB will throw a GLXBadFBConfig X error, causing the app to crash. + // So, the first time a context is requested, we set a X error trap to disable crashes with GLXBadFBConfig + // and then check whether the return value is a context or not. + + static bool canCreate320Context = false; + static bool canCreate320ContextInitialized = false; + + static const int contextAttributes[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 2, + 0 + }; + + if (!canCreate320ContextInitialized) { + canCreate320ContextInitialized = true; + + { + // Set an X error trapper that ignores errors to avoid crashing on GLXBadFBConfig. Use a scope + // here to limit the error trap to just this context creation call. + XErrorTrapper trapper(display, XErrorTrapper::Policy::Ignore); + GLXContext context = glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, contextAttributes); + if (context) { + canCreate320Context = true; + return context; + } + } + + // Creating the 3.2 context failed, so use whatever is available. + return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, nullptr); + } + + if (canCreate320Context) + return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, contextAttributes); + + return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, nullptr); +} + +std::unique_ptr<GLContextGLX> GLContextGLX::createWindowContext(GLNativeWindowType window, PlatformDisplay& platformDisplay, GLXContext sharingContext) +{ + Display* display = downcast<PlatformDisplayX11>(platformDisplay).native(); XWindowAttributes attributes; - if (!XGetWindowAttributes(display, window, &attributes)) + if (!XGetWindowAttributes(display, static_cast<Window>(window), &attributes)) return nullptr; XVisualInfo visualInfo; visualInfo.visualid = XVisualIDFromVisual(attributes.visual); - int numReturned = 0; - XVisualInfo* visualInfoList = XGetVisualInfo(display, VisualIDMask, &visualInfo, &numReturned); - - GLXContext glxSharingContext = sharingContext ? static_cast<GLContextGLX*>(sharingContext)->m_context : 0; - GLXContext context = glXCreateContext(display, visualInfoList, glxSharingContext, True); - XFree(visualInfoList); + int numConfigs = 0; + GLXFBConfig config = nullptr; + XUniquePtr<GLXFBConfig> configs(glXGetFBConfigs(display, DefaultScreen(display), &numConfigs)); + for (int i = 0; i < numConfigs; i++) { + XUniquePtr<XVisualInfo> glxVisualInfo(glXGetVisualFromFBConfig(display, configs.get()[i])); + if (!glxVisualInfo) + continue; + + if (glxVisualInfo.get()->visualid == visualInfo.visualid) { + config = configs.get()[i]; + break; + } + } + ASSERT(config); + + XUniqueGLXContext context; + if (hasGLXARBCreateContextExtension(display)) + context.reset(createGLXARBContext(display, config, sharingContext)); + else { + // Legacy OpenGL version. + XUniquePtr<XVisualInfo> visualInfoList(glXGetVisualFromFBConfig(display, config)); + context.reset(glXCreateContext(display, visualInfoList.get(), sharingContext, True)); + } if (!context) return nullptr; - // GLXPbuffer and XID are both the same types underneath, so we have to share - // a constructor here with the window path. - GLContextGLX* contextWrapper = new GLContextGLX(context); - contextWrapper->m_window = window; - return adoptPtr(contextWrapper); + return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), window)); } -PassOwnPtr<GLContextGLX> GLContextGLX::createPbufferContext(GLXContext sharingContext) +std::unique_ptr<GLContextGLX> GLContextGLX::createPbufferContext(PlatformDisplay& platformDisplay, GLXContext sharingContext) { - int fbConfigAttributes[] = { + static const int fbConfigAttributes[] = { GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RED_SIZE, 1, @@ -73,36 +168,32 @@ PassOwnPtr<GLContextGLX> GLContextGLX::createPbufferContext(GLXContext sharingCo }; int returnedElements; - Display* display = sharedX11Display(); - GLXFBConfig* configs = glXChooseFBConfig(display, 0, fbConfigAttributes, &returnedElements); - if (!returnedElements) { - XFree(configs); + Display* display = downcast<PlatformDisplayX11>(platformDisplay).native(); + XUniquePtr<GLXFBConfig> configs(glXChooseFBConfig(display, 0, fbConfigAttributes, &returnedElements)); + if (!returnedElements) return nullptr; - } // We will be rendering to a texture, so our pbuffer does not need to be large. static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 }; - GLXPbuffer pbuffer = glXCreatePbuffer(display, configs[0], pbufferAttributes); - if (!pbuffer) { - XFree(configs); + XUniqueGLXPbuffer pbuffer(glXCreatePbuffer(display, configs.get()[0], pbufferAttributes)); + if (!pbuffer) return nullptr; + + XUniqueGLXContext context; + if (hasGLXARBCreateContextExtension(display)) + context.reset(createGLXARBContext(display, configs.get()[0], sharingContext)); + else { + // Legacy OpenGL version. + context.reset(glXCreateNewContext(display, configs.get()[0], GLX_RGBA_TYPE, sharingContext, GL_TRUE)); } - GLXContext context = glXCreateNewContext(display, configs[0], GLX_RGBA_TYPE, sharingContext, GL_TRUE); - XFree(configs); - if (!context) { - glXDestroyPbuffer(display, pbuffer); + if (!context) return nullptr; - } - // GLXPbuffer and XID are both the same types underneath, so we have to share - // a constructor here with the window path. - GLContextGLX* contextWrapper = new GLContextGLX(context); - contextWrapper->m_pbuffer = pbuffer; - return adoptPtr(contextWrapper); + return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), WTFMove(pbuffer))); } -PassOwnPtr<GLContextGLX> GLContextGLX::createPixmapContext(GLXContext sharingContext) +std::unique_ptr<GLContextGLX> GLContextGLX::createPixmapContext(PlatformDisplay& platformDisplay, GLXContext sharingContext) { static int visualAttributes[] = { GLX_RGBA, @@ -113,77 +204,68 @@ PassOwnPtr<GLContextGLX> GLContextGLX::createPixmapContext(GLXContext sharingCon 0 }; - Display* display = sharedX11Display(); - XVisualInfo* visualInfo = glXChooseVisual(display, DefaultScreen(display), visualAttributes); + Display* display = downcast<PlatformDisplayX11>(platformDisplay).native(); + XUniquePtr<XVisualInfo> visualInfo(glXChooseVisual(display, DefaultScreen(display), visualAttributes)); if (!visualInfo) return nullptr; - GLXContext context = glXCreateContext(display, visualInfo, sharingContext, GL_TRUE); - if (!context) { - XFree(visualInfo); + XUniqueGLXContext context(glXCreateContext(display, visualInfo.get(), sharingContext, GL_TRUE)); + if (!context) return nullptr; - } - Pixmap pixmap = XCreatePixmap(display, DefaultRootWindow(display), 1, 1, visualInfo->depth); - if (!pixmap) { - XFree(visualInfo); + XUniquePixmap pixmap(XCreatePixmap(display, DefaultRootWindow(display), 1, 1, visualInfo->depth)); + if (!pixmap) return nullptr; - } - GLXPixmap glxPixmap = glXCreateGLXPixmap(display, visualInfo, pixmap); - if (!glxPixmap) { - XFreePixmap(display, pixmap); - XFree(visualInfo); + XUniqueGLXPixmap glxPixmap(glXCreateGLXPixmap(display, visualInfo.get(), pixmap.get())); + if (!glxPixmap) return nullptr; - } - XFree(visualInfo); - return adoptPtr(new GLContextGLX(context, pixmap, glxPixmap)); + return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), WTFMove(pixmap), WTFMove(glxPixmap))); } -PassOwnPtr<GLContextGLX> GLContextGLX::createContext(XID window, GLContext* sharingContext) +std::unique_ptr<GLContextGLX> GLContextGLX::createContext(GLNativeWindowType window, PlatformDisplay& platformDisplay) { - if (!sharedX11Display()) - return nullptr; - - static bool initialized = false; - static bool success = true; - if (!initialized) { - success = initializeOpenGLShims(); - initialized = true; - } - if (!success) - return nullptr; - - GLXContext glxSharingContext = sharingContext ? static_cast<GLContextGLX*>(sharingContext)->m_context : 0; - OwnPtr<GLContextGLX> context = window ? createWindowContext(window, sharingContext) : nullptr; + GLXContext glxSharingContext = platformDisplay.sharingGLContext() ? static_cast<GLContextGLX*>(platformDisplay.sharingGLContext())->m_context.get() : nullptr; + auto context = window ? createWindowContext(window, platformDisplay, glxSharingContext) : nullptr; if (!context) - context = createPbufferContext(glxSharingContext); + context = createPbufferContext(platformDisplay, glxSharingContext); if (!context) - context = createPixmapContext(glxSharingContext); + context = createPixmapContext(platformDisplay, glxSharingContext); + + return context; +} + +std::unique_ptr<GLContextGLX> GLContextGLX::createSharingContext(PlatformDisplay& platformDisplay) +{ + auto context = createPbufferContext(platformDisplay); if (!context) - return nullptr; + context = createPixmapContext(platformDisplay); + return context; +} - return context.release(); +GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, GLNativeWindowType window) + : GLContext(display) + , m_x11Display(downcast<PlatformDisplayX11>(m_display).native()) + , m_context(WTFMove(context)) + , m_window(static_cast<Window>(window)) +{ } -GLContextGLX::GLContextGLX(GLXContext context) - : m_context(context) - , m_window(0) - , m_pbuffer(0) - , m_pixmap(0) - , m_glxPixmap(0) - , m_cairoDevice(0) +GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, XUniqueGLXPbuffer&& pbuffer) + : GLContext(display) + , m_x11Display(downcast<PlatformDisplayX11>(m_display).native()) + , m_context(WTFMove(context)) + , m_pbuffer(WTFMove(pbuffer)) { } -GLContextGLX::GLContextGLX(GLXContext context, Pixmap pixmap, GLXPixmap glxPixmap) - : m_context(context) - , m_window(0) - , m_pbuffer(0) - , m_pixmap(pixmap) - , m_glxPixmap(glxPixmap) - , m_cairoDevice(0) +GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, XUniquePixmap&& pixmap, XUniqueGLXPixmap&& glxPixmap) + : GLContext(display) + , m_x11Display(downcast<PlatformDisplayX11>(m_display).native()) + , m_context(WTFMove(context)) + , m_pixmap(WTFMove(pixmap)) + , m_glxPixmap(WTFMove(glxPixmap)) { } @@ -193,24 +275,21 @@ GLContextGLX::~GLContextGLX() cairo_device_destroy(m_cairoDevice); if (m_context) { - // This may be necessary to prevent crashes with NVidia's closed source drivers. Originally - // from Mozilla's 3D canvas implementation at: http://bitbucket.org/ilmari/canvas3d/ + // Due to a bug in some nvidia drivers, we need bind the default framebuffer in a context before + // destroying it to avoid a crash. In order to do that, we need to make the context current and, + // after the bind change, we need to set the previous context again. + GLContext* previousActiveContext = GLContext::current(); + makeContextCurrent(); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glXMakeCurrent(sharedX11Display(), None, None); - glXDestroyContext(sharedX11Display(), m_context); - } - - if (m_pbuffer) { - glXDestroyPbuffer(sharedX11Display(), m_pbuffer); - m_pbuffer = 0; - } - if (m_glxPixmap) { - glXDestroyGLXPixmap(sharedX11Display(), m_glxPixmap); - m_glxPixmap = 0; - } - if (m_pixmap) { - XFreePixmap(sharedX11Display(), m_pixmap); - m_pixmap = 0; + if (previousActiveContext && previousActiveContext != this) { + // If there was a previous context different from this one, just make it current again. + previousActiveContext->makeContextCurrent(); + } else { + // If there was no previous context or this was the previous, set a void context as current. + // We use the GLX function here, and the destructor of GLContext will clean the pointer + // returned by GLContext::current(). + glXMakeCurrent(m_x11Display, None, None); + } } } @@ -227,7 +306,7 @@ IntSize GLContextGLX::defaultFrameBufferSize() int x, y; Window rootWindow; unsigned int width, height, borderWidth, depth; - if (!XGetGeometry(sharedX11Display(), m_window, &rootWindow, &x, &y, &width, &height, &borderWidth, &depth)) + if (!XGetGeometry(m_x11Display, m_window, &rootWindow, &x, &y, &width, &height, &borderWidth, &depth)) return IntSize(); return IntSize(width, height); @@ -238,22 +317,22 @@ bool GLContextGLX::makeContextCurrent() ASSERT(m_context && (m_window || m_pbuffer || m_glxPixmap)); GLContext::makeContextCurrent(); - if (glXGetCurrentContext() == m_context) + if (glXGetCurrentContext() == m_context.get()) return true; if (m_window) - return glXMakeCurrent(sharedX11Display(), m_window, m_context); + return glXMakeCurrent(m_x11Display, m_window, m_context.get()); if (m_pbuffer) - return glXMakeCurrent(sharedX11Display(), m_pbuffer, m_context); + return glXMakeCurrent(m_x11Display, m_pbuffer.get(), m_context.get()); - return ::glXMakeCurrent(sharedX11Display(), m_glxPixmap, m_context); + return ::glXMakeCurrent(m_x11Display, m_glxPixmap.get(), m_context.get()); } void GLContextGLX::swapBuffers() { if (m_window) - glXSwapBuffers(sharedX11Display(), m_window); + glXSwapBuffers(m_x11Display, m_window); } void GLContextGLX::waitNative() @@ -261,22 +340,29 @@ void GLContextGLX::waitNative() glXWaitX(); } +void GLContextGLX::swapInterval(int interval) +{ + if (!hasSGISwapControlExtension(m_x11Display)) + return; + glXSwapIntervalSGI(interval); +} + cairo_device_t* GLContextGLX::cairoDevice() { if (m_cairoDevice) return m_cairoDevice; -#if ENABLE(ACCELERATED_2D_CANVAS) - m_cairoDevice = cairo_glx_device_create(sharedX11Display(), m_context); +#if ENABLE(ACCELERATED_2D_CANVAS) && CAIRO_HAS_GLX_FUNCTIONS + m_cairoDevice = cairo_glx_device_create(m_x11Display, m_context.get()); #endif return m_cairoDevice; } -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) PlatformGraphicsContext3D GLContextGLX::platformContext() { - return m_context; + return m_context.get(); } #endif diff --git a/Source/WebCore/platform/graphics/glx/GLContextGLX.h b/Source/WebCore/platform/graphics/glx/GLContextGLX.h index edefc04c2..ca24024f6 100644 --- a/Source/WebCore/platform/graphics/glx/GLContextGLX.h +++ b/Source/WebCore/platform/graphics/glx/GLContextGLX.h @@ -17,58 +17,61 @@ * Boston, MA 02110-1301 USA */ -#ifndef GLContextGLX_h -#define GLContextGLX_h +#pragma once #if USE(GLX) #include "GLContext.h" +#include "XUniquePtr.h" +#include "XUniqueResource.h" -typedef struct __GLXcontextRec* GLXContext; -typedef unsigned long GLXPbuffer; -typedef unsigned long GLXPixmap; typedef unsigned char GLubyte; -typedef unsigned long Pixmap; -typedef unsigned long XID; +typedef unsigned long Window; typedef void* ContextKeyType; +typedef struct _XDisplay Display; namespace WebCore { -class GLContextGLX : public GLContext { +class GLContextGLX final : public GLContext { WTF_MAKE_NONCOPYABLE(GLContextGLX); public: - static PassOwnPtr<GLContextGLX> createContext(XID window, GLContext* sharingContext); - static PassOwnPtr<GLContextGLX> createWindowContext(XID window, GLContext* sharingContext); + static std::unique_ptr<GLContextGLX> createContext(GLNativeWindowType, PlatformDisplay&); + static std::unique_ptr<GLContextGLX> createSharingContext(PlatformDisplay&); virtual ~GLContextGLX(); - virtual bool makeContextCurrent(); - virtual void swapBuffers(); - virtual void waitNative(); - virtual bool canRenderToDefaultFramebuffer(); - virtual IntSize defaultFrameBufferSize(); - virtual cairo_device_t* cairoDevice(); -#if USE(3D_GRAPHICS) - virtual PlatformGraphicsContext3D platformContext(); +private: + bool makeContextCurrent() override; + void swapBuffers() override; + void waitNative() override; + bool canRenderToDefaultFramebuffer() override; + IntSize defaultFrameBufferSize() override; + void swapInterval(int) override; + cairo_device_t* cairoDevice() override; + bool isEGLContext() const override { return false; } + +#if ENABLE(GRAPHICS_CONTEXT_3D) + PlatformGraphicsContext3D platformContext() override; #endif -private: - static PassOwnPtr<GLContextGLX> createPbufferContext(GLXContext sharingContext); - static PassOwnPtr<GLContextGLX> createPixmapContext(GLXContext sharingContext); + GLContextGLX(PlatformDisplay&, XUniqueGLXContext&&, GLNativeWindowType); + GLContextGLX(PlatformDisplay&, XUniqueGLXContext&&, XUniqueGLXPbuffer&&); + GLContextGLX(PlatformDisplay&, XUniqueGLXContext&&, XUniquePixmap&&, XUniqueGLXPixmap&&); - GLContextGLX(GLXContext); - GLContextGLX(GLXContext, Pixmap, GLXPixmap); + static std::unique_ptr<GLContextGLX> createWindowContext(GLNativeWindowType, PlatformDisplay&, GLXContext sharingContext = nullptr); + static std::unique_ptr<GLContextGLX> createPbufferContext(PlatformDisplay&, GLXContext sharingContext = nullptr); + static std::unique_ptr<GLContextGLX> createPixmapContext(PlatformDisplay&, GLXContext sharingContext = nullptr); - GLXContext m_context; - XID m_window; - GLXPbuffer m_pbuffer; - Pixmap m_pixmap; - GLXPixmap m_glxPixmap; - cairo_device_t* m_cairoDevice; + Display* m_x11Display { nullptr }; + XUniqueGLXContext m_context; + Window m_window { 0 }; + XUniqueGLXPbuffer m_pbuffer; + XUniquePixmap m_pixmap; + XUniqueGLXPixmap m_glxPixmap; + cairo_device_t* m_cairoDevice { nullptr }; }; } // namespace WebCore #endif // USE(GLX) -#endif // GLContextGLX_h diff --git a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp b/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp deleted file mode 100644 index 2b96cb594..000000000 --- a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2010, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ACCELERATED_2D_CANVAS) || USE(3D_GRAPHICS) - -#include "DrawingBuffer.h" - -#include "Extensions3D.h" -#include "GraphicsContext3D.h" - -namespace WebCore { - -#if PLATFORM(WIN) || USE(CAIRO) -DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, const IntSize& size, bool multisampleExtensionSupported, bool packedDepthStencilExtensionSupported, PreserveDrawingBuffer preserveDrawingBuffer, AlphaRequirement alpha) - : m_preserveDrawingBuffer(preserveDrawingBuffer) - , m_alpha(alpha) - , m_scissorEnabled(false) - , m_texture2DBinding(0) - , m_framebufferBinding(0) - , m_activeTextureUnit(GraphicsContext3D::TEXTURE0) - , m_context(context) - , m_size(-1, -1) - , m_multisampleExtensionSupported(multisampleExtensionSupported) - , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported) - , m_fbo(context->createFramebuffer()) - , m_colorBuffer(0) - , m_frontColorBuffer(0) - , m_separateFrontTexture(false) - , m_depthStencilBuffer(0) - , m_depthBuffer(0) - , m_stencilBuffer(0) - , m_multisampleFBO(0) - , m_multisampleColorBuffer(0) -{ - ASSERT(m_fbo); - if (!m_fbo) { - clear(); - return; - } - - // create a texture to render into - m_colorBuffer = context->createTexture(); - context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer); - context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); - context->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); - context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); - context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); - context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); - - createSecondaryBuffers(); - reset(size); -} - -DrawingBuffer::~DrawingBuffer() -{ - clear(); -} -#endif - -// Global resource ceiling (expressed in terms of pixels) for DrawingBuffer creation and resize. -// When this limit is set, DrawingBuffer::create() and DrawingBuffer::reset() calls that would -// exceed the global cap will instead clear the buffer. -static int s_maximumResourceUsePixels = 0; -static int s_currentResourceUsePixels = 0; -static const float s_resourceAdjustedRatio = 0.5; - -PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size, PreserveDrawingBuffer preserve, AlphaRequirement alpha) -{ - Extensions3D* extensions = context->getExtensions(); - bool multisampleSupported = extensions->maySupportMultisampling() - && extensions->supports("GL_ANGLE_framebuffer_blit") - && extensions->supports("GL_ANGLE_framebuffer_multisample") - && extensions->supports("GL_OES_rgb8_rgba8"); - if (multisampleSupported) { - extensions->ensureEnabled("GL_ANGLE_framebuffer_blit"); - extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample"); - extensions->ensureEnabled("GL_OES_rgb8_rgba8"); - } - bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil"); - if (packedDepthStencilSupported) - extensions->ensureEnabled("GL_OES_packed_depth_stencil"); - RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size, multisampleSupported, packedDepthStencilSupported, preserve, alpha)); - return (drawingBuffer->m_context) ? drawingBuffer.release() : 0; -} - -void DrawingBuffer::clear() -{ - if (!m_context) - return; - - m_context->makeContextCurrent(); - - if (!m_size.isEmpty()) { - s_currentResourceUsePixels -= m_size.width() * m_size.height(); - m_size = IntSize(); - } - - if (m_colorBuffer) { - m_context->deleteTexture(m_colorBuffer); - m_colorBuffer = 0; - } - - if (m_frontColorBuffer) { - m_context->deleteTexture(m_frontColorBuffer); - m_frontColorBuffer = 0; - } - - if (m_multisampleColorBuffer) { - m_context->deleteRenderbuffer(m_multisampleColorBuffer); - m_multisampleColorBuffer = 0; - } - - if (m_depthStencilBuffer) { - m_context->deleteRenderbuffer(m_depthStencilBuffer); - m_depthStencilBuffer = 0; - } - - if (m_depthBuffer) { - m_context->deleteRenderbuffer(m_depthBuffer); - m_depthBuffer = 0; - } - - if (m_stencilBuffer) { - m_context->deleteRenderbuffer(m_stencilBuffer); - m_stencilBuffer = 0; - } - - if (m_multisampleFBO) { - m_context->deleteFramebuffer(m_multisampleFBO); - m_multisampleFBO = 0; - } - - if (m_fbo) { - m_context->deleteFramebuffer(m_fbo); - m_fbo = 0; - } -} - -void DrawingBuffer::createSecondaryBuffers() -{ - // create a multisample FBO - if (multisample()) { - m_multisampleFBO = m_context->createFramebuffer(); - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); - m_multisampleColorBuffer = m_context->createRenderbuffer(); - } -} - -void DrawingBuffer::resizeDepthStencil(int sampleCount) -{ - const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes(); - if (attributes.depth && attributes.stencil && m_packedDepthStencilExtensionSupported) { - if (!m_depthStencilBuffer) - m_depthStencilBuffer = m_context->createRenderbuffer(); - m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer); - if (multisample()) - m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height()); - else - m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, Extensions3D::DEPTH24_STENCIL8, m_size.width(), m_size.height()); - m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer); - m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthStencilBuffer); - } else { - if (attributes.depth) { - if (!m_depthBuffer) - m_depthBuffer = m_context->createRenderbuffer(); - m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer); - if (multisample()) - m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height()); - else - m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_size.width(), m_size.height()); - m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer); - } - if (attributes.stencil) { - if (!m_stencilBuffer) - m_stencilBuffer = m_context->createRenderbuffer(); - m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_stencilBuffer); - if (multisample()) - m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height()); - else - m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_size.width(), m_size.height()); - m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_stencilBuffer); - } - } - m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); -} - -void DrawingBuffer::clearFramebuffers(GC3Dbitfield clearMask) -{ - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo); - - m_context->clear(clearMask); - - // The multisample fbo was just cleared, but we also need to clear the non-multisampled buffer too. - if (m_multisampleFBO) { - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); - m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); - } -} - -// Only way to ensure that we're not getting a bad framebuffer on some AMD/OSX devices. -// FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly. -bool DrawingBuffer::checkBufferIntegrity() -{ - if (!m_multisampleFBO) - return true; - - if (m_scissorEnabled) - m_context->disable(GraphicsContext3D::SCISSOR_TEST); - - m_context->colorMask(true, true, true, true); - - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); - m_context->clearColor(1.0f, 0.0f, 1.0f, 1.0f); - m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); - - commit(0, 0, 1, 1); - - unsigned char pixel[4] = {0, 0, 0, 0}; - m_context->readPixels(0, 0, 1, 1, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, &pixel); - - if (m_scissorEnabled) - m_context->enable(GraphicsContext3D::SCISSOR_TEST); - - return (pixel[0] == 0xFF && pixel[1] == 0x00 && pixel[2] == 0xFF && pixel[3] == 0xFF); -} - -bool DrawingBuffer::reset(const IntSize& newSize) -{ - if (!m_context) - return false; - - m_context->makeContextCurrent(); - - int maxTextureSize = 0; - m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize); - if (newSize.height() > maxTextureSize || newSize.width() > maxTextureSize) { - clear(); - return false; - } - - int pixelDelta = newSize.width() * newSize.height(); - int oldSize = 0; - if (!m_size.isEmpty()) { - oldSize = m_size.width() * m_size.height(); - pixelDelta -= oldSize; - } - - IntSize adjustedSize = newSize; - if (s_maximumResourceUsePixels) { - while ((s_currentResourceUsePixels + pixelDelta) > s_maximumResourceUsePixels) { - adjustedSize.scale(s_resourceAdjustedRatio); - if (adjustedSize.isEmpty()) { - clear(); - return false; - } - pixelDelta = adjustedSize.width() * adjustedSize.height(); - pixelDelta -= oldSize; - } - } - - const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes(); - - if (adjustedSize != m_size) { - - unsigned internalColorFormat, colorFormat, internalRenderbufferFormat; - if (attributes.alpha) { - internalColorFormat = GraphicsContext3D::RGBA; - colorFormat = GraphicsContext3D::RGBA; - internalRenderbufferFormat = Extensions3D::RGBA8_OES; - } else { - internalColorFormat = GraphicsContext3D::RGB; - colorFormat = GraphicsContext3D::RGB; - internalRenderbufferFormat = Extensions3D::RGB8_OES; - } - - do { - m_size = adjustedSize; - // resize multisample FBO - if (multisample()) { - int maxSampleCount = 0; - - m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSampleCount); - int sampleCount = std::min(4, maxSampleCount); - - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); - - m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer); - m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height()); - - if (m_context->getError() == GraphicsContext3D::OUT_OF_MEMORY) { - adjustedSize.scale(s_resourceAdjustedRatio); - continue; - } - - m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer); - resizeDepthStencil(sampleCount); - if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { - adjustedSize.scale(s_resourceAdjustedRatio); - continue; - } - } - - // resize regular FBO - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); - - m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_colorBuffer); - m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0); - - m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0); - - // resize the front color buffer - if (m_separateFrontTexture) { - m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_frontColorBuffer); - m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE, 0); - } - - m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); - - if (!multisample()) - resizeDepthStencil(0); - if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { - adjustedSize.scale(s_resourceAdjustedRatio); - continue; - } - -#if OS(DARWIN) - // FIXME: This can be removed once renderbufferStorageMultisample starts reporting GL_OUT_OF_MEMORY properly on OSX. - if (!checkBufferIntegrity()) { - adjustedSize.scale(s_resourceAdjustedRatio); - continue; - } -#endif - - break; - - } while (!adjustedSize.isEmpty()); - - pixelDelta = m_size.width() * m_size.height(); - pixelDelta -= oldSize; - s_currentResourceUsePixels += pixelDelta; - - if (!newSize.isEmpty() && adjustedSize.isEmpty()) { - clear(); - return false; - } - } - - m_context->disable(GraphicsContext3D::SCISSOR_TEST); - m_context->clearColor(0, 0, 0, 0); - m_context->colorMask(true, true, true, true); - - GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT; - if (attributes.depth) { - m_context->clearDepth(1.0f); - clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT; - m_context->depthMask(true); - } - if (attributes.stencil) { - m_context->clearStencil(0); - clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT; - m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF); - } - - clearFramebuffers(clearMask); - - return true; -} - -void DrawingBuffer::commit(long x, long y, long width, long height) -{ - if (!m_context) - return; - - if (width < 0) - width = m_size.width(); - if (height < 0) - height = m_size.height(); - - m_context->makeContextCurrent(); - - if (m_multisampleFBO) { - m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFBO); - m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_fbo); - - if (m_scissorEnabled) - m_context->disable(GraphicsContext3D::SCISSOR_TEST); - - // Use NEAREST, because there is no scale performed during the blit. - m_context->getExtensions()->blitFramebuffer(x, y, width, height, x, y, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST); - - if (m_scissorEnabled) - m_context->enable(GraphicsContext3D::SCISSOR_TEST); - } - - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); -} - -void DrawingBuffer::restoreFramebufferBinding() -{ - if (!m_context || !m_framebufferBinding) - return; - - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding); -} - -bool DrawingBuffer::multisample() const -{ - return m_context && m_context->getContextAttributes().antialias && m_multisampleExtensionSupported; -} - -void DrawingBuffer::discardResources() -{ - m_colorBuffer = 0; - m_frontColorBuffer = 0; - m_multisampleColorBuffer = 0; - - m_depthStencilBuffer = 0; - m_depthBuffer = 0; - - m_stencilBuffer = 0; - - m_multisampleFBO = 0; - m_fbo = 0; -} - -void DrawingBuffer::bind() -{ - if (!m_context) - return; - - m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO ? m_multisampleFBO : m_fbo); -} - -} // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.h b/Source/WebCore/platform/graphics/gpu/DrawingBuffer.h deleted file mode 100644 index e234e420c..000000000 --- a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2010, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef DrawingBuffer_h -#define DrawingBuffer_h - -#include "GraphicsContext3D.h" -#include "GraphicsTypes3D.h" -#include "IntSize.h" -#include "PlatformLayer.h" - -#include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#if PLATFORM(MAC) -#include <wtf/RetainPtr.h> -#endif - -namespace WebCore { -class GraphicsContext3D; - -// Manages a rendering target (framebuffer + attachment) for a canvas. Can publish its rendering -// results to a PlatformLayer for compositing. -class DrawingBuffer : public RefCounted<DrawingBuffer> { -public: - enum PreserveDrawingBuffer { - Preserve, - Discard - }; - - enum AlphaRequirement { - Alpha, - Opaque - }; - - static PassRefPtr<DrawingBuffer> create(GraphicsContext3D*, const IntSize&, PreserveDrawingBuffer, AlphaRequirement); - friend class GraphicsContext3D; - - ~DrawingBuffer(); - - // Issues a glClear() on all framebuffers associated with this DrawingBuffer. The caller is responsible for - // making the context current and setting the clear values and masks. Modifies the framebuffer binding. - void clearFramebuffers(GC3Dbitfield clearMask); - - // Returns true if the buffer was successfully resized. - bool reset(const IntSize&); - void bind(); - IntSize size() const { return m_size; } - Platform3DObject colorBuffer() const { return m_colorBuffer; } - - // Clear all resources from this object, as well as context. Called when context is destroyed - // to prevent invalid accesses to the resources. - void clear(); - - // Create the depth/stencil and multisample buffers, if needed. - void createSecondaryBuffers(); - - void resizeDepthStencil(int sampleCount); - - // Copies the multisample color buffer to the normal color buffer and leaves m_fbo bound - void commit(long x = 0, long y = 0, long width = -1, long height = -1); - - // commit should copy the full multisample buffer, and not respect the - // current scissor bounds. Track the state of the scissor test so that it - // can be disabled during calls to commit. - void setScissorEnabled(bool scissorEnabled) { m_scissorEnabled = scissorEnabled; } - - // The DrawingBuffer needs to track the texture bound to texture unit 0. - // The bound texture is tracked to avoid costly queries during rendering. - void setTexture2DBinding(Platform3DObject texture) { m_texture2DBinding = texture; } - - // The DrawingBuffer needs to track the currently bound framebuffer so it - // restore the binding when needed. - void setFramebufferBinding(Platform3DObject fbo) { m_framebufferBinding = fbo; } - - // Bind to the m_framebufferBinding if it's not 0. - void restoreFramebufferBinding(); - - // Track the currently active texture unit. Texture unit 0 is used as host for a scratch - // texture. - void setActiveTextureUnit(GC3Dint textureUnit) { m_activeTextureUnit = textureUnit; } - - bool multisample() const; - - Platform3DObject framebuffer() const; - - // Immediately releases ownership of all resources. Call upon loss of the - // graphics context to prevent freeing invalid resources. - void discardResources(); - - void markContentsChanged() { m_contentsChanged = true; } - -#if USE(ACCELERATED_COMPOSITING) - PlatformLayer* platformLayer(); - unsigned frontColorBuffer() const; - void paintCompositedResultsToCanvas(ImageBuffer*); -#endif - - GraphicsContext3D* graphicsContext3D() const { return m_context.get(); } - -private: - DrawingBuffer(GraphicsContext3D*, const IntSize&, bool multisampleExtensionSupported, - bool packedDepthStencilExtensionSupported, PreserveDrawingBuffer, AlphaRequirement); - - void initialize(const IntSize&); - - bool checkBufferIntegrity(); - - PreserveDrawingBuffer m_preserveDrawingBuffer; - AlphaRequirement m_alpha; - bool m_scissorEnabled; - Platform3DObject m_texture2DBinding; - Platform3DObject m_framebufferBinding; - GC3Denum m_activeTextureUnit; - - RefPtr<GraphicsContext3D> m_context; - IntSize m_size; - bool m_multisampleExtensionSupported; - bool m_packedDepthStencilExtensionSupported; - Platform3DObject m_fbo; - Platform3DObject m_colorBuffer; - Platform3DObject m_frontColorBuffer; - bool m_separateFrontTexture; - - // This is used when we have OES_packed_depth_stencil. - Platform3DObject m_depthStencilBuffer; - - // These are used when we don't. - Platform3DObject m_depthBuffer; - Platform3DObject m_stencilBuffer; - - // For multisampling - Platform3DObject m_multisampleFBO; - Platform3DObject m_multisampleColorBuffer; - - // True if our contents have been modified since the last presentation of this buffer. - bool m_contentsChanged; - -#if PLATFORM(MAC) - RetainPtr<WebGLLayer> m_platformLayer; -#endif -}; - -} // namespace WebCore - -#endif // DrawingBuffer_h diff --git a/Source/WebCore/platform/graphics/gpu/Texture.cpp b/Source/WebCore/platform/graphics/gpu/Texture.cpp index 31aaa63ee..aad13f34d 100644 --- a/Source/WebCore/platform/graphics/gpu/Texture.cpp +++ b/Source/WebCore/platform/graphics/gpu/Texture.cpp @@ -43,11 +43,11 @@ namespace WebCore { -Texture::Texture(GraphicsContext3D* context, PassOwnPtr<Vector<unsigned int>> tileTextureIds, Format format, int width, int height, int maxTextureSize) +Texture::Texture(GraphicsContext3D* context, std::unique_ptr<Vector<unsigned>> tileTextureIds, Format format, int width, int height, int maxTextureSize) : m_context(context) , m_format(format) , m_tiles(IntSize(maxTextureSize, maxTextureSize), IntSize(width, height), true) - , m_tileTextureIds(tileTextureIds) + , m_tileTextureIds(WTFMove(tileTextureIds)) { } @@ -66,7 +66,7 @@ static void convertFormat(GraphicsContext3D* context, Texture::Format format, un *glType = GraphicsContext3D::UNSIGNED_BYTE; break; case Texture::BGRA8: - if (context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888")) { + if (context->getExtensions().supports("GL_EXT_texture_format_BGRA8888")) { *glFormat = Extensions3D::BGRA_EXT; *glType = GraphicsContext3D::UNSIGNED_BYTE; } else { @@ -94,7 +94,7 @@ PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, i numTiles = 0; } - OwnPtr<Vector<unsigned int>> textureIds = adoptPtr(new Vector<unsigned int>(numTiles)); + auto textureIds = std::make_unique<Vector<unsigned>>(numTiles); textureIds->fill(0, numTiles); for (int i = 0; i < numTiles; i++) { @@ -120,7 +120,7 @@ PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, i tileBoundsWithBorder.height(), 0, glFormat, glType); } - return adoptRef(new Texture(context, textureIds.release(), format, width, height, maxTextureSize)); + return adoptRef(new Texture(context, WTFMove(textureIds), format, width, height, maxTextureSize)); } template <bool swizzle> diff --git a/Source/WebCore/platform/graphics/gpu/Texture.h b/Source/WebCore/platform/graphics/gpu/Texture.h index 6e93b91c2..3aa55f821 100644 --- a/Source/WebCore/platform/graphics/gpu/Texture.h +++ b/Source/WebCore/platform/graphics/gpu/Texture.h @@ -32,8 +32,6 @@ #define Texture_h #include "TilingData.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> @@ -54,11 +52,11 @@ public: Format format() const { return m_format; } const TilingData& tiles() const { return m_tiles; } private: - Texture(GraphicsContext3D*, PassOwnPtr<Vector<unsigned int>> tileTextureIds, Format format, int width, int height, int maxTextureSize); + Texture(GraphicsContext3D*, std::unique_ptr<Vector<unsigned>> tileTextureIds, Format, int width, int height, int maxTextureSize); GraphicsContext3D* m_context; Format m_format; TilingData m_tiles; - OwnPtr<Vector<unsigned int>> m_tileTextureIds; + std::unique_ptr<Vector<unsigned>> m_tileTextureIds; }; } diff --git a/Source/WebCore/platform/graphics/gpu/TilingData.cpp b/Source/WebCore/platform/graphics/gpu/TilingData.cpp index 23df37da0..6a3d04c4d 100644 --- a/Source/WebCore/platform/graphics/gpu/TilingData.cpp +++ b/Source/WebCore/platform/graphics/gpu/TilingData.cpp @@ -30,8 +30,6 @@ #include "config.h" -#if USE(ACCELERATED_COMPOSITING) || ENABLE(ACCELERATED_2D_CANVAS) - #include "TilingData.h" #include "FloatRect.h" @@ -203,5 +201,3 @@ void TilingData::recomputeNumTiles() } } - -#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp index 2f0debcc0..fc2d67471 100644 --- a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp @@ -37,6 +37,8 @@ AudioTrackPrivateGStreamer::AudioTrackPrivateGStreamer(GRefPtr<GstElement> playb : TrackPrivateBaseGStreamer(this, index, pad) , m_playbin(playbin) { + // FIXME: Get a real ID from the tkhd atom. + m_id = "A" + String::number(index); notifyTrackOfActiveChanged(); } @@ -53,7 +55,7 @@ void AudioTrackPrivateGStreamer::setEnabled(bool enabled) AudioTrackPrivate::setEnabled(enabled); if (enabled && m_playbin) - g_object_set(m_playbin.get(), "current-audio", m_index, NULL); + g_object_set(m_playbin.get(), "current-audio", m_index, nullptr); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h index 1775b2021..bd18e5c58 100644 --- a/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/AudioTrackPrivateGStreamer.h @@ -41,19 +41,21 @@ public: return adoptRef(new AudioTrackPrivateGStreamer(playbin, index, pad)); } - virtual void disconnect() override; + void disconnect() override; - virtual void setEnabled(bool) override; - virtual void setActive(bool enabled) override { setEnabled(enabled); } + void setEnabled(bool) override; + void setActive(bool enabled) override { setEnabled(enabled); } - virtual int trackIndex() const override { return m_index; } + int trackIndex() const override { return m_index; } - virtual AtomicString label() const override { return m_label; } - virtual AtomicString language() const override { return m_language; } + AtomicString id() const override { return m_id; } + AtomicString label() const override { return m_label; } + AtomicString language() const override { return m_language; } private: AudioTrackPrivateGStreamer(GRefPtr<GstElement> playbin, gint index, GRefPtr<GstPad>); + AtomicString m_id; GRefPtr<GstElement> m_playbin; }; diff --git a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp index f7ef46dd8..0ed9b5633 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp @@ -27,7 +27,7 @@ namespace WTF { template <> GRefPtr<GstElement> adoptGRef(GstElement* ptr) { - ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr))); + ASSERT(!ptr || !g_object_is_floating(ptr)); return GRefPtr<GstElement>(ptr, GRefPtrAdopt); } @@ -47,7 +47,7 @@ template <> void derefGPtr<GstElement>(GstElement* ptr) template <> GRefPtr<GstPad> adoptGRef(GstPad* ptr) { - ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr))); + ASSERT(!ptr || !g_object_is_floating(ptr)); return GRefPtr<GstPad>(ptr, GRefPtrAdopt); } @@ -67,7 +67,7 @@ template <> void derefGPtr<GstPad>(GstPad* ptr) template <> GRefPtr<GstPadTemplate> adoptGRef(GstPadTemplate* ptr) { - ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr))); + ASSERT(!ptr || !g_object_is_floating(ptr)); return GRefPtr<GstPadTemplate>(ptr, GRefPtrAdopt); } @@ -103,10 +103,28 @@ template <> void derefGPtr<GstCaps>(GstCaps* ptr) gst_caps_unref(ptr); } +template <> GRefPtr<GstContext> adoptGRef(GstContext* ptr) +{ + return GRefPtr<GstContext>(ptr, GRefPtrAdopt); +} + +template <> GstContext* refGPtr<GstContext>(GstContext* ptr) +{ + if (ptr) + gst_context_ref(ptr); + return ptr; +} + +template <> void derefGPtr<GstContext>(GstContext* ptr) +{ + if (ptr) + gst_context_unref(ptr); +} template <> GRefPtr<GstTask> adoptGRef(GstTask* ptr) { - ASSERT(!g_object_is_floating(G_OBJECT(ptr))); + // There is no need to check the object reference is floating here because + // gst_task_init() always sinks it. return GRefPtr<GstTask>(ptr, GRefPtrAdopt); } @@ -126,7 +144,7 @@ template <> void derefGPtr<GstTask>(GstTask* ptr) template <> GRefPtr<GstBus> adoptGRef(GstBus* ptr) { - ASSERT(!g_object_is_floating(G_OBJECT(ptr))); + ASSERT(!ptr || !g_object_is_floating(ptr)); return GRefPtr<GstBus>(ptr, GRefPtrAdopt); } @@ -146,7 +164,7 @@ template <> void derefGPtr<GstBus>(GstBus* ptr) template <> GRefPtr<GstElementFactory> adoptGRef(GstElementFactory* ptr) { - ASSERT(!g_object_is_floating(G_OBJECT(ptr))); + ASSERT(!ptr || !g_object_is_floating(ptr)); return GRefPtr<GstElementFactory>(ptr, GRefPtrAdopt); } @@ -183,6 +201,45 @@ template<> void derefGPtr<GstBuffer>(GstBuffer* ptr) gst_buffer_unref(ptr); } +template<> GRefPtr<GstBufferList> adoptGRef(GstBufferList* ptr) +{ + return GRefPtr<GstBufferList>(ptr, GRefPtrAdopt); +} + +template<> GstBufferList* refGPtr<GstBufferList>(GstBufferList* ptr) +{ + if (ptr) + gst_buffer_list_ref(ptr); + + return ptr; +} + +template<> void derefGPtr<GstBufferList>(GstBufferList* ptr) +{ + if (ptr) + gst_buffer_list_unref(ptr); +} + +template<> GRefPtr<GstBufferPool> adoptGRef(GstBufferPool* ptr) +{ + ASSERT(!ptr || !g_object_is_floating(ptr)); + return GRefPtr<GstBufferPool>(ptr, GRefPtrAdopt); +} + +template<> GstBufferPool* refGPtr<GstBufferPool>(GstBufferPool* ptr) +{ + if (ptr) + gst_object_ref_sink(GST_OBJECT(ptr)); + + return ptr; +} + +template<> void derefGPtr<GstBufferPool>(GstBufferPool* ptr) +{ + if (ptr) + gst_object_unref(ptr); +} + template<> GRefPtr<GstSample> adoptGRef(GstSample* ptr) { return GRefPtr<GstSample>(ptr, GRefPtrAdopt); @@ -248,7 +305,7 @@ template<> GRefPtr<GstToc> adoptGRef(GstToc* ptr) template<> GstToc* refGPtr<GstToc>(GstToc* ptr) { if (ptr) - gst_toc_ref(ptr); + return gst_toc_ref(ptr); return ptr; } @@ -258,5 +315,76 @@ template<> void derefGPtr<GstToc>(GstToc* ptr) if (ptr) gst_toc_unref(ptr); } + +template<> GRefPtr<GstMessage> adoptGRef(GstMessage* ptr) +{ + return GRefPtr<GstMessage>(ptr, GRefPtrAdopt); } + +template<> GstMessage* refGPtr<GstMessage>(GstMessage* ptr) +{ + if (ptr) + return gst_message_ref(ptr); + + return ptr; +} + +template<> void derefGPtr<GstMessage>(GstMessage* ptr) +{ + if (ptr) + gst_message_unref(ptr); +} + +template <> GRefPtr<WebKitVideoSink> adoptGRef(WebKitVideoSink* ptr) +{ + ASSERT(!ptr || !g_object_is_floating(ptr)); + return GRefPtr<WebKitVideoSink>(ptr, GRefPtrAdopt); +} + +template <> WebKitVideoSink* refGPtr<WebKitVideoSink>(WebKitVideoSink* ptr) +{ + if (ptr) + gst_object_ref_sink(GST_OBJECT(ptr)); + + return ptr; +} + +template <> void derefGPtr<WebKitVideoSink>(WebKitVideoSink* ptr) +{ + if (ptr) + gst_object_unref(GST_OBJECT(ptr)); +} + +template <> GRefPtr<WebKitWebSrc> adoptGRef(WebKitWebSrc* ptr) +{ + ASSERT(!ptr || !g_object_is_floating(ptr)); + return GRefPtr<WebKitWebSrc>(ptr, GRefPtrAdopt); +} + +// This method is only available for WebKitWebSrc and should not be used for any other type. +// This is only to work around a bug in GST where the URI downloader is not taking the ownership of WebKitWebSrc. +// See https://bugs.webkit.org/show_bug.cgi?id=144040. +GRefPtr<WebKitWebSrc> ensureGRef(WebKitWebSrc* ptr) +{ + if (ptr && g_object_is_floating(ptr)) + gst_object_ref_sink(GST_OBJECT(ptr)); + return GRefPtr<WebKitWebSrc>(ptr); +} + +template <> WebKitWebSrc* refGPtr<WebKitWebSrc>(WebKitWebSrc* ptr) +{ + if (ptr) + gst_object_ref_sink(GST_OBJECT(ptr)); + + return ptr; +} + +template <> void derefGPtr<WebKitWebSrc>(WebKitWebSrc* ptr) +{ + if (ptr) + gst_object_unref(GST_OBJECT(ptr)); +} + +} // namespace WTF + #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h index 2ce0a1d80..9f9bb6d17 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h @@ -21,20 +21,26 @@ #define GRefPtrGStreamer_h #if USE(GSTREAMER) -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> typedef struct _GstElement GstElement; typedef struct _GstPad GstPad; typedef struct _GstPadTemplate GstPadTemplate; typedef struct _GstCaps GstCaps; +typedef struct _GstContext GstContext; typedef struct _GstTask GstTask; typedef struct _GstBus GstBus; typedef struct _GstElementFactory GstElementFactory; typedef struct _GstBuffer GstBuffer; +typedef struct _GstBufferList GstBufferList; +typedef struct _GstBufferPool GstBufferPool; typedef struct _GstSample GstSample; typedef struct _GstTagList GstTagList; typedef struct _GstEvent GstEvent; typedef struct _GstToc GstToc; +typedef struct _GstMessage GstMessage; +typedef struct _WebKitVideoSink WebKitVideoSink; +typedef struct _WebKitWebSrc WebKitWebSrc; namespace WTF { @@ -54,6 +60,10 @@ template<> GRefPtr<GstCaps> adoptGRef(GstCaps* ptr); template<> GstCaps* refGPtr<GstCaps>(GstCaps* ptr); template<> void derefGPtr<GstCaps>(GstCaps* ptr); +template<> GRefPtr<GstContext> adoptGRef(GstContext* ptr); +template<> GstContext* refGPtr<GstContext>(GstContext* ptr); +template<> void derefGPtr<GstContext>(GstContext* ptr); + template<> GRefPtr<GstTask> adoptGRef(GstTask* ptr); template<> GstTask* refGPtr<GstTask>(GstTask* ptr); template<> void derefGPtr<GstTask>(GstTask* ptr); @@ -70,6 +80,14 @@ template<> GRefPtr<GstBuffer> adoptGRef(GstBuffer* ptr); template<> GstBuffer* refGPtr<GstBuffer>(GstBuffer* ptr); template<> void derefGPtr<GstBuffer>(GstBuffer* ptr); +template<> GRefPtr<GstBufferList> adoptGRef(GstBufferList*); +template<> GstBufferList* refGPtr<GstBufferList>(GstBufferList*); +template<> void derefGPtr<GstBufferList>(GstBufferList*); + +template<> GRefPtr<GstBufferPool> adoptGRef(GstBufferPool*); +template<> GstBufferPool* refGPtr<GstBufferPool>(GstBufferPool*); +template<> void derefGPtr<GstBufferPool>(GstBufferPool*); + template<> GRefPtr<GstSample> adoptGRef(GstSample* ptr); template<> GstSample* refGPtr<GstSample>(GstSample* ptr); template<> void derefGPtr<GstSample>(GstSample* ptr); @@ -85,7 +103,22 @@ template<> void derefGPtr<GstEvent>(GstEvent* ptr); template<> GRefPtr<GstToc> adoptGRef(GstToc* ptr); template<> GstToc* refGPtr<GstToc>(GstToc* ptr); template<> void derefGPtr<GstToc>(GstToc* ptr); -} + +template<> GRefPtr<GstMessage> adoptGRef(GstMessage*); +template<> GstMessage* refGPtr<GstMessage>(GstMessage*); +template<> void derefGPtr<GstMessage>(GstMessage*); + +template<> GRefPtr<WebKitVideoSink> adoptGRef(WebKitVideoSink* ptr); +template<> WebKitVideoSink* refGPtr<WebKitVideoSink>(WebKitVideoSink* ptr); +template<> void derefGPtr<WebKitVideoSink>(WebKitVideoSink* ptr); + +template<> GRefPtr<WebKitWebSrc> adoptGRef(WebKitWebSrc* ptr); +GRefPtr<WebKitWebSrc> ensureGRef(WebKitWebSrc* ptr); +template<> WebKitWebSrc* refGPtr<WebKitWebSrc>(WebKitWebSrc* ptr); +template<> void derefGPtr<WebKitWebSrc>(WebKitWebSrc* ptr); + +} // namespace WTF #endif // USE(GSTREAMER) -#endif + +#endif // GRefPtrGStreamer_h diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp index 7b666944e..770675266 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2012 Igalia S.L + * Copyright (C) 2012, 2015, 2016 Igalia S.L + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,11 +23,19 @@ #if USE(GSTREAMER) #include "GStreamerUtilities.h" +#include "GRefPtrGStreamer.h" #include "IntSize.h" -#include <gst/audio/audio.h> +#include <gst/audio/audio-info.h> #include <gst/gst.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/MathExtras.h> +#include <wtf/glib/GUniquePtr.h> + +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) +#define GST_USE_UNSTABLE_API +#include <gst/mpegts/mpegts.h> +#undef GST_USE_UNSTABLE_API +#endif namespace WebCore { @@ -65,6 +74,22 @@ bool getVideoSizeAndFormatFromCaps(GstCaps* caps, WebCore::IntSize& size, GstVid return true; } + +bool getSampleVideoInfo(GstSample* sample, GstVideoInfo& videoInfo) +{ + if (!GST_IS_SAMPLE(sample)) + return false; + + GstCaps* caps = gst_sample_get_caps(sample); + if (!caps) + return false; + + gst_video_info_init(&videoInfo); + if (!gst_video_info_from_caps(&videoInfo, caps)) + return false; + + return true; +} #endif GstBuffer* createGstBuffer(GstBuffer* buffer) @@ -95,17 +120,17 @@ char* getGstBufferDataPointer(GstBuffer* buffer) return reinterpret_cast<char*>(mapInfo->data); } -void mapGstBuffer(GstBuffer* buffer) +void mapGstBuffer(GstBuffer* buffer, uint32_t flags) { - GstMapInfo* mapInfo = g_slice_new(GstMapInfo); - if (!gst_buffer_map(buffer, mapInfo, GST_MAP_WRITE)) { - g_slice_free(GstMapInfo, mapInfo); + GstMapInfo* mapInfo = static_cast<GstMapInfo*>(fastMalloc(sizeof(GstMapInfo))); + if (!gst_buffer_map(buffer, mapInfo, static_cast<GstMapFlags>(flags))) { + fastFree(mapInfo); gst_buffer_unref(buffer); return; } GstMiniObject* miniObject = reinterpret_cast<GstMiniObject*>(buffer); - gst_mini_object_set_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString), mapInfo, 0); + gst_mini_object_set_qdata(miniObject, g_quark_from_static_string(webkitGstMapInfoQuarkString), mapInfo, nullptr); } void unmapGstBuffer(GstBuffer* buffer) @@ -117,23 +142,101 @@ void unmapGstBuffer(GstBuffer* buffer) return; gst_buffer_unmap(buffer, mapInfo); - g_slice_free(GstMapInfo, mapInfo); + fastFree(mapInfo); } bool initializeGStreamer() { -#if GST_CHECK_VERSION(0, 10, 31) if (gst_is_initialized()) return true; -#endif GUniqueOutPtr<GError> error; // FIXME: We should probably pass the arguments from the command line. - bool gstInitialized = gst_init_check(0, 0, &error.outPtr()); + bool gstInitialized = gst_init_check(nullptr, nullptr, &error.outPtr()); ASSERT_WITH_MESSAGE(gstInitialized, "GStreamer initialization failed: %s", error ? error->message : "unknown error occurred"); + +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) + if (gstInitialized) + gst_mpegts_initialize(); +#endif + return gstInitialized; } +unsigned getGstPlayFlag(const char* nick) +{ + static GFlagsClass* flagsClass = static_cast<GFlagsClass*>(g_type_class_ref(g_type_from_name("GstPlayFlags"))); + ASSERT(flagsClass); + + GFlagsValue* flag = g_flags_get_value_by_nick(flagsClass, nick); + if (!flag) + return 0; + + return flag->value; +} + +GstClockTime toGstClockTime(float time) +{ + // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to + // round the microseconds so no floating point precision is lost and we can perform an accurate seek. + float seconds; + float microSeconds = modff(time, &seconds) * 1000000; + GTimeVal timeValue; + timeValue.tv_sec = static_cast<glong>(seconds); + timeValue.tv_usec = static_cast<glong>(floor(microSeconds + 0.5)); + return GST_TIMEVAL_TO_TIME(timeValue); +} + +bool gstRegistryHasElementForMediaType(GList* elementFactories, const char* capsString) +{ + GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string(capsString)); + GList* candidates = gst_element_factory_list_filter(elementFactories, caps.get(), GST_PAD_SINK, false); + bool result = candidates; + + gst_plugin_feature_list_free(candidates); + return result; +} + +#if GST_CHECK_VERSION(1, 5, 3) && (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) +GstElement* createGstDecryptor(const gchar* protectionSystem) +{ + GstElement* decryptor = nullptr; + GList* decryptors = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECRYPTOR, GST_RANK_MARGINAL); + + GST_TRACE("looking for decryptor for %s", protectionSystem); + + for (GList* walk = decryptors; !decryptor && walk; walk = g_list_next(walk)) { + GstElementFactory* factory = reinterpret_cast<GstElementFactory*>(walk->data); + + GST_TRACE("checking factory %s", GST_OBJECT_NAME(factory)); + + for (const GList* current = gst_element_factory_get_static_pad_templates(factory); current && !decryptor; current = g_list_next(current)) { + GstStaticPadTemplate* staticPadTemplate = static_cast<GstStaticPadTemplate*>(current->data); + GRefPtr<GstCaps> caps = adoptGRef(gst_static_pad_template_get_caps(staticPadTemplate)); + unsigned length = gst_caps_get_size(caps.get()); + + GST_TRACE("factory %s caps has size %u", GST_OBJECT_NAME(factory), length); + for (unsigned i = 0; !decryptor && i < length; ++i) { + GstStructure* structure = gst_caps_get_structure(caps.get(), i); + GST_TRACE("checking structure %s", gst_structure_get_name(structure)); + if (gst_structure_has_field_typed(structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING)) { + const gchar* sysId = gst_structure_get_string(structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD); + GST_TRACE("structure %s has protection system %s", gst_structure_get_name(structure), sysId); + if (!g_ascii_strcasecmp(protectionSystem, sysId)) { + GST_DEBUG("found decryptor %s for %s", GST_OBJECT_NAME(factory), protectionSystem); + decryptor = gst_element_factory_create(factory, nullptr); + break; + } + } + } + } + } + gst_plugin_feature_list_free(decryptors); + GST_TRACE("returning decryptor %p", decryptor); + return decryptor; +} +#endif + } #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h index 755dbdb72..f79a8cf6f 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h +++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerUtilities.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2012 Igalia S.L + * Copyright (C) 2012, 2015, 2016 Igalia S.L + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,26 +17,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#pragma once + #include "Logging.h" #include <gst/gst.h> -#include <gst/video/video.h> - -#define LOG_MEDIA_MESSAGE(...) do { \ - GST_DEBUG(__VA_ARGS__); \ - LOG_VERBOSE(Media, __VA_ARGS__); } while (0) - -#define ERROR_MEDIA_MESSAGE(...) do { \ - GST_ERROR(__VA_ARGS__); \ - LOG_VERBOSE(Media, __VA_ARGS__); } while (0) - -#define INFO_MEDIA_MESSAGE(...) do { \ - GST_INFO(__VA_ARGS__); \ - LOG_VERBOSE(Media, __VA_ARGS__); } while (0) - -#define WARN_MEDIA_MESSAGE(...) do { \ - GST_WARNING(__VA_ARGS__); \ - LOG_VERBOSE(Media, __VA_ARGS__); } while (0) +#include <gst/video/video-format.h> +#include <gst/video/video-info.h> namespace WebCore { @@ -65,12 +53,19 @@ inline bool webkitGstCheckVersion(guint major, guint minor, guint micro) GstPad* webkitGstGhostPadFromStaticTemplate(GstStaticPadTemplate*, const gchar* name, GstPad* target); #if ENABLE(VIDEO) bool getVideoSizeAndFormatFromCaps(GstCaps*, WebCore::IntSize&, GstVideoFormat&, int& pixelAspectRatioNumerator, int& pixelAspectRatioDenominator, int& stride); +bool getSampleVideoInfo(GstSample*, GstVideoInfo&); #endif GstBuffer* createGstBuffer(GstBuffer*); GstBuffer* createGstBufferForData(const char* data, int length); char* getGstBufferDataPointer(GstBuffer*); -void mapGstBuffer(GstBuffer*); +void mapGstBuffer(GstBuffer*, uint32_t); void unmapGstBuffer(GstBuffer*); bool initializeGStreamer(); +unsigned getGstPlayFlag(const char* nick); +GstClockTime toGstClockTime(float time); +bool gstRegistryHasElementForMediaType(GList* elementFactories, const char* capsString); +#if GST_CHECK_VERSION(1, 5, 3) && (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) +GstElement* createGstDecryptor(const gchar* protectionSystem); +#endif } diff --git a/Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h new file mode 100644 index 000000000..693990884 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 GUniquePtrGStreamer_h +#define GUniquePtrGStreamer_h +#if USE(GSTREAMER) + +#include <gst/gststructure.h> +#include <gst/pbutils/install-plugins.h> +#include <wtf/glib/GUniquePtr.h> + +namespace WTF { + +WTF_DEFINE_GPTR_DELETER(GstStructure, gst_structure_free) +WTF_DEFINE_GPTR_DELETER(GstInstallPluginsContext, gst_install_plugins_context_free) + +} + +#endif // USE(GSTREAMER) +#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h index c9d2fb4e9..1ecd334ba 100644 --- a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamer.h @@ -27,7 +27,7 @@ #include "GRefPtrGStreamer.h" #include <gst/gst.h> -#include <gst/video/video.h> +#include <gst/video/video-frame.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -38,9 +38,9 @@ class IntSize; class ImageGStreamer : public RefCounted<ImageGStreamer> { public: - static PassRefPtr<ImageGStreamer> createImage(GstBuffer* buffer, GstCaps* caps) + static PassRefPtr<ImageGStreamer> createImage(GstSample* sample) { - return adoptRef(new ImageGStreamer(buffer, caps)); + return adoptRef(new ImageGStreamer(sample)); } ~ImageGStreamer(); @@ -60,7 +60,7 @@ class ImageGStreamer : public RefCounted<ImageGStreamer> { } private: - ImageGStreamer(GstBuffer*, GstCaps*); + ImageGStreamer(GstSample*); RefPtr<BitmapImage> m_image; FloatRect m_cropRect; diff --git a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp index b153f09ec..c55cfdf97 100644 --- a/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/ImageGStreamerCairo.cpp @@ -32,8 +32,9 @@ using namespace std; using namespace WebCore; -ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps) +ImageGStreamer::ImageGStreamer(GstSample* sample) { + GstCaps* caps = gst_sample_get_caps(sample); GstVideoInfo videoInfo; gst_video_info_init(&videoInfo); if (!gst_video_info_from_caps(&videoInfo, caps)) @@ -42,6 +43,7 @@ ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps) // Right now the TextureMapper only supports chromas with one plane ASSERT(GST_VIDEO_INFO_N_PLANES(&videoInfo) == 1); + GstBuffer* buffer = gst_sample_get_buffer(sample); if (!gst_video_frame_map(&m_videoFrame, &videoInfo, buffer, GST_MAP_READ)) return; @@ -60,7 +62,7 @@ ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps) RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create_for_data(bufferData, cairoFormat, width, height, stride)); ASSERT(cairo_surface_status(surface.get()) == CAIRO_STATUS_SUCCESS); - m_image = BitmapImage::create(surface.release()); + m_image = BitmapImage::create(WTFMove(surface)); if (GstVideoCropMeta* cropMeta = gst_buffer_get_video_crop_meta(buffer)) setCropRect(FloatRect(cropMeta->x, cropMeta->y, cropMeta->width, cropMeta->height)); @@ -69,9 +71,7 @@ ImageGStreamer::ImageGStreamer(GstBuffer* buffer, GstCaps* caps) ImageGStreamer::~ImageGStreamer() { if (m_image) - m_image.clear(); - - m_image = 0; + m_image = nullptr; // We keep the buffer memory mapped until the image is destroyed because the internal // cairo_surface_t was created using cairo_image_surface_create_for_data(). diff --git a/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h index b8adc641f..e942a2c5f 100644 --- a/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/InbandMetadataTextTrackPrivateGStreamer.h @@ -23,8 +23,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InbandMetadataTextTrackPrivateGStreamer_h -#define InbandMetadataTextTrackPrivateGStreamer_h +#pragma once #if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK) @@ -35,28 +34,44 @@ namespace WebCore { class InbandMetadataTextTrackPrivateGStreamer : public InbandTextTrackPrivate { public: - static PassRefPtr<InbandMetadataTextTrackPrivateGStreamer> create(Kind kind) + static PassRefPtr<InbandMetadataTextTrackPrivateGStreamer> create(Kind kind, CueFormat cueFormat, const AtomicString& id = emptyAtom) { - return adoptRef(new InbandMetadataTextTrackPrivateGStreamer(kind)); + return adoptRef(new InbandMetadataTextTrackPrivateGStreamer(kind, cueFormat, id)); } ~InbandMetadataTextTrackPrivateGStreamer() { } - virtual Kind kind() const override { return m_kind; } + Kind kind() const override { return m_kind; } + AtomicString id() const override { return m_id; } + AtomicString inBandMetadataTrackDispatchType() const override { return m_inBandMetadataTrackDispatchType; } + void setInBandMetadataTrackDispatchType(const AtomicString& value) { m_inBandMetadataTrackDispatchType = value; } + + void addDataCue(const MediaTime& start, const MediaTime& end, const void* data, unsigned length) + { + ASSERT(cueFormat() == Data); + client()->addDataCue(start, end, data, length); + } + + void addGenericCue(PassRefPtr<GenericCueData> data) + { + ASSERT(cueFormat() == Generic); + client()->addGenericCue(*data); + } private: - InbandMetadataTextTrackPrivateGStreamer(Kind kind) - : InbandTextTrackPrivate(Generic) + InbandMetadataTextTrackPrivateGStreamer(Kind kind, CueFormat cueFormat, const AtomicString& id) + : InbandTextTrackPrivate(cueFormat) , m_kind(kind) + , m_id(id) { } Kind m_kind; + AtomicString m_id; + AtomicString m_inBandMetadataTrackDispatchType; }; } // namespace WebCore #endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK) - -#endif // InbandMetadataTextTrackPrivateGStreamer_h diff --git a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp index bed10faa7..24e58f160 100644 --- a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.cpp @@ -39,38 +39,20 @@ GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug); namespace WebCore { -static GstPadProbeReturn textTrackPrivateEventCallback(GstPad*, GstPadProbeInfo* info, InbandTextTrackPrivateGStreamer* track) -{ - GstEvent* event = gst_pad_probe_info_get_event(info); - switch (GST_EVENT_TYPE(event)) { - case GST_EVENT_STREAM_START: - track->streamChanged(); - break; - default: - break; - } - return GST_PAD_PROBE_OK; -} - -static gboolean textTrackPrivateSampleTimeoutCallback(InbandTextTrackPrivateGStreamer* track) -{ - track->notifyTrackOfSample(); - return FALSE; -} - -static gboolean textTrackPrivateStreamTimeoutCallback(InbandTextTrackPrivateGStreamer* track) -{ - track->notifyTrackOfStreamChanged(); - return FALSE; -} - InbandTextTrackPrivateGStreamer::InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad> pad) : InbandTextTrackPrivate(WebVTT), TrackPrivateBaseGStreamer(this, index, pad) - , m_sampleTimerHandler(0) - , m_streamTimerHandler(0) { - m_eventProbe = gst_pad_add_probe(m_pad.get(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, - reinterpret_cast<GstPadProbeCallback>(textTrackPrivateEventCallback), this, 0); + m_eventProbe = gst_pad_add_probe(m_pad.get(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, [] (GstPad*, GstPadProbeInfo* info, gpointer userData) -> GstPadProbeReturn { + auto* track = static_cast<InbandTextTrackPrivateGStreamer*>(userData); + switch (GST_EVENT_TYPE(gst_pad_probe_info_get_event(info))) { + case GST_EVENT_STREAM_START: + track->streamChanged(); + break; + default: + break; + } + return GST_PAD_PROBE_OK; + }, this, nullptr); notifyTrackOfStreamChanged(); } @@ -82,39 +64,35 @@ void InbandTextTrackPrivateGStreamer::disconnect() gst_pad_remove_probe(m_pad.get(), m_eventProbe); - if (m_streamTimerHandler) - g_source_remove(m_streamTimerHandler); - TrackPrivateBaseGStreamer::disconnect(); } void InbandTextTrackPrivateGStreamer::handleSample(GRefPtr<GstSample> sample) { - if (m_sampleTimerHandler) - g_source_remove(m_sampleTimerHandler); { - MutexLocker lock(m_sampleMutex); + LockHolder lock(m_sampleMutex); m_pendingSamples.append(sample); } - m_sampleTimerHandler = g_timeout_add(0, - reinterpret_cast<GSourceFunc>(textTrackPrivateSampleTimeoutCallback), this); + + RefPtr<InbandTextTrackPrivateGStreamer> protectedThis(this); + m_notifier->notify(MainThreadNotification::NewSample, [protectedThis] { + protectedThis->notifyTrackOfSample(); + }); } void InbandTextTrackPrivateGStreamer::streamChanged() { - if (m_streamTimerHandler) - g_source_remove(m_streamTimerHandler); - m_streamTimerHandler = g_timeout_add(0, - reinterpret_cast<GSourceFunc>(textTrackPrivateStreamTimeoutCallback), this); + RefPtr<InbandTextTrackPrivateGStreamer> protectedThis(this); + m_notifier->notify(MainThreadNotification::StreamChanged, [protectedThis] { + protectedThis->notifyTrackOfStreamChanged(); + }); } void InbandTextTrackPrivateGStreamer::notifyTrackOfSample() { - m_sampleTimerHandler = 0; - Vector<GRefPtr<GstSample> > samples; { - MutexLocker lock(m_sampleMutex); + LockHolder lock(m_sampleMutex); m_pendingSamples.swap(samples); } @@ -122,28 +100,26 @@ void InbandTextTrackPrivateGStreamer::notifyTrackOfSample() GRefPtr<GstSample> sample = samples[i]; GstBuffer* buffer = gst_sample_get_buffer(sample.get()); if (!buffer) { - WARN_MEDIA_MESSAGE("Track %d got sample with no buffer.", m_index); + GST_WARNING("Track %d got sample with no buffer.", m_index); continue; } GstMapInfo info; gboolean ret = gst_buffer_map(buffer, &info, GST_MAP_READ); ASSERT(ret); if (!ret) { - WARN_MEDIA_MESSAGE("Track %d unable to map buffer.", m_index); + GST_WARNING("Track %d unable to map buffer.", m_index); continue; } - INFO_MEDIA_MESSAGE("Track %d parsing sample: %.*s", m_index, static_cast<int>(info.size), + GST_INFO("Track %d parsing sample: %.*s", m_index, static_cast<int>(info.size), reinterpret_cast<char*>(info.data)); - client()->parseWebVTTCueData(this, reinterpret_cast<char*>(info.data), info.size); + client()->parseWebVTTCueData(reinterpret_cast<char*>(info.data), info.size); gst_buffer_unmap(buffer, &info); } } void InbandTextTrackPrivateGStreamer::notifyTrackOfStreamChanged() { - m_streamTimerHandler = 0; - GRefPtr<GstEvent> event = adoptGRef(gst_pad_get_sticky_event(m_pad.get(), GST_EVENT_STREAM_START, 0)); if (!event) @@ -151,7 +127,7 @@ void InbandTextTrackPrivateGStreamer::notifyTrackOfStreamChanged() const gchar* streamId; gst_event_parse_stream_start(event.get(), &streamId); - INFO_MEDIA_MESSAGE("Track %d got stream start for stream %s.", m_index, streamId); + GST_INFO("Track %d got stream start for stream %s.", m_index, streamId); m_streamId = streamId; } diff --git a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h index 285cd86c8..cc3a6f0e9 100644 --- a/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/InbandTextTrackPrivateGStreamer.h @@ -31,6 +31,7 @@ #include "GRefPtrGStreamer.h" #include "InbandTextTrackPrivate.h" #include "TrackPrivateBaseGStreamer.h" +#include <wtf/Lock.h> namespace WebCore { @@ -44,29 +45,28 @@ public: return adoptRef(new InbandTextTrackPrivateGStreamer(index, pad)); } - virtual void disconnect() override; + void disconnect() override; - virtual AtomicString label() const override { return m_label; } - virtual AtomicString language() const override { return m_language; } + AtomicString label() const override { return m_label; } + AtomicString language() const override { return m_language; } - virtual int trackIndex() const override { return m_index; } + int trackIndex() const override { return m_index; } String streamId() const { return m_streamId; } void handleSample(GRefPtr<GstSample>); + +private: + InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad>); + void streamChanged(); void notifyTrackOfSample(); void notifyTrackOfStreamChanged(); -private: - InbandTextTrackPrivateGStreamer(gint index, GRefPtr<GstPad>); - - guint m_sampleTimerHandler; - guint m_streamTimerHandler; gulong m_eventProbe; Vector<GRefPtr<GstSample> > m_pendingSamples; String m_streamId; - Mutex m_sampleMutex; + Lock m_sampleMutex; }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h b/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h new file mode 100644 index 000000000..96b587ff9 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * 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 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 + */ + +#pragma once + +#include <wtf/Atomics.h> +#include <wtf/Lock.h> +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> +#include <wtf/ThreadSafeRefCounted.h> + +namespace WebCore { + +template <typename T> +class MainThreadNotifier final : public ThreadSafeRefCounted<MainThreadNotifier<T>> { +public: + static Ref<MainThreadNotifier> create() + { + return adoptRef(*new MainThreadNotifier()); + } + + template<typename F> + void notify(T notificationType, const F& callbackFunctor) + { + ASSERT(m_isValid.load()); + if (isMainThread()) { + removePendingNotification(notificationType); + callbackFunctor(); + return; + } + + if (!addPendingNotification(notificationType)) + return; + + RunLoop::main().dispatch([this, protectedThis = makeRef(*this), notificationType, callback = std::function<void()>(callbackFunctor)] { + if (!m_isValid.load()) + return; + if (removePendingNotification(notificationType)) + callback(); + }); + } + + void cancelPendingNotifications(unsigned mask = 0) + { + ASSERT(m_isValid.load()); + LockHolder locker(m_pendingNotificationsLock); + if (mask) + m_pendingNotifications &= ~mask; + else + m_pendingNotifications = 0; + } + + void invalidate() + { + ASSERT(m_isValid.load()); + m_isValid.store(false); + } + +private: + MainThreadNotifier() + { + m_isValid.store(true); + } + + bool addPendingNotification(T notificationType) + { + LockHolder locker(m_pendingNotificationsLock); + if (notificationType & m_pendingNotifications) + return false; + m_pendingNotifications |= notificationType; + return true; + } + + bool removePendingNotification(T notificationType) + { + LockHolder locker(m_pendingNotificationsLock); + if (notificationType & m_pendingNotifications) { + m_pendingNotifications &= ~notificationType; + return true; + } + return false; + } + + Lock m_pendingNotificationsLock; + unsigned m_pendingNotifications { 0 }; + Atomic<bool> m_isValid; +}; + +} // namespace WebCore + diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 165c5a06a..cadf905ed 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -3,7 +3,9 @@ * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> - * Copyright (C) 2009, 2010, 2011, 2012, 2013 Igalia S.L + * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015, 2016 Igalia S.L + * Copyright (C) 2014 Cable Television Laboratories, Inc. + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,18 +28,24 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) +#include "FileSystem.h" #include "GStreamerUtilities.h" #include "URL.h" #include "MIMETypeRegistry.h" #include "MediaPlayer.h" +#include "MediaPlayerRequestInstallMissingPluginsCallback.h" #include "NotImplemented.h" #include "SecurityOrigin.h" #include "TimeRanges.h" #include "WebKitWebSourceGStreamer.h" +#include <glib.h> #include <gst/gst.h> #include <gst/pbutils/missing-plugins.h> #include <limits> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/HexNumber.h> +#include <wtf/MediaTime.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> #if ENABLE(VIDEO_TRACK) @@ -49,6 +57,11 @@ #include "VideoTrackPrivateGStreamer.h" #endif +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) +#define GST_USE_UNSTABLE_API +#include <gst/mpegts/mpegts.h> +#undef GST_USE_UNSTABLE_API +#endif #include <gst/audio/streamvolume.h> #if ENABLE(MEDIA_SOURCE) @@ -56,24 +69,9 @@ #include "WebKitMediaSourceGStreamer.h" #endif -// GstPlayFlags flags from playbin2. It is the policy of GStreamer to -// not publicly expose element-specific enums. That's why this -// GstPlayFlags enum has been copied here. -typedef enum { - GST_PLAY_FLAG_VIDEO = 0x00000001, - GST_PLAY_FLAG_AUDIO = 0x00000002, - GST_PLAY_FLAG_TEXT = 0x00000004, - GST_PLAY_FLAG_VIS = 0x00000008, - GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, - GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, - GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, - GST_PLAY_FLAG_DOWNLOAD = 0x00000080, - GST_PLAY_FLAG_BUFFERING = 0x000000100 -} GstPlayFlags; - -// Max interval in seconds to stay in the READY state on manual -// state change requests. -static const guint gReadyStateTimerInterval = 60; +#if ENABLE(WEB_AUDIO) +#include "AudioSourceProviderGStreamer.h" +#endif GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug); #define GST_CAT_DEFAULT webkit_media_player_debug @@ -82,127 +80,34 @@ using namespace std; namespace WebCore { -static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player) -{ - return player->handleMessage(message); -} - -static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) -{ - player->sourceChanged(); -} - -static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) -{ - player->videoCapsChanged(); -} - -static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) -{ - player->videoChanged(); -} - -static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) +static void busMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player) { - player->audioChanged(); + player->handleMessage(message); } -static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) -{ - // This is the callback of the timeout source created in ::audioChanged. - player->notifyPlayerOfAudio(); - return FALSE; -} - -static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, gchar*, - MediaPlayerPrivateGStreamer* player) +void MediaPlayerPrivateGStreamer::setAudioStreamPropertiesCallback(MediaPlayerPrivateGStreamer* player, GObject* object) { player->setAudioStreamProperties(object); } -static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) -{ - // This is the callback of the timeout source created in ::videoChanged. - player->notifyPlayerOfVideo(); - return FALSE; -} - -static gboolean mediaPlayerPrivateVideoCapsChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) -{ - // This is the callback of the timeout source created in ::videoCapsChanged. - player->notifyPlayerOfVideoCaps(); - return FALSE; -} - -#if ENABLE(VIDEO_TRACK) -static void mediaPlayerPrivateTextChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) -{ - player->textChanged(); -} - -static gboolean mediaPlayerPrivateTextChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) -{ - // This is the callback of the timeout source created in ::textChanged. - player->notifyPlayerOfText(); - return FALSE; -} - -static GstFlowReturn mediaPlayerPrivateNewTextSampleCallback(GObject*, MediaPlayerPrivateGStreamer* player) -{ - player->newTextSample(); - return GST_FLOW_OK; -} -#endif - -static gboolean mediaPlayerPrivateReadyStateTimeoutCallback(MediaPlayerPrivateGStreamer* player) -{ - // This is the callback of the timeout source created in ::changePipelineState. - // Reset pipeline if we are sitting on READY state when timeout is reached - player->changePipelineState(GST_STATE_NULL); - return FALSE; -} - -static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData) -{ - MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData); - player->handlePluginInstallerResult(result); -} - -static GstClockTime toGstClockTime(float time) -{ - // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to - // round the microseconds so no floating point precision is lost and we can perform an accurate seek. - float seconds; - float microSeconds = modf(time, &seconds) * 1000000; - GTimeVal timeValue; - timeValue.tv_sec = static_cast<glong>(seconds); - timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000); - return GST_TIMEVAL_TO_TIME(timeValue); -} - void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object) { if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink")) return; - const char* role = m_player->mediaPlayerClient() && m_player->mediaPlayerClient()->mediaPlayerIsVideo() - ? "video" : "music"; - GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL); - g_object_set(object, "stream-properties", structure, NULL); + const char* role = m_player->client().mediaPlayerIsVideo() ? "video" : "music"; + GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, nullptr); + g_object_set(object, "stream-properties", structure, nullptr); gst_structure_free(structure); GUniquePtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object))); - LOG_MEDIA_MESSAGE("Set media.role as %s at %s", role, elementName.get()); -} - -PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player) -{ - return adoptPtr(new MediaPlayerPrivateGStreamer(player)); + GST_DEBUG("Set media.role as %s at %s", role, elementName.get()); } void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar) { if (isAvailable()) - registrar(create, getSupportedTypes, supportsType, 0, 0, 0); + registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateGStreamer>(player); }, + getSupportedTypes, supportsType, nullptr, nullptr, nullptr, supportsKeySystem); } bool initializeGStreamerAndRegisterWebKitElements() @@ -210,17 +115,14 @@ bool initializeGStreamerAndRegisterWebKitElements() if (!initializeGStreamer()) return false; - GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc"); + registerWebKitGStreamerElements(); + + GRefPtr<GstElementFactory> srcFactory = adoptGRef(gst_element_factory_find("webkitwebsrc")); if (!srcFactory) { GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player"); - gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC); + gst_element_register(nullptr, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC); } -#if ENABLE(MEDIA_SOURCE) - GRefPtr<GstElementFactory> WebKitMediaSrcFactory = gst_element_factory_find("webkitmediasrc"); - if (!WebKitMediaSrcFactory) - gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_SRC); -#endif return true; } @@ -229,52 +131,50 @@ bool MediaPlayerPrivateGStreamer::isAvailable() if (!initializeGStreamerAndRegisterWebKitElements()) return false; - GRefPtr<GstElementFactory> factory = gst_element_factory_find("playbin"); + GRefPtr<GstElementFactory> factory = adoptGRef(gst_element_factory_find("playbin")); return factory; } MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player) : MediaPlayerPrivateGStreamerBase(player) - , m_source(0) - , m_seekTime(0) + , m_buffering(false) + , m_bufferingPercentage(0) + , m_canFallBackToLastFinishedSeekPosition(false) , m_changingRate(false) - , m_endTime(numeric_limits<float>::infinity()) + , m_downloadFinished(false) + , m_errorOccured(false) , m_isEndReached(false) , m_isStreaming(false) - , m_mediaLocations(0) - , m_mediaLocationCurrentIndex(0) - , m_resetPipeline(false) + , m_durationAtEOS(0) , m_paused(true) - , m_playbackRatePause(false) + , m_playbackRate(1) + , m_requestedState(GST_STATE_VOID_PENDING) + , m_resetPipeline(false) , m_seeking(false) , m_seekIsPending(false) + , m_seekTime(0) + , m_source(nullptr) + , m_volumeAndMuteInitialized(false) + , m_weakPtrFactory(this) + , m_mediaLocations(nullptr) + , m_mediaLocationCurrentIndex(0) + , m_playbackRatePause(false) , m_timeOfOverlappingSeek(-1) - , m_buffering(false) - , m_playbackRate(1) , m_lastPlaybackRate(1) - , m_errorOccured(false) - , m_mediaDuration(0) - , m_downloadFinished(false) - , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired) + , m_fillTimer(*this, &MediaPlayerPrivateGStreamer::fillTimerFired) , m_maxTimeLoaded(0) - , m_bufferingPercentage(0) , m_preload(player->preload()) , m_delayingLoad(false) - , m_mediaDurationKnown(true) , m_maxTimeLoadedAtLastDidLoadingProgress(0) - , m_volumeAndMuteInitialized(false) , m_hasVideo(false) , m_hasAudio(false) - , m_audioTimerHandler(0) - , m_textTimerHandler(0) - , m_videoTimerHandler(0) - , m_videoCapsTimerHandler(0) - , m_readyTimerHandler(0) - , m_totalBytes(-1) + , m_readyTimerHandler(RunLoop::main(), this, &MediaPlayerPrivateGStreamer::readyTimerFired) + , m_totalBytes(0) , m_preservesPitch(false) - , m_requestedState(GST_STATE_VOID_PENDING) - , m_missingPlugins(false) { +#if USE(GLIB) + m_readyTimerHandler.setPriority(G_PRIORITY_DEFAULT_IDLE); +#endif } MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer() @@ -294,50 +194,35 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer() if (m_mediaLocations) { gst_structure_free(m_mediaLocations); - m_mediaLocations = 0; + m_mediaLocations = nullptr; } + if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get())) + g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this); + if (m_autoAudioSink) g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()), reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this); - if (m_readyTimerHandler) - g_source_remove(m_readyTimerHandler); - - if (m_playBin) { - GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_playBin.get()))); - ASSERT(bus); - g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this); - gst_bus_remove_signal_watch(bus.get()); - - g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateSourceChangedCallback), this); - g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoChangedCallback), this); - g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateAudioChangedCallback), this); -#if ENABLE(VIDEO_TRACK) - g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateNewTextSampleCallback), this); - g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateTextChangedCallback), this); -#endif - - gst_element_set_state(m_playBin.get(), GST_STATE_NULL); - m_playBin.clear(); + m_readyTimerHandler.stop(); + if (m_missingPluginsCallback) { + m_missingPluginsCallback->invalidate(); + m_missingPluginsCallback = nullptr; } - if (m_webkitVideoSink) { - GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink")); - g_signal_handlers_disconnect_by_func(videoSinkPad.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoSinkCapsChangedCallback), this); + if (m_videoSink) { + GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink")); + g_signal_handlers_disconnect_matched(videoSinkPad.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); } - if (m_videoTimerHandler) - g_source_remove(m_videoTimerHandler); - - if (m_audioTimerHandler) - g_source_remove(m_audioTimerHandler); - - if (m_textTimerHandler) - g_source_remove(m_textTimerHandler); - - if (m_videoCapsTimerHandler) - g_source_remove(m_videoCapsTimerHandler); + if (m_pipeline) { + GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get()))); + ASSERT(bus); + g_signal_handlers_disconnect_by_func(bus.get(), gpointer(busMessageCallback), this); + gst_bus_remove_signal_watch(bus.get()); + gst_bus_set_sync_handler(bus.get(), nullptr, nullptr, nullptr); + g_signal_handlers_disconnect_matched(m_pipeline.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + } } void MediaPlayerPrivateGStreamer::load(const String& urlString) @@ -354,18 +239,21 @@ void MediaPlayerPrivateGStreamer::load(const String& urlString) if (url.isLocalFile()) cleanURL = cleanURL.substring(0, url.pathEnd()); - if (!m_playBin) + if (!m_pipeline) createGSTPlayBin(); - ASSERT(m_playBin); + if (m_fillTimer.isActive()) + m_fillTimer.stop(); + + ASSERT(m_pipeline); m_url = URL(URL(), cleanURL); - g_object_set(m_playBin.get(), "uri", cleanURL.utf8().data(), NULL); + g_object_set(m_pipeline.get(), "uri", cleanURL.utf8().data(), nullptr); - INFO_MEDIA_MESSAGE("Load %s", cleanURL.utf8().data()); + GST_INFO("Load %s", cleanURL.utf8().data()); if (m_preload == MediaPlayer::None) { - LOG_MEDIA_MESSAGE("Delaying load."); + GST_DEBUG("Delaying load."); m_delayingLoad = true; } @@ -376,24 +264,32 @@ void MediaPlayerPrivateGStreamer::load(const String& urlString) m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); m_volumeAndMuteInitialized = false; + m_durationAtEOS = 0; if (!m_delayingLoad) commitLoad(); } #if ENABLE(MEDIA_SOURCE) -void MediaPlayerPrivateGStreamer::load(const String& url, PassRefPtr<HTMLMediaSource> mediaSource) +void MediaPlayerPrivateGStreamer::load(const String&, MediaSourcePrivateClient*) +{ + // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate. + m_networkState = MediaPlayer::FormatError; + m_player->networkStateChanged(); +} +#endif + +#if ENABLE(MEDIA_STREAM) +void MediaPlayerPrivateGStreamer::load(MediaStreamPrivate&) { - String mediasourceUri = String::format("mediasource%s", url.utf8().data()); - m_mediaSource = mediaSource; - load(mediasourceUri); + notImplemented(); } #endif void MediaPlayerPrivateGStreamer::commitLoad() { ASSERT(!m_delayingLoad); - LOG_MEDIA_MESSAGE("Committing load."); + GST_DEBUG("Committing load."); // GStreamer needs to have the pipeline set to a paused state to // start providing anything useful. @@ -403,7 +299,7 @@ void MediaPlayerPrivateGStreamer::commitLoad() updateStates(); } -float MediaPlayerPrivateGStreamer::playbackPosition() const +double MediaPlayerPrivateGStreamer::playbackPosition() const { if (m_isEndReached) { // Position queries on a null pipeline return 0. If we're at @@ -412,48 +308,56 @@ float MediaPlayerPrivateGStreamer::playbackPosition() const // what the Media element spec expects us to do. if (m_seeking) return m_seekTime; - if (m_mediaDuration) - return m_mediaDuration; + + MediaTime mediaDuration = durationMediaTime(); + if (mediaDuration) + return mediaDuration.toDouble(); return 0; } // Position is only available if no async state change is going on and the state is either paused or playing. gint64 position = GST_CLOCK_TIME_NONE; GstQuery* query= gst_query_new_position(GST_FORMAT_TIME); - if (gst_element_query(m_playBin.get(), query)) + if (gst_element_query(m_pipeline.get(), query)) gst_query_parse_position(query, 0, &position); + gst_query_unref(query); - float result = 0.0f; - if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE) - result = static_cast<double>(position) / GST_SECOND; - else if (m_canFallBackToLastFinishedSeekPositon) - result = m_seekTime; - - LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); + GST_DEBUG("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); - gst_query_unref(query); + double result = 0.0f; + if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE) { + GTimeVal timeValue; + GST_TIME_TO_TIMEVAL(position, timeValue); + result = static_cast<double>(timeValue.tv_sec + (timeValue.tv_usec / 1000000.0)); + } else if (m_canFallBackToLastFinishedSeekPosition) + result = m_seekTime; return result; } +void MediaPlayerPrivateGStreamer::readyTimerFired() +{ + changePipelineState(GST_STATE_NULL); +} + bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState) { - ASSERT(m_playBin); + ASSERT(m_pipeline); GstState currentState; GstState pending; - gst_element_get_state(m_playBin.get(), ¤tState, &pending, 0); + gst_element_get_state(m_pipeline.get(), ¤tState, &pending, 0); if (currentState == newState || pending == newState) { - LOG_MEDIA_MESSAGE("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState), + GST_DEBUG("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState), gst_element_state_get_name(currentState), gst_element_state_get_name(pending)); return true; } - LOG_MEDIA_MESSAGE("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState), + GST_DEBUG("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState), gst_element_state_get_name(currentState), gst_element_state_get_name(pending)); - GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState); + GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), newState); GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING; if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) { return false; @@ -463,13 +367,13 @@ bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState) // if we stay for too long on READY. // Also lets remove the timer if we request a state change for any state other than READY. // See also https://bugs.webkit.org/show_bug.cgi?id=117354 - if (newState == GST_STATE_READY && !m_readyTimerHandler) { - m_readyTimerHandler = g_timeout_add_seconds(gReadyStateTimerInterval, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateReadyStateTimeoutCallback), this); - g_source_set_name_by_id(m_readyTimerHandler, "[WebKit] mediaPlayerPrivateReadyStateTimeoutCallback"); - } else if (newState != GST_STATE_READY && m_readyTimerHandler) { - g_source_remove(m_readyTimerHandler); - m_readyTimerHandler = 0; - } + if (newState == GST_STATE_READY && !m_readyTimerHandler.isActive()) { + // Max interval in seconds to stay in the READY state on manual + // state change requests. + static const double readyStateTimerDelay = 60; + m_readyTimerHandler.startOneShot(readyStateTimerDelay); + } else if (newState != GST_STATE_READY) + m_readyTimerHandler.stop(); return true; } @@ -495,7 +399,7 @@ void MediaPlayerPrivateGStreamer::play() m_delayingLoad = false; m_preload = MediaPlayer::Auto; setDownloadBuffering(); - LOG_MEDIA_MESSAGE("Play"); + GST_DEBUG("Play"); } else { loadingFailed(MediaPlayer::Empty); } @@ -505,57 +409,56 @@ void MediaPlayerPrivateGStreamer::pause() { m_playbackRatePause = false; GstState currentState, pendingState; - gst_element_get_state(m_playBin.get(), ¤tState, &pendingState, 0); + gst_element_get_state(m_pipeline.get(), ¤tState, &pendingState, 0); if (currentState < GST_STATE_PAUSED && pendingState <= GST_STATE_PAUSED) return; if (changePipelineState(GST_STATE_PAUSED)) - INFO_MEDIA_MESSAGE("Pause"); + GST_INFO("Pause"); else loadingFailed(MediaPlayer::Empty); } -float MediaPlayerPrivateGStreamer::duration() const +MediaTime MediaPlayerPrivateGStreamer::durationMediaTime() const { - if (!m_playBin) - return 0.0f; + if (!m_pipeline) + return { }; if (m_errorOccured) - return 0.0f; + return { }; - // Media duration query failed already, don't attempt new useless queries. - if (!m_mediaDurationKnown) - return numeric_limits<float>::infinity(); + if (m_durationAtEOS) + return MediaTime::createWithDouble(m_durationAtEOS); - if (m_mediaDuration) - return m_mediaDuration; + // The duration query would fail on a not-prerolled pipeline. + if (GST_STATE(m_pipeline.get()) < GST_STATE_PAUSED) + return { }; GstFormat timeFormat = GST_FORMAT_TIME; gint64 timeLength = 0; - bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE; + bool failure = !gst_element_query_duration(m_pipeline.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE; if (failure) { - LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data()); - return numeric_limits<float>::infinity(); + GST_DEBUG("Time duration query failed for %s", m_url.string().utf8().data()); + return MediaTime::positiveInfiniteTime(); } - LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); + GST_DEBUG("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); - m_mediaDuration = static_cast<double>(timeLength) / GST_SECOND; - return m_mediaDuration; + return MediaTime::createWithDouble(static_cast<double>(timeLength) / GST_SECOND); // FIXME: handle 3.14.9.5 properly } -float MediaPlayerPrivateGStreamer::currentTime() const +MediaTime MediaPlayerPrivateGStreamer::currentMediaTime() const { - if (!m_playBin) - return 0.0f; + if (!m_pipeline) + return { }; if (m_errorOccured) - return 0.0f; + return { }; if (m_seeking) - return m_seekTime; + return MediaTime::createWithFloat(m_seekTime); // Workaround for // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer @@ -563,30 +466,30 @@ float MediaPlayerPrivateGStreamer::currentTime() const // negative playback rate. There's no upstream accepted patch for // this bug yet, hence this temporary workaround. if (m_isEndReached && m_playbackRate < 0) - return 0.0f; + return { }; - return playbackPosition(); + return MediaTime::createWithDouble(playbackPosition()); } void MediaPlayerPrivateGStreamer::seek(float time) { - if (!m_playBin) + if (!m_pipeline) return; if (m_errorOccured) return; - INFO_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time); + GST_INFO("[Seek] seek attempt to %f secs", time); // Avoid useless seeking. - if (time == currentTime()) + if (MediaTime::createWithFloat(time) == currentMediaTime()) return; if (isLiveStream()) return; GstClockTime clockTime = toGstClockTime(time); - INFO_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time); + GST_INFO("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time); if (m_seeking) { m_timeOfOverlappingSeek = time; @@ -597,15 +500,15 @@ void MediaPlayerPrivateGStreamer::seek(float time) } GstState state; - GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0); + GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, nullptr, 0); if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) { - LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult)); + GST_DEBUG("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult)); return; } if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) { m_seekIsPending = true; if (m_isEndReached) { - LOG_MEDIA_MESSAGE("[Seek] reset pipeline"); + GST_DEBUG("[Seek] reset pipeline"); m_resetPipeline = true; if (!changePipelineState(GST_STATE_PAUSED)) loadingFailed(MediaPlayer::Empty); @@ -613,7 +516,7 @@ void MediaPlayerPrivateGStreamer::seek(float time) } else { // We can seek now. if (!doSeek(clockTime, m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE))) { - LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time); + GST_DEBUG("[Seek] seeking to %f failed", time); return; } } @@ -627,6 +530,11 @@ bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFla { gint64 startTime, endTime; + // TODO: Should do more than that, need to notify the media source + // and probably flush the pipeline at least. + if (isMediaSource()) + return true; + if (rate > 0) { startTime = position; endTime = GST_CLOCK_TIME_NONE; @@ -635,7 +543,7 @@ bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFla // If we are at beginning of media, start from the end to // avoid immediate EOS. if (position < 0) - endTime = static_cast<gint64>(duration() * GST_SECOND); + endTime = static_cast<gint64>(durationMediaTime().toDouble() * GST_SECOND); else endTime = position; } @@ -643,7 +551,7 @@ bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFla if (!rate) rate = 1.0; - return gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, seekType, + return gst_element_seek(m_pipeline.get(), rate, GST_FORMAT_TIME, seekType, GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime); } @@ -655,7 +563,7 @@ void MediaPlayerPrivateGStreamer::updatePlaybackRate() float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND); bool mute = false; - INFO_MEDIA_MESSAGE("Set Rate to %f", m_playbackRate); + GST_INFO("Set Rate to %f", m_playbackRate); if (m_playbackRate > 0) { // Mute the sound if the playback rate is too extreme and @@ -667,20 +575,20 @@ void MediaPlayerPrivateGStreamer::updatePlaybackRate() mute = true; } - INFO_MEDIA_MESSAGE("Need to mute audio?: %d", (int) mute); + GST_INFO("Need to mute audio?: %d", (int) mute); if (doSeek(currentPosition, m_playbackRate, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH))) { - g_object_set(m_playBin.get(), "mute", mute, NULL); + g_object_set(m_pipeline.get(), "mute", mute, nullptr); m_lastPlaybackRate = m_playbackRate; } else { m_playbackRate = m_lastPlaybackRate; - ERROR_MEDIA_MESSAGE("Set rate to %f failed", m_playbackRate); + GST_ERROR("Set rate to %f failed", m_playbackRate); } if (m_playbackRatePause) { GstState state; GstState pending; - gst_element_get_state(m_playBin.get(), &state, &pending, 0); + gst_element_get_state(m_pipeline.get(), &state, &pending, 0); if (state != GST_STATE_PLAYING && pending != GST_STATE_PLAYING) changePipelineState(GST_STATE_PLAYING); m_playbackRatePause = false; @@ -693,7 +601,7 @@ void MediaPlayerPrivateGStreamer::updatePlaybackRate() bool MediaPlayerPrivateGStreamer::paused() const { if (m_isEndReached) { - LOG_MEDIA_MESSAGE("Ignoring pause at EOS"); + GST_DEBUG("Ignoring pause at EOS"); return true; } @@ -701,8 +609,8 @@ bool MediaPlayerPrivateGStreamer::paused() const return false; GstState state; - gst_element_get_state(m_playBin.get(), &state, 0, 0); - return state == GST_STATE_PAUSED; + gst_element_get_state(m_pipeline.get(), &state, nullptr, 0); + return state <= GST_STATE_PAUSED; } bool MediaPlayerPrivateGStreamer::seeking() const @@ -710,34 +618,35 @@ bool MediaPlayerPrivateGStreamer::seeking() const return m_seeking; } -void MediaPlayerPrivateGStreamer::videoChanged() -{ - if (m_videoTimerHandler) - g_source_remove(m_videoTimerHandler); - m_videoTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this, 0); -} - -void MediaPlayerPrivateGStreamer::videoCapsChanged() +void MediaPlayerPrivateGStreamer::videoChangedCallback(MediaPlayerPrivateGStreamer* player) { - if (m_videoCapsTimerHandler) - g_source_remove(m_videoCapsTimerHandler); - m_videoCapsTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoCapsChangeTimeoutCallback), this); + player->m_notifier->notify(MainThreadNotification::VideoChanged, [player] { player->notifyPlayerOfVideo(); }); } void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo() { - m_videoTimerHandler = 0; + if (UNLIKELY(!m_pipeline || !m_source)) + return; gint numTracks = 0; - if (m_playBin) - g_object_get(m_playBin.get(), "n-video", &numTracks, NULL); + bool useMediaSource = isMediaSource(); + GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get(); + g_object_get(element, "n-video", &numTracks, nullptr); m_hasVideo = numTracks > 0; + if (m_hasVideo) + m_player->sizeChanged(); + + if (useMediaSource) { + GST_DEBUG("Tracks managed by source element. Bailing out now."); + m_player->client().mediaPlayerEngineUpdated(m_player); + return; + } #if ENABLE(VIDEO_TRACK) for (gint i = 0; i < numTracks; ++i) { GRefPtr<GstPad> pad; - g_signal_emit_by_name(m_playBin.get(), "get-video-pad", i, &pad.outPtr(), NULL); + g_signal_emit_by_name(m_pipeline.get(), "get-video-pad", i, &pad.outPtr(), nullptr); ASSERT(pad); if (i < static_cast<gint>(m_videoTracks.size())) { @@ -747,50 +656,60 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo() continue; } - RefPtr<VideoTrackPrivateGStreamer> track = VideoTrackPrivateGStreamer::create(m_playBin, i, pad); + RefPtr<VideoTrackPrivateGStreamer> track = VideoTrackPrivateGStreamer::create(m_pipeline, i, pad); m_videoTracks.append(track); - m_player->addVideoTrack(track.release()); + m_player->addVideoTrack(*track); } while (static_cast<gint>(m_videoTracks.size()) > numTracks) { RefPtr<VideoTrackPrivateGStreamer> track = m_videoTracks.last(); track->disconnect(); m_videoTracks.removeLast(); - m_player->removeVideoTrack(track.release()); + m_player->removeVideoTrack(*track); } #endif - m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); + m_player->client().mediaPlayerEngineUpdated(m_player); +} + +void MediaPlayerPrivateGStreamer::videoSinkCapsChangedCallback(MediaPlayerPrivateGStreamer* player) +{ + player->m_notifier->notify(MainThreadNotification::VideoCapsChanged, [player] { player->notifyPlayerOfVideoCaps(); }); } void MediaPlayerPrivateGStreamer::notifyPlayerOfVideoCaps() { - m_videoCapsTimerHandler = 0; m_videoSize = IntSize(); - m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); + m_player->client().mediaPlayerEngineUpdated(m_player); } -void MediaPlayerPrivateGStreamer::audioChanged() +void MediaPlayerPrivateGStreamer::audioChangedCallback(MediaPlayerPrivateGStreamer* player) { - if (m_audioTimerHandler) - g_source_remove(m_audioTimerHandler); - m_audioTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this, 0); + player->m_notifier->notify(MainThreadNotification::AudioChanged, [player] { player->notifyPlayerOfAudio(); }); } void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio() { - m_audioTimerHandler = 0; + if (UNLIKELY(!m_pipeline || !m_source)) + return; gint numTracks = 0; - if (m_playBin) - g_object_get(m_playBin.get(), "n-audio", &numTracks, NULL); + bool useMediaSource = isMediaSource(); + GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get(); + g_object_get(element, "n-audio", &numTracks, nullptr); m_hasAudio = numTracks > 0; + if (useMediaSource) { + GST_DEBUG("Tracks managed by source element. Bailing out now."); + m_player->client().mediaPlayerEngineUpdated(m_player); + return; + } + #if ENABLE(VIDEO_TRACK) for (gint i = 0; i < numTracks; ++i) { GRefPtr<GstPad> pad; - g_signal_emit_by_name(m_playBin.get(), "get-audio-pad", i, &pad.outPtr(), NULL); + g_signal_emit_by_name(m_pipeline.get(), "get-audio-pad", i, &pad.outPtr(), nullptr); ASSERT(pad); if (i < static_cast<gint>(m_audioTracks.size())) { @@ -800,41 +719,46 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio() continue; } - RefPtr<AudioTrackPrivateGStreamer> track = AudioTrackPrivateGStreamer::create(m_playBin, i, pad); + RefPtr<AudioTrackPrivateGStreamer> track = AudioTrackPrivateGStreamer::create(m_pipeline, i, pad); m_audioTracks.insert(i, track); - m_player->addAudioTrack(track.release()); + m_player->addAudioTrack(*track); } while (static_cast<gint>(m_audioTracks.size()) > numTracks) { RefPtr<AudioTrackPrivateGStreamer> track = m_audioTracks.last(); track->disconnect(); m_audioTracks.removeLast(); - m_player->removeAudioTrack(track.release()); + m_player->removeAudioTrack(*track); } #endif - m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); + m_player->client().mediaPlayerEngineUpdated(m_player); } #if ENABLE(VIDEO_TRACK) -void MediaPlayerPrivateGStreamer::textChanged() +void MediaPlayerPrivateGStreamer::textChangedCallback(MediaPlayerPrivateGStreamer* player) { - if (m_textTimerHandler) - g_source_remove(m_textTimerHandler); - m_textTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateTextChangeTimeoutCallback), this); + player->m_notifier->notify(MainThreadNotification::TextChanged, [player] { player->notifyPlayerOfText(); }); } void MediaPlayerPrivateGStreamer::notifyPlayerOfText() { - m_textTimerHandler = 0; + if (UNLIKELY(!m_pipeline || !m_source)) + return; gint numTracks = 0; - if (m_playBin) - g_object_get(m_playBin.get(), "n-text", &numTracks, NULL); + bool useMediaSource = isMediaSource(); + GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get(); + g_object_get(element, "n-text", &numTracks, nullptr); + + if (useMediaSource) { + GST_DEBUG("Tracks managed by source element. Bailing out now."); + return; + } for (gint i = 0; i < numTracks; ++i) { GRefPtr<GstPad> pad; - g_signal_emit_by_name(m_playBin.get(), "get-text-pad", i, &pad.outPtr(), NULL); + g_signal_emit_by_name(m_pipeline.get(), "get-text-pad", i, &pad.outPtr(), nullptr); ASSERT(pad); if (i < static_cast<gint>(m_textTracks.size())) { @@ -846,17 +770,23 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfText() RefPtr<InbandTextTrackPrivateGStreamer> track = InbandTextTrackPrivateGStreamer::create(i, pad); m_textTracks.insert(i, track); - m_player->addTextTrack(track.release()); + m_player->addTextTrack(*track); } while (static_cast<gint>(m_textTracks.size()) > numTracks) { RefPtr<InbandTextTrackPrivateGStreamer> track = m_textTracks.last(); track->disconnect(); m_textTracks.removeLast(); - m_player->removeTextTrack(track.release()); + m_player->removeTextTrack(*track); } } +GstFlowReturn MediaPlayerPrivateGStreamer::newTextSampleCallback(MediaPlayerPrivateGStreamer* player) +{ + player->newTextSample(); + return GST_FLOW_OK; +} + void MediaPlayerPrivateGStreamer::newTextSample() { if (!m_textAppSink) @@ -866,7 +796,7 @@ void MediaPlayerPrivateGStreamer::newTextSample() gst_pad_get_sticky_event(m_textAppSinkPad.get(), GST_EVENT_STREAM_START, 0)); GRefPtr<GstSample> sample; - g_signal_emit_by_name(m_textAppSink.get(), "pull-sample", &sample.outPtr(), NULL); + g_signal_emit_by_name(m_textAppSink.get(), "pull-sample", &sample.outPtr(), nullptr); ASSERT(sample); if (streamStartEvent) { @@ -882,9 +812,9 @@ void MediaPlayerPrivateGStreamer::newTextSample() } } if (!found) - WARN_MEDIA_MESSAGE("Got sample with unknown stream ID."); + GST_WARNING("Got sample with unknown stream ID."); } else - WARN_MEDIA_MESSAGE("Unable to handle sample with no stream start event."); + GST_WARNING("Unable to handle sample with no stream start event."); } #endif @@ -915,7 +845,7 @@ void MediaPlayerPrivateGStreamer::setRate(float rate) m_playbackRate = rate; m_changingRate = true; - gst_element_get_state(m_playBin.get(), &state, &pending, 0); + gst_element_get_state(m_pipeline.get(), &state, &pending, 0); if (!rate) { m_changingRate = false; @@ -932,52 +862,53 @@ void MediaPlayerPrivateGStreamer::setRate(float rate) updatePlaybackRate(); } +double MediaPlayerPrivateGStreamer::rate() const +{ + return m_playbackRate; +} + void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch) { m_preservesPitch = preservesPitch; } -PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const +std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateGStreamer::buffered() const { - RefPtr<TimeRanges> timeRanges = TimeRanges::create(); + auto timeRanges = std::make_unique<PlatformTimeRanges>(); if (m_errorOccured || isLiveStream()) - return timeRanges.release(); + return timeRanges; -#if GST_CHECK_VERSION(0, 10, 31) - float mediaDuration(duration()); + float mediaDuration(durationMediaTime().toDouble()); if (!mediaDuration || std::isinf(mediaDuration)) - return timeRanges.release(); + return timeRanges; GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); - if (!gst_element_query(m_playBin.get(), query)) { + if (!gst_element_query(m_pipeline.get(), query)) { gst_query_unref(query); - return timeRanges.release(); + return timeRanges; } - for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) { + guint numBufferingRanges = gst_query_get_n_buffering_ranges(query); + for (guint index = 0; index < numBufferingRanges; index++) { gint64 rangeStart = 0, rangeStop = 0; if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop)) - timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / GST_FORMAT_PERCENT_MAX), - static_cast<float>((rangeStop * mediaDuration) / GST_FORMAT_PERCENT_MAX)); + timeRanges->add(MediaTime::createWithDouble((rangeStart * mediaDuration) / GST_FORMAT_PERCENT_MAX), + MediaTime::createWithDouble((rangeStop * mediaDuration) / GST_FORMAT_PERCENT_MAX)); } // Fallback to the more general maxTimeLoaded() if no range has // been found. if (!timeRanges->length()) if (float loaded = maxTimeLoaded()) - timeRanges->add(0, loaded); + timeRanges->add(MediaTime::zeroTime(), MediaTime::createWithDouble(loaded)); gst_query_unref(query); -#else - float loaded = maxTimeLoaded(); - if (!m_errorOccured && !isLiveStream() && loaded > 0) - timeRanges->add(0, loaded); -#endif - return timeRanges.release(); + + return timeRanges; } -gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) +void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) { GUniqueOutPtr<GError> err; GUniqueOutPtr<gchar> debug; @@ -987,7 +918,7 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) const GstStructure* structure = gst_message_get_structure(message); GstState requestedState, currentState; - m_canFallBackToLastFinishedSeekPositon = false; + m_canFallBackToLastFinishedSeekPosition = false; if (structure) { const gchar* messageTypeName = gst_structure_get_name(structure); @@ -996,40 +927,37 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) // notify of the new location(s) of the media. if (!g_strcmp0(messageTypeName, "redirect")) { mediaLocationChanged(message); - return TRUE; + return; } } // We ignore state changes from internal elements. They are forwarded to playbin2 anyway. - bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get()); + bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_pipeline.get()); - LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message)); + GST_DEBUG("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message)); switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: - if (m_resetPipeline) - break; - if (m_missingPlugins) + if (m_resetPipeline || m_missingPluginsCallback || m_errorOccured) break; gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); - ERROR_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data()); + GST_ERROR("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data()); - GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error"); + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error"); error = MediaPlayer::Empty; - if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND - || err->code == GST_STREAM_ERROR_WRONG_TYPE - || err->code == GST_STREAM_ERROR_FAILED - || err->code == GST_CORE_ERROR_MISSING_PLUGIN - || err->code == GST_RESOURCE_ERROR_NOT_FOUND) + if (g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_CODEC_NOT_FOUND) + || g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE) + || g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED) + || g_error_matches(err.get(), GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN) + || g_error_matches(err.get(), GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND)) error = MediaPlayer::FormatError; - else if (err->domain == GST_STREAM_ERROR) { + else if (g_error_matches(err.get(), GST_STREAM_ERROR, GST_STREAM_ERROR_TYPE_NOT_FOUND)) { // Let the mediaPlayerClient handle the stream error, in // this case the HTMLMediaElement will emit a stalled // event. - if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) { - ERROR_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event."); - break; - } + GST_ERROR("Decode error, let the Media element emit a stalled event."); + break; + } else if (err->domain == GST_STREAM_ERROR) { error = MediaPlayer::DecodeError; attemptNextLocation = true; } else if (err->domain == GST_RESOURCE_ERROR) @@ -1055,9 +983,9 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) // Construct a filename for the graphviz dot file output. GstState newState; - gst_message_parse_state_changed(message, ¤tState, &newState, 0); + gst_message_parse_state_changed(message, ¤tState, &newState, nullptr); CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8(); - GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data()); + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data()); break; } @@ -1065,71 +993,176 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) processBufferingStats(message); break; case GST_MESSAGE_DURATION_CHANGED: - if (messageSourceIsPlaybin) + // Duration in MSE is managed by MediaSource, SourceBuffer and AppendPipeline. + if (messageSourceIsPlaybin && !isMediaSource()) durationChanged(); break; case GST_MESSAGE_REQUEST_STATE: gst_message_parse_request_state(message, &requestedState); - gst_element_get_state(m_playBin.get(), ¤tState, NULL, 250); + gst_element_get_state(m_pipeline.get(), ¤tState, nullptr, 250 * GST_NSECOND); if (requestedState < currentState) { GUniquePtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message))); - INFO_MEDIA_MESSAGE("Element %s requested state change to %s", elementName.get(), + GST_INFO("Element %s requested state change to %s", elementName.get(), gst_element_state_get_name(requestedState)); m_requestedState = requestedState; if (!changePipelineState(requestedState)) loadingFailed(MediaPlayer::Empty); } break; + case GST_MESSAGE_CLOCK_LOST: + // This can only happen in PLAYING state and we should just + // get a new clock by moving back to PAUSED and then to + // PLAYING again. + // This can happen if the stream that ends in a sink that + // provides the current clock disappears, for example if + // the audio sink provides the clock and the audio stream + // is disabled. It also happens relatively often with + // HTTP adaptive streams when switching between different + // variants of a stream. + gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED); + gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING); + break; + case GST_MESSAGE_LATENCY: + // Recalculate the latency, we don't need any special handling + // here other than the GStreamer default. + // This can happen if the latency of live elements changes, or + // for one reason or another a new live element is added or + // removed from the pipeline. + gst_bin_recalculate_latency(GST_BIN(m_pipeline.get())); + break; case GST_MESSAGE_ELEMENT: if (gst_is_missing_plugin_message(message)) { - gchar* detail = gst_missing_plugin_message_get_installer_detail(message); - gchar* detailArray[2] = {detail, 0}; - GstInstallPluginsReturn result = gst_install_plugins_async(detailArray, 0, mediaPlayerPrivatePluginInstallerResultFunction, this); - m_missingPlugins = result == GST_INSTALL_PLUGINS_STARTED_OK; - g_free(detail); + if (gst_install_plugins_supported()) { + m_missingPluginsCallback = MediaPlayerRequestInstallMissingPluginsCallback::create([this](uint32_t result) { + m_missingPluginsCallback = nullptr; + if (result != GST_INSTALL_PLUGINS_SUCCESS) + return; + + changePipelineState(GST_STATE_READY); + changePipelineState(GST_STATE_PAUSED); + }); + GUniquePtr<char> detail(gst_missing_plugin_message_get_installer_detail(message)); + GUniquePtr<char> description(gst_missing_plugin_message_get_description(message)); + m_player->client().requestInstallMissingPlugins(String::fromUTF8(detail.get()), String::fromUTF8(description.get()), *m_missingPluginsCallback); + } + } +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + else if (gst_structure_has_name(structure, "drm-key-needed")) { + GST_DEBUG("drm-key-needed message from %s", GST_MESSAGE_SRC_NAME(message)); + GRefPtr<GstEvent> event; + gst_structure_get(structure, "event", GST_TYPE_EVENT, &event.outPtr(), nullptr); + handleProtectionEvent(event.get()); + } +#endif +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) + else { + GstMpegtsSection* section = gst_message_parse_mpegts_section(message); + if (section) { + processMpegTsSection(section); + gst_mpegts_section_unref(section); + } } +#endif break; #if ENABLE(VIDEO_TRACK) case GST_MESSAGE_TOC: processTableOfContents(message); break; #endif + case GST_MESSAGE_TAG: { + GstTagList* tags = nullptr; + GUniqueOutPtr<gchar> tag; + gst_message_parse_tag(message, &tags); + if (gst_tag_list_get_string(tags, GST_TAG_IMAGE_ORIENTATION, &tag.outPtr())) { + if (!g_strcmp0(tag.get(), "rotate-90")) + setVideoSourceOrientation(ImageOrientation(OriginRightTop)); + else if (!g_strcmp0(tag.get(), "rotate-180")) + setVideoSourceOrientation(ImageOrientation(OriginBottomRight)); + else if (!g_strcmp0(tag.get(), "rotate-270")) + setVideoSourceOrientation(ImageOrientation(OriginLeftBottom)); + } + gst_tag_list_unref(tags); + break; + } default: - LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s", + GST_DEBUG("Unhandled GStreamer message type: %s", GST_MESSAGE_TYPE_NAME(message)); break; } - return TRUE; -} - -void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsReturn result) -{ - m_missingPlugins = false; - if (result == GST_INSTALL_PLUGINS_SUCCESS) { - changePipelineState(GST_STATE_READY); - changePipelineState(GST_STATE_PAUSED); - } + return; } void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message) { m_buffering = true; - const GstStructure *structure = gst_message_get_structure(message); - gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage); + gst_message_parse_buffering(message, &m_bufferingPercentage); - LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage); + GST_DEBUG("[Buffering] Buffering: %d%%.", m_bufferingPercentage); updateStates(); } +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) +void MediaPlayerPrivateGStreamer::processMpegTsSection(GstMpegtsSection* section) +{ + ASSERT(section); + + if (section->section_type == GST_MPEGTS_SECTION_PMT) { + const GstMpegtsPMT* pmt = gst_mpegts_section_get_pmt(section); + m_metadataTracks.clear(); + for (guint i = 0; i < pmt->streams->len; ++i) { + const GstMpegtsPMTStream* stream = static_cast<const GstMpegtsPMTStream*>(g_ptr_array_index(pmt->streams, i)); + if (stream->stream_type == 0x05 || stream->stream_type >= 0x80) { + AtomicString pid = String::number(stream->pid); + RefPtr<InbandMetadataTextTrackPrivateGStreamer> track = InbandMetadataTextTrackPrivateGStreamer::create( + InbandTextTrackPrivate::Metadata, InbandTextTrackPrivate::Data, pid); + + // 4.7.10.12.2 Sourcing in-band text tracks + // If the new text track's kind is metadata, then set the text track in-band metadata track dispatch + // type as follows, based on the type of the media resource: + // Let stream type be the value of the "stream_type" field describing the text track's type in the + // file's program map section, interpreted as an 8-bit unsigned integer. Let length be the value of + // the "ES_info_length" field for the track in the same part of the program map section, interpreted + // as an integer as defined by the MPEG-2 specification. Let descriptor bytes be the length bytes + // following the "ES_info_length" field. The text track in-band metadata track dispatch type must be + // set to the concatenation of the stream type byte and the zero or more descriptor bytes bytes, + // expressed in hexadecimal using uppercase ASCII hex digits. + String inbandMetadataTrackDispatchType; + appendUnsignedAsHexFixedSize(stream->stream_type, inbandMetadataTrackDispatchType, 2); + for (guint j = 0; j < stream->descriptors->len; ++j) { + const GstMpegtsDescriptor* descriptor = static_cast<const GstMpegtsDescriptor*>(g_ptr_array_index(stream->descriptors, j)); + for (guint k = 0; k < descriptor->length; ++k) + appendByteAsHex(descriptor->data[k], inbandMetadataTrackDispatchType); + } + track->setInBandMetadataTrackDispatchType(inbandMetadataTrackDispatchType); + + m_metadataTracks.add(pid, track); + m_player->addTextTrack(*track); + } + } + } else { + AtomicString pid = String::number(section->pid); + RefPtr<InbandMetadataTextTrackPrivateGStreamer> track = m_metadataTracks.get(pid); + if (!track) + return; + + GRefPtr<GBytes> data = gst_mpegts_section_get_data(section); + gsize size; + const void* bytes = g_bytes_get_data(data.get(), &size); + + track->addDataCue(currentMediaTime(), currentMediaTime(), bytes, size); + } +} +#endif + #if ENABLE(VIDEO_TRACK) void MediaPlayerPrivateGStreamer::processTableOfContents(GstMessage* message) { if (m_chaptersTrack) - m_player->removeTextTrack(m_chaptersTrack); + m_player->removeTextTrack(*m_chaptersTrack); - m_chaptersTrack = InbandMetadataTextTrackPrivateGStreamer::create(InbandTextTrackPrivate::Chapters); - m_player->addTextTrack(m_chaptersTrack); + m_chaptersTrack = InbandMetadataTextTrackPrivateGStreamer::create(InbandTextTrackPrivate::Chapters, InbandTextTrackPrivate::Generic); + m_player->addTextTrack(*m_chaptersTrack); GRefPtr<GstToc> toc; gboolean updated; @@ -1137,12 +1170,11 @@ void MediaPlayerPrivateGStreamer::processTableOfContents(GstMessage* message) ASSERT(toc); for (GList* i = gst_toc_get_entries(toc.get()); i; i = i->next) - processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data), 0); + processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data)); } -void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry, GstTocEntry* parent) +void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry) { - UNUSED_PARAM(parent); ASSERT(entry); RefPtr<GenericCueData> cue = GenericCueData::create(); @@ -1150,13 +1182,13 @@ void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry gint64 start = -1, stop = -1; gst_toc_entry_get_start_stop_times(entry, &start, &stop); if (start != -1) - cue->setStartTime(static_cast<double>(start) / GST_SECOND); + cue->setStartTime(MediaTime(start, GST_SECOND)); if (stop != -1) - cue->setEndTime(static_cast<double>(stop) / GST_SECOND); + cue->setEndTime(MediaTime(stop, GST_SECOND)); GstTagList* tags = gst_toc_entry_get_tags(entry); if (tags) { - gchar* title = 0; + gchar* title = nullptr; gst_tag_list_get_string(tags, GST_TAG_TITLE, &title); if (title) { cue->setContent(title); @@ -1164,18 +1196,18 @@ void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry } } - m_chaptersTrack->client()->addGenericCue(m_chaptersTrack.get(), cue.release()); + m_chaptersTrack->addGenericCue(cue.release()); for (GList* i = gst_toc_entry_get_sub_entries(entry); i; i = i->next) - processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data), entry); + processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data)); } #endif -void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*) +void MediaPlayerPrivateGStreamer::fillTimerFired() { GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); - if (!gst_element_query(m_playBin.get(), query)) { + if (!gst_element_query(m_pipeline.get(), query)) { gst_query_unref(query); return; } @@ -1183,25 +1215,24 @@ void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStream gint64 start, stop; gdouble fillStatus = 100.0; - gst_query_parse_buffering_range(query, 0, &start, &stop, 0); + gst_query_parse_buffering_range(query, nullptr, &start, &stop, nullptr); gst_query_unref(query); if (stop != -1) fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX; - LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus); + GST_DEBUG("[Buffering] Download buffer filled up to %f%%", fillStatus); - if (!m_mediaDuration) - durationChanged(); + float mediaDuration = durationMediaTime().toDouble(); // Update maxTimeLoaded only if the media duration is // available. Otherwise we can't compute it. - if (m_mediaDuration) { + if (mediaDuration) { if (fillStatus == 100.0) - m_maxTimeLoaded = m_mediaDuration; + m_maxTimeLoaded = mediaDuration; else - m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0); - LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded); + m_maxTimeLoaded = static_cast<float>((fillStatus * mediaDuration) / 100.0); + GST_DEBUG("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded); } m_downloadFinished = fillStatus == 100.0; @@ -1222,12 +1253,13 @@ float MediaPlayerPrivateGStreamer::maxTimeSeekable() const if (m_errorOccured) return 0.0f; - LOG_MEDIA_MESSAGE("maxTimeSeekable"); + float mediaDuration = durationMediaTime().toDouble(); + GST_DEBUG("maxTimeSeekable, duration: %f", mediaDuration); // infinite duration means live stream - if (std::isinf(duration())) + if (std::isinf(mediaDuration)) return 0.0f; - return duration(); + return mediaDuration; } float MediaPlayerPrivateGStreamer::maxTimeLoaded() const @@ -1236,29 +1268,29 @@ float MediaPlayerPrivateGStreamer::maxTimeLoaded() const return 0.0f; float loaded = m_maxTimeLoaded; - if (m_isEndReached && m_mediaDuration) - loaded = m_mediaDuration; - LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded); + if (m_isEndReached) + loaded = durationMediaTime().toDouble(); + GST_DEBUG("maxTimeLoaded: %f", loaded); return loaded; } bool MediaPlayerPrivateGStreamer::didLoadingProgress() const { - if (!m_playBin || !m_mediaDuration || !totalBytes()) + if (UNLIKELY(!m_pipeline || !durationMediaTime() || (!isMediaSource() && !totalBytes()))) return false; float currentMaxTimeLoaded = maxTimeLoaded(); bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress; m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded; - LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress); + GST_DEBUG("didLoadingProgress: %d", didLoadingProgress); return didLoadingProgress; } -unsigned MediaPlayerPrivateGStreamer::totalBytes() const +unsigned long long MediaPlayerPrivateGStreamer::totalBytes() const { if (m_errorOccured) return 0; - if (m_totalBytes != -1) + if (m_totalBytes) return m_totalBytes; if (!m_source) @@ -1267,8 +1299,8 @@ unsigned MediaPlayerPrivateGStreamer::totalBytes() const GstFormat fmt = GST_FORMAT_BYTES; gint64 length = 0; if (gst_element_query_duration(m_source.get(), fmt, &length)) { - INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length); - m_totalBytes = static_cast<unsigned>(length); + GST_INFO("totalBytes %" G_GINT64_FORMAT, length); + m_totalBytes = static_cast<unsigned long long>(length); m_isStreaming = !length; return m_totalBytes; } @@ -1302,25 +1334,105 @@ unsigned MediaPlayerPrivateGStreamer::totalBytes() const gst_iterator_free(iter); - INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length); - m_totalBytes = static_cast<unsigned>(length); + GST_INFO("totalBytes %" G_GINT64_FORMAT, length); + m_totalBytes = static_cast<unsigned long long>(length); m_isStreaming = !length; return m_totalBytes; } +void MediaPlayerPrivateGStreamer::sourceChangedCallback(MediaPlayerPrivateGStreamer* player) +{ + player->sourceChanged(); +} + +void MediaPlayerPrivateGStreamer::uriDecodeBinElementAddedCallback(GstBin* bin, GstElement* element, MediaPlayerPrivateGStreamer* player) +{ + if (g_strcmp0(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(G_OBJECT(element))), "GstDownloadBuffer")) + return; + + player->m_downloadBuffer = element; + g_signal_handlers_disconnect_by_func(bin, reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), player); + g_signal_connect_swapped(element, "notify::temp-location", G_CALLBACK(downloadBufferFileCreatedCallback), player); + + GUniqueOutPtr<char> oldDownloadTemplate; + g_object_get(element, "temp-template", &oldDownloadTemplate.outPtr(), nullptr); + + GUniquePtr<char> newDownloadTemplate(g_build_filename(G_DIR_SEPARATOR_S, "var", "tmp", "WebKit-Media-XXXXXX", nullptr)); + g_object_set(element, "temp-template", newDownloadTemplate.get(), nullptr); + GST_TRACE("Reconfigured file download template from '%s' to '%s'", oldDownloadTemplate.get(), newDownloadTemplate.get()); + + player->purgeOldDownloadFiles(oldDownloadTemplate.get()); +} + +void MediaPlayerPrivateGStreamer::downloadBufferFileCreatedCallback(MediaPlayerPrivateGStreamer* player) +{ + ASSERT(player->m_downloadBuffer); + + g_signal_handlers_disconnect_by_func(player->m_downloadBuffer.get(), reinterpret_cast<gpointer>(downloadBufferFileCreatedCallback), player); + + GUniqueOutPtr<char> downloadFile; + g_object_get(player->m_downloadBuffer.get(), "temp-location", &downloadFile.outPtr(), nullptr); + player->m_downloadBuffer = nullptr; + + if (UNLIKELY(!deleteFile(downloadFile.get()))) { + GST_WARNING("Couldn't unlink media temporary file %s after creation", downloadFile.get()); + return; + } + + GST_TRACE("Unlinked media temporary file %s after creation", downloadFile.get()); +} + +void MediaPlayerPrivateGStreamer::purgeOldDownloadFiles(const char* downloadFileTemplate) +{ + if (!downloadFileTemplate) + return; + + GUniquePtr<char> templatePath(g_path_get_dirname(downloadFileTemplate)); + GUniquePtr<char> templateFile(g_path_get_basename(downloadFileTemplate)); + String templatePattern = String(templateFile.get()).replace("X", "?"); + + for (auto& filePath : listDirectory(templatePath.get(), templatePattern)) { + if (UNLIKELY(!deleteFile(filePath))) { + GST_WARNING("Couldn't unlink legacy media temporary file: %s", filePath.utf8().data()); + continue; + } + + GST_TRACE("Unlinked legacy media temporary file: %s", filePath.utf8().data()); + } +} + void MediaPlayerPrivateGStreamer::sourceChanged() { + if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get())) + g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this); + m_source.clear(); - g_object_get(m_playBin.get(), "source", &m_source.outPtr(), NULL); + g_object_get(m_pipeline.get(), "source", &m_source.outPtr(), nullptr); - if (WEBKIT_IS_WEB_SRC(m_source.get())) + if (WEBKIT_IS_WEB_SRC(m_source.get())) { webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player); -#if ENABLE(MEDIA_SOURCE) - if (m_mediaSource && WEBKIT_IS_MEDIA_SRC(m_source.get())) { - MediaSourceGStreamer::open(m_mediaSource.get(), WEBKIT_MEDIA_SRC(m_source.get())); - webKitMediaSrcSetPlayBin(WEBKIT_MEDIA_SRC(m_source.get()), m_playBin.get()); + g_signal_connect(GST_ELEMENT_PARENT(m_source.get()), "element-added", G_CALLBACK(uriDecodeBinElementAddedCallback), this); } -#endif +} + +bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const +{ + if (!m_source) + return false; + + if (!WEBKIT_IS_WEB_SRC(m_source.get())) + return true; + + GUniqueOutPtr<char> originalURI, resolvedURI; + g_object_get(m_source.get(), "location", &originalURI.outPtr(), "resolved-location", &resolvedURI.outPtr(), nullptr); + if (!originalURI || !resolvedURI) + return false; + if (!g_strcmp0(originalURI.get(), resolvedURI.get())) + return true; + + Ref<SecurityOrigin> resolvedOrigin(SecurityOrigin::createFromString(String::fromUTF8(resolvedURI.get()))); + Ref<SecurityOrigin> requestedOrigin(SecurityOrigin::createFromString(String::fromUTF8(originalURI.get()))); + return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get()); } void MediaPlayerPrivateGStreamer::cancelLoad() @@ -1328,20 +1440,20 @@ void MediaPlayerPrivateGStreamer::cancelLoad() if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) return; - if (m_playBin) + if (m_pipeline) changePipelineState(GST_STATE_READY); } void MediaPlayerPrivateGStreamer::asyncStateChangeDone() { - if (!m_playBin || m_errorOccured) + if (!m_pipeline || m_errorOccured) return; if (m_seeking) { if (m_seekIsPending) updateStates(); else { - LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime); + GST_DEBUG("[Seek] seeked to %f", m_seekTime); m_seeking = false; if (m_timeOfOverlappingSeek != m_seekTime && m_timeOfOverlappingSeek != -1) { seek(m_timeOfOverlappingSeek); @@ -1352,7 +1464,7 @@ void MediaPlayerPrivateGStreamer::asyncStateChangeDone() // The pipeline can still have a pending state. In this case a position query will fail. // Right now we can use m_seekTime as a fallback. - m_canFallBackToLastFinishedSeekPositon = true; + m_canFallBackToLastFinishedSeekPosition = true; timeChanged(); } } else @@ -1361,7 +1473,7 @@ void MediaPlayerPrivateGStreamer::asyncStateChangeDone() void MediaPlayerPrivateGStreamer::updateStates() { - if (!m_playBin) + if (!m_pipeline) return; if (m_errorOccured) @@ -1372,25 +1484,19 @@ void MediaPlayerPrivateGStreamer::updateStates() GstState state; GstState pending; - GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND); + GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND); bool shouldUpdatePlaybackState = false; switch (getStateResult) { case GST_STATE_CHANGE_SUCCESS: { - LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); // Do nothing if on EOS and state changed to READY to avoid recreating the player // on HTMLMediaElement and properly generate the video 'ended' event. if (m_isEndReached && state == GST_STATE_READY) break; - if (state <= GST_STATE_READY) { - m_resetPipeline = true; - m_mediaDuration = 0; - } else { - m_resetPipeline = false; - cacheDuration(); - } + m_resetPipeline = state <= GST_STATE_READY; bool didBuffering = m_buffering; @@ -1408,7 +1514,7 @@ void MediaPlayerPrivateGStreamer::updateStates() case GST_STATE_PLAYING: if (m_buffering) { if (m_bufferingPercentage == 100) { - LOG_MEDIA_MESSAGE("[Buffering] Complete."); + GST_DEBUG("[Buffering] Complete."); m_buffering = false; m_readyState = MediaPlayer::HaveEnoughData; m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading; @@ -1439,14 +1545,14 @@ void MediaPlayerPrivateGStreamer::updateStates() } if (didBuffering && !m_buffering && !m_paused && m_playbackRate) { - LOG_MEDIA_MESSAGE("[Buffering] Restarting playback."); + GST_DEBUG("[Buffering] Restarting playback."); changePipelineState(GST_STATE_PLAYING); } } else if (state == GST_STATE_PLAYING) { m_paused = false; if ((m_buffering && !isLiveStream()) || !m_playbackRate) { - LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering."); + GST_DEBUG("[Buffering] Pausing stream for buffering."); changePipelineState(GST_STATE_PAUSED); } } else @@ -1454,21 +1560,21 @@ void MediaPlayerPrivateGStreamer::updateStates() if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) { shouldUpdatePlaybackState = true; - LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state)); + GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state)); } break; } case GST_STATE_CHANGE_ASYNC: - LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); // Change in progress. break; case GST_STATE_CHANGE_FAILURE: - LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + GST_DEBUG("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); // Change failed return; case GST_STATE_CHANGE_NO_PREROLL: - LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); // Live pipelines go in PAUSED without prerolling. m_isStreaming = true; @@ -1488,7 +1594,7 @@ void MediaPlayerPrivateGStreamer::updateStates() m_networkState = MediaPlayer::Loading; break; default: - LOG_MEDIA_MESSAGE("Else : %d", getStateResult); + GST_DEBUG("Else : %d", getStateResult); break; } @@ -1498,22 +1604,22 @@ void MediaPlayerPrivateGStreamer::updateStates() m_player->playbackStateChanged(); if (m_networkState != oldNetworkState) { - LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState); + GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState); m_player->networkStateChanged(); } if (m_readyState != oldReadyState) { - LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState); + GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState); m_player->readyStateChanged(); } if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) { updatePlaybackRate(); if (m_seekIsPending) { - LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime); + GST_DEBUG("[Seek] committing pending seek to %f", m_seekTime); m_seekIsPending = false; m_seeking = doSeek(toGstClockTime(m_seekTime), m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE)); if (!m_seeking) - LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime); + GST_DEBUG("[Seek] seeking to %f failed", m_seekTime); } } } @@ -1544,7 +1650,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation() return false; const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); - const gchar* newLocation = 0; + const gchar* newLocation = nullptr; if (!locations) { // Fallback on new-location string. @@ -1555,7 +1661,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation() if (!newLocation) { if (m_mediaLocationCurrentIndex < 0) { - m_mediaLocations = 0; + m_mediaLocations = nullptr; return false; } @@ -1580,7 +1686,7 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation() RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url); if (securityOrigin->canRequest(newUrl)) { - INFO_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data()); + GST_INFO("New media url: %s", newUrl.string().utf8().data()); // Reset player states. m_networkState = MediaPlayer::Loading; @@ -1593,16 +1699,16 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation() changePipelineState(GST_STATE_READY); GstState state; - gst_element_get_state(m_playBin.get(), &state, 0, 0); + gst_element_get_state(m_pipeline.get(), &state, nullptr, 0); if (state <= GST_STATE_READY) { // Set the new uri and start playing. - g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL); + g_object_set(m_pipeline.get(), "uri", newUrl.string().utf8().data(), nullptr); m_url = newUrl; changePipelineState(GST_STATE_PLAYING); return true; } } else - INFO_MEDIA_MESSAGE("Not allowed to load new media location: %s", newUrl.string().utf8().data()); + GST_INFO("Not allowed to load new media location: %s", newUrl.string().utf8().data()); } m_mediaLocationCurrentIndex--; return false; @@ -1624,49 +1730,29 @@ void MediaPlayerPrivateGStreamer::didEnd() // Synchronize position and duration values to not confuse the // HTMLMediaElement. In some cases like reverse playback the // position is not always reported as 0 for instance. - float now = currentTime(); - if (now > 0 && now <= duration() && m_mediaDuration != now) { - m_mediaDurationKnown = true; - m_mediaDuration = now; + MediaTime now = currentMediaTime(); + if (now > MediaTime { } && now <= durationMediaTime()) m_player->durationChanged(); - } m_isEndReached = true; timeChanged(); - if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) { + if (!m_player->client().mediaPlayerIsLooping()) { m_paused = true; + m_durationAtEOS = durationMediaTime().toDouble(); changePipelineState(GST_STATE_READY); m_downloadFinished = false; } } -void MediaPlayerPrivateGStreamer::cacheDuration() -{ - if (m_mediaDuration || !m_mediaDurationKnown) - return; - - float newDuration = duration(); - if (std::isinf(newDuration)) { - // Only pretend that duration is not available if the the query failed in a stable pipeline state. - GstState state; - if (gst_element_get_state(m_playBin.get(), &state, 0, 0) == GST_STATE_CHANGE_SUCCESS && state > GST_STATE_READY) - m_mediaDurationKnown = false; - return; - } - - m_mediaDuration = newDuration; -} - void MediaPlayerPrivateGStreamer::durationChanged() { - float previousDuration = m_mediaDuration; + float previousDuration = durationMediaTime().toDouble(); - cacheDuration(); // Avoid emiting durationchanged in the case where the previous // duration was 0 because that case is already handled by the // HTMLMediaElement. - if (previousDuration && m_mediaDuration != previousDuration) + if (previousDuration && durationMediaTime().toDouble() != previousDuration) m_player->durationChanged(); } @@ -1683,154 +1769,187 @@ void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error) } // Loading failed, remove ready timer. - if (m_readyTimerHandler) { - g_source_remove(m_readyTimerHandler); - m_readyTimerHandler = 0; - } -} - -static HashSet<String> mimeTypeCache() -{ - initializeGStreamerAndRegisterWebKitElements(); - - DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); - static bool typeListInitialized = false; - - if (typeListInitialized) - return cache; - - const char* mimeTypes[] = { - "application/ogg", - "application/vnd.apple.mpegurl", - "application/vnd.rn-realmedia", - "application/x-3gp", - "application/x-pn-realaudio", - "audio/3gpp", - "audio/aac", - "audio/flac", - "audio/iLBC-sh", - "audio/midi", - "audio/mobile-xmf", - "audio/mp1", - "audio/mp2", - "audio/mp3", - "audio/mp4", - "audio/mpeg", - "audio/ogg", - "audio/opus", - "audio/qcelp", - "audio/riff-midi", - "audio/speex", - "audio/wav", - "audio/webm", - "audio/x-ac3", - "audio/x-aiff", - "audio/x-amr-nb-sh", - "audio/x-amr-wb-sh", - "audio/x-au", - "audio/x-ay", - "audio/x-celt", - "audio/x-dts", - "audio/x-flac", - "audio/x-gbs", - "audio/x-gsm", - "audio/x-gym", - "audio/x-imelody", - "audio/x-ircam", - "audio/x-kss", - "audio/x-m4a", - "audio/x-mod", - "audio/x-mp3", - "audio/x-mpeg", - "audio/x-musepack", - "audio/x-nist", - "audio/x-nsf", - "audio/x-paris", - "audio/x-sap", - "audio/x-sbc", - "audio/x-sds", - "audio/x-shorten", - "audio/x-sid", - "audio/x-spc", - "audio/x-speex", - "audio/x-svx", - "audio/x-ttafile", - "audio/x-vgm", - "audio/x-voc", - "audio/x-vorbis+ogg", - "audio/x-w64", - "audio/x-wav", - "audio/x-wavpack", - "audio/x-wavpack-correction", - "video/3gpp", - "video/mj2", - "video/mp4", - "video/mpeg", - "video/mpegts", - "video/ogg", - "video/quicktime", - "video/vivo", - "video/webm", - "video/x-cdxa", - "video/x-dirac", - "video/x-dv", - "video/x-fli", - "video/x-flv", - "video/x-h263", - "video/x-ivf", - "video/x-m4v", - "video/x-matroska", - "video/x-mng", - "video/x-ms-asf", - "video/x-msvideo", - "video/x-mve", - "video/x-nuv", - "video/x-vcd" - }; - - for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i) - cache.add(String(mimeTypes[i])); - - typeListInitialized = true; - return cache; -} - -void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types) -{ - types = mimeTypeCache(); + m_readyTimerHandler.stop(); +} + +static HashSet<String, ASCIICaseInsensitiveHash>& mimeTypeSet() +{ + static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> mimeTypes = []() + { + initializeGStreamerAndRegisterWebKitElements(); + HashSet<String, ASCIICaseInsensitiveHash> set; + + GList* audioDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL); + GList* videoDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL); + GList* demuxerFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, GST_RANK_MARGINAL); + + enum ElementType { + AudioDecoder = 0, + VideoDecoder, + Demuxer + }; + struct GstCapsWebKitMapping { + ElementType elementType; + const char* capsString; + Vector<AtomicString> webkitMimeTypes; + }; + + Vector<GstCapsWebKitMapping> mapping = { + {AudioDecoder, "audio/midi", {"audio/midi", "audio/riff-midi"}}, + {AudioDecoder, "audio/x-sbc", { }}, + {AudioDecoder, "audio/x-sid", { }}, + {AudioDecoder, "audio/x-flac", {"audio/x-flac", "audio/flac"}}, + {AudioDecoder, "audio/x-wav", {"audio/x-wav", "audio/wav"}}, + {AudioDecoder, "audio/x-wavpack", {"audio/x-wavpack"}}, + {AudioDecoder, "audio/x-speex", {"audio/speex", "audio/x-speex"}}, + {AudioDecoder, "audio/x-ac3", { }}, + {AudioDecoder, "audio/x-eac3", {"audio/x-ac3"}}, + {AudioDecoder, "audio/x-dts", { }}, + {VideoDecoder, "video/x-h264, profile=(string)high", {"video/mp4", "video/x-m4v"}}, + {VideoDecoder, "video/x-msvideocodec", {"video/x-msvideo"}}, + {VideoDecoder, "video/x-h263", { }}, + {VideoDecoder, "video/mpegts", { }}, + {VideoDecoder, "video/mpeg, mpegversion=(int){1,2}, systemstream=(boolean)false", {"video/mpeg"}}, + {VideoDecoder, "video/x-dirac", { }}, + {VideoDecoder, "video/x-flash-video", {"video/flv", "video/x-flv"}}, + {Demuxer, "video/quicktime", { }}, + {Demuxer, "video/quicktime, variant=(string)3gpp", {"video/3gpp"}}, + {Demuxer, "application/x-3gp", { }}, + {Demuxer, "video/x-ms-asf", { }}, + {Demuxer, "audio/x-aiff", { }}, + {Demuxer, "application/x-pn-realaudio", { }}, + {Demuxer, "application/vnd.rn-realmedia", { }}, + {Demuxer, "audio/x-wav", {"audio/x-wav", "audio/wav"}}, + {Demuxer, "application/x-hls", {"application/vnd.apple.mpegurl", "application/x-mpegurl"}} + }; + + for (auto& current : mapping) { + GList* factories = demuxerFactories; + if (current.elementType == AudioDecoder) + factories = audioDecoderFactories; + else if (current.elementType == VideoDecoder) + factories = videoDecoderFactories; + + if (gstRegistryHasElementForMediaType(factories, current.capsString)) { + if (!current.webkitMimeTypes.isEmpty()) { + for (const auto& mimeType : current.webkitMimeTypes) + set.add(mimeType); + } else + set.add(AtomicString(current.capsString)); + } + } + + bool opusSupported = false; + if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-opus")) { + opusSupported = true; + set.add(AtomicString("audio/opus")); + } + + bool vorbisSupported = false; + if (gstRegistryHasElementForMediaType(demuxerFactories, "application/ogg")) { + set.add(AtomicString("application/ogg")); + + vorbisSupported = gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-vorbis"); + if (vorbisSupported) { + set.add(AtomicString("audio/ogg")); + set.add(AtomicString("audio/x-vorbis+ogg")); + } + + if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-theora")) + set.add(AtomicString("video/ogg")); + } + + bool audioMpegSupported = false; + if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int)1, layer=(int)[1, 3]")) { + audioMpegSupported = true; + set.add(AtomicString("audio/mp1")); + set.add(AtomicString("audio/mp3")); + set.add(AtomicString("audio/x-mp3")); + } + + if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int){2, 4}")) { + audioMpegSupported = true; + set.add(AtomicString("audio/aac")); + set.add(AtomicString("audio/mp2")); + set.add(AtomicString("audio/mp4")); + set.add(AtomicString("audio/x-m4a")); + } + + if (audioMpegSupported) { + set.add(AtomicString("audio/mpeg")); + set.add(AtomicString("audio/x-mpeg")); + } + + if (gstRegistryHasElementForMediaType(demuxerFactories, "video/x-matroska")) { + set.add(AtomicString("video/x-matroska")); + + if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp8") + || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp9") + || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp10")) + set.add(AtomicString("video/webm")); + + if (vorbisSupported || opusSupported) + set.add(AtomicString("audio/webm")); + } + + gst_plugin_feature_list_free(audioDecoderFactories); + gst_plugin_feature_list_free(videoDecoderFactories); + gst_plugin_feature_list_free(demuxerFactories); + return set; + }(); + return mimeTypes; +} + +void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types) +{ + types = mimeTypeSet(); } MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const MediaEngineSupportParameters& parameters) { + MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported; +#if ENABLE(MEDIA_SOURCE) + // MediaPlayerPrivateGStreamerMSE is in charge of mediasource playback, not us. + if (parameters.isMediaSource) + return result; +#endif + + // MediaStream playback is handled by the OpenWebRTC player. + if (parameters.isMediaStream) + return result; + if (parameters.type.isNull() || parameters.type.isEmpty()) - return MediaPlayer::IsNotSupported; + return result; // spec says we should not return "probably" if the codecs string is empty - if (mimeTypeCache().contains(parameters.type)) - return parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; - return MediaPlayer::IsNotSupported; + if (mimeTypeSet().contains(parameters.type)) + result = parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; + + return extendedSupportsType(parameters, result); } void MediaPlayerPrivateGStreamer::setDownloadBuffering() { - if (!m_playBin) + if (!m_pipeline) return; - GstPlayFlags flags; - g_object_get(m_playBin.get(), "flags", &flags, NULL); + unsigned flags; + g_object_get(m_pipeline.get(), "flags", &flags, nullptr); + + unsigned flagDownload = getGstPlayFlag("download"); // We don't want to stop downloading if we already started it. - if (flags & GST_PLAY_FLAG_DOWNLOAD && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline) + if (flags & flagDownload && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline) return; bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto; if (shouldDownload) { - LOG_MEDIA_MESSAGE("Enabling on-disk buffering"); - g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL); + GST_DEBUG("Enabling on-disk buffering"); + g_object_set(m_pipeline.get(), "flags", flags | flagDownload, nullptr); m_fillTimer.startRepeating(0.2); } else { - LOG_MEDIA_MESSAGE("Disabling on-disk buffering"); - g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL); + GST_DEBUG("Disabling on-disk buffering"); + g_object_set(m_pipeline.get(), "flags", flags & ~flagDownload, nullptr); m_fillTimer.stop(); } } @@ -1851,93 +1970,172 @@ void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload) GstElement* MediaPlayerPrivateGStreamer::createAudioSink() { - m_autoAudioSink = gst_element_factory_make("autoaudiosink", 0); - g_signal_connect(m_autoAudioSink.get(), "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this); + m_autoAudioSink = gst_element_factory_make("autoaudiosink", nullptr); + if (!m_autoAudioSink) { + GST_WARNING("GStreamer's autoaudiosink not found. Please check your gst-plugins-good installation"); + return nullptr; + } - // Construct audio sink only if pitch preserving is enabled. - if (!m_preservesPitch) - return m_autoAudioSink.get(); + g_signal_connect_swapped(m_autoAudioSink.get(), "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this); + + GstElement* audioSinkBin; - GstElement* scale = gst_element_factory_make("scaletempo", 0); - if (!scale) { - GST_WARNING("Failed to create scaletempo"); + if (webkitGstCheckVersion(1, 4, 2)) { +#if ENABLE(WEB_AUDIO) + audioSinkBin = gst_bin_new("audio-sink"); + ensureAudioSourceProvider(); + m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr); + return audioSinkBin; +#else return m_autoAudioSink.get(); +#endif } - GstElement* audioSinkBin = gst_bin_new("audio-sink"); - GstElement* convert = gst_element_factory_make("audioconvert", 0); - GstElement* resample = gst_element_factory_make("audioresample", 0); + // Construct audio sink only if pitch preserving is enabled. + // If GStreamer 1.4.2 is used the audio-filter playbin property is used instead. + if (m_preservesPitch) { + GstElement* scale = gst_element_factory_make("scaletempo", nullptr); + if (!scale) { + GST_WARNING("Failed to create scaletempo"); + return m_autoAudioSink.get(); + } - gst_bin_add_many(GST_BIN(audioSinkBin), scale, convert, resample, m_autoAudioSink.get(), NULL); + audioSinkBin = gst_bin_new("audio-sink"); + gst_bin_add(GST_BIN(audioSinkBin), scale); + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink")); + gst_element_add_pad(audioSinkBin, gst_ghost_pad_new("sink", pad.get())); - if (!gst_element_link_many(scale, convert, resample, m_autoAudioSink.get(), NULL)) { - GST_WARNING("Failed to link audio sink elements"); - gst_object_unref(audioSinkBin); - return m_autoAudioSink.get(); +#if ENABLE(WEB_AUDIO) + ensureAudioSourceProvider(); + m_audioSourceProvider->configureAudioBin(audioSinkBin, scale); +#else + GstElement* convert = gst_element_factory_make("audioconvert", nullptr); + GstElement* resample = gst_element_factory_make("audioresample", nullptr); + + gst_bin_add_many(GST_BIN(audioSinkBin), convert, resample, m_autoAudioSink.get(), nullptr); + + if (!gst_element_link_many(scale, convert, resample, m_autoAudioSink.get(), nullptr)) { + GST_WARNING("Failed to link audio sink elements"); + gst_object_unref(audioSinkBin); + return m_autoAudioSink.get(); + } +#endif + return audioSinkBin; } - GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink")); - gst_element_add_pad(audioSinkBin, gst_ghost_pad_new("sink", pad.get())); +#if ENABLE(WEB_AUDIO) + audioSinkBin = gst_bin_new("audio-sink"); + ensureAudioSourceProvider(); + m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr); return audioSinkBin; +#endif + ASSERT_NOT_REACHED(); + return nullptr; } GstElement* MediaPlayerPrivateGStreamer::audioSink() const { GstElement* sink; - g_object_get(m_playBin.get(), "audio-sink", &sink, nullptr); + g_object_get(m_pipeline.get(), "audio-sink", &sink, nullptr); return sink; } +#if ENABLE(WEB_AUDIO) +void MediaPlayerPrivateGStreamer::ensureAudioSourceProvider() +{ + if (!m_audioSourceProvider) + m_audioSourceProvider = std::make_unique<AudioSourceProviderGStreamer>(); +} + +AudioSourceProvider* MediaPlayerPrivateGStreamer::audioSourceProvider() +{ + ensureAudioSourceProvider(); + return m_audioSourceProvider.get(); +} +#endif + void MediaPlayerPrivateGStreamer::createGSTPlayBin() { - ASSERT(!m_playBin); + ASSERT(!m_pipeline); // gst_element_factory_make() returns a floating reference so // we should not adopt. - m_playBin = gst_element_factory_make("playbin", "play"); - setStreamVolumeElement(GST_STREAM_VOLUME(m_playBin.get())); + setPipeline(gst_element_factory_make("playbin", "play")); + setStreamVolumeElement(GST_STREAM_VOLUME(m_pipeline.get())); + + GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get()))); + gst_bus_set_sync_handler(bus.get(), [](GstBus*, GstMessage* message, gpointer userData) { + auto& player = *static_cast<MediaPlayerPrivateGStreamer*>(userData); + + if (player.handleSyncMessage(message)) { + gst_message_unref(message); + return GST_BUS_DROP; + } - GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_playBin.get()))); + return GST_BUS_PASS; + }, this, nullptr); + + // Let also other listeners subscribe to (application) messages in this bus. gst_bus_add_signal_watch(bus.get()); - g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); + g_signal_connect(bus.get(), "message", G_CALLBACK(busMessageCallback), this); - g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL); + g_object_set(m_pipeline.get(), "mute", m_player->muted(), nullptr); - g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this); - g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this); - g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this); + g_signal_connect_swapped(m_pipeline.get(), "notify::source", G_CALLBACK(sourceChangedCallback), this); + g_signal_connect_swapped(m_pipeline.get(), "video-changed", G_CALLBACK(videoChangedCallback), this); + g_signal_connect_swapped(m_pipeline.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this); #if ENABLE(VIDEO_TRACK) - if (webkitGstCheckVersion(1, 1, 2)) { - g_signal_connect(m_playBin.get(), "text-changed", G_CALLBACK(mediaPlayerPrivateTextChangedCallback), this); + g_signal_connect_swapped(m_pipeline.get(), "text-changed", G_CALLBACK(textChangedCallback), this); - GstElement* textCombiner = webkitTextCombinerNew(); - ASSERT(textCombiner); - g_object_set(m_playBin.get(), "text-stream-combiner", textCombiner, NULL); + GstElement* textCombiner = webkitTextCombinerNew(); + ASSERT(textCombiner); + g_object_set(m_pipeline.get(), "text-stream-combiner", textCombiner, nullptr); - m_textAppSink = webkitTextSinkNew(); - ASSERT(m_textAppSink); + m_textAppSink = webkitTextSinkNew(); + ASSERT(m_textAppSink); - m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink")); - ASSERT(m_textAppSinkPad); + m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink")); + ASSERT(m_textAppSinkPad); - g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), NULL); - g_signal_connect(m_textAppSink.get(), "new-sample", G_CALLBACK(mediaPlayerPrivateNewTextSampleCallback), this); + g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), nullptr); + g_signal_connect_swapped(m_textAppSink.get(), "new-sample", G_CALLBACK(newTextSampleCallback), this); - g_object_set(m_playBin.get(), "text-sink", m_textAppSink.get(), NULL); - } + g_object_set(m_pipeline.get(), "text-sink", m_textAppSink.get(), nullptr); #endif - g_object_set(m_playBin.get(), "video-sink", createVideoSink(), "audio-sink", createAudioSink(), nullptr); + g_object_set(m_pipeline.get(), "video-sink", createVideoSink(), "audio-sink", createAudioSink(), nullptr); + + configurePlaySink(); + + // On 1.4.2 and newer we use the audio-filter property instead. + // See https://bugzilla.gnome.org/show_bug.cgi?id=735748 for + // the reason for using >= 1.4.2 instead of >= 1.4.0. + if (m_preservesPitch && webkitGstCheckVersion(1, 4, 2)) { + GstElement* scale = gst_element_factory_make("scaletempo", nullptr); - GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink")); + if (!scale) + GST_WARNING("Failed to create scaletempo"); + else + g_object_set(m_pipeline.get(), "audio-filter", scale, nullptr); + } + + if (!m_renderingCanBeAccelerated) { + // If not using accelerated compositing, let GStreamer handle + // the image-orientation tag. + GstElement* videoFlip = gst_element_factory_make("videoflip", nullptr); + g_object_set(videoFlip, "method", 8, nullptr); + g_object_set(m_pipeline.get(), "video-filter", videoFlip, nullptr); + } + + GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink")); if (videoSinkPad) - g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this); + g_signal_connect_swapped(videoSinkPad.get(), "notify::caps", G_CALLBACK(videoSinkCapsChangedCallback), this); } void MediaPlayerPrivateGStreamer::simulateAudioInterruption() { - GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_playBin.get()), GST_STATE_PAUSED); - gst_element_post_message(m_playBin.get(), message); + GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_pipeline.get()), GST_STATE_PAUSED); + gst_element_post_message(m_pipeline.get(), message); } bool MediaPlayerPrivateGStreamer::didPassCORSAccessCheck() const @@ -1947,6 +2145,25 @@ bool MediaPlayerPrivateGStreamer::didPassCORSAccessCheck() const return false; } +bool MediaPlayerPrivateGStreamer::canSaveMediaData() const +{ + if (isLiveStream()) + return false; + + if (m_url.isLocalFile()) + return true; + + if (m_url.protocolIsInHTTPFamily()) + return true; + + return false; +} + +bool MediaPlayerPrivateGStreamer::handleSyncMessage(GstMessage* message) +{ + return MediaPlayerPrivateGStreamerBase::handleSyncMessage(message); +} + } #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h index 1990bb20b..953239b58 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h @@ -2,7 +2,9 @@ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2009, 2010 Igalia S.L + * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015, 2016 Igalia S.L + * Copyright (C) 2014 Cable Television Laboratories, Inc. + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -32,185 +34,233 @@ #include <gst/gst.h> #include <gst/pbutils/install-plugins.h> #include <wtf/Forward.h> +#include <wtf/RunLoop.h> +#include <wtf/WeakPtr.h> -#if ENABLE(MEDIA_SOURCE) -#include "MediaSourceGStreamer.h" +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) +#include <wtf/text/AtomicStringHash.h> #endif typedef struct _GstBuffer GstBuffer; typedef struct _GstMessage GstMessage; typedef struct _GstElement GstElement; +typedef struct _GstMpegtsSection GstMpegtsSection; namespace WebCore { +#if ENABLE(WEB_AUDIO) +class AudioSourceProvider; +class AudioSourceProviderGStreamer; +#endif + class AudioTrackPrivateGStreamer; +class InbandMetadataTextTrackPrivateGStreamer; class InbandTextTrackPrivateGStreamer; +class MediaPlayerRequestInstallMissingPluginsCallback; class VideoTrackPrivateGStreamer; +#if ENABLE(MEDIA_SOURCE) +class MediaSourcePrivateClient; +#endif + class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateGStreamerBase { public: - ~MediaPlayerPrivateGStreamer(); + explicit MediaPlayerPrivateGStreamer(MediaPlayer*); + virtual ~MediaPlayerPrivateGStreamer(); + static void registerMediaEngine(MediaEngineRegistrar); - gboolean handleMessage(GstMessage*); + void handleMessage(GstMessage*); void handlePluginInstallerResult(GstInstallPluginsReturn); - bool hasVideo() const { return m_hasVideo; } - bool hasAudio() const { return m_hasAudio; } + bool hasVideo() const override { return m_hasVideo; } + bool hasAudio() const override { return m_hasAudio; } - void load(const String &url); + void load(const String &url) override; #if ENABLE(MEDIA_SOURCE) - void load(const String& url, PassRefPtr<HTMLMediaSource>); + void load(const String& url, MediaSourcePrivateClient*) override; +#endif +#if ENABLE(MEDIA_STREAM) + void load(MediaStreamPrivate&) override; #endif void commitLoad(); - void cancelLoad(); + void cancelLoad() override; - void prepareToPlay(); - void play(); - void pause(); + void prepareToPlay() override; + void play() override; + void pause() override; - bool paused() const; - bool seeking() const; + bool paused() const override; + bool seeking() const override; - float duration() const; - float currentTime() const; - void seek(float); + MediaTime durationMediaTime() const override; + MediaTime currentMediaTime() const override; + void seek(float) override; - void setRate(float); - void setPreservesPitch(bool); + void setRate(float) override; + double rate() const override; + void setPreservesPitch(bool) override; - void setPreload(MediaPlayer::Preload); - void fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*); + void setPreload(MediaPlayer::Preload) override; + void fillTimerFired(); - PassRefPtr<TimeRanges> buffered() const; - float maxTimeSeekable() const; - bool didLoadingProgress() const; - unsigned totalBytes() const; - float maxTimeLoaded() const; + std::unique_ptr<PlatformTimeRanges> buffered() const override; + float maxTimeSeekable() const override; + bool didLoadingProgress() const override; + unsigned long long totalBytes() const override; + float maxTimeLoaded() const override; + + bool hasSingleSecurityOrigin() const override; void loadStateChanged(); void timeChanged(); void didEnd(); - void durationChanged(); + virtual void durationChanged(); void loadingFailed(MediaPlayer::NetworkState); - void videoChanged(); - void videoCapsChanged(); - void audioChanged(); - void notifyPlayerOfVideo(); - void notifyPlayerOfVideoCaps(); - void notifyPlayerOfAudio(); + virtual void sourceChanged(); -#if ENABLE(VIDEO_TRACK) - void textChanged(); - void notifyPlayerOfText(); + GstElement* audioSink() const override; + virtual void configurePlaySink() { } - void newTextSample(); - void notifyPlayerOfNewTextSample(); -#endif + void simulateAudioInterruption() override; - void sourceChanged(); - GstElement* audioSink() const; + virtual bool changePipelineState(GstState); - void setAudioStreamProperties(GObject*); +#if ENABLE(WEB_AUDIO) + AudioSourceProvider* audioSourceProvider() override; +#endif - void simulateAudioInterruption(); + bool isLiveStream() const override { return m_isStreaming; } - bool changePipelineState(GstState); + bool handleSyncMessage(GstMessage*) override; private: - MediaPlayerPrivateGStreamer(MediaPlayer*); - - static PassOwnPtr<MediaPlayerPrivateInterface> create(MediaPlayer*); - - static void getSupportedTypes(HashSet<String>&); + static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&); static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&); static bool isAvailable(); - GstElement* createAudioSink(); + WeakPtr<MediaPlayerPrivateGStreamer> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } - float playbackPosition() const; + GstElement* createAudioSink() override; - void cacheDuration(); - void updateStates(); - void asyncStateChangeDone(); + double playbackPosition() const; + + virtual void updateStates(); + virtual void asyncStateChangeDone(); void createGSTPlayBin(); bool loadNextLocation(); void mediaLocationChanged(GstMessage*); - void setDownloadBuffering(); + virtual void setDownloadBuffering(); void processBufferingStats(GstMessage*); +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) + void processMpegTsSection(GstMpegtsSection*); +#endif #if ENABLE(VIDEO_TRACK) void processTableOfContents(GstMessage*); - void processTableOfContentsEntry(GstTocEntry*, GstTocEntry* parent); + void processTableOfContentsEntry(GstTocEntry*); #endif - bool doSeek(gint64 position, float rate, GstSeekFlags seekType); - void updatePlaybackRate(); + virtual bool doSeek(gint64 position, float rate, GstSeekFlags seekType); + virtual void updatePlaybackRate(); + String engineDescription() const override { return "GStreamer"; } + bool didPassCORSAccessCheck() const override; + bool canSaveMediaData() const override; - virtual String engineDescription() const { return "GStreamer"; } - virtual bool isLiveStream() const { return m_isStreaming; } - virtual bool didPassCORSAccessCheck() const; + void purgeOldDownloadFiles(const char*); + static void uriDecodeBinElementAddedCallback(GstBin*, GstElement*, MediaPlayerPrivateGStreamer*); + static void downloadBufferFileCreatedCallback(MediaPlayerPrivateGStreamer*); -private: - GRefPtr<GstElement> m_playBin; +protected: + void cacheDuration(); + + bool m_buffering; + int m_bufferingPercentage; + mutable float m_cachedPosition; + bool m_canFallBackToLastFinishedSeekPosition; + bool m_changingRate; + bool m_downloadFinished; + bool m_errorOccured; + mutable bool m_isEndReached; + mutable bool m_isStreaming; + mutable gdouble m_durationAtEOS; + bool m_paused; + float m_playbackRate; + GstState m_requestedState; + bool m_resetPipeline; + bool m_seeking; + bool m_seekIsPending; + float m_seekTime; GRefPtr<GstElement> m_source; + bool m_volumeAndMuteInitialized; + + void readyTimerFired(); + + void notifyPlayerOfVideo(); + void notifyPlayerOfVideoCaps(); + void notifyPlayerOfAudio(); + +#if ENABLE(VIDEO_TRACK) + void notifyPlayerOfText(); + void newTextSample(); +#endif + + void ensureAudioSourceProvider(); + void setAudioStreamProperties(GObject*); + + static void setAudioStreamPropertiesCallback(MediaPlayerPrivateGStreamer*, GObject*); + + static void sourceChangedCallback(MediaPlayerPrivateGStreamer*); + static void videoChangedCallback(MediaPlayerPrivateGStreamer*); + static void videoSinkCapsChangedCallback(MediaPlayerPrivateGStreamer*); + static void audioChangedCallback(MediaPlayerPrivateGStreamer*); +#if ENABLE(VIDEO_TRACK) + static void textChangedCallback(MediaPlayerPrivateGStreamer*); + static GstFlowReturn newTextSampleCallback(MediaPlayerPrivateGStreamer*); +#endif + +private: + WeakPtrFactory<MediaPlayerPrivateGStreamer> m_weakPtrFactory; + #if ENABLE(VIDEO_TRACK) GRefPtr<GstElement> m_textAppSink; GRefPtr<GstPad> m_textAppSinkPad; #endif - float m_seekTime; - bool m_changingRate; - float m_endTime; - bool m_isEndReached; - mutable bool m_isStreaming; GstStructure* m_mediaLocations; int m_mediaLocationCurrentIndex; - bool m_resetPipeline; - bool m_paused; bool m_playbackRatePause; - bool m_seeking; - bool m_seekIsPending; float m_timeOfOverlappingSeek; - bool m_canFallBackToLastFinishedSeekPositon; - bool m_buffering; - float m_playbackRate; float m_lastPlaybackRate; - bool m_errorOccured; - mutable gfloat m_mediaDuration; - bool m_downloadFinished; - Timer<MediaPlayerPrivateGStreamer> m_fillTimer; + Timer m_fillTimer; float m_maxTimeLoaded; - int m_bufferingPercentage; MediaPlayer::Preload m_preload; bool m_delayingLoad; - bool m_mediaDurationKnown; mutable float m_maxTimeLoadedAtLastDidLoadingProgress; - bool m_volumeAndMuteInitialized; bool m_hasVideo; bool m_hasAudio; - guint m_audioTimerHandler; - guint m_textTimerHandler; - guint m_videoTimerHandler; - guint m_videoCapsTimerHandler; - guint m_readyTimerHandler; - mutable long m_totalBytes; + RunLoop::Timer<MediaPlayerPrivateGStreamer> m_readyTimerHandler; + mutable unsigned long long m_totalBytes; URL m_url; bool m_preservesPitch; - GstState m_requestedState; +#if ENABLE(WEB_AUDIO) + std::unique_ptr<AudioSourceProviderGStreamer> m_audioSourceProvider; +#endif GRefPtr<GstElement> m_autoAudioSink; - bool m_missingPlugins; + GRefPtr<GstElement> m_downloadBuffer; + RefPtr<MediaPlayerRequestInstallMissingPluginsCallback> m_missingPluginsCallback; #if ENABLE(VIDEO_TRACK) Vector<RefPtr<AudioTrackPrivateGStreamer>> m_audioTracks; Vector<RefPtr<InbandTextTrackPrivateGStreamer>> m_textTracks; Vector<RefPtr<VideoTrackPrivateGStreamer>> m_videoTracks; - RefPtr<InbandTextTrackPrivate> m_chaptersTrack; + RefPtr<InbandMetadataTextTrackPrivateGStreamer> m_chaptersTrack; #endif -#if ENABLE(MEDIA_SOURCE) - RefPtr<HTMLMediaSource> m_mediaSource; +#if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS) + HashMap<AtomicString, RefPtr<InbandMetadataTextTrackPrivateGStreamer>> m_metadataTracks; #endif + virtual bool isMediaSource() const { return false; } }; } diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp index c6564730a..9332aab49 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp @@ -3,7 +3,8 @@ * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> - * Copyright (C) 2009, 2010 Igalia S.L + * Copyright (C) 2009, 2010, 2015, 2016 Igalia S.L + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +27,6 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) -#include "ColorSpace.h" #include "GStreamerUtilities.h" #include "GraphicsContext.h" #include "GraphicsTypes.h" @@ -37,16 +37,71 @@ #include "NotImplemented.h" #include "VideoSinkGStreamer.h" #include "WebKitWebSourceGStreamer.h" -#include <gst/gst.h> -#include <wtf/gobject/GMutexLocker.h> +#include <wtf/glib/GMutexLocker.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/AtomicString.h> #include <wtf/text/CString.h> +#include <wtf/MathExtras.h> #include <gst/audio/streamvolume.h> #include <gst/video/gstvideometa.h> -#if GST_CHECK_VERSION(1, 1, 0) && USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) +#if USE(GSTREAMER_GL) +#include <gst/app/gstappsink.h> +#define GST_USE_UNSTABLE_API +#include <gst/gl/gl.h> +#undef GST_USE_UNSTABLE_API + +#include "GLContext.h" +#if USE(GLX) +#include "GLContextGLX.h" +#include <gst/gl/x11/gstgldisplay_x11.h> +#endif + +#if USE(EGL) +#include "GLContextEGL.h" +#include <gst/gl/egl/gstgldisplay_egl.h> +#endif + +#if PLATFORM(X11) +#include "PlatformDisplayX11.h" +#endif + +#if PLATFORM(WAYLAND) +#include "PlatformDisplayWayland.h" +#endif + +// gstglapi.h may include eglplatform.h and it includes X.h, which +// defines None, breaking MediaPlayer::None enum +#if PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL +#undef None +#endif // PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL +#include "VideoTextureCopierGStreamer.h" +#endif // USE(GSTREAMER_GL) + +#if USE(TEXTURE_MAPPER_GL) +#include "BitmapTextureGL.h" +#include "BitmapTexturePool.h" #include "TextureMapperGL.h" #endif +#if USE(COORDINATED_GRAPHICS_THREADED) +#include "TextureMapperPlatformLayerBuffer.h" +#endif + +#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS) +#include <cairo-gl.h> +#endif + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) +#include "SharedBuffer.h" +#include "WebKitClearKeyDecryptorGStreamer.h" +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +#include "UUID.h" +#include <runtime/JSCInlines.h> +#include <runtime/TypedArrayInlines.h> +#include <runtime/Uint8Array.h> +#endif +#endif GST_DEBUG_CATEGORY(webkit_media_player_debug); #define GST_CAT_DEFAULT webkit_media_player_debug @@ -55,6 +110,22 @@ using namespace std; namespace WebCore { +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +static AtomicString keySystemIdToUuid(const AtomicString&); +#endif + +void registerWebKitGStreamerElements() +{ +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) + if (!webkitGstCheckVersion(1, 6, 1)) + return; + + GRefPtr<GstElementFactory> clearKeyDecryptorFactory = gst_element_factory_find("webkitclearkey"); + if (!clearKeyDecryptorFactory) + gst_element_register(nullptr, "webkitclearkey", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_CK_DECRYPT); +#endif +} + static int greatestCommonDivisor(int a, int b) { while (b) { @@ -66,112 +137,345 @@ static int greatestCommonDivisor(int a, int b) return ABS(a); } -static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player) +#if USE(TEXTURE_MAPPER_GL) +static inline TextureMapperGL::Flags texMapFlagFromOrientation(const ImageOrientation& orientation) { - // This is called when m_volumeElement receives the notify::volume signal. - LOG_MEDIA_MESSAGE("Volume changed to: %f", player->volume()); - player->volumeChanged(); + switch (orientation) { + case DefaultImageOrientation: + return 0; + case OriginRightTop: + return TextureMapperGL::ShouldRotateTexture90; + case OriginBottomRight: + return TextureMapperGL::ShouldRotateTexture180; + case OriginLeftBottom: + return TextureMapperGL::ShouldRotateTexture270; + default: + ASSERT_NOT_REACHED(); + } + + return 0; } +#endif + +#if USE(COORDINATED_GRAPHICS_THREADED) && USE(GSTREAMER_GL) +class GstVideoFrameHolder : public TextureMapperPlatformLayerBuffer::UnmanagedBufferDataHolder { +public: + explicit GstVideoFrameHolder(GstSample* sample, TextureMapperGL::Flags flags) + { + GstVideoInfo videoInfo; + if (UNLIKELY(!getSampleVideoInfo(sample, videoInfo))) + return; + + m_size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); + m_flags = flags | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0); + + GstBuffer* buffer = gst_sample_get_buffer(sample); + if (UNLIKELY(!gst_video_frame_map(&m_videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL)))) + return; -static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player) + m_textureID = *reinterpret_cast<GLuint*>(m_videoFrame.data[0]); + m_isValid = true; + } + + virtual ~GstVideoFrameHolder() + { + if (UNLIKELY(!m_isValid)) + return; + + gst_video_frame_unmap(&m_videoFrame); + } + + const IntSize& size() const { return m_size; } + TextureMapperGL::Flags flags() const { return m_flags; } + GLuint textureID() const { return m_textureID; } + bool isValid() const { return m_isValid; } + +private: + GstVideoFrame m_videoFrame; + IntSize m_size; + TextureMapperGL::Flags m_flags; + GLuint m_textureID; + bool m_isValid { false }; +}; +#endif // USE(COORDINATED_GRAPHICS_THREADED) && USE(GSTREAMER_GL) + +MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* player) + : m_notifier(MainThreadNotifier<MainThreadNotification>::create()) + , m_player(player) + , m_fpsSink(nullptr) + , m_readyState(MediaPlayer::HaveNothing) + , m_networkState(MediaPlayer::Empty) +#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED) + , m_drawTimer(RunLoop::main(), this, &MediaPlayerPrivateGStreamerBase::repaint) +#endif + , m_usingFallbackVideoSink(false) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + , m_cdmSession(nullptr) +#endif { - // This is the callback of the timeout source created in ::volumeChanged. - player->notifyPlayerOfVolumeChange(); - return FALSE; + g_mutex_init(&m_sampleMutex); +#if USE(COORDINATED_GRAPHICS_THREADED) + m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy()); +#endif } -static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player) +MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase() { - // This is called when m_volumeElement receives the notify::mute signal. - player->muteChanged(); +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + m_protectionCondition.notifyOne(); +#endif + + m_notifier->invalidate(); + + cancelRepaint(); + + if (m_videoSink) { + g_signal_handlers_disconnect_matched(m_videoSink.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); +#if USE(GSTREAMER_GL) + if (GST_IS_BIN(m_videoSink.get())) { + GRefPtr<GstElement> appsink = adoptGRef(gst_bin_get_by_name(GST_BIN_CAST(m_videoSink.get()), "webkit-gl-video-sink")); + g_signal_handlers_disconnect_by_data(appsink.get(), this); + } +#endif + } + + g_mutex_clear(&m_sampleMutex); + + m_player = nullptr; + + if (m_volumeElement) + g_signal_handlers_disconnect_matched(m_volumeElement.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + +#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) + if (client()) + client()->platformLayerWillBeDestroyed(); +#endif + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + m_cdmSession = nullptr; +#endif + + if (m_pipeline) + gst_element_set_state(m_pipeline.get(), GST_STATE_NULL); } -static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player) +void MediaPlayerPrivateGStreamerBase::setPipeline(GstElement* pipeline) { - // This is the callback of the timeout source created in ::muteChanged. - player->notifyPlayerOfMute(); - return FALSE; + m_pipeline = pipeline; } -static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamerBase* playerPrivate) +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +static std::pair<Vector<GRefPtr<GstEvent>>, Vector<String>> extractEventsAndSystemsFromMessage(GstMessage* message) { - playerPrivate->triggerRepaint(buffer); + const GstStructure* structure = gst_message_get_structure(message); + + const GValue* streamEncryptionAllowedSystemsValue = gst_structure_get_value(structure, "stream-encryption-systems"); + ASSERT(streamEncryptionAllowedSystemsValue && G_VALUE_HOLDS(streamEncryptionAllowedSystemsValue, G_TYPE_STRV)); + const char** streamEncryptionAllowedSystems = reinterpret_cast<const char**>(g_value_get_boxed(streamEncryptionAllowedSystemsValue)); + ASSERT(streamEncryptionAllowedSystems); + Vector<String> streamEncryptionAllowedSystemsVector; + unsigned i; + for (i = 0; streamEncryptionAllowedSystems[i]; ++i) + streamEncryptionAllowedSystemsVector.append(streamEncryptionAllowedSystems[i]); + + const GValue* streamEncryptionEventsList = gst_structure_get_value(structure, "stream-encryption-events"); + ASSERT(streamEncryptionEventsList && GST_VALUE_HOLDS_LIST(streamEncryptionEventsList)); + unsigned streamEncryptionEventsListSize = gst_value_list_get_size(streamEncryptionEventsList); + Vector<GRefPtr<GstEvent>> streamEncryptionEventsVector; + for (i = 0; i < streamEncryptionEventsListSize; ++i) + streamEncryptionEventsVector.append(GRefPtr<GstEvent>(static_cast<GstEvent*>(g_value_get_boxed(gst_value_list_get_value(streamEncryptionEventsList, i))))); + + return std::make_pair(streamEncryptionEventsVector, streamEncryptionAllowedSystemsVector); } - -MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* player) - : m_player(player) - , m_fpsSink(0) - , m_readyState(MediaPlayer::HaveNothing) - , m_networkState(MediaPlayer::Empty) - , m_buffer(0) - , m_volumeTimerHandler(0) - , m_muteTimerHandler(0) - , m_repaintHandler(0) - , m_volumeSignalHandler(0) - , m_muteSignalHandler(0) -{ -#if GLIB_CHECK_VERSION(2, 31, 0) - m_bufferMutex = new GMutex; - g_mutex_init(m_bufferMutex); -#else - m_bufferMutex = g_mutex_new(); #endif + +bool MediaPlayerPrivateGStreamerBase::handleSyncMessage(GstMessage* message) +{ + UNUSED_PARAM(message); + if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_NEED_CONTEXT) + return false; + + const gchar* contextType; + gst_message_parse_context_type(message, &contextType); + +#if USE(GSTREAMER_GL) + GRefPtr<GstContext> elementContext = adoptGRef(requestGLContext(contextType, this)); + if (elementContext) { + gst_element_set_context(GST_ELEMENT(message->src), elementContext.get()); + return true; + } +#endif // USE(GSTREAMER_GL) + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id")) { + if (isMainThread()) { + GST_ERROR("can't handle drm-preferred-decryption-system-id need context message in the main thread"); + ASSERT_NOT_REACHED(); + return false; + } + GST_DEBUG("handling drm-preferred-decryption-system-id need context message"); + std::pair<Vector<GRefPtr<GstEvent>>, Vector<String>> streamEncryptionInformation = extractEventsAndSystemsFromMessage(message); + GST_TRACE("found %" G_GSIZE_FORMAT " protection events", streamEncryptionInformation.first.size()); + Vector<uint8_t> concatenatedInitDataChunks; + unsigned concatenatedInitDataChunksNumber = 0; + String eventKeySystemIdString; + for (auto& event : streamEncryptionInformation.first) { + GST_TRACE("handling protection event %u", GST_EVENT_SEQNUM(event.get())); + const char* eventKeySystemId = nullptr; + GstBuffer* data = nullptr; + gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr); + + // Here we receive the DRM init data from the pipeline: we will emit + // the needkey event with that data and the browser might create a + // CDMSession from this event handler. If such a session was created + // We will emit the message event from the session to provide the + // DRM challenge to the browser and wait for an update. If on the + // contrary no session was created we won't wait and let the pipeline + // error out by itself. + GstMapInfo mapInfo; + if (!gst_buffer_map(data, &mapInfo, GST_MAP_READ)) { + GST_WARNING("cannot map %s protection data", eventKeySystemId); + break; + } + + GST_TRACE("appending init data for %s of size %" G_GSIZE_FORMAT, eventKeySystemId, mapInfo.size); + GST_MEMDUMP("init data", reinterpret_cast<const unsigned char *>(mapInfo.data), mapInfo.size); + concatenatedInitDataChunks.append(mapInfo.data, mapInfo.size); + ++concatenatedInitDataChunksNumber; + eventKeySystemIdString = eventKeySystemId; + if (streamEncryptionInformation.second.contains(eventKeySystemId)) { + GST_TRACE("considering init data handled for %s", eventKeySystemId); + m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get())); + } + gst_buffer_unmap(data, &mapInfo); + } + + if (!concatenatedInitDataChunksNumber) + return false; + + if (concatenatedInitDataChunksNumber > 1) + eventKeySystemIdString = emptyString(); + + RunLoop::main().dispatch([this, eventKeySystemIdString, initData = WTFMove(concatenatedInitDataChunks)] { + GST_DEBUG("scheduling keyNeeded event for %s with concatenated init datas size of %" G_GSIZE_FORMAT, eventKeySystemIdString.utf8().data(), initData.size()); + GST_MEMDUMP("init datas", initData.data(), initData.size()); + + // FIXME: Provide a somehow valid sessionId. + RefPtr<Uint8Array> initDataArray = Uint8Array::create(initData.data(), initData.size()); + needKey(initDataArray); + }); + + GST_INFO("waiting for a key request to arrive"); + LockHolder lock(m_protectionMutex); + m_protectionCondition.waitFor(m_protectionMutex, Seconds(4), [this] { + return !this->m_lastGenerateKeyRequestKeySystemUuid.isEmpty(); + }); + if (!m_lastGenerateKeyRequestKeySystemUuid.isEmpty()) { + GST_INFO("got a key request, continuing with %s on %s", m_lastGenerateKeyRequestKeySystemUuid.utf8().data(), GST_MESSAGE_SRC_NAME(message)); + + GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-preferred-decryption-system-id", FALSE)); + GstStructure* contextStructure = gst_context_writable_structure(context.get()); + gst_structure_set(contextStructure, "decryption-system-id", G_TYPE_STRING, m_lastGenerateKeyRequestKeySystemUuid.utf8().data(), nullptr); + gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context.get()); + } else + GST_WARNING("did not get a proper key request"); + + return true; + } +#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) + + return false; } -MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase() +#if USE(GSTREAMER_GL) +GstContext* MediaPlayerPrivateGStreamerBase::requestGLContext(const gchar* contextType, MediaPlayerPrivateGStreamerBase* player) { - if (m_repaintHandler) { - g_signal_handler_disconnect(m_webkitVideoSink.get(), m_repaintHandler); - m_repaintHandler = 0; + if (!player->ensureGstGLContext()) + return nullptr; + + if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) { + GstContext* displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); + gst_context_set_gl_display(displayContext, player->gstGLDisplay()); + return displayContext; } -#if GLIB_CHECK_VERSION(2, 31, 0) - g_mutex_clear(m_bufferMutex); - delete m_bufferMutex; + if (!g_strcmp0(contextType, "gst.gl.app_context")) { + GstContext* appContext = gst_context_new("gst.gl.app_context", TRUE); + GstStructure* structure = gst_context_writable_structure(appContext); +#if GST_CHECK_VERSION(1, 11, 0) + gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, player->gstGLContext(), nullptr); #else - g_mutex_free(m_bufferMutex); + gst_structure_set(structure, "context", GST_GL_TYPE_CONTEXT, player->gstGLContext(), nullptr); #endif + return appContext; + } - if (m_buffer) - gst_buffer_unref(m_buffer); - m_buffer = 0; - - m_player = 0; + return nullptr; +} - if (m_muteTimerHandler) - g_source_remove(m_muteTimerHandler); +bool MediaPlayerPrivateGStreamerBase::ensureGstGLContext() +{ + if (m_glContext) + return true; + + auto& sharedDisplay = PlatformDisplay::sharedDisplayForCompositing(); + if (!m_glDisplay) { +#if PLATFORM(X11) +#if USE(GLX) + if (is<PlatformDisplayX11>(sharedDisplay)) + m_glDisplay = GST_GL_DISPLAY(gst_gl_display_x11_new_with_display(downcast<PlatformDisplayX11>(sharedDisplay).native())); +#elif USE(EGL) + if (is<PlatformDisplayX11>(sharedDisplay)) + m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayX11>(sharedDisplay).eglDisplay())); +#endif +#endif - if (m_volumeTimerHandler) - g_source_remove(m_volumeTimerHandler); +#if PLATFORM(WAYLAND) + if (is<PlatformDisplayWayland>(sharedDisplay)) + m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayWayland>(sharedDisplay).eglDisplay())); +#endif - if (m_volumeSignalHandler) { - g_signal_handler_disconnect(m_volumeElement.get(), m_volumeSignalHandler); - m_volumeSignalHandler = 0; + ASSERT(m_glDisplay); } - if (m_muteSignalHandler) { - g_signal_handler_disconnect(m_volumeElement.get(), m_muteSignalHandler); - m_muteSignalHandler = 0; - } + GLContext* webkitContext = sharedDisplay.sharingGLContext(); + // EGL and GLX are mutually exclusive, no need for ifdefs here. + GstGLPlatform glPlatform = webkitContext->isEGLContext() ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_GLX; -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) - if (client()) - client()->platformLayerWillBeDestroyed(); +#if USE(OPENGL_ES_2) + GstGLAPI glAPI = GST_GL_API_GLES2; +#elif USE(OPENGL) + GstGLAPI glAPI = GST_GL_API_OPENGL; +#else + ASSERT_NOT_REACHED(); #endif + + PlatformGraphicsContext3D contextHandle = webkitContext->platformContext(); + if (!contextHandle) + return false; + + m_glContext = gst_gl_context_new_wrapped(m_glDisplay.get(), reinterpret_cast<guintptr>(contextHandle), glPlatform, glAPI); + + return true; } +#endif // USE(GSTREAMER_GL) // Returns the size of the video -IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const +FloatSize MediaPlayerPrivateGStreamerBase::naturalSize() const { if (!hasVideo()) - return IntSize(); + return FloatSize(); if (!m_videoSize.isEmpty()) return m_videoSize; - GRefPtr<GstCaps> caps = currentVideoSinkCaps(); + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + if (!GST_IS_SAMPLE(m_sample.get())) + return FloatSize(); + + GstCaps* caps = gst_sample_get_caps(m_sample.get()); if (!caps) - return IntSize(); + return FloatSize(); // TODO: handle possible clean aperture data. See @@ -184,11 +488,19 @@ IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride; IntSize originalSize; GstVideoFormat format; - if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride)) - return IntSize(); + if (!getVideoSizeAndFormatFromCaps(caps, originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride)) + return FloatSize(); + +#if USE(TEXTURE_MAPPER_GL) + // When using accelerated compositing, if the video is tagged as rotated 90 or 270 degrees, swap width and height. + if (m_renderingCanBeAccelerated) { + if (m_videoSourceOrientation.usesWidthAsHeight()) + originalSize = originalSize.transposedSize(); + } +#endif - LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height()); - LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator); + GST_DEBUG("Original video size: %dx%d", originalSize.width(), originalSize.height()); + GST_DEBUG("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator); // Calculate DAR based on PAR and video size. int displayWidth = originalSize.width() * pixelAspectRatioNumerator; @@ -202,21 +514,21 @@ IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function. guint64 width = 0, height = 0; if (!(originalSize.height() % displayHeight)) { - LOG_MEDIA_MESSAGE("Keeping video original height"); + GST_DEBUG("Keeping video original height"); width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight); height = static_cast<guint64>(originalSize.height()); } else if (!(originalSize.width() % displayWidth)) { - LOG_MEDIA_MESSAGE("Keeping video original width"); + GST_DEBUG("Keeping video original width"); height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth); width = static_cast<guint64>(originalSize.width()); } else { - LOG_MEDIA_MESSAGE("Approximating while keeping original video height"); + GST_DEBUG("Approximating while keeping original video height"); width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight); height = static_cast<guint64>(originalSize.height()); } - LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height); - m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height)); + GST_DEBUG("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height); + m_videoSize = FloatSize(static_cast<int>(width), static_cast<int>(height)); return m_videoSize; } @@ -225,7 +537,7 @@ void MediaPlayerPrivateGStreamerBase::setVolume(float volume) if (!m_volumeElement) return; - LOG_MEDIA_MESSAGE("Setting volume: %f", volume); + GST_DEBUG("Setting volume: %f", volume); gst_stream_volume_set_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC, static_cast<double>(volume)); } @@ -240,8 +552,6 @@ float MediaPlayerPrivateGStreamerBase::volume() const void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange() { - m_volumeTimerHandler = 0; - if (!m_player || !m_volumeElement) return; double volume; @@ -253,11 +563,12 @@ void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange() m_player->volumeChanged(static_cast<float>(volume)); } -void MediaPlayerPrivateGStreamerBase::volumeChanged() +void MediaPlayerPrivateGStreamerBase::volumeChangedCallback(MediaPlayerPrivateGStreamerBase* player) { - if (m_volumeTimerHandler) - g_source_remove(m_volumeTimerHandler); - m_volumeTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this, 0); + // This is called when m_volumeElement receives the notify::volume signal. + GST_DEBUG("Volume changed to: %f", player->volume()); + + player->m_notifier->notify(MainThreadNotification::VolumeChanged, [player] { player->notifyPlayerOfVolumeChange(); }); } MediaPlayer::NetworkState MediaPlayerPrivateGStreamerBase::networkState() const @@ -280,7 +591,7 @@ void MediaPlayerPrivateGStreamerBase::setMuted(bool muted) if (!m_volumeElement) return; - g_object_set(m_volumeElement.get(), "mute", muted, NULL); + g_object_set(m_volumeElement.get(), "mute", muted, nullptr); } bool MediaPlayerPrivateGStreamerBase::muted() const @@ -289,145 +600,456 @@ bool MediaPlayerPrivateGStreamerBase::muted() const return false; bool muted; - g_object_get(m_volumeElement.get(), "mute", &muted, NULL); + g_object_get(m_volumeElement.get(), "mute", &muted, nullptr); return muted; } void MediaPlayerPrivateGStreamerBase::notifyPlayerOfMute() { - m_muteTimerHandler = 0; - if (!m_player || !m_volumeElement) return; gboolean muted; - g_object_get(m_volumeElement.get(), "mute", &muted, NULL); + g_object_get(m_volumeElement.get(), "mute", &muted, nullptr); m_player->muteChanged(static_cast<bool>(muted)); } -void MediaPlayerPrivateGStreamerBase::muteChanged() +void MediaPlayerPrivateGStreamerBase::muteChangedCallback(MediaPlayerPrivateGStreamerBase* player) { - if (m_muteTimerHandler) - g_source_remove(m_muteTimerHandler); - m_muteTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this, 0); + // This is called when m_volumeElement receives the notify::mute signal. + player->m_notifier->notify(MainThreadNotification::MuteChanged, [player] { player->notifyPlayerOfMute(); }); } - -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) -PassRefPtr<BitmapTexture> MediaPlayerPrivateGStreamerBase::updateTexture(TextureMapper* textureMapper) +void MediaPlayerPrivateGStreamerBase::acceleratedRenderingStateChanged() { - WTF::GMutexLocker lock(m_bufferMutex); - if (!m_buffer) - return nullptr; - - GRefPtr<GstCaps> caps = currentVideoSinkCaps(); - if (!caps) - return nullptr; - - GstVideoInfo videoInfo; - gst_video_info_init(&videoInfo); - if (!gst_video_info_from_caps(&videoInfo, caps.get())) - return nullptr; + m_renderingCanBeAccelerated = m_player && m_player->client().mediaPlayerAcceleratedCompositingEnabled() && m_player->client().mediaPlayerRenderingCanBeAccelerated(m_player); +} - IntSize size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); - RefPtr<BitmapTexture> texture = textureMapper->acquireTextureFromPool(size, GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag); +#if USE(TEXTURE_MAPPER_GL) +void MediaPlayerPrivateGStreamerBase::updateTexture(BitmapTextureGL& texture, GstVideoInfo& videoInfo) +{ + GstBuffer* buffer = gst_sample_get_buffer(m_sample.get()); -#if GST_CHECK_VERSION(1, 1, 0) GstVideoGLTextureUploadMeta* meta; - if ((meta = gst_buffer_get_video_gl_texture_upload_meta(m_buffer))) { + if ((meta = gst_buffer_get_video_gl_texture_upload_meta(buffer))) { if (meta->n_textures == 1) { // BRGx & BGRA formats use only one texture. - const BitmapTextureGL* textureGL = static_cast<const BitmapTextureGL*>(texture.get()); - guint ids[4] = { textureGL->id(), 0, 0, 0 }; + guint ids[4] = { texture.id(), 0, 0, 0 }; if (gst_video_gl_texture_upload_meta_upload(meta, ids)) - return texture; + return; } } -#endif // Right now the TextureMapper only supports chromas with one plane ASSERT(GST_VIDEO_INFO_N_PLANES(&videoInfo) == 1); GstVideoFrame videoFrame; - if (!gst_video_frame_map(&videoFrame, &videoInfo, m_buffer, GST_MAP_READ)) - return nullptr; + if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, GST_MAP_READ)) + return; int stride = GST_VIDEO_FRAME_PLANE_STRIDE(&videoFrame, 0); const void* srcData = GST_VIDEO_FRAME_PLANE_DATA(&videoFrame, 0); - texture->updateContents(srcData, WebCore::IntRect(WebCore::IntPoint(0, 0), size), WebCore::IntPoint(0, 0), stride, BitmapTexture::UpdateCannotModifyOriginalImageData); + texture.updateContents(srcData, WebCore::IntRect(0, 0, GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)), WebCore::IntPoint(0, 0), stride, BitmapTexture::UpdateCannotModifyOriginalImageData); gst_video_frame_unmap(&videoFrame); - - return texture; } #endif -void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstBuffer* buffer) +#if USE(COORDINATED_GRAPHICS_THREADED) +void MediaPlayerPrivateGStreamerBase::pushTextureToCompositor() { - g_return_if_fail(GST_IS_BUFFER(buffer)); +#if !USE(GSTREAMER_GL) + class ConditionNotifier { + public: + ConditionNotifier(Lock& lock, Condition& condition) + : m_locker(lock), m_condition(condition) + { + } + ~ConditionNotifier() + { + m_condition.notifyOne(); + } + private: + LockHolder m_locker; + Condition& m_condition; + }; + ConditionNotifier notifier(m_drawMutex, m_drawCondition); +#endif - { - WTF::GMutexLocker lock(m_bufferMutex); - gst_buffer_replace(&m_buffer, buffer); + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + if (!GST_IS_SAMPLE(m_sample.get())) + return; + + LockHolder holder(m_platformLayerProxy->lock()); + + if (!m_platformLayerProxy->isActive()) { + // Consume the buffer (so it gets eventually unreffed) but keep the rest of the info. + const GstStructure* info = gst_sample_get_info(m_sample.get()); + GstStructure* infoCopy = nullptr; + if (info) + infoCopy = gst_structure_copy(info); + m_sample = adoptGRef(gst_sample_new(nullptr, gst_sample_get_caps(m_sample.get()), + gst_sample_get_segment(m_sample.get()), infoCopy)); + return; } -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) - if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player) && client()) { +#if USE(GSTREAMER_GL) + std::unique_ptr<GstVideoFrameHolder> frameHolder = std::make_unique<GstVideoFrameHolder>(m_sample.get(), texMapFlagFromOrientation(m_videoSourceOrientation)); + if (UNLIKELY(!frameHolder->isValid())) + return; + + std::unique_ptr<TextureMapperPlatformLayerBuffer> layerBuffer = std::make_unique<TextureMapperPlatformLayerBuffer>(frameHolder->textureID(), frameHolder->size(), frameHolder->flags()); + layerBuffer->setUnmanagedBufferDataHolder(WTFMove(frameHolder)); + m_platformLayerProxy->pushNextBuffer(WTFMove(layerBuffer)); +#else + GstVideoInfo videoInfo; + if (UNLIKELY(!getSampleVideoInfo(m_sample.get(), videoInfo))) + return; + + IntSize size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); + std::unique_ptr<TextureMapperPlatformLayerBuffer> buffer = m_platformLayerProxy->getAvailableBuffer(size, GraphicsContext3D::DONT_CARE); + if (UNLIKELY(!buffer)) { + if (UNLIKELY(!m_context3D)) + m_context3D = GraphicsContext3D::create(GraphicsContext3DAttributes(), nullptr, GraphicsContext3D::RenderToCurrentGLContext); + + auto texture = BitmapTextureGL::create(*m_context3D); + texture->reset(size, GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag); + buffer = std::make_unique<TextureMapperPlatformLayerBuffer>(WTFMove(texture)); + } + updateTexture(buffer->textureGL(), videoInfo); + buffer->setExtraFlags(texMapFlagFromOrientation(m_videoSourceOrientation) | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0)); + m_platformLayerProxy->pushNextBuffer(WTFMove(buffer)); +#endif +} +#endif + +void MediaPlayerPrivateGStreamerBase::repaint() +{ + ASSERT(m_sample); + ASSERT(isMainThread()); + +#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) + if (m_renderingCanBeAccelerated && client()) { client()->setPlatformLayerNeedsDisplay(); +#if USE(GSTREAMER_GL) + LockHolder lock(m_drawMutex); + m_drawCondition.notifyOne(); +#endif return; } #endif m_player->repaint(); + +#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED) + LockHolder lock(m_drawMutex); + m_drawCondition.notifyOne(); +#endif } -void MediaPlayerPrivateGStreamerBase::setSize(const IntSize& size) +void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstSample* sample) { - m_size = size; + bool triggerResize; + { + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + triggerResize = !m_sample; + m_sample = sample; + } + + if (triggerResize) { + GST_DEBUG("First sample reached the sink, triggering video dimensions update"); + m_notifier->notify(MainThreadNotification::SizeChanged, [this] { m_player->sizeChanged(); }); + } + +#if USE(COORDINATED_GRAPHICS_THREADED) + if (!m_renderingCanBeAccelerated) { + LockHolder locker(m_drawMutex); + m_drawTimer.startOneShot(0); + m_drawCondition.wait(m_drawMutex); + return; + } + +#if USE(GSTREAMER_GL) + pushTextureToCompositor(); +#else + { + LockHolder lock(m_drawMutex); + if (!m_platformLayerProxy->scheduleUpdateOnCompositorThread([this] { this->pushTextureToCompositor(); })) + return; + m_drawCondition.wait(m_drawMutex); + } +#endif + return; +#else +#if USE(GSTREAMER_GL) + { + ASSERT(!isMainThread()); + + LockHolder locker(m_drawMutex); + m_drawTimer.startOneShot(0); + m_drawCondition.wait(m_drawMutex); + } +#else + repaint(); +#endif +#endif } -void MediaPlayerPrivateGStreamerBase::paint(GraphicsContext* context, const IntRect& rect) +void MediaPlayerPrivateGStreamerBase::repaintCallback(MediaPlayerPrivateGStreamerBase* player, GstSample* sample) { -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) - if (client()) - return; + player->triggerRepaint(sample); +} + +void MediaPlayerPrivateGStreamerBase::cancelRepaint() +{ +#if USE(TEXTURE_MAPPER_GL) || USE(COORDINATED_GRAPHICS_THREADED) + m_drawTimer.stop(); + LockHolder locker(m_drawMutex); + m_drawCondition.notifyOne(); #endif +} + +void MediaPlayerPrivateGStreamerBase::repaintCancelledCallback(MediaPlayerPrivateGStreamerBase* player) +{ + player->cancelRepaint(); +} + +#if USE(GSTREAMER_GL) +GstFlowReturn MediaPlayerPrivateGStreamerBase::newSampleCallback(GstElement* sink, MediaPlayerPrivateGStreamerBase* player) +{ + GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink))); + player->triggerRepaint(sample.get()); + return GST_FLOW_OK; +} + +GstFlowReturn MediaPlayerPrivateGStreamerBase::newPrerollCallback(GstElement* sink, MediaPlayerPrivateGStreamerBase* player) +{ + GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_preroll(GST_APP_SINK(sink))); + player->triggerRepaint(sample.get()); + return GST_FLOW_OK; +} +#endif + +void MediaPlayerPrivateGStreamerBase::setSize(const IntSize& size) +{ + m_size = size; +} - if (context->paintingDisabled()) +void MediaPlayerPrivateGStreamerBase::paint(GraphicsContext& context, const FloatRect& rect) +{ + if (context.paintingDisabled()) return; if (!m_player->visible()) return; - WTF::GMutexLocker lock(m_bufferMutex); - if (!m_buffer) + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + if (!GST_IS_SAMPLE(m_sample.get())) return; - GRefPtr<GstCaps> caps = currentVideoSinkCaps(); - if (!caps) - return; + ImagePaintingOptions paintingOptions(CompositeCopy); + if (m_renderingCanBeAccelerated) + paintingOptions.m_orientationDescription.setImageOrientationEnum(m_videoSourceOrientation); - RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get()); + RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_sample.get()); if (!gstImage) return; - context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB, - rect, gstImage->rect(), CompositeCopy, ImageOrientationDescription(), false); + if (Image* image = reinterpret_cast<Image*>(gstImage->image().get())) + context.drawImage(*image, rect, gstImage->rect(), paintingOptions); } -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) -void MediaPlayerPrivateGStreamerBase::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) +#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) +void MediaPlayerPrivateGStreamerBase::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) { - if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) + if (!m_player->visible()) return; - if (!m_player->visible()) + if (m_usingFallbackVideoSink) { + RefPtr<BitmapTexture> texture; + IntSize size; + TextureMapperGL::Flags flags; + { + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + + GstVideoInfo videoInfo; + if (UNLIKELY(!getSampleVideoInfo(m_sample.get(), videoInfo))) + return; + + size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); + flags = texMapFlagFromOrientation(m_videoSourceOrientation) | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0); + texture = textureMapper.acquireTextureFromPool(size, GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag); + updateTexture(static_cast<BitmapTextureGL&>(*texture), videoInfo); + } + TextureMapperGL& texmapGL = reinterpret_cast<TextureMapperGL&>(textureMapper); + BitmapTextureGL* textureGL = static_cast<BitmapTextureGL*>(texture.get()); + texmapGL.drawTexture(textureGL->id(), flags, textureGL->size(), targetRect, matrix, opacity); return; + } + +#if USE(GSTREAMER_GL) + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + + GstVideoInfo videoInfo; + if (!getSampleVideoInfo(m_sample.get(), videoInfo)) + return; + + GstBuffer* buffer = gst_sample_get_buffer(m_sample.get()); + GstVideoFrame videoFrame; + if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL))) + return; + + unsigned textureID = *reinterpret_cast<unsigned*>(videoFrame.data[0]); + TextureMapperGL::Flags flags = texMapFlagFromOrientation(m_videoSourceOrientation) | (GST_VIDEO_INFO_HAS_ALPHA(&videoInfo) ? TextureMapperGL::ShouldBlend : 0); + + IntSize size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); + TextureMapperGL& textureMapperGL = reinterpret_cast<TextureMapperGL&>(textureMapper); + textureMapperGL.drawTexture(textureID, flags, size, targetRect, matrix, opacity); + gst_video_frame_unmap(&videoFrame); +#endif +} +#endif + +#if USE(GSTREAMER_GL) +#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS) +// This should be called with the sample mutex locked. +GLContext* MediaPlayerPrivateGStreamerBase::prepareContextForCairoPaint(GstVideoInfo& videoInfo, IntSize& size, IntSize& rotatedSize) +{ + if (!getSampleVideoInfo(m_sample.get(), videoInfo)) + return nullptr; + + GLContext* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext(); + context->makeContextCurrent(); + + // Thread-awareness is a huge performance hit on non-Intel drivers. + cairo_gl_device_set_thread_aware(context->cairoDevice(), FALSE); + + size = IntSize(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); + rotatedSize = m_videoSourceOrientation.usesWidthAsHeight() ? size.transposedSize() : size; + + return context; +} + +// This should be called with the sample mutex locked. +bool MediaPlayerPrivateGStreamerBase::paintToCairoSurface(cairo_surface_t* outputSurface, cairo_device_t* device, GstVideoInfo& videoInfo, const IntSize& size, const IntSize& rotatedSize, bool flipY) +{ + GstBuffer* buffer = gst_sample_get_buffer(m_sample.get()); + GstVideoFrame videoFrame; + if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL))) + return false; + + unsigned textureID = *reinterpret_cast<unsigned*>(videoFrame.data[0]); + RefPtr<cairo_surface_t> surface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, textureID, size.width(), size.height())); + RefPtr<cairo_t> cr = adoptRef(cairo_create(outputSurface)); + + switch (m_videoSourceOrientation) { + case DefaultImageOrientation: + break; + case OriginRightTop: + cairo_translate(cr.get(), rotatedSize.width() * 0.5, rotatedSize.height() * 0.5); + cairo_rotate(cr.get(), piOverTwoDouble); + cairo_translate(cr.get(), -rotatedSize.height() * 0.5, -rotatedSize.width() * 0.5); + break; + case OriginBottomRight: + cairo_translate(cr.get(), rotatedSize.width() * 0.5, rotatedSize.height() * 0.5); + cairo_rotate(cr.get(), piDouble); + cairo_translate(cr.get(), -rotatedSize.width() * 0.5, -rotatedSize.height() * 0.5); + break; + case OriginLeftBottom: + cairo_translate(cr.get(), rotatedSize.width() * 0.5, rotatedSize.height() * 0.5); + cairo_rotate(cr.get(), 3 * piOverTwoDouble); + cairo_translate(cr.get(), -rotatedSize.height() * 0.5, -rotatedSize.width() * 0.5); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + if (flipY) { + cairo_scale(cr.get(), 1.0f, -1.0f); + cairo_translate(cr.get(), 0.0f, -size.height()); + } + + cairo_set_source_surface(cr.get(), surface.get(), 0, 0); + cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE); + cairo_paint(cr.get()); + + gst_video_frame_unmap(&videoFrame); + + return true; +} +#endif // USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS) + +bool MediaPlayerPrivateGStreamerBase::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY) +{ +#if USE(GSTREAMER_GL) + UNUSED_PARAM(context); + + if (m_usingFallbackVideoSink) + return false; + + if (premultiplyAlpha) + return false; + + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + + GstVideoInfo videoInfo; + if (!getSampleVideoInfo(m_sample.get(), videoInfo)) + return false; + + GstBuffer* buffer = gst_sample_get_buffer(m_sample.get()); + GstVideoFrame videoFrame; + if (!gst_video_frame_map(&videoFrame, &videoInfo, buffer, static_cast<GstMapFlags>(GST_MAP_READ | GST_MAP_GL))) + return false; + + IntSize size(GST_VIDEO_INFO_WIDTH(&videoInfo), GST_VIDEO_INFO_HEIGHT(&videoInfo)); + if (m_videoSourceOrientation.usesWidthAsHeight()) + size = size.transposedSize(); + unsigned textureID = *reinterpret_cast<unsigned*>(videoFrame.data[0]); + + if (!m_videoTextureCopier) + m_videoTextureCopier = std::make_unique<VideoTextureCopierGStreamer>(); + + bool copied = m_videoTextureCopier->copyVideoTextureToPlatformTexture(textureID, size, outputTexture, outputTarget, level, internalFormat, format, type, flipY, m_videoSourceOrientation); + + gst_video_frame_unmap(&videoFrame); - RefPtr<BitmapTexture> texture = updateTexture(textureMapper); - if (texture) - textureMapper->drawTexture(*texture.get(), targetRect, matrix, opacity); + return copied; +#else + return false; +#endif } + +NativeImagePtr MediaPlayerPrivateGStreamerBase::nativeImageForCurrentTime() +{ +#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS) + if (m_usingFallbackVideoSink) + return nullptr; + + GstVideoInfo videoInfo; + IntSize size, rotatedSize; + WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + GLContext* context = prepareContextForCairoPaint(videoInfo, size, rotatedSize); + if (!context) + return nullptr; + + RefPtr<cairo_surface_t> rotatedSurface = adoptRef(cairo_gl_surface_create(context->cairoDevice(), CAIRO_CONTENT_COLOR_ALPHA, rotatedSize.width(), rotatedSize.height())); + if (!paintToCairoSurface(rotatedSurface.get(), context->cairoDevice(), videoInfo, size, rotatedSize, false)) + return nullptr; + + return rotatedSurface; +#else + return nullptr; #endif +} +#endif + +void MediaPlayerPrivateGStreamerBase::setVideoSourceOrientation(const ImageOrientation& orientation) +{ + if (m_videoSourceOrientation == orientation) + return; + + m_videoSourceOrientation = orientation; +} bool MediaPlayerPrivateGStreamerBase::supportsFullscreen() const { @@ -450,25 +1072,88 @@ MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamerBase::movieLoadType() cons return MediaPlayer::Download; } -GRefPtr<GstCaps> MediaPlayerPrivateGStreamerBase::currentVideoSinkCaps() const +#if USE(GSTREAMER_GL) +GstElement* MediaPlayerPrivateGStreamerBase::createGLAppSink() +{ + if (!webkitGstCheckVersion(1, 8, 0)) + return nullptr; + + GstElement* appsink = gst_element_factory_make("appsink", "webkit-gl-video-sink"); + if (!appsink) + return nullptr; + + g_object_set(appsink, "enable-last-sample", FALSE, "emit-signals", TRUE, "max-buffers", 1, nullptr); + g_signal_connect(appsink, "new-sample", G_CALLBACK(newSampleCallback), this); + g_signal_connect(appsink, "new-preroll", G_CALLBACK(newPrerollCallback), this); + + return appsink; +} + +GstElement* MediaPlayerPrivateGStreamerBase::createVideoSinkGL() { - if (!m_webkitVideoSink) + // FIXME: Currently it's not possible to get the video frames and caps using this approach until + // the pipeline gets into playing state. Due to this, trying to grab a frame and painting it by some + // other mean (canvas or webgl) before playing state can result in a crash. + // This is being handled in https://bugs.webkit.org/show_bug.cgi?id=159460. + if (!webkitGstCheckVersion(1, 8, 0)) return nullptr; - GRefPtr<GstCaps> currentCaps; - g_object_get(G_OBJECT(m_webkitVideoSink.get()), "current-caps", ¤tCaps.outPtr(), NULL); - return currentCaps; + gboolean result = TRUE; + GstElement* videoSink = gst_bin_new(nullptr); + GstElement* upload = gst_element_factory_make("glupload", nullptr); + GstElement* colorconvert = gst_element_factory_make("glcolorconvert", nullptr); + GstElement* appsink = createGLAppSink(); + + if (!appsink || !upload || !colorconvert) { + GST_WARNING("Failed to create GstGL elements"); + gst_object_unref(videoSink); + + if (upload) + gst_object_unref(upload); + if (colorconvert) + gst_object_unref(colorconvert); + if (appsink) + gst_object_unref(appsink); + + return nullptr; + } + + gst_bin_add_many(GST_BIN(videoSink), upload, colorconvert, appsink, nullptr); + + GRefPtr<GstCaps> caps = adoptGRef(gst_caps_from_string("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), format = (string) { RGBA }")); + + result &= gst_element_link_pads(upload, "src", colorconvert, "sink"); + result &= gst_element_link_pads_filtered(colorconvert, "src", appsink, "sink", caps.get()); + + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(upload, "sink")); + gst_element_add_pad(videoSink, gst_ghost_pad_new("sink", pad.get())); + + if (!result) { + GST_WARNING("Failed to link GstGL elements"); + gst_object_unref(videoSink); + videoSink = nullptr; + } + return videoSink; } +#endif GstElement* MediaPlayerPrivateGStreamerBase::createVideoSink() { - ASSERT(initializeGStreamer()); + acceleratedRenderingStateChanged(); - GstElement* videoSink = nullptr; - m_webkitVideoSink = webkitVideoSinkNew(); +#if USE(GSTREAMER_GL) + if (m_renderingCanBeAccelerated) + m_videoSink = createVideoSinkGL(); +#endif - m_repaintHandler = g_signal_connect(m_webkitVideoSink.get(), "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); + if (!m_videoSink) { + m_usingFallbackVideoSink = true; + m_videoSink = webkitVideoSinkNew(); + g_signal_connect_swapped(m_videoSink.get(), "repaint-requested", G_CALLBACK(repaintCallback), this); + g_signal_connect_swapped(m_videoSink.get(), "repaint-cancelled", G_CALLBACK(repaintCancelledCallback), this); + } + GstElement* videoSink = nullptr; m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink"); if (m_fpsSink) { g_object_set(m_fpsSink.get(), "silent", TRUE , nullptr); @@ -477,20 +1162,19 @@ GstElement* MediaPlayerPrivateGStreamerBase::createVideoSink() #if LOG_DISABLED g_object_set(m_fpsSink.get(), "text-overlay", FALSE , nullptr); #else - WTFLogChannel* channel = logChannelByName("Media"); - if (channel->state != WTFLogChannelOn) + if (!isLogChannelEnabled("Media")) g_object_set(m_fpsSink.get(), "text-overlay", FALSE , nullptr); #endif // LOG_DISABLED if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink.get()), "video-sink")) { - g_object_set(m_fpsSink.get(), "video-sink", m_webkitVideoSink.get(), nullptr); + g_object_set(m_fpsSink.get(), "video-sink", m_videoSink.get(), nullptr); videoSink = m_fpsSink.get(); } else m_fpsSink = nullptr; } if (!m_fpsSink) - videoSink = m_webkitVideoSink.get(); + videoSink = m_videoSink.get(); ASSERT(videoSink); @@ -505,23 +1189,23 @@ void MediaPlayerPrivateGStreamerBase::setStreamVolumeElement(GstStreamVolume* vo // We don't set the initial volume because we trust the sink to keep it for us. See // https://bugs.webkit.org/show_bug.cgi?id=118974 for more information. if (!m_player->platformVolumeConfigurationRequired()) { - LOG_MEDIA_MESSAGE("Setting stream volume to %f", m_player->volume()); - g_object_set(m_volumeElement.get(), "volume", m_player->volume(), NULL); + GST_DEBUG("Setting stream volume to %f", m_player->volume()); + g_object_set(m_volumeElement.get(), "volume", m_player->volume(), nullptr); } else - LOG_MEDIA_MESSAGE("Not setting stream volume, trusting system one"); + GST_DEBUG("Not setting stream volume, trusting system one"); - LOG_MEDIA_MESSAGE("Setting stream muted %d", m_player->muted()); - g_object_set(m_volumeElement.get(), "mute", m_player->muted(), NULL); + GST_DEBUG("Setting stream muted %d", m_player->muted()); + g_object_set(m_volumeElement.get(), "mute", m_player->muted(), nullptr); - m_volumeSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this); - m_muteSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this); + g_signal_connect_swapped(m_volumeElement.get(), "notify::volume", G_CALLBACK(volumeChangedCallback), this); + g_signal_connect_swapped(m_volumeElement.get(), "notify::mute", G_CALLBACK(muteChangedCallback), this); } unsigned MediaPlayerPrivateGStreamerBase::decodedFrameCount() const { guint64 decodedFrames = 0; if (m_fpsSink) - g_object_get(m_fpsSink.get(), "frames-rendered", &decodedFrames, NULL); + g_object_get(m_fpsSink.get(), "frames-rendered", &decodedFrames, nullptr); return static_cast<unsigned>(decodedFrames); } @@ -529,7 +1213,7 @@ unsigned MediaPlayerPrivateGStreamerBase::droppedFrameCount() const { guint64 framesDropped = 0; if (m_fpsSink) - g_object_get(m_fpsSink.get(), "frames-dropped", &framesDropped, NULL); + g_object_get(m_fpsSink.get(), "frames-dropped", &framesDropped, nullptr); return static_cast<unsigned>(framesDropped); } @@ -550,13 +1234,95 @@ unsigned MediaPlayerPrivateGStreamerBase::videoDecodedByteCount() const GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES); gint64 position = 0; - if (gst_element_query(m_webkitVideoSink.get(), query)) + if (gst_element_query(m_videoSink.get(), query)) gst_query_parse_position(query, 0, &position); gst_query_unref(query); return static_cast<unsigned>(position); } +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +void MediaPlayerPrivateGStreamerBase::needKey(RefPtr<Uint8Array> initData) +{ + if (!m_player->keyNeeded(initData.get())) + GST_INFO("no event handler for key needed"); +} + +void MediaPlayerPrivateGStreamerBase::setCDMSession(CDMSession* session) +{ + GST_DEBUG("setting CDM session to %p", session); + m_cdmSession = session; +} + +void MediaPlayerPrivateGStreamerBase::keyAdded() +{ +} + +void MediaPlayerPrivateGStreamerBase::handleProtectionEvent(GstEvent* event) +{ + if (m_handledProtectionEvents.contains(GST_EVENT_SEQNUM(event))) { + GST_DEBUG("event %u already handled", GST_EVENT_SEQNUM(event)); + m_handledProtectionEvents.remove(GST_EVENT_SEQNUM(event)); + return; + } + + const gchar* eventKeySystemId = nullptr; + GstBuffer* data = nullptr; + gst_event_parse_protection(event, &eventKeySystemId, &data, nullptr); + + GstMapInfo mapInfo; + if (!gst_buffer_map(data, &mapInfo, GST_MAP_READ)) { + GST_WARNING("cannot map %s protection data", eventKeySystemId); + return; + } + + GST_DEBUG("scheduling keyNeeded event for %s with init data size of %" G_GSIZE_FORMAT, eventKeySystemId, mapInfo.size); + GST_MEMDUMP("init datas", mapInfo.data, mapInfo.size); + RefPtr<Uint8Array> initDataArray = Uint8Array::create(mapInfo.data, mapInfo.size); + needKey(initDataArray); + gst_buffer_unmap(data, &mapInfo); +} + +void MediaPlayerPrivateGStreamerBase::receivedGenerateKeyRequest(const String& keySystem) +{ + GST_DEBUG("received generate key request for %s", keySystem.utf8().data()); + m_lastGenerateKeyRequestKeySystemUuid = keySystemIdToUuid(keySystem); + m_protectionCondition.notifyOne(); +} + +static AtomicString keySystemIdToUuid(const AtomicString& id) +{ + if (equalIgnoringASCIICase(id, CLEAR_KEY_PROTECTION_SYSTEM_ID)) + return AtomicString(CLEAR_KEY_PROTECTION_SYSTEM_UUID); + + return { }; +} + +std::unique_ptr<CDMSession> MediaPlayerPrivateGStreamerBase::createSession(const String& keySystem, CDMSessionClient*) +{ + GST_INFO("Requested CDMSession for KeySystem %s: Returning null.", keySystem.utf8().data()); + return nullptr; +} + +void MediaPlayerPrivateGStreamerBase::dispatchDecryptionKey(GstBuffer* buffer) +{ + gst_element_send_event(m_pipeline.get(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB, + gst_structure_new("drm-cipher", "key", GST_TYPE_BUFFER, buffer, nullptr))); +} +#endif + +bool MediaPlayerPrivateGStreamerBase::supportsKeySystem(const String& keySystem, const String& mimeType) +{ + GST_INFO("Checking for KeySystem support with %s and type %s: false.", keySystem.utf8().data(), mimeType.utf8().data()); + return false; +} + +MediaPlayer::SupportsType MediaPlayerPrivateGStreamerBase::extendedSupportsType(const MediaEngineSupportParameters& parameters, MediaPlayer::SupportsType result) +{ + UNUSED_PARAM(parameters); + return result; +} + } #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h index dfcab5994..3092c1eff 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h @@ -2,7 +2,8 @@ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2009, 2010 Igalia S.L + * Copyright (C) 2009, 2010, 2015, 2016 Igalia S.L + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,110 +26,227 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) #include "GRefPtrGStreamer.h" +#include "MainThreadNotifier.h" #include "MediaPlayerPrivate.h" - +#include "PlatformLayer.h" #include <glib.h> - +#include <gst/gst.h> +#include <wtf/Condition.h> #include <wtf/Forward.h> +#include <wtf/RunLoop.h> -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) +#if USE(TEXTURE_MAPPER) #include "TextureMapperPlatformLayer.h" +#include "TextureMapperPlatformLayerProxy.h" #endif -typedef struct _GstBuffer GstBuffer; -typedef struct _GstElement GstElement; -typedef struct _GstMessage GstMessage; typedef struct _GstStreamVolume GstStreamVolume; -typedef struct _WebKitVideoSink WebKitVideoSink; +typedef struct _GstVideoInfo GstVideoInfo; +typedef struct _GstGLContext GstGLContext; +typedef struct _GstGLDisplay GstGLDisplay; namespace WebCore { +class BitmapTextureGL; +class GLContext; class GraphicsContext; +class GraphicsContext3D; class IntSize; class IntRect; +class VideoTextureCopierGStreamer; + +void registerWebKitGStreamerElements(); class MediaPlayerPrivateGStreamerBase : public MediaPlayerPrivateInterface -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) - , public TextureMapperPlatformLayer +#if USE(COORDINATED_GRAPHICS_THREADED) || (USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)) + , public PlatformLayer #endif { public: virtual ~MediaPlayerPrivateGStreamerBase(); - IntSize naturalSize() const; + FloatSize naturalSize() const override; - void setVolume(float); - float volume() const; - void volumeChanged(); - void notifyPlayerOfVolumeChange(); + void setVolume(float) override; + virtual float volume() const; + +#if USE(GSTREAMER_GL) + bool ensureGstGLContext(); + static GstContext* requestGLContext(const gchar* contextType, MediaPlayerPrivateGStreamerBase*); +#endif - bool supportsMuting() const { return true; } - void setMuted(bool); + bool supportsMuting() const override { return true; } + void setMuted(bool) override; bool muted() const; - void muteChanged(); - void notifyPlayerOfMute(); - MediaPlayer::NetworkState networkState() const; - MediaPlayer::ReadyState readyState() const; + MediaPlayer::NetworkState networkState() const override; + MediaPlayer::ReadyState readyState() const override; - void setVisible(bool) { } - void setSize(const IntSize&); + void setVisible(bool) override { } + void setSize(const IntSize&) override; void sizeChanged(); - void triggerRepaint(GstBuffer*); - void paint(GraphicsContext*, const IntRect&); + void paint(GraphicsContext&, const FloatRect&) override; - virtual bool hasSingleSecurityOrigin() const { return true; } + bool hasSingleSecurityOrigin() const override { return true; } virtual float maxTimeLoaded() const { return 0.0; } - bool supportsFullscreen() const; - PlatformMedia platformMedia() const; + bool supportsFullscreen() const override; + PlatformMedia platformMedia() const override; - MediaPlayer::MovieLoadType movieLoadType() const; + MediaPlayer::MovieLoadType movieLoadType() const override; virtual bool isLiveStream() const = 0; MediaPlayer* mediaPlayer() const { return m_player; } - unsigned decodedFrameCount() const; - unsigned droppedFrameCount() const; - unsigned audioDecodedByteCount() const; - unsigned videoDecodedByteCount() const; + unsigned decodedFrameCount() const override; + unsigned droppedFrameCount() const override; + unsigned audioDecodedByteCount() const override; + unsigned videoDecodedByteCount() const override; + + void acceleratedRenderingStateChanged() override; + +#if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) + PlatformLayer* platformLayer() const override { return const_cast<MediaPlayerPrivateGStreamerBase*>(this); } +#if PLATFORM(WIN_CAIRO) + // FIXME: Accelerated rendering has not been implemented for WinCairo yet. + bool supportsAcceleratedRendering() const override { return false; } +#else + bool supportsAcceleratedRendering() const override { return true; } +#endif + void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix&, float) override; +#endif + +#if USE(COORDINATED_GRAPHICS_THREADED) + PlatformLayer* platformLayer() const override { return const_cast<MediaPlayerPrivateGStreamerBase*>(this); } + bool supportsAcceleratedRendering() const override { return true; } +#endif + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + void needKey(RefPtr<Uint8Array>); + void setCDMSession(CDMSession*) override; + void keyAdded() override; + virtual void dispatchDecryptionKey(GstBuffer*); + void handleProtectionEvent(GstEvent*); + void receivedGenerateKeyRequest(const String&); +#endif + + static bool supportsKeySystem(const String& keySystem, const String& mimeType); + static MediaPlayer::SupportsType extendedSupportsType(const MediaEngineSupportParameters&, MediaPlayer::SupportsType); -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) - virtual PlatformLayer* platformLayer() const { return const_cast<MediaPlayerPrivateGStreamerBase*>(this); } - virtual bool supportsAcceleratedRendering() const { return true; } - virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float); +#if USE(GSTREAMER_GL) + bool copyVideoTextureToPlatformTexture(GraphicsContext3D*, Platform3DObject, GC3Denum, GC3Dint, GC3Denum, GC3Denum, GC3Denum, bool, bool) override; + NativeImagePtr nativeImageForCurrentTime() override; #endif + void setVideoSourceOrientation(const ImageOrientation&); + GstElement* pipeline() const { return m_pipeline.get(); } + + virtual bool handleSyncMessage(GstMessage*); + protected: MediaPlayerPrivateGStreamerBase(MediaPlayer*); virtual GstElement* createVideoSink(); - GRefPtr<GstCaps> currentVideoSinkCaps() const; + +#if USE(GSTREAMER_GL) + static GstFlowReturn newSampleCallback(GstElement*, MediaPlayerPrivateGStreamerBase*); + static GstFlowReturn newPrerollCallback(GstElement*, MediaPlayerPrivateGStreamerBase*); + GstElement* createGLAppSink(); + GstElement* createVideoSinkGL(); + GstGLContext* gstGLContext() const { return m_glContext.get(); } + GstGLDisplay* gstGLDisplay() const { return m_glDisplay.get(); } +#if USE(CAIRO) && ENABLE(ACCELERATED_2D_CANVAS) + GLContext* prepareContextForCairoPaint(GstVideoInfo&, IntSize&, IntSize&); + bool paintToCairoSurface(cairo_surface_t*, cairo_device_t*, GstVideoInfo&, const IntSize&, const IntSize&, bool); +#endif +#endif + + GstElement* videoSink() const { return m_videoSink.get(); } void setStreamVolumeElement(GstStreamVolume*); virtual GstElement* createAudioSink() { return 0; } virtual GstElement* audioSink() const { return 0; } + void setPipeline(GstElement*); + + void triggerRepaint(GstSample*); + void repaint(); + void cancelRepaint(); + + static void repaintCallback(MediaPlayerPrivateGStreamerBase*, GstSample*); + static void repaintCancelledCallback(MediaPlayerPrivateGStreamerBase*); + + void notifyPlayerOfVolumeChange(); + void notifyPlayerOfMute(); + + static void volumeChangedCallback(MediaPlayerPrivateGStreamerBase*); + static void muteChangedCallback(MediaPlayerPrivateGStreamerBase*); + + enum MainThreadNotification { + VideoChanged = 1 << 0, + VideoCapsChanged = 1 << 1, + AudioChanged = 1 << 2, + VolumeChanged = 1 << 3, + MuteChanged = 1 << 4, +#if ENABLE(VIDEO_TRACK) + TextChanged = 1 << 5, +#endif + SizeChanged = 1 << 6 + }; + + Ref<MainThreadNotifier<MainThreadNotification>> m_notifier; MediaPlayer* m_player; + GRefPtr<GstElement> m_pipeline; GRefPtr<GstStreamVolume> m_volumeElement; - GRefPtr<GstElement> m_webkitVideoSink; + GRefPtr<GstElement> m_videoSink; GRefPtr<GstElement> m_fpsSink; MediaPlayer::ReadyState m_readyState; - MediaPlayer::NetworkState m_networkState; + mutable MediaPlayer::NetworkState m_networkState; IntSize m_size; - GMutex* m_bufferMutex; - GstBuffer* m_buffer; - unsigned long m_volumeTimerHandler; - unsigned long m_muteTimerHandler; - unsigned long m_repaintHandler; - unsigned long m_volumeSignalHandler; - unsigned long m_muteSignalHandler; - mutable IntSize m_videoSize; -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) - PassRefPtr<BitmapTexture> updateTexture(TextureMapper*); + mutable GMutex m_sampleMutex; + GRefPtr<GstSample> m_sample; +#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED) + RunLoop::Timer<MediaPlayerPrivateGStreamerBase> m_drawTimer; +#endif + mutable FloatSize m_videoSize; + bool m_usingFallbackVideoSink; + bool m_renderingCanBeAccelerated { false }; +#if USE(TEXTURE_MAPPER_GL) + void updateTexture(BitmapTextureGL&, GstVideoInfo&); +#endif +#if USE(GSTREAMER_GL) + GRefPtr<GstGLContext> m_glContext; + GRefPtr<GstGLDisplay> m_glDisplay; +#endif + +#if USE(COORDINATED_GRAPHICS_THREADED) + RefPtr<TextureMapperPlatformLayerProxy> proxy() const override { return m_platformLayerProxy.copyRef(); } + void swapBuffersIfNeeded() override { }; + void pushTextureToCompositor(); + RefPtr<TextureMapperPlatformLayerProxy> m_platformLayerProxy; +#endif + +#if USE(GSTREAMER_GL) || USE(COORDINATED_GRAPHICS_THREADED) + RefPtr<GraphicsContext3D> m_context3D; + Condition m_drawCondition; + Lock m_drawMutex; +#endif + + ImageOrientation m_videoSourceOrientation; +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + std::unique_ptr<CDMSession> createSession(const String&, CDMSessionClient*) override; + CDMSession* m_cdmSession; + Lock m_protectionMutex; + Condition m_protectionCondition; + String m_lastGenerateKeyRequestKeySystemUuid; + HashSet<uint32_t> m_handledProtectionEvents; +#endif +#if USE(GSTREAMER_GL) + std::unique_ptr<VideoTextureCopierGStreamer> m_videoTextureCopier; #endif }; + } #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp new file mode 100644 index 000000000..8e3736c8b --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.cpp @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2012 Collabora Ltd. All rights reserved. + * Copyright (C) 2014, 2015 Igalia S.L. All rights reserved. + * Copyright (C) 2015 Metrological All rights reserved. + * + * 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 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 "MediaPlayerPrivateGStreamerOwr.h" + +#if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC) + +#include "GStreamerUtilities.h" +#include "MediaPlayer.h" +#include "MediaStreamPrivate.h" +#include "NotImplemented.h" +#include "RealtimeMediaSourceOwr.h" +#include "URL.h" +#include <owr/owr.h> +#include <owr/owr_gst_audio_renderer.h> +#include <owr/owr_gst_video_renderer.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/CString.h> + +GST_DEBUG_CATEGORY(webkit_openwebrtc_debug); +#define GST_CAT_DEFAULT webkit_openwebrtc_debug + +namespace WebCore { + +MediaPlayerPrivateGStreamerOwr::MediaPlayerPrivateGStreamerOwr(MediaPlayer* player) + : MediaPlayerPrivateGStreamerBase(player) +{ + initializeGStreamerAndGStreamerDebugging(); +} + +MediaPlayerPrivateGStreamerOwr::~MediaPlayerPrivateGStreamerOwr() +{ + GST_TRACE("Destroying"); + + if (hasAudio()) + m_audioTrack->removeObserver(*this); + if (hasVideo()) + m_videoTrack->removeObserver(*this); + + m_audioTrackMap.clear(); + m_videoTrackMap.clear(); + + stop(); +} + +void MediaPlayerPrivateGStreamerOwr::play() +{ + GST_DEBUG("Play"); + + if (!m_streamPrivate || !m_streamPrivate->active()) { + m_readyState = MediaPlayer::HaveNothing; + loadingFailed(MediaPlayer::Empty); + return; + } + + m_ended = false; + m_paused = false; + + GST_DEBUG("Connecting to live stream, descriptor: %p", m_streamPrivate.get()); + + if (m_videoTrack) + maybeHandleChangeMutedState(*m_videoTrack.get()); + + if (m_audioTrack) + maybeHandleChangeMutedState(*m_audioTrack.get()); +} + +void MediaPlayerPrivateGStreamerOwr::pause() +{ + GST_DEBUG("Pause"); + m_paused = true; + disableMediaTracks(); +} + +bool MediaPlayerPrivateGStreamerOwr::hasVideo() const +{ + return m_videoTrack; +} + +bool MediaPlayerPrivateGStreamerOwr::hasAudio() const +{ + return m_audioTrack; +} + +void MediaPlayerPrivateGStreamerOwr::setVolume(float volume) +{ + if (!m_audioTrack) + return; + + auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_audioTrack->source()); + auto mediaSource = OWR_MEDIA_SOURCE(realTimeMediaSource.mediaSource()); + + GST_DEBUG("Setting volume: %f", volume); + g_object_set(mediaSource, "volume", static_cast<gdouble>(volume), nullptr); +} + +void MediaPlayerPrivateGStreamerOwr::setMuted(bool muted) +{ + if (!m_audioTrack) + return; + + auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_audioTrack->source()); + auto mediaSource = OWR_MEDIA_SOURCE(realTimeMediaSource.mediaSource()); + if (!mediaSource) + return; + + GST_DEBUG("Setting mute: %s", muted ? "on":"off"); + g_object_set(mediaSource, "mute", muted, nullptr); +} + +float MediaPlayerPrivateGStreamerOwr::currentTime() const +{ + gint64 position = GST_CLOCK_TIME_NONE; + GstQuery* query = gst_query_new_position(GST_FORMAT_TIME); + + if (m_videoTrack && gst_element_query(m_videoSink.get(), query)) + gst_query_parse_position(query, 0, &position); + else if (m_audioTrack && gst_element_query(m_audioSink.get(), query)) + gst_query_parse_position(query, 0, &position); + + float result = 0; + if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE) + result = static_cast<double>(position) / GST_SECOND; + + GST_LOG("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); + gst_query_unref(query); + + return result; +} + +void MediaPlayerPrivateGStreamerOwr::load(const String &) +{ + // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate. + m_networkState = MediaPlayer::FormatError; + m_player->networkStateChanged(); +} + +#if ENABLE(MEDIA_SOURCE) +void MediaPlayerPrivateGStreamerOwr::load(const String&, MediaSourcePrivateClient*) +{ + // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate. + m_networkState = MediaPlayer::FormatError; + m_player->networkStateChanged(); +} +#endif + +void MediaPlayerPrivateGStreamerOwr::load(MediaStreamPrivate& streamPrivate) +{ + if (!initializeGStreamer()) + return; + + m_streamPrivate = &streamPrivate; + if (!m_streamPrivate->active()) { + loadingFailed(MediaPlayer::NetworkError); + return; + } + + if (streamPrivate.hasVideo() && !m_videoSink) + createVideoSink(); + + if (streamPrivate.hasAudio() && !m_audioSink) + createGSTAudioSinkBin(); + + GST_DEBUG("Loading MediaStreamPrivate %p video: %s, audio: %s", &streamPrivate, streamPrivate.hasVideo() ? "yes":"no", streamPrivate.hasAudio() ? "yes":"no"); + + m_readyState = MediaPlayer::HaveNothing; + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + m_player->readyStateChanged(); + + for (auto track : m_streamPrivate->tracks()) { + if (!track->enabled()) { + GST_DEBUG("Track %s disabled", track->label().ascii().data()); + continue; + } + + GST_DEBUG("Processing track %s", track->label().ascii().data()); + + bool observeTrack = false; + + // TODO: Support for multiple tracks of the same type. + + switch (track->type()) { + case RealtimeMediaSource::Audio: + if (!m_audioTrack) { + String preSelectedDevice = getenv("WEBKIT_AUDIO_DEVICE"); + if (!preSelectedDevice || (preSelectedDevice == track->label())) { + m_audioTrack = track; + auto audioTrack = AudioTrackPrivateMediaStream::create(*m_audioTrack.get()); + m_player->addAudioTrack(*audioTrack); + m_audioTrackMap.add(track->id(), audioTrack); + observeTrack = true; + } + } + break; + case RealtimeMediaSource::Video: + if (!m_videoTrack) { + String preSelectedDevice = getenv("WEBKIT_VIDEO_DEVICE"); + if (!preSelectedDevice || (preSelectedDevice == track->label())) { + m_videoTrack = track; + auto videoTrack = VideoTrackPrivateMediaStream::create(*m_videoTrack.get()); + m_player->addVideoTrack(*videoTrack); + videoTrack->setSelected(true); + m_videoTrackMap.add(track->id(), videoTrack); + observeTrack = true; + } + } + break; + case RealtimeMediaSource::None: + GST_WARNING("Loading a track with None type"); + } + + if (observeTrack) + track->addObserver(*this); + } + + m_readyState = MediaPlayer::HaveEnoughData; + m_player->readyStateChanged(); +} + +void MediaPlayerPrivateGStreamerOwr::loadingFailed(MediaPlayer::NetworkState error) +{ + if (m_networkState != error) { + GST_WARNING("Loading failed, error: %d", error); + m_networkState = error; + m_player->networkStateChanged(); + } + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; + m_player->readyStateChanged(); + } +} + +bool MediaPlayerPrivateGStreamerOwr::didLoadingProgress() const +{ + // FIXME: Implement loading progress support. + return true; +} + +void MediaPlayerPrivateGStreamerOwr::disableMediaTracks() +{ + if (m_audioTrack) { + GST_DEBUG("Stop: disconnecting audio"); + g_object_set(m_audioRenderer.get(), "disabled", true, nullptr); + owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_audioRenderer.get()), nullptr); + } + + if (m_videoTrack) { + GST_DEBUG("Stop: disconnecting video"); + g_object_set(m_videoRenderer.get(), "disabled", true, nullptr); + owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_videoRenderer.get()), nullptr); + } +} + +void MediaPlayerPrivateGStreamerOwr::stop() +{ + disableMediaTracks(); + if (m_videoTrack) { + auto videoTrack = m_videoTrackMap.get(m_videoTrack->id()); + if (videoTrack) + videoTrack->setSelected(false); + } +} + +void MediaPlayerPrivateGStreamerOwr::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (initializeGStreamerAndGStreamerDebugging()) { + registrar([](MediaPlayer* player) { + return std::make_unique<MediaPlayerPrivateGStreamerOwr>(player); + }, getSupportedTypes, supportsType, nullptr, nullptr, nullptr, nullptr); + } +} + +void MediaPlayerPrivateGStreamerOwr::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types) +{ + // Not supported in this media player. + static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> cache; + types = cache; +} + +MediaPlayer::SupportsType MediaPlayerPrivateGStreamerOwr::supportsType(const MediaEngineSupportParameters& parameters) +{ + if (parameters.isMediaStream) + return MediaPlayer::IsSupported; + return MediaPlayer::IsNotSupported; +} + +bool MediaPlayerPrivateGStreamerOwr::initializeGStreamerAndGStreamerDebugging() +{ + if (!initializeGStreamer()) + return false; + + static std::once_flag debugRegisteredFlag; + std::call_once(debugRegisteredFlag, [] { + GST_DEBUG_CATEGORY_INIT(webkit_openwebrtc_debug, "webkitowrplayer", 0, "WebKit OpenWebRTC player"); + }); + + return true; +} + +void MediaPlayerPrivateGStreamerOwr::createGSTAudioSinkBin() +{ + ASSERT(!m_audioSink); + GST_DEBUG("Creating audio sink"); + // FIXME: volume/mute support: https://webkit.org/b/153828. + + // Pre-roll an autoaudiosink so that the platform audio sink is created and + // can be retrieved from the autoaudiosink bin. + GRefPtr<GstElement> sink = gst_element_factory_make("autoaudiosink", nullptr); + GstChildProxy* childProxy = GST_CHILD_PROXY(sink.get()); + gst_element_set_state(sink.get(), GST_STATE_READY); + GRefPtr<GstElement> platformSink = adoptGRef(GST_ELEMENT(gst_child_proxy_get_child_by_index(childProxy, 0))); + GstElementFactory* factory = gst_element_get_factory(platformSink.get()); + + // Dispose now un-needed autoaudiosink. + gst_element_set_state(sink.get(), GST_STATE_NULL); + + // Create a fresh new audio sink compatible with the platform. + m_audioSink = gst_element_factory_create(factory, nullptr); + m_audioRenderer = adoptGRef(owr_gst_audio_renderer_new(m_audioSink.get())); +} + +void MediaPlayerPrivateGStreamerOwr::trackEnded(MediaStreamTrackPrivate& track) +{ + GST_DEBUG("Track ended"); + + if (!m_streamPrivate || !m_streamPrivate->active()) { + stop(); + return; + } + + if (&track == m_audioTrack) + g_object_set(m_audioRenderer.get(), "disabled", true, nullptr); + else if (&track == m_videoTrack) { + g_object_set(m_videoRenderer.get(), "disabled", true, nullptr); + auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_videoTrack->source()); + realTimeMediaSource.setWidth(0); + realTimeMediaSource.setHeight(0); + auto videoTrack = m_videoTrackMap.get(m_videoTrack->id()); + if (videoTrack) + videoTrack->setSelected(false); + } + + bool audioDisabled; + bool videoDisabled; + g_object_get(m_audioRenderer.get(), "disabled", &audioDisabled, nullptr); + g_object_get(m_videoRenderer.get(), "disabled", &videoDisabled, nullptr); + if (audioDisabled && videoDisabled) { + m_ended = true; + m_player->timeChanged(); + } +} + +void MediaPlayerPrivateGStreamerOwr::trackMutedChanged(MediaStreamTrackPrivate& track) +{ + GST_DEBUG("Track muted state changed"); + + maybeHandleChangeMutedState(track); +} + +void MediaPlayerPrivateGStreamerOwr::maybeHandleChangeMutedState(MediaStreamTrackPrivate& track) +{ + auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(track.source()); + auto mediaSource = OWR_MEDIA_SOURCE(realTimeMediaSource.mediaSource()); + + GST_DEBUG("%s track now %s", track.type() == RealtimeMediaSource::Audio ? "audio":"video", realTimeMediaSource.muted() ? "muted":"un-muted"); + switch (track.type()) { + case RealtimeMediaSource::Audio: + if (!realTimeMediaSource.muted()) { + g_object_set(m_audioRenderer.get(), "disabled", false, nullptr); + owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_audioRenderer.get()), mediaSource); + } else { + g_object_set(m_audioRenderer.get(), "disabled", true, nullptr); + owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_audioRenderer.get()), nullptr); + } + if (mediaSource) + g_object_set(mediaSource, "mute", !track.enabled(), nullptr); + break; + case RealtimeMediaSource::Video: + if (!realTimeMediaSource.muted()) { + g_object_set(m_videoRenderer.get(), "disabled", false, nullptr); + owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_videoRenderer.get()), mediaSource); + } else { + g_object_set(m_videoRenderer.get(), "disabled", true, nullptr); + owr_media_renderer_set_source(OWR_MEDIA_RENDERER(m_videoRenderer.get()), nullptr); + } + break; + case RealtimeMediaSource::None: + GST_WARNING("Trying to change mute state of a track with None type"); + } +} + +void MediaPlayerPrivateGStreamerOwr::trackSettingsChanged(MediaStreamTrackPrivate&) +{ + GST_DEBUG("Track settings changed"); +} + +void MediaPlayerPrivateGStreamerOwr::trackEnabledChanged(MediaStreamTrackPrivate& track) +{ + GST_DEBUG("%s track now %s", track.type() == RealtimeMediaSource::Audio ? "audio":"video", track.enabled() ? "enabled":"disabled"); + + switch (track.type()) { + case RealtimeMediaSource::Audio: + g_object_set(m_audioRenderer.get(), "disabled", !track.enabled(), nullptr); + break; + case RealtimeMediaSource::Video: + g_object_set(m_videoRenderer.get(), "disabled", !track.enabled(), nullptr); + break; + case RealtimeMediaSource::None: + GST_WARNING("Trying to change enabled state of a track with None type"); + } +} + +GstElement* MediaPlayerPrivateGStreamerOwr::createVideoSink() +{ + GstElement* sink; +#if USE(GSTREAMER_GL) + // No need to create glupload and glcolorconvert here because they are + // already created by the video renderer. + // FIXME: This should probably return a RefPtr. See https://bugs.webkit.org/show_bug.cgi?id=164709. + sink = MediaPlayerPrivateGStreamerBase::createGLAppSink(); + m_videoSink = sink; +#else + if (m_streamPrivate->getVideoRenderer()) { + m_videoRenderer = m_streamPrivate->getVideoRenderer(); + m_videoSink = m_streamPrivate->getVideoSinkElement(); + g_signal_connect_swapped(m_videoSink.get(), "repaint-requested", G_CALLBACK(MediaPlayerPrivateGStreamerBase::repaintCallback), this); + g_object_get(m_videoRenderer.get(), "sink", &sink, nullptr); + } else { + GstElement* gldownload = gst_element_factory_make("gldownload", nullptr); + GstElement* videoconvert = gst_element_factory_make("videoconvert", nullptr); + GstElement* webkitSink = MediaPlayerPrivateGStreamerBase::createVideoSink(); + sink = gst_bin_new(nullptr); + gst_bin_add_many(GST_BIN(sink), gldownload, videoconvert, webkitSink, nullptr); + gst_element_link_many(gldownload, videoconvert, webkitSink, nullptr); + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(gldownload, "sink")); + gst_element_add_pad(sink, gst_ghost_pad_new("sink", pad.get())); + } +#endif + if (!m_videoRenderer) { + m_videoRenderer = adoptGRef(owr_gst_video_renderer_new(sink)); +#if USE(GSTREAMER_GL) + owr_video_renderer_set_request_context_callback(OWR_VIDEO_RENDERER(m_videoRenderer.get()), (OwrVideoRendererRequestContextCallback) MediaPlayerPrivateGStreamerBase::requestGLContext, this, nullptr); +#endif + m_streamPrivate->setVideoRenderer(m_videoRenderer.get(), videoSink()); + } + return sink; +} + +void MediaPlayerPrivateGStreamerOwr::setSize(const IntSize& size) +{ + if (size == m_size) + return; + + MediaPlayerPrivateGStreamerBase::setSize(size); + if (m_videoRenderer) + g_object_set(m_videoRenderer.get(), "width", size.width(), "height", size.height(), nullptr); + + if (!m_videoTrack) + return; + + auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_videoTrack->source()); + realTimeMediaSource.setWidth(size.width()); + realTimeMediaSource.setHeight(size.height()); +} + +FloatSize MediaPlayerPrivateGStreamerOwr::naturalSize() const +{ + auto size = MediaPlayerPrivateGStreamerBase::naturalSize(); + + // In case we are not playing the video we return the size we set to the media source. + if (m_videoTrack && size.isZero()) { + auto& realTimeMediaSource = static_cast<RealtimeMediaSourceOwr&>(m_videoTrack->source()); + return realTimeMediaSource.size(); + } + + return size; +} + +} // namespace WebCore + +#endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h new file mode 100644 index 000000000..334630e45 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerOwr.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 Igalia S.L. All rights reserved. + * Copyright (C) 2015 Metrological. All rights reserved. + * + * 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 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 + */ + +#ifndef MediaPlayerPrivateGStreamerOwr_h +#define MediaPlayerPrivateGStreamerOwr_h + +#if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC) + +#include "AudioTrackPrivateMediaStream.h" +#include "MediaPlayerPrivateGStreamerBase.h" +#include "MediaStreamTrackPrivate.h" +#include "VideoTrackPrivateMediaStream.h" + +typedef struct _OwrGstVideoRenderer OwrGstVideoRenderer; +typedef struct _OwrGstAudioRenderer OwrGstAudioRenderer; + +namespace WebCore { + +class MediaStreamPrivate; +class RealtimeMediaSourceOwr; + +class MediaPlayerPrivateGStreamerOwr : public MediaPlayerPrivateGStreamerBase, private MediaStreamTrackPrivate::Observer { +public: + explicit MediaPlayerPrivateGStreamerOwr(MediaPlayer*); + ~MediaPlayerPrivateGStreamerOwr(); + + static void registerMediaEngine(MediaEngineRegistrar); + + void setSize(const IntSize&) final; + + FloatSize naturalSize() const final; + +private: + GstElement* createVideoSink() final; + GstElement* audioSink() const final { return m_audioSink.get(); } + bool isLiveStream() const final { return true; } + + String engineDescription() const final { return "OpenWebRTC"; } + + void load(const String&) final; +#if ENABLE(MEDIA_SOURCE) + void load(const String&, MediaSourcePrivateClient*) final; +#endif + void load(MediaStreamPrivate&) final; + void cancelLoad() final { } + + void prepareToPlay() final { } + void play() final; + void pause() final; + + bool hasVideo() const final; + bool hasAudio() const final; + + float duration() const final { return 0; } + + float currentTime() const final; + void seek(float) final { } + bool seeking() const final { return false; } + + void setRate(float) final { } + void setPreservesPitch(bool) final { } + bool paused() const final { return m_paused; } + + void setVolume(float) final; + void setMuted(bool) final; + + bool hasClosedCaptions() const final { return false; } + void setClosedCaptionsVisible(bool) final { }; + + float maxTimeSeekable() const final { return 0; } + std::unique_ptr<PlatformTimeRanges> buffered() const final { return std::make_unique<PlatformTimeRanges>(); } + bool didLoadingProgress() const final; + + unsigned long long totalBytes() const final { return 0; } + + bool canLoadPoster() const final { return false; } + void setPoster(const String&) final { } + bool ended() const final { return m_ended; } + + // MediaStreamTrackPrivate::Observer implementation. + void trackEnded(MediaStreamTrackPrivate&) final; + void trackMutedChanged(MediaStreamTrackPrivate&) final; + void trackSettingsChanged(MediaStreamTrackPrivate&) final; + void trackEnabledChanged(MediaStreamTrackPrivate&) final; + + static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&); + static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&); + static bool initializeGStreamerAndGStreamerDebugging(); + void createGSTAudioSinkBin(); + void loadingFailed(MediaPlayer::NetworkState error); + void stop(); + void maybeHandleChangeMutedState(MediaStreamTrackPrivate&); + void disableMediaTracks(); + + bool m_paused { true }; + bool m_ended { false }; + RefPtr<MediaStreamTrackPrivate> m_videoTrack; + RefPtr<MediaStreamTrackPrivate> m_audioTrack; + GRefPtr<GstElement> m_audioSink; + RefPtr<MediaStreamPrivate> m_streamPrivate; + GRefPtr<OwrGstVideoRenderer> m_videoRenderer; + GRefPtr<OwrGstAudioRenderer> m_audioRenderer; + + HashMap<String, RefPtr<AudioTrackPrivateMediaStream>> m_audioTrackMap; + HashMap<String, RefPtr<VideoTrackPrivateMediaStream>> m_videoTrackMap; +}; + +} // namespace WebCore + +#endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(GSTREAMER) && USE(OPENWEBRTC) + +#endif // MediaPlayerPrivateGStreamerOwr_h diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h new file mode 100644 index 000000000..95ed2da63 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerRequestInstallMissingPluginsCallback.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef MediaPlayerRequestInstallMissingPluginsCallback_h +#define MediaPlayerRequestInstallMissingPluginsCallback_h + +#if ENABLE(VIDEO) && USE(GSTREAMER) +#include <wtf/RefCounted.h> + +namespace WebCore { + +class MediaPlayerRequestInstallMissingPluginsCallback : public RefCounted<MediaPlayerRequestInstallMissingPluginsCallback> { + WTF_MAKE_FAST_ALLOCATED(); +public: + static Ref<MediaPlayerRequestInstallMissingPluginsCallback> create(std::function<void (uint32_t)>&& function) + { + return adoptRef(*new MediaPlayerRequestInstallMissingPluginsCallback(WTFMove(function))); + } + + void invalidate() + { + m_function = nullptr; + } + + void complete(uint32_t result) + { + if (!m_function) + return; + m_function(result); + m_function = nullptr; + } + +private: + MediaPlayerRequestInstallMissingPluginsCallback(std::function<void (uint32_t)>&& function) + : m_function(WTFMove(function)) + { + } + + std::function<void (uint32_t)> m_function; +}; + +} // namespace WebCore + +#endif // ENABLE(VIDEO) && USE(GSTREAMER) +#endif // MediaPlayerRequestInstallMissingPluginsCallback_h diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp deleted file mode 100644 index 5982f80b9..000000000 --- a/Source/WebCore/platform/graphics/gstreamer/MediaSourceGStreamer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * Copyright (C) 2013 Orange - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "MediaSourceGStreamer.h" - -#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) - -#include "SourceBufferPrivateGStreamer.h" -#include "WebKitMediaSourceGStreamer.h" -#include <wtf/gobject/GRefPtr.h> - -namespace WebCore { - -void MediaSourceGStreamer::open(PassRefPtr<HTMLMediaSource> mediaSource, WebKitMediaSrc* src) -{ - mediaSource->setPrivateAndOpen(adoptRef(*new MediaSourceGStreamer(src))); -} - -MediaSourceGStreamer::MediaSourceGStreamer(WebKitMediaSrc* src) - : m_client(adoptRef(new MediaSourceClientGstreamer(src))) - , m_duration(0.0) - , m_readyState(MediaPlayer::HaveNothing) -{ -} - -MediaSourceGStreamer::~MediaSourceGStreamer() -{ -} - -MediaSourceGStreamer::AddStatus MediaSourceGStreamer::addSourceBuffer(const ContentType& contentType, RefPtr<SourceBufferPrivate>& sourceBufferPrivate) -{ - sourceBufferPrivate = adoptRef(new SourceBufferPrivateGStreamer(m_client.get(), contentType)); - return MediaSourceGStreamer::Ok; -} - -void MediaSourceGStreamer::setDuration(double duration) -{ - ASSERT(m_client); - m_duration = duration; - m_client->didReceiveDuration(duration); -} - -void MediaSourceGStreamer::markEndOfStream(EndOfStreamStatus) -{ - ASSERT(m_client); - m_client->didFinishLoading(0); -} - -void MediaSourceGStreamer::unmarkEndOfStream() -{ - ASSERT(m_client); -} - -} -#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp deleted file mode 100644 index 7068a558c..000000000 --- a/Source/WebCore/platform/graphics/gstreamer/SourceBufferPrivateGStreamer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * Copyright (C) 2013 Orange - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SourceBufferPrivateGStreamer.h" - -#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) - -#include "ContentType.h" -#include "NotImplemented.h" - -namespace WebCore { - -SourceBufferPrivateGStreamer::SourceBufferPrivateGStreamer(PassRefPtr<MediaSourceClientGstreamer> client, const ContentType& contentType) - : m_readyState(MediaPlayer::HaveNothing) -{ - m_client = client; - m_type = contentType.type(); -} - -SourceBufferPrivate::AppendResult SourceBufferPrivateGStreamer::append(const unsigned char* data, unsigned length) -{ - AppendResult result = AppendSucceeded; - ASSERT(m_client); - m_client->didReceiveData(reinterpret_cast_ptr<const char*>(data), length, m_type); - return result; -} - -void SourceBufferPrivateGStreamer::abort() -{ - notImplemented(); -} - -void SourceBufferPrivateGStreamer::removedFromMediaSource() -{ - notImplemented(); -} - -} -#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp index 339ca37eb..02de1e8bd 100644 --- a/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/TextCombinerGStreamer.cpp @@ -76,7 +76,7 @@ static gboolean webkitTextCombinerPadEvent(GstPad*, GstObject* parent, GstEvent* static void webkit_text_combiner_init(WebKitTextCombiner* combiner) { - combiner->funnel = gst_element_factory_make("funnel", NULL); + combiner->funnel = gst_element_factory_make("funnel", nullptr); ASSERT(combiner->funnel); gboolean ret = gst_bin_add(GST_BIN(combiner), combiner->funnel); @@ -147,7 +147,7 @@ static gboolean webkitTextCombinerPadEvent(GstPad* pad, GstObject* parent, GstEv * the funnel */ if (targetParent == combiner->funnel) { /* Setup a WebVTT encoder */ - GstElement* encoder = gst_element_factory_make("webvttenc", NULL); + GstElement* encoder = gst_element_factory_make("webvttenc", nullptr); ASSERT(encoder); ret = gst_bin_add(GST_BIN(combiner), encoder); @@ -232,7 +232,7 @@ static GstPad* webkitTextCombinerRequestNewPad(GstElement * element, GstPad* pad = gst_element_request_pad(combiner->funnel, templ, name, caps); ASSERT(pad); - GstPad* ghostPad = GST_PAD(g_object_new(WEBKIT_TYPE_TEXT_COMBINER_PAD, "direction", gst_pad_get_direction(pad), NULL)); + GstPad* ghostPad = GST_PAD(g_object_new(WEBKIT_TYPE_TEXT_COMBINER_PAD, "direction", gst_pad_get_direction(pad), nullptr)); ASSERT(ghostPad); ret = gst_ghost_pad_construct(GST_GHOST_PAD(ghostPad)); @@ -295,7 +295,7 @@ static void webkit_text_combiner_pad_class_init(WebKitTextCombinerPadClass* klas GstElement* webkitTextCombinerNew() { - return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_COMBINER, 0)); + return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_COMBINER, nullptr)); } #endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp index 678e7ac35..e651debfe 100644 --- a/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/TextSinkGStreamer.cpp @@ -95,7 +95,7 @@ static void webkit_text_sink_class_init(WebKitTextSinkClass* klass) GstElement* webkitTextSinkNew() { - return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_SINK, 0)); + return GST_ELEMENT(g_object_new(WEBKIT_TYPE_TEXT_SINK, nullptr)); } #endif // ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp index 16bd494a9..af068cb08 100644 --- a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.cpp @@ -34,46 +34,25 @@ #include "TrackPrivateBase.h" #include <glib-object.h> #include <gst/gst.h> -#include <wtf/gobject/GUniquePtr.h> +#include <gst/tag/tag.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug); #define GST_CAT_DEFAULT webkit_media_player_debug namespace WebCore { -static void trackPrivateActiveChangedCallback(GObject*, GParamSpec*, TrackPrivateBaseGStreamer* track) -{ - track->activeChanged(); -} - -static void trackPrivateTagsChangedCallback(GObject*, GParamSpec*, TrackPrivateBaseGStreamer* track) -{ - track->tagsChanged(); -} - -static gboolean trackPrivateActiveChangeTimeoutCallback(TrackPrivateBaseGStreamer* track) -{ - track->notifyTrackOfActiveChanged(); - return FALSE; -} - -static gboolean trackPrivateTagsChangeTimeoutCallback(TrackPrivateBaseGStreamer* track) -{ - track->notifyTrackOfTagsChanged(); - return FALSE; -} - TrackPrivateBaseGStreamer::TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad> pad) - : m_index(index) + : m_notifier(MainThreadNotifier<MainThreadNotification>::create()) + , m_index(index) , m_pad(pad) , m_owner(owner) - , m_activeTimerHandler(0) - , m_tagTimerHandler(0) { ASSERT(m_pad); - g_signal_connect(m_pad.get(), "notify::active", G_CALLBACK(trackPrivateActiveChangedCallback), this); - g_signal_connect(m_pad.get(), "notify::tags", G_CALLBACK(trackPrivateTagsChangedCallback), this); + g_signal_connect_swapped(m_pad.get(), "notify::active", G_CALLBACK(activeChangedCallback), this); + g_signal_connect_swapped(m_pad.get(), "notify::tags", G_CALLBACK(tagsChangedCallback), this); // We can't call notifyTrackOfTagsChanged() directly, because we need tagsChanged() // to setup m_tags. @@ -83,6 +62,7 @@ TrackPrivateBaseGStreamer::TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gi TrackPrivateBaseGStreamer::~TrackPrivateBaseGStreamer() { disconnect(); + m_notifier->invalidate(); } void TrackPrivateBaseGStreamer::disconnect() @@ -90,67 +70,71 @@ void TrackPrivateBaseGStreamer::disconnect() if (!m_pad) return; - g_signal_handlers_disconnect_by_func(m_pad.get(), - reinterpret_cast<gpointer>(trackPrivateActiveChangedCallback), this); - g_signal_handlers_disconnect_by_func(m_pad.get(), - reinterpret_cast<gpointer>(trackPrivateTagsChangedCallback), this); - - if (m_activeTimerHandler) - g_source_remove(m_activeTimerHandler); - m_activeTimerHandler = 0; - - if (m_tagTimerHandler) - g_source_remove(m_tagTimerHandler); - m_tagTimerHandler = 0; + m_notifier->cancelPendingNotifications(); + g_signal_handlers_disconnect_matched(m_pad.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); m_pad.clear(); m_tags.clear(); } -void TrackPrivateBaseGStreamer::activeChanged() +void TrackPrivateBaseGStreamer::activeChangedCallback(TrackPrivateBaseGStreamer* track) { - if (m_activeTimerHandler) - g_source_remove(m_activeTimerHandler); - m_activeTimerHandler = g_timeout_add(0, - reinterpret_cast<GSourceFunc>(trackPrivateActiveChangeTimeoutCallback), this); - g_source_set_name_by_id(m_activeTimerHandler, "[WebKit] trackPrivateActiveChangeTimeoutCallback"); + track->m_notifier->notify(MainThreadNotification::ActiveChanged, [track] { track->notifyTrackOfActiveChanged(); }); } -void TrackPrivateBaseGStreamer::tagsChanged() +void TrackPrivateBaseGStreamer::tagsChangedCallback(TrackPrivateBaseGStreamer* track) { - if (m_tagTimerHandler) - g_source_remove(m_tagTimerHandler); + track->tagsChanged(); +} +void TrackPrivateBaseGStreamer::tagsChanged() +{ GRefPtr<GstTagList> tags; - g_object_get(m_pad.get(), "tags", &tags.outPtr(), NULL); + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_pad.get()), "tags")) + g_object_get(m_pad.get(), "tags", &tags.outPtr(), nullptr); + else + tags = adoptGRef(gst_tag_list_new_empty()); + { - MutexLocker lock(m_tagMutex); + LockHolder lock(m_tagMutex); m_tags.swap(tags); } - m_tagTimerHandler = g_timeout_add(0, - reinterpret_cast<GSourceFunc>(trackPrivateTagsChangeTimeoutCallback), this); - g_source_set_name_by_id(m_tagTimerHandler, "[WebKit] trackPrivateTagsChangeTimeoutCallback"); + m_notifier->notify(MainThreadNotification::TagsChanged, [this] { notifyTrackOfTagsChanged(); }); } void TrackPrivateBaseGStreamer::notifyTrackOfActiveChanged() { - m_activeTimerHandler = 0; if (!m_pad) return; gboolean active = false; - if (m_pad) - g_object_get(m_pad.get(), "active", &active, NULL); + if (m_pad && g_object_class_find_property(G_OBJECT_GET_CLASS(m_pad.get()), "active")) + g_object_get(m_pad.get(), "active", &active, nullptr); setActive(active); } -bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, String& value) +bool TrackPrivateBaseGStreamer::getLanguageCode(GstTagList* tags, AtomicString& value) +{ + String language; + if (getTag(tags, GST_TAG_LANGUAGE_CODE, language)) { + language = gst_tag_get_language_code_iso_639_1(language.utf8().data()); + GST_INFO("Converted track %d's language code to %s.", m_index, language.utf8().data()); + if (language != value) { + value = language; + return true; + } + } + return false; +} + +template<class StringType> +bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, StringType& value) { GUniqueOutPtr<gchar> tagValue; if (gst_tag_list_get_string(tags, tagName, &tagValue.outPtr())) { - INFO_MEDIA_MESSAGE("Track %d got %s %s.", m_index, tagName, tagValue.get()); + GST_INFO("Track %d got %s %s.", m_index, tagName, tagValue.get()); value = tagValue.get(); return true; } @@ -159,24 +143,33 @@ bool TrackPrivateBaseGStreamer::getTag(GstTagList* tags, const gchar* tagName, S void TrackPrivateBaseGStreamer::notifyTrackOfTagsChanged() { - m_tagTimerHandler = 0; if (!m_pad) return; TrackPrivateBaseClient* client = m_owner->client(); + if (!client) + return; + GRefPtr<GstTagList> tags; { - MutexLocker lock(m_tagMutex); + LockHolder lock(m_tagMutex); tags.swap(m_tags); } if (!tags) return; - if (getTag(tags.get(), GST_TAG_TITLE, m_label) && client) - client->labelChanged(m_owner, m_label); + if (getTag(tags.get(), GST_TAG_TITLE, m_label)) + client->labelChanged(m_label); + + AtomicString language; + if (!getLanguageCode(tags.get(), language)) + return; + + if (language == m_language) + return; - if (getTag(tags.get(), GST_TAG_LANGUAGE_CODE, m_language) && client) - client->languageChanged(m_owner, m_language); + m_language = language; + client->languageChanged(m_language); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h index 1e3b8c898..8e3488497 100644 --- a/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/TrackPrivateBaseGStreamer.h @@ -29,6 +29,8 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(VIDEO_TRACK) #include "GRefPtrGStreamer.h" +#include "MainThreadNotifier.h" +#include <wtf/Lock.h> #include <wtf/ThreadingPrimitives.h> #include <wtf/text/WTFString.h> @@ -48,28 +50,38 @@ public: void setIndex(int index) { m_index = index; } - void activeChanged(); - void tagsChanged(); +protected: + TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad>); void notifyTrackOfActiveChanged(); void notifyTrackOfTagsChanged(); -protected: - TrackPrivateBaseGStreamer(TrackPrivateBase* owner, gint index, GRefPtr<GstPad>); + enum MainThreadNotification { + ActiveChanged = 1 << 0, + TagsChanged = 1 << 1, + NewSample = 1 << 2, + StreamChanged = 1 << 3 + }; + Ref<MainThreadNotifier<MainThreadNotification>> m_notifier; gint m_index; - String m_label; - String m_language; + AtomicString m_label; + AtomicString m_language; GRefPtr<GstPad> m_pad; private: - bool getTag(GstTagList* tags, const gchar* tagName, String& value); + bool getLanguageCode(GstTagList* tags, AtomicString& value); - TrackPrivateBase* m_owner; - guint m_activeTimerHandler; - guint m_tagTimerHandler; + template<class StringType> + bool getTag(GstTagList* tags, const gchar* tagName, StringType& value); + + static void activeChangedCallback(TrackPrivateBaseGStreamer*); + static void tagsChangedCallback(TrackPrivateBaseGStreamer*); - Mutex m_tagMutex; + void tagsChanged(); + + TrackPrivateBase* m_owner; + Lock m_tagMutex; GRefPtr<GstTagList> m_tags; }; diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp index 7dd894e28..9adaaa0bb 100644 --- a/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp @@ -1,7 +1,8 @@ /* * Copyright (C) 2007 OpenedHand * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L + * Copyright (C) 2009, 2010, 2011, 2012, 2015, 2016 Igalia S.L + * Copyright (C) 2015, 2016 Metrological Group B.V. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -35,8 +36,8 @@ #include <glib.h> #include <gst/gst.h> #include <gst/video/gstvideometa.h> -#include <wtf/OwnPtr.h> -#include <wtf/gobject/GMutexLocker.h> +#include <wtf/Condition.h> +#include <wtf/RunLoop.h> using namespace WebCore; @@ -46,13 +47,8 @@ using namespace WebCore; #else #define GST_CAPS_FORMAT "{ xRGB, ARGB }" #endif -#if GST_CHECK_VERSION(1, 1, 0) -#define GST_FEATURED_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, GST_CAPS_FORMAT) ";" -#else -#define GST_FEATURED_CAPS -#endif -#define WEBKIT_VIDEO_SINK_PAD_CAPS GST_FEATURED_CAPS GST_VIDEO_CAPS_MAKE(GST_CAPS_FORMAT) +#define WEBKIT_VIDEO_SINK_PAD_CAPS GST_VIDEO_CAPS_MAKE_WITH_FEATURES(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, GST_CAPS_FORMAT) ";" GST_VIDEO_CAPS_MAKE(GST_CAPS_FORMAT) static GstStaticPadTemplate s_sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(WEBKIT_VIDEO_SINK_PAD_CAPS)); @@ -62,35 +58,123 @@ GST_DEBUG_CATEGORY_STATIC(webkitVideoSinkDebug); enum { REPAINT_REQUESTED, + REPAINT_CANCELLED, LAST_SIGNAL }; -enum { - PROP_0, - PROP_CAPS -}; - static guint webkitVideoSinkSignals[LAST_SIGNAL] = { 0, }; -struct _WebKitVideoSinkPrivate { - GstBuffer* buffer; - guint timeoutId; - GMutex* bufferMutex; - GCond* dataCondition; +static void webkitVideoSinkRepaintRequested(WebKitVideoSink*, GstSample*); +static GRefPtr<GstSample> webkitVideoSinkRequestRender(WebKitVideoSink*, GstBuffer*); - GstVideoInfo info; +class VideoRenderRequestScheduler { +public: + VideoRenderRequestScheduler() +#if !USE(COORDINATED_GRAPHICS_THREADED) + : m_timer(RunLoop::main(), this, &VideoRenderRequestScheduler::render) +#endif + { +#if PLATFORM(GTK) && !USE(COORDINATED_GRAPHICS_THREADED) + // Use a higher priority than WebCore timers (G_PRIORITY_HIGH_IDLE + 20). + m_timer.setPriority(G_PRIORITY_HIGH_IDLE + 19); +#endif + } - GstCaps* currentCaps; + void start() + { + LockHolder locker(m_sampleMutex); + m_unlocked = false; + } + + void stop() + { + LockHolder locker(m_sampleMutex); + m_sample = nullptr; + m_unlocked = true; +#if !USE(COORDINATED_GRAPHICS_THREADED) + m_timer.stop(); + m_dataCondition.notifyOne(); +#endif + } + + void drain() + { + LockHolder locker(m_sampleMutex); + m_sample = nullptr; + } + + bool requestRender(WebKitVideoSink* sink, GstBuffer* buffer) + { + LockHolder locker(m_sampleMutex); + if (m_unlocked) + return true; + + m_sample = webkitVideoSinkRequestRender(sink, buffer); + if (!m_sample) + return false; + +#if USE(COORDINATED_GRAPHICS_THREADED) + auto sample = WTFMove(m_sample); + locker.unlockEarly(); + if (LIKELY(GST_IS_SAMPLE(sample.get()))) + webkitVideoSinkRepaintRequested(sink, sample.get()); +#else + m_sink = sink; + m_timer.startOneShot(0); + m_dataCondition.wait(m_sampleMutex); +#endif + return true; + } - // If this is TRUE all processing should finish ASAP +private: + +#if !USE(COORDINATED_GRAPHICS_THREADED) + void render() + { + LockHolder locker(m_sampleMutex); + GRefPtr<GstSample> sample = WTFMove(m_sample); + GRefPtr<WebKitVideoSink> sink = WTFMove(m_sink); + if (sample && !m_unlocked && LIKELY(GST_IS_SAMPLE(sample.get()))) + webkitVideoSinkRepaintRequested(sink.get(), sample.get()); + m_dataCondition.notifyOne(); + } +#endif + + Lock m_sampleMutex; + GRefPtr<GstSample> m_sample; + +#if !USE(COORDINATED_GRAPHICS_THREADED) + RunLoop::Timer<VideoRenderRequestScheduler> m_timer; + Condition m_dataCondition; + GRefPtr<WebKitVideoSink> m_sink; +#endif + + // If this is true all processing should finish ASAP // This is necessary because there could be a race between // unlock() and render(), where unlock() wins, signals the - // GCond, then render() tries to render a frame although + // Condition, then render() tries to render a frame although // everything else isn't running anymore. This will lead // to deadlocks because render() holds the stream lock. // - // Protected by the buffer mutex - bool unlocked; + // Protected by the sample mutex + bool m_unlocked { false }; +}; + +struct _WebKitVideoSinkPrivate { + _WebKitVideoSinkPrivate() + { + gst_video_info_init(&info); + } + + ~_WebKitVideoSinkPrivate() + { + if (currentCaps) + gst_caps_unref(currentCaps); + } + + VideoRenderRequestScheduler scheduler; + GstVideoInfo info; + GstCaps* currentCaps; }; #define webkit_video_sink_parent_class parent_class @@ -100,59 +184,29 @@ G_DEFINE_TYPE_WITH_CODE(WebKitVideoSink, webkit_video_sink, GST_TYPE_VIDEO_SINK, static void webkit_video_sink_init(WebKitVideoSink* sink) { sink->priv = G_TYPE_INSTANCE_GET_PRIVATE(sink, WEBKIT_TYPE_VIDEO_SINK, WebKitVideoSinkPrivate); -#if GLIB_CHECK_VERSION(2, 31, 0) - sink->priv->dataCondition = new GCond; - g_cond_init(sink->priv->dataCondition); - sink->priv->bufferMutex = new GMutex; - g_mutex_init(sink->priv->bufferMutex); -#else - sink->priv->dataCondition = g_cond_new(); - sink->priv->bufferMutex = g_mutex_new(); -#endif - - gst_video_info_init(&sink->priv->info); + g_object_set(GST_BASE_SINK(sink), "enable-last-sample", FALSE, nullptr); + new (sink->priv) WebKitVideoSinkPrivate(); } -static gboolean webkitVideoSinkTimeoutCallback(gpointer data) +static void webkitVideoSinkRepaintRequested(WebKitVideoSink* sink, GstSample* sample) { - WebKitVideoSink* sink = reinterpret_cast<WebKitVideoSink*>(data); - WebKitVideoSinkPrivate* priv = sink->priv; - - WTF::GMutexLocker lock(priv->bufferMutex); - GstBuffer* buffer = priv->buffer; - priv->buffer = 0; - priv->timeoutId = 0; - - if (!buffer || priv->unlocked || UNLIKELY(!GST_IS_BUFFER(buffer))) { - g_cond_signal(priv->dataCondition); - return FALSE; - } - - g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_REQUESTED], 0, buffer); - gst_buffer_unref(buffer); - g_cond_signal(priv->dataCondition); + g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_REQUESTED], 0, sample); +} - return FALSE; +static void webkitVideoSinkRepaintCancelled(WebKitVideoSink* sink) +{ + g_signal_emit(sink, webkitVideoSinkSignals[REPAINT_CANCELLED], 0); } -static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer) +static GRefPtr<GstSample> webkitVideoSinkRequestRender(WebKitVideoSink* sink, GstBuffer* buffer) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); WebKitVideoSinkPrivate* priv = sink->priv; - - WTF::GMutexLocker lock(priv->bufferMutex); - - if (priv->unlocked) - return GST_FLOW_OK; - - priv->buffer = gst_buffer_ref(buffer); + GRefPtr<GstSample> sample = adoptGRef(gst_sample_new(buffer, priv->currentCaps, nullptr, nullptr)); // The video info structure is valid only if the sink handled an allocation query. GstVideoFormat format = GST_VIDEO_INFO_FORMAT(&priv->info); - if (format == GST_VIDEO_FORMAT_UNKNOWN) { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } + if (format == GST_VIDEO_FORMAT_UNKNOWN) + return nullptr; #if !(USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)) // Cairo's ARGB has pre-multiplied alpha while GStreamer's doesn't. @@ -166,10 +220,8 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf GstBuffer* newBuffer = WebCore::createGstBuffer(buffer); // Check if allocation failed. - if (UNLIKELY(!newBuffer)) { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } + if (UNLIKELY(!newBuffer)) + return nullptr; // We don't use Color::premultipliedARGBFromColor() here because // one function call per video pixel is just too expensive: @@ -179,15 +231,13 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf GstVideoFrame destinationFrame; if (!gst_video_frame_map(&sourceFrame, &priv->info, buffer, GST_MAP_READ)) { - gst_buffer_unref(buffer); gst_buffer_unref(newBuffer); - return GST_FLOW_ERROR; + return nullptr; } if (!gst_video_frame_map(&destinationFrame, &priv->info, newBuffer, GST_MAP_WRITE)) { gst_video_frame_unmap(&sourceFrame); - gst_buffer_unref(buffer); gst_buffer_unref(newBuffer); - return GST_FLOW_ERROR; + return nullptr; } const guint8* source = static_cast<guint8*>(GST_VIDEO_FRAME_PLANE_DATA(&sourceFrame, 0)); @@ -215,87 +265,32 @@ static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buf gst_video_frame_unmap(&sourceFrame); gst_video_frame_unmap(&destinationFrame); - gst_buffer_unref(buffer); - buffer = priv->buffer = newBuffer; - } -#endif - - // This should likely use a lower priority, but glib currently starves - // lower priority sources. - // See: https://bugzilla.gnome.org/show_bug.cgi?id=610830. - priv->timeoutId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, webkitVideoSinkTimeoutCallback, - gst_object_ref(sink), reinterpret_cast<GDestroyNotify>(gst_object_unref)); - g_source_set_name_by_id(priv->timeoutId, "[WebKit] webkitVideoSinkTimeoutCallback"); - - g_cond_wait(priv->dataCondition, priv->bufferMutex); - return GST_FLOW_OK; -} - -static void webkitVideoSinkDispose(GObject* object) -{ - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); - WebKitVideoSinkPrivate* priv = sink->priv; - - if (priv->dataCondition) { -#if GLIB_CHECK_VERSION(2, 31, 0) - g_cond_clear(priv->dataCondition); - delete priv->dataCondition; -#else - g_cond_free(priv->dataCondition); -#endif - priv->dataCondition = 0; + sample = adoptGRef(gst_sample_new(newBuffer, priv->currentCaps, nullptr, nullptr)); + gst_buffer_unref(newBuffer); } - - if (priv->bufferMutex) { -#if GLIB_CHECK_VERSION(2, 31, 0) - g_mutex_clear(priv->bufferMutex); - delete priv->bufferMutex; -#else - g_mutex_free(priv->bufferMutex); #endif - priv->bufferMutex = 0; - } - G_OBJECT_CLASS(parent_class)->dispose(object); + return sample; } -static void webkitVideoSinkGetProperty(GObject* object, guint propertyId, GValue* value, GParamSpec* parameterSpec) +static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(object); - WebKitVideoSinkPrivate* priv = sink->priv; - - switch (propertyId) { - case PROP_CAPS: { - GstCaps* caps = priv->currentCaps; - if (caps) - gst_caps_ref(caps); - g_value_take_boxed(value, caps); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, parameterSpec); - } + WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); + return sink->priv->scheduler.requestRender(sink, buffer) ? GST_FLOW_OK : GST_FLOW_ERROR; } -static void unlockBufferMutex(WebKitVideoSinkPrivate* priv) +static void webkitVideoSinkFinalize(GObject* object) { - WTF::GMutexLocker lock(priv->bufferMutex); - - if (priv->buffer) { - gst_buffer_unref(priv->buffer); - priv->buffer = 0; - } - - priv->unlocked = true; - - g_cond_signal(priv->dataCondition); + WEBKIT_VIDEO_SINK(object)->priv->~WebKitVideoSinkPrivate(); + G_OBJECT_CLASS(parent_class)->finalize(object); } static gboolean webkitVideoSinkUnlock(GstBaseSink* baseSink) { - WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); + WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - unlockBufferMutex(sink->priv); + priv->scheduler.stop(); + webkitVideoSinkRepaintCancelled(WEBKIT_VIDEO_SINK(baseSink)); return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock, (baseSink), TRUE); } @@ -304,10 +299,7 @@ static gboolean webkitVideoSinkUnlockStop(GstBaseSink* baseSink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - { - WTF::GMutexLocker lock(priv->bufferMutex); - priv->unlocked = false; - } + priv->scheduler.start(); return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, unlock_stop, (baseSink), TRUE); } @@ -316,11 +308,11 @@ static gboolean webkitVideoSinkStop(GstBaseSink* baseSink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - unlockBufferMutex(priv); - + priv->scheduler.stop(); + webkitVideoSinkRepaintCancelled(WEBKIT_VIDEO_SINK(baseSink)); if (priv->currentCaps) { gst_caps_unref(priv->currentCaps); - priv->currentCaps = 0; + priv->currentCaps = nullptr; } return TRUE; @@ -330,8 +322,8 @@ static gboolean webkitVideoSinkStart(GstBaseSink* baseSink) { WebKitVideoSinkPrivate* priv = WEBKIT_VIDEO_SINK(baseSink)->priv; - WTF::GMutexLocker lock(priv->bufferMutex); - priv->unlocked = false; + priv->scheduler.start(); + return TRUE; } @@ -357,7 +349,7 @@ static gboolean webkitVideoSinkSetCaps(GstBaseSink* baseSink, GstCaps* caps) static gboolean webkitVideoSinkProposeAllocation(GstBaseSink* baseSink, GstQuery* query) { GstCaps* caps; - gst_query_parse_allocation(query, &caps, 0); + gst_query_parse_allocation(query, &caps, nullptr); if (!caps) return FALSE; @@ -365,14 +357,27 @@ static gboolean webkitVideoSinkProposeAllocation(GstBaseSink* baseSink, GstQuery if (!gst_video_info_from_caps(&sink->priv->info, caps)) return FALSE; - gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, 0); - gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, 0); -#if GST_CHECK_VERSION(1, 1, 0) - gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, 0); -#endif + gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr); + gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, nullptr); + gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, nullptr); return TRUE; } +static gboolean webkitVideoSinkEvent(GstBaseSink* baseSink, GstEvent* event) +{ + switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_FLUSH_START: { + WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink); + sink->priv->scheduler.drain(); + + GST_DEBUG_OBJECT(sink, "Flush-start, releasing m_sample"); + } + FALLTHROUGH; + default: + return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SINK_CLASS, event, (baseSink, event), TRUE); + } +} + static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) { GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); @@ -384,8 +389,7 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) g_type_class_add_private(klass, sizeof(WebKitVideoSinkPrivate)); - gobjectClass->dispose = webkitVideoSinkDispose; - gobjectClass->get_property = webkitVideoSinkGetProperty; + gobjectClass->finalize = webkitVideoSinkFinalize; baseSinkClass->unlock = webkitVideoSinkUnlock; baseSinkClass->unlock_stop = webkitVideoSinkUnlockStop; @@ -395,9 +399,7 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) baseSinkClass->start = webkitVideoSinkStart; baseSinkClass->set_caps = webkitVideoSinkSetCaps; baseSinkClass->propose_allocation = webkitVideoSinkProposeAllocation; - - g_object_class_install_property(gobjectClass, PROP_CAPS, - g_param_spec_boxed("current-caps", "Current-Caps", "Current caps", GST_TYPE_CAPS, G_PARAM_READABLE)); + baseSinkClass->event = webkitVideoSinkEvent; webkitVideoSinkSignals[REPAINT_REQUESTED] = g_signal_new("repaint-requested", G_TYPE_FROM_CLASS(klass), @@ -408,13 +410,23 @@ static void webkit_video_sink_class_init(WebKitVideoSinkClass* klass) g_cclosure_marshal_generic, G_TYPE_NONE, // Return type 1, // Only one parameter - GST_TYPE_BUFFER); + GST_TYPE_SAMPLE); + webkitVideoSinkSignals[REPAINT_CANCELLED] = g_signal_new("repaint-cancelled", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, // Class offset + nullptr, // Accumulator + nullptr, // Accumulator data + g_cclosure_marshal_generic, + G_TYPE_NONE, // Return type + 0, // No parameters + G_TYPE_NONE); } GstElement* webkitVideoSinkNew() { - return GST_ELEMENT(g_object_new(WEBKIT_TYPE_VIDEO_SINK, 0)); + return GST_ELEMENT(g_object_new(WEBKIT_TYPE_VIDEO_SINK, nullptr)); } #endif // ENABLE(VIDEO) && USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp new file mode 100644 index 000000000..abb8b9bcd --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.cpp @@ -0,0 +1,190 @@ +/* + Copyright (C) 2016 Igalia S.L. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 "VideoTextureCopierGStreamer.h" + +#if USE(GSTREAMER_GL) + +#include "GLContext.h" +#include "ImageOrientation.h" +#include "TextureMapperShaderProgram.h" + +namespace WebCore { + +VideoTextureCopierGStreamer::VideoTextureCopierGStreamer() +{ + GLContext* previousContext = GLContext::current(); + ASSERT(previousContext); + PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent(); + + m_context3D = GraphicsContext3D::createForCurrentGLContext(); + + m_shaderProgram = TextureMapperShaderProgram::create(*m_context3D, TextureMapperShaderProgram::Texture); + + m_framebuffer = m_context3D->createFramebuffer(); + + static const GLfloat vertices[] = { 0, 0, 1, 0, 1, 1, 0, 1 }; + m_vbo = m_context3D->createBuffer(); + m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vbo); + m_context3D->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, vertices, GraphicsContext3D::STATIC_DRAW); + + updateTextureSpaceMatrix(); + + previousContext->makeContextCurrent(); +} + +VideoTextureCopierGStreamer::~VideoTextureCopierGStreamer() +{ + GLContext* previousContext = GLContext::current(); + ASSERT(previousContext); + PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent(); + + m_context3D->deleteFramebuffer(m_framebuffer); + m_context3D->deleteBuffer(m_vbo); + m_shaderProgram = nullptr; + m_context3D = nullptr; + + previousContext->makeContextCurrent(); +} + +void VideoTextureCopierGStreamer::updateTextureSpaceMatrix() +{ + m_textureSpaceMatrix.makeIdentity(); + + switch (m_orientation) { + case OriginRightTop: + m_textureSpaceMatrix.rotate(-90); + m_textureSpaceMatrix.translate(-1, 0); + break; + case OriginBottomRight: + m_textureSpaceMatrix.rotate(180); + m_textureSpaceMatrix.translate(-1, -1); + break; + case OriginLeftBottom: + m_textureSpaceMatrix.rotate(-270); + m_textureSpaceMatrix.translate(0, -1); + break; + default: + ASSERT_NOT_REACHED(); + } + + if (!m_flipY) { + m_textureSpaceMatrix.flipY(); + m_textureSpaceMatrix.translate(0, -1); + } +} + +void VideoTextureCopierGStreamer::updateTransformationMatrix() +{ + FloatRect targetRect = FloatRect(FloatPoint(), m_size); + TransformationMatrix identityMatrix; + m_modelViewMatrix = TransformationMatrix(identityMatrix).multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect)); + + // Taken from TextureMapperGL. + const float nearValue = 9999999; + const float farValue = -99999; + + m_projectionMatrix = TransformationMatrix(2.0 / float(m_size.width()), 0, 0, 0, + 0, (-2.0) / float(m_size.height()), 0, 0, + 0, 0, -2.f / (farValue - nearValue), 0, + -1, 1, -(farValue + nearValue) / (farValue - nearValue), 1); +} + +bool VideoTextureCopierGStreamer::copyVideoTextureToPlatformTexture(Platform3DObject inputTexture, IntSize& frameSize, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool flipY, ImageOrientation& sourceOrientation) +{ + if (!m_shaderProgram || !m_framebuffer || !m_vbo || frameSize.isEmpty()) + return false; + + if (m_size != frameSize) { + m_size = frameSize; + updateTransformationMatrix(); + } + + if (m_flipY != flipY || m_orientation != sourceOrientation) { + m_flipY = flipY; + m_orientation = sourceOrientation; + updateTextureSpaceMatrix(); + } + + // Save previous context and activate the sharing one. + GLContext* previousContext = GLContext::current(); + ASSERT(previousContext); + PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent(); + + // Save previous bound framebuffer, texture and viewport. + GC3Dint boundFramebuffer = 0; + GC3Dint boundTexture = 0; + GC3Dint previousViewport[4] = { 0, 0, 0, 0}; + m_context3D->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &boundFramebuffer); + m_context3D->getIntegerv(GraphicsContext3D::TEXTURE_BINDING_2D, &boundTexture); + m_context3D->getIntegerv(GraphicsContext3D::VIEWPORT, previousViewport); + + // Set proper parameters to the output texture and allocate uninitialized memory for it. + m_context3D->bindTexture(outputTarget, outputTexture); + m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + m_context3D->texImage2DDirect(outputTarget, level, internalFormat, m_size.width(), m_size.height(), 0, format, type, nullptr); + + // Bind framebuffer to paint and attach the destination texture to it. + m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebuffer); + m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0); + + // Set proper wrap parameter to the source texture. + m_context3D->bindTexture(GL_TEXTURE_2D, inputTexture); + m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + m_context3D->texParameterf(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + + // Set the viewport. + m_context3D->viewport(0, 0, m_size.width(), m_size.height()); + + // Set program parameters. + m_context3D->useProgram(m_shaderProgram->programID()); + m_context3D->uniform1i(m_shaderProgram->samplerLocation(), 0); + m_shaderProgram->setMatrix(m_shaderProgram->modelViewMatrixLocation(), m_modelViewMatrix); + m_shaderProgram->setMatrix(m_shaderProgram->projectionMatrixLocation(), m_projectionMatrix); + m_shaderProgram->setMatrix(m_shaderProgram->textureSpaceMatrixLocation(), m_textureSpaceMatrix); + + // Perform the copy. + m_context3D->enableVertexAttribArray(m_shaderProgram->vertexLocation()); + m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vbo); + m_context3D->vertexAttribPointer(m_shaderProgram->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0); + m_context3D->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4); + m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0); + m_context3D->disableVertexAttribArray(m_shaderProgram->vertexLocation()); + m_context3D->useProgram(0); + + // Restore previous bindings and viewport. + m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, boundFramebuffer); + m_context3D->bindTexture(outputTarget, boundTexture); + m_context3D->viewport(previousViewport[0], previousViewport[1], previousViewport[2], previousViewport[3]); + + bool ok = (m_context3D->getError() == GraphicsContext3D::NO_ERROR); + + // Restore previous context. + previousContext->makeContextCurrent(); + return ok; +} + +} // namespace WebCore + +#endif // USE(GSTREAMER_GL) diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h new file mode 100644 index 000000000..945a7b3b4 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/VideoTextureCopierGStreamer.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2016 Igalia S.L. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 VideoTextureCopierGStreamer_h +#define VideoTextureCopierGStreamer_h + +#if USE(GSTREAMER_GL) + +#include "GraphicsContext3D.h" +#include "TransformationMatrix.h" + +namespace WebCore { + +class TextureMapperShaderProgram; +class ImageOrientation; + +class VideoTextureCopierGStreamer { +public: + VideoTextureCopierGStreamer(); + ~VideoTextureCopierGStreamer(); + + bool copyVideoTextureToPlatformTexture(Platform3DObject inputTexture, IntSize& frameSize, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool flipY, ImageOrientation& sourceOrientation); + void updateTextureSpaceMatrix(); + void updateTransformationMatrix(); + +private: + RefPtr<GraphicsContext3D> m_context3D; + RefPtr<TextureMapperShaderProgram> m_shaderProgram; + Platform3DObject m_framebuffer { 0 }; + Platform3DObject m_vbo { 0 }; + bool m_flipY { false }; + ImageOrientation m_orientation; + IntSize m_size; + TransformationMatrix m_modelViewMatrix; + TransformationMatrix m_projectionMatrix; + TransformationMatrix m_textureSpaceMatrix; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER_GL) + +#endif // VideoTextureCopierGStreamer_h diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp index e3652c350..a6f94b82c 100644 --- a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp @@ -37,6 +37,8 @@ VideoTrackPrivateGStreamer::VideoTrackPrivateGStreamer(GRefPtr<GstElement> playb : TrackPrivateBaseGStreamer(this, index, pad) , m_playbin(playbin) { + // FIXME: Get a real ID from the tkhd atom. + m_id = "V" + String::number(index); notifyTrackOfActiveChanged(); } @@ -53,7 +55,7 @@ void VideoTrackPrivateGStreamer::setSelected(bool selected) VideoTrackPrivate::setSelected(selected); if (selected && m_playbin) - g_object_set(m_playbin.get(), "current-video", m_index, NULL); + g_object_set(m_playbin.get(), "current-video", m_index, nullptr); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h index b216221e0..ba46a69b2 100644 --- a/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/VideoTrackPrivateGStreamer.h @@ -41,19 +41,21 @@ public: return adoptRef(new VideoTrackPrivateGStreamer(playbin, index, pad)); } - virtual void disconnect() override; + void disconnect() override; - virtual void setSelected(bool) override; - virtual void setActive(bool enabled) override { setSelected(enabled); } + void setSelected(bool) override; + void setActive(bool enabled) override { setSelected(enabled); } - virtual int trackIndex() const override { return m_index; } + int trackIndex() const override { return m_index; } - virtual AtomicString label() const override { return m_label; } - virtual AtomicString language() const override { return m_language; } + AtomicString id() const override { return m_id; } + AtomicString label() const override { return m_label; } + AtomicString language() const override { return m_language; } private: VideoTrackPrivateGStreamer(GRefPtr<GstElement> playbin, gint index, GRefPtr<GstPad>); + AtomicString m_id; GRefPtr<GstElement> m_playbin; }; diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp deleted file mode 100644 index bade219f8..000000000 --- a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.cpp +++ /dev/null @@ -1,846 +0,0 @@ -/* - * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * Copyright (C) 2013 Collabora Ltd. - * Copyright (C) 2013 Orange - * - * 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 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 "WebKitMediaSourceGStreamer.h" - -#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) - -#include "GRefPtrGStreamer.h" -#include "GStreamerUtilities.h" -#include "NotImplemented.h" -#include "TimeRanges.h" -#include <gst/app/gstappsrc.h> -#include <gst/gst.h> -#include <gst/pbutils/missing-plugins.h> -#include <wtf/gobject/GUniquePtr.h> -#include <wtf/text/CString.h> - -typedef struct _Source { - GstElement* appsrc; - guint sourceid; /* To control the GSource */ - GstPad* srcpad; - gboolean padAdded; - - guint64 offset; - guint64 size; - gboolean paused; - - guint startId; - guint stopId; - guint needDataId; - guint enoughDataId; - guint seekId; - - guint64 requestedOffset; -} Source; - - -#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate)) - -struct _WebKitMediaSrcPrivate { - gchar* uri; - Source sourceVideo; - Source sourceAudio; - WebCore::MediaPlayer* player; - GstElement* playbin; - gint64 duration; - gboolean seekable; - gboolean noMorePad; - // TRUE if appsrc's version is >= 0.10.27, see - // https://bugzilla.gnome.org/show_bug.cgi?id=609423 - gboolean haveAppSrc27; - guint nbSource; -}; - -enum { - PropLocation = 1, - ProLast -}; - -static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug); -#define GST_CAT_DEFAULT webkit_media_src_debug - -static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData); -static void webKitMediaSrcFinalize(GObject*); -static void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*); -static void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*); -static GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange); -static gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); - -static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint, gpointer); -static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer); -static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64, gpointer); -static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint, gpointer); -static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer); -static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64, gpointer); -static GstAppSrcCallbacks appsrcCallbacksVideo = { - webKitMediaVideoSrcNeedDataCb, - webKitMediaVideoSrcEnoughDataCb, - webKitMediaVideoSrcSeekDataCb, - { 0 } -}; -static GstAppSrcCallbacks appsrcCallbacksAudio = { - webKitMediaAudioSrcNeedDataCb, - webKitMediaAudioSrcEnoughDataCb, - webKitMediaAudioSrcSeekDataCb, - { 0 } -}; -#define webkit_media_src_parent_class parent_class -// We split this out into another macro to avoid a check-webkit-style error. -#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element"); -G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN, - G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit); - WEBKIT_MEDIA_SRC_CATEGORY_INIT); - -static void webkit_media_src_class_init(WebKitMediaSrcClass* klass) -{ - GObjectClass* oklass = G_OBJECT_CLASS(klass); - GstElementClass* eklass = GST_ELEMENT_CLASS(klass); - - oklass->finalize = webKitMediaSrcFinalize; - oklass->set_property = webKitMediaSrcSetProperty; - oklass->get_property = webKitMediaSrcGetProperty; - - gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate)); - - gst_element_class_set_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>"); - - /* Allows setting the uri using the 'location' property, which is used - * for example by gst_element_make_from_uri() */ - g_object_class_install_property(oklass, - PropLocation, - g_param_spec_string("location", "location", "Location to read from", 0, - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - eklass->change_state = webKitMediaSrcChangeState; - - g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate)); -} - -static void webKitMediaSrcAddSrc(WebKitMediaSrc* src, GstElement* element) -{ - GstPad* ghostPad; - WebKitMediaSrcPrivate* priv = src->priv; - - if (!gst_bin_add(GST_BIN(src), element)) { - GST_DEBUG_OBJECT(src, "Src element not added"); - return; - } - GRefPtr<GstPad> targetsrc = adoptGRef(gst_element_get_static_pad(element, "src")); - if (!targetsrc) { - GST_DEBUG_OBJECT(src, "Pad not found"); - return; - } - - gst_element_sync_state_with_parent(element); - GUniquePtr<gchar> name(g_strdup_printf("src_%u", priv->nbSource)); - ghostPad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, name.get(), targetsrc.get()); - gst_pad_set_active(ghostPad, TRUE); - - priv->nbSource++; - - if (priv->sourceVideo.appsrc == element) - priv->sourceVideo.srcpad = ghostPad; - else if (priv->sourceAudio.appsrc == element) - priv->sourceAudio.srcpad = ghostPad; - - GST_OBJECT_FLAG_SET(ghostPad, GST_PAD_FLAG_NEED_PARENT); - gst_pad_set_query_function(ghostPad, webKitMediaSrcQueryWithParent); -} - -static void webkit_media_src_init(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(src); - src->priv = priv; - - priv->sourceVideo.appsrc = gst_element_factory_make("appsrc", "videoappsrc"); - gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceVideo.appsrc), &appsrcCallbacksVideo, src, 0); - webKitMediaSrcAddSrc(src, priv->sourceVideo.appsrc); - - priv->sourceAudio.appsrc = gst_element_factory_make("appsrc", "audioappsrc"); - gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceAudio.appsrc), &appsrcCallbacksAudio, src, 0); - webKitMediaSrcAddSrc(src, priv->sourceAudio.appsrc); -} - -static void webKitMediaSrcFinalize(GObject* object) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object); - WebKitMediaSrcPrivate* priv = src->priv; - - g_free(priv->uri); - - GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); -} - -static void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object); - switch (propId) { - case PropLocation: - gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); - break; - } -} - -static void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - switch (propId) { - case PropLocation: - g_value_set_string(value, priv->uri); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); - break; - } - GST_OBJECT_UNLOCK(src); -} - -// must be called on main thread and with object unlocked -static gboolean webKitMediaVideoSrcStop(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - gboolean seeking; - - GST_OBJECT_LOCK(src); - - seeking = priv->sourceVideo.seekId; - - if (priv->sourceVideo.startId) { - g_source_remove(priv->sourceVideo.startId); - priv->sourceVideo.startId = 0; - } - - priv->player = 0; - priv->playbin = 0; - - if (priv->sourceVideo.needDataId) - g_source_remove(priv->sourceVideo.needDataId); - priv->sourceVideo.needDataId = 0; - - if (priv->sourceVideo.enoughDataId) - g_source_remove(priv->sourceVideo.enoughDataId); - priv->sourceVideo.enoughDataId = 0; - - if (priv->sourceVideo.seekId) - g_source_remove(priv->sourceVideo.seekId); - - priv->sourceVideo.seekId = 0; - - priv->sourceVideo.paused = FALSE; - priv->sourceVideo.offset = 0; - priv->seekable = FALSE; - - priv->duration = 0; - priv->nbSource = 0; - - priv->sourceVideo.stopId = 0; - - GST_OBJECT_UNLOCK(src); - - if (priv->sourceVideo.appsrc) { - gst_app_src_set_caps(GST_APP_SRC(priv->sourceVideo.appsrc), 0); - if (!seeking) - gst_app_src_set_size(GST_APP_SRC(priv->sourceVideo.appsrc), -1); - } - - GST_DEBUG_OBJECT(src, "Stopped request"); - - return FALSE; -} - -static gboolean webKitMediaAudioSrcStop(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - gboolean seeking; - - GST_OBJECT_LOCK(src); - - seeking = priv->sourceAudio.seekId; - - if (priv->sourceAudio.startId) { - g_source_remove(priv->sourceAudio.startId); - priv->sourceAudio.startId = 0; - } - - priv->player = 0; - - priv->playbin = 0; - - if (priv->sourceAudio.needDataId) - g_source_remove(priv->sourceAudio.needDataId); - priv->sourceAudio.needDataId = 0; - - if (priv->sourceAudio.enoughDataId) - g_source_remove(priv->sourceAudio.enoughDataId); - priv->sourceAudio.enoughDataId = 0; - - if (priv->sourceAudio.seekId) - g_source_remove(priv->sourceAudio.seekId); - - priv->sourceAudio.seekId = 0; - - priv->sourceAudio.paused = FALSE; - - priv->sourceAudio.offset = 0; - - priv->seekable = FALSE; - - priv->duration = 0; - priv->nbSource = 0; - - priv->sourceAudio.stopId = 0; - - GST_OBJECT_UNLOCK(src); - - if (priv->sourceAudio.appsrc) { - gst_app_src_set_caps(GST_APP_SRC(priv->sourceAudio.appsrc), 0); - if (!seeking) - gst_app_src_set_size(GST_APP_SRC(priv->sourceAudio.appsrc), -1); - } - - GST_DEBUG_OBJECT(src, "Stopped request"); - - return FALSE; -} - -// must be called on main thread and with object unlocked -static gboolean webKitMediaVideoSrcStart(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - if (!priv->uri) { - GST_ERROR_OBJECT(src, "No URI provided"); - GST_OBJECT_UNLOCK(src); - webKitMediaVideoSrcStop(src); - return FALSE; - } - - priv->sourceVideo.startId = 0; - - GST_OBJECT_UNLOCK(src); - GST_DEBUG_OBJECT(src, "Started request"); - - return FALSE; -} - -// must be called on main thread and with object unlocked -static gboolean webKitMediaAudioSrcStart(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - if (!priv->uri) { - GST_ERROR_OBJECT(src, "No URI provided"); - GST_OBJECT_UNLOCK(src); - webKitMediaAudioSrcStop(src); - return FALSE; - } - - priv->sourceAudio.startId = 0; - - GST_OBJECT_UNLOCK(src); - GST_DEBUG_OBJECT(src, "Started request"); - - return FALSE; -} - -static GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(element); - WebKitMediaSrcPrivate* priv = src->priv; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!priv->sourceVideo.appsrc && !priv->sourceAudio.appsrc) { - gst_element_post_message(element, - gst_missing_element_message_new(element, "appsrc")); - GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc")); - return GST_STATE_CHANGE_FAILURE; - } - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); - if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) { - GST_DEBUG_OBJECT(src, "State change failed"); - return ret; - } - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_DEBUG_OBJECT(src, "READY->PAUSED"); - GST_OBJECT_LOCK(src); - priv->sourceVideo.startId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceVideo.startId, "[WebKit] webKitMediaVideoSrcStart"); - priv->sourceAudio.startId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceAudio.startId, "[WebKit] webKitMediaAudioSrcStart"); - GST_OBJECT_UNLOCK(src); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG_OBJECT(src, "PAUSED->READY"); - GST_OBJECT_LOCK(src); - priv->sourceVideo.stopId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceVideo.stopId, "[WebKit] webKitMediaVideoSrcStop"); - priv->sourceAudio.stopId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceAudio.stopId, "[WebKit] webKitMediaAudioSrcStop"); - GST_OBJECT_UNLOCK(src); - break; - default: - break; - } - - return ret; -} - -static gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent)); - gboolean result = FALSE; - - switch (GST_QUERY_TYPE(query)) { - case GST_QUERY_DURATION: { - GstFormat format; - gst_query_parse_duration(query, &format, NULL); - - GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format)); - GST_OBJECT_LOCK(src); - if ((format == GST_FORMAT_TIME) && (src->priv->duration > 0)) { - gst_query_set_duration(query, format, src->priv->duration); - result = TRUE; - } - GST_OBJECT_UNLOCK(src); - break; - } - case GST_QUERY_URI: { - GST_OBJECT_LOCK(src); - gst_query_set_uri(query, src->priv->uri); - GST_OBJECT_UNLOCK(src); - result = TRUE; - break; - } - default: { - GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad))); - // Forward the query to the proxy target pad. - if (target) - result = gst_pad_query(target.get(), query); - break; - } - } - - return result; -} - -// uri handler interface -static GstURIType webKitMediaSrcUriGetType(GType) -{ - return GST_URI_SRC; -} - -const gchar* const* webKitMediaSrcGetProtocols(GType) -{ - static const char* protocols[] = {"mediasourceblob", 0 }; - return protocols; -} - -static gchar* webKitMediaSrcGetUri(GstURIHandler* handler) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler); - gchar* ret; - - GST_OBJECT_LOCK(src); - ret = g_strdup(src->priv->uri); - GST_OBJECT_UNLOCK(src); - return ret; -} - -static gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler); - WebKitMediaSrcPrivate* priv = src->priv; - if (GST_STATE(src) >= GST_STATE_PAUSED) { - GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED"); - return FALSE; - } - - GST_OBJECT_LOCK(src); - g_free(priv->uri); - priv->uri = 0; - if (!uri) { - GST_OBJECT_UNLOCK(src); - return TRUE; - } - - WebCore::URL url(WebCore::URL(), uri); - - priv->uri = g_strdup(url.string().utf8().data()); - GST_OBJECT_UNLOCK(src); - return TRUE; -} - -static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer) -{ - GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface; - - iface->get_type = webKitMediaSrcUriGetType; - iface->get_protocols = webKitMediaSrcGetProtocols; - iface->get_uri = webKitMediaSrcGetUri; - iface->set_uri = webKitMediaSrcSetUri; -} - -// appsrc callbacks -static gboolean webKitMediaVideoSrcNeedDataMainCb(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - // already stopped - if (!priv->sourceVideo.needDataId) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - - priv->sourceVideo.paused = FALSE; - priv->sourceVideo.needDataId = 0; - GST_OBJECT_UNLOCK(src); - - return FALSE; -} - -static gboolean webKitMediaAudioSrcNeedDataMainCb(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - // already stopped - if (!priv->sourceAudio.needDataId) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - - priv->sourceAudio.paused = FALSE; - priv->sourceAudio.needDataId = 0; - GST_OBJECT_UNLOCK(src); - - return FALSE; -} - -static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Need more data: %u", length); - - GST_OBJECT_LOCK(src); - if (priv->sourceVideo.needDataId || !priv->sourceVideo.paused) { - GST_OBJECT_UNLOCK(src); - return; - } - - priv->sourceVideo.needDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceVideo.needDataId, "[WebKit] webKitMediaVideoSrcNeedDataMainCb"); - GST_OBJECT_UNLOCK(src); -} - -static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Need more data: %u", length); - - GST_OBJECT_LOCK(src); - if (priv->sourceAudio.needDataId || !priv->sourceAudio.paused) { - GST_OBJECT_UNLOCK(src); - return; - } - - priv->sourceAudio.needDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceAudio.needDataId, "[WebKit] webKitMediaAudioSrcNeedDataMainCb"); - GST_OBJECT_UNLOCK(src); -} - -static gboolean webKitMediaVideoSrcEnoughDataMainCb(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - // already stopped - if (!priv->sourceVideo.enoughDataId) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - - priv->sourceVideo.paused = TRUE; - priv->sourceVideo.enoughDataId = 0; - GST_OBJECT_UNLOCK(src); - - return FALSE; -} - -static gboolean webKitMediaAudioSrcEnoughDataMainCb(WebKitMediaSrc* src) -{ - WebKitMediaSrcPrivate* priv = src->priv; - - GST_OBJECT_LOCK(src); - // already stopped - if (!priv->sourceAudio.enoughDataId) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - - priv->sourceAudio.paused = TRUE; - priv->sourceAudio.enoughDataId = 0; - GST_OBJECT_UNLOCK(src); - - return FALSE; -} - -static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer userData) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Have enough data"); - - GST_OBJECT_LOCK(src); - if (priv->sourceVideo.enoughDataId || priv->sourceVideo.paused) { - GST_OBJECT_UNLOCK(src); - return; - } - - priv->sourceVideo.enoughDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceVideo.enoughDataId, "[WebKit] webKitMediaVideoSrcEnoughDataMainCb"); - GST_OBJECT_UNLOCK(src); -} - -static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer userData) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Have enough data"); - - GST_OBJECT_LOCK(src); - if (priv->sourceAudio.enoughDataId || priv->sourceAudio.paused) { - GST_OBJECT_UNLOCK(src); - return; - } - - priv->sourceAudio.enoughDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceAudio.enoughDataId, "[WebKit] webKitMediaAudioSrcEnoughDataMainCb"); - GST_OBJECT_UNLOCK(src); -} - -static gboolean webKitMediaVideoSrcSeekMainCb(WebKitMediaSrc* src) -{ - notImplemented(); - src->priv->sourceVideo.seekId = 0; - return FALSE; -} - -static gboolean webKitMediaAudioSrcSeekMainCb(WebKitMediaSrc* src) -{ - notImplemented(); - src->priv->sourceAudio.seekId = 0; - return FALSE; -} - -static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); - GST_OBJECT_LOCK(src); - if (offset == priv->sourceVideo.offset && priv->sourceVideo.requestedOffset == priv->sourceVideo.offset) { - GST_OBJECT_UNLOCK(src); - return TRUE; - } - - if (!priv->seekable) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - if (offset > priv->sourceVideo.size) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - - GST_DEBUG_OBJECT(src, "Doing range-request seek"); - priv->sourceVideo.requestedOffset = offset; - - if (priv->sourceVideo.seekId) - g_source_remove(priv->sourceVideo.seekId); - priv->sourceVideo.seekId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceVideo.seekId, "[WebKit] webKitMediaVideoSrcSeekMainCb"); - GST_OBJECT_UNLOCK(src); - - return TRUE; -} - -static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData) -{ - WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); - WebKitMediaSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); - GST_OBJECT_LOCK(src); - if (offset == priv->sourceAudio.offset && priv->sourceAudio.requestedOffset == priv->sourceAudio.offset) { - GST_OBJECT_UNLOCK(src); - return TRUE; - } - - if (!priv->seekable) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - if (offset > priv->sourceAudio.size) { - GST_OBJECT_UNLOCK(src); - return FALSE; - } - - GST_DEBUG_OBJECT(src, "Doing range-request seek"); - priv->sourceAudio.requestedOffset = offset; - - if (priv->sourceAudio.seekId) - g_source_remove(priv->sourceAudio.seekId); - priv->sourceAudio.seekId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - g_source_set_name_by_id(priv->sourceAudio.seekId, "[WebKit] webKitMediaAudioSrcSeekMainCb"); - GST_OBJECT_UNLOCK(src); - - return TRUE; -} - -void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc* src, WebCore::MediaPlayer* player) -{ - WebKitMediaSrcPrivate* priv = src->priv; - priv->player = player; -} - -void webKitMediaSrcSetPlayBin(WebKitMediaSrc* src, GstElement* playBin) -{ - WebKitMediaSrcPrivate* priv = src->priv; - priv->playbin = playBin; -} - -MediaSourceClientGstreamer::MediaSourceClientGstreamer(WebKitMediaSrc* src) - : m_src(static_cast<WebKitMediaSrc*>(gst_object_ref(src))) -{ -} - -MediaSourceClientGstreamer::~MediaSourceClientGstreamer() -{ - gst_object_unref(m_src); -} - -void MediaSourceClientGstreamer::didReceiveDuration(double duration) -{ - WebKitMediaSrcPrivate* priv = m_src->priv; - GST_DEBUG_OBJECT(m_src, "Received duration: %lf", duration); - - GST_OBJECT_LOCK(m_src); - priv->duration = duration >= 0.0 ? static_cast<gint64>(duration*GST_SECOND) : 0; - GST_OBJECT_UNLOCK(m_src); -} - -void MediaSourceClientGstreamer::didReceiveData(const char* data, int length, String type) -{ - WebKitMediaSrcPrivate* priv = m_src->priv; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer * buffer; - - if (type.startsWith("video")) { - if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == TRUE) { - gst_element_no_more_pads(GST_ELEMENT(m_src)); - priv->noMorePad = TRUE; - } - if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == FALSE) { - gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceVideo.srcpad); - priv->sourceVideo.padAdded = TRUE; - } - GST_OBJECT_LOCK(m_src); - buffer = WebCore::createGstBufferForData(data, length); - GST_OBJECT_UNLOCK(m_src); - - ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceVideo.appsrc), buffer); - } else if (type.startsWith("audio")) { - if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == TRUE) { - gst_element_no_more_pads(GST_ELEMENT(m_src)); - priv->noMorePad = TRUE; - } - if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == FALSE) { - gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceAudio.srcpad); - priv->sourceAudio.padAdded = TRUE; - } - GST_OBJECT_LOCK(m_src); - buffer = WebCore::createGstBufferForData(data, length); - GST_OBJECT_UNLOCK(m_src); - - ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceAudio.appsrc), buffer); - } - - if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) - GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0)); -} - -void MediaSourceClientGstreamer::didFinishLoading(double) -{ - WebKitMediaSrcPrivate* priv = m_src->priv; - - GST_DEBUG_OBJECT(m_src, "Have EOS"); - - GST_OBJECT_LOCK(m_src); - if (!priv->sourceVideo.seekId) { - GST_OBJECT_UNLOCK(m_src); - gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceVideo.appsrc)); - } else - GST_OBJECT_UNLOCK(m_src); - - GST_OBJECT_LOCK(m_src); - if (!priv->sourceAudio.seekId) { - GST_OBJECT_UNLOCK(m_src); - gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceAudio.appsrc)); - } else - GST_OBJECT_UNLOCK(m_src); -} - -void MediaSourceClientGstreamer::didFail() -{ - gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceVideo.appsrc)); - gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceAudio.appsrc)); -} - -#endif // USE(GSTREAMER) - diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp index c7d8eca76..1b31b380b 100644 --- a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp @@ -22,144 +22,160 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) -#include "CachedRawResource.h" -#include "CachedRawResourceClient.h" -#include "CachedResourceHandle.h" -#include "CachedResourceLoader.h" -#include "CachedResourceRequest.h" -#include "CrossOriginAccessControl.h" #include "GRefPtrGStreamer.h" #include "GStreamerUtilities.h" +#include "GUniquePtrGStreamer.h" +#include "HTTPHeaderNames.h" +#include "MainThreadNotifier.h" #include "MediaPlayer.h" #include "NotImplemented.h" +#include "PlatformMediaResourceLoader.h" +#include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceHandleClient.h" #include "ResourceRequest.h" #include "ResourceResponse.h" -#include "SecurityOrigin.h" #include "SharedBuffer.h" #include <gst/app/gstappsrc.h> #include <gst/gst.h> #include <gst/pbutils/missing-plugins.h> +#include <wtf/MainThread.h> #include <wtf/Noncopyable.h> -#include <wtf/gobject/GMutexLocker.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GMutexLocker.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> +#if USE(SOUP) +#include "SoupNetworkSession.h" +#endif + using namespace WebCore; -enum CORSAccessCheckResult { - CORSNoCheck, - CORSSuccess, - CORSFailure +class StreamingClient { +public: + StreamingClient(WebKitWebSrc*, ResourceRequest&&); + virtual ~StreamingClient(); + +protected: + char* createReadBuffer(size_t requestedSize, size_t& actualSize); + void handleResponseReceived(const ResourceResponse&); + void handleDataReceived(const char*, int); + void handleNotifyFinished(); + + GRefPtr<GstElement> m_src; + ResourceRequest m_request; }; -class StreamingClient { - public: - StreamingClient(WebKitWebSrc*); - virtual ~StreamingClient(); +class CachedResourceStreamingClient final : public PlatformMediaResourceClient, public StreamingClient { + WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient); +public: + CachedResourceStreamingClient(WebKitWebSrc*, ResourceRequest&&); + virtual ~CachedResourceStreamingClient(); + +private: + // PlatformMediaResourceClient virtual methods. +#if USE(SOUP) + char* getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) override; +#endif + void responseReceived(PlatformMediaResource&, const ResourceResponse&) override; + void dataReceived(PlatformMediaResource&, const char*, int) override; + void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override; + void loadFailed(PlatformMediaResource&, const ResourceError&) override; + void loadFinished(PlatformMediaResource&) override; +}; - virtual bool loadFailed() const = 0; - virtual void setDefersLoading(bool) = 0; +class ResourceHandleStreamingClient : public ThreadSafeRefCounted<ResourceHandleStreamingClient>, public ResourceHandleClient, public StreamingClient { +public: + static Ref<ResourceHandleStreamingClient> create(WebKitWebSrc* src, ResourceRequest&& request) + { + return adoptRef(*new ResourceHandleStreamingClient(src, WTFMove(request))); + } + virtual ~ResourceHandleStreamingClient(); - protected: - char* createReadBuffer(size_t requestedSize, size_t& actualSize); - void handleResponseReceived(const ResourceResponse&, CORSAccessCheckResult); - void handleDataReceived(const char*, int); - void handleNotifyFinished(); + void invalidate(); - GstElement* m_src; -}; + // StreamingClient virtual methods. + bool loadFailed() const; + void setDefersLoading(bool); -class CachedResourceStreamingClient : public CachedRawResourceClient, public StreamingClient { - WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient); WTF_MAKE_FAST_ALLOCATED; - public: - CachedResourceStreamingClient(WebKitWebSrc*, CachedResourceLoader*, const ResourceRequest&, MediaPlayerClient::CORSMode); - virtual ~CachedResourceStreamingClient(); - - // StreamingClient virtual methods. - virtual bool loadFailed() const; - virtual void setDefersLoading(bool); - - private: - // CachedResourceClient virtual methods. - virtual char* getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize); - virtual void responseReceived(CachedResource*, const ResourceResponse&); - virtual void dataReceived(CachedResource*, const char*, int); - virtual void notifyFinished(CachedResource*); - - CachedResourceHandle<CachedRawResource> m_resource; - RefPtr<SecurityOrigin> m_origin; +private: + ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&&); + void cleanupAndStopRunLoop(); + + // ResourceHandleClient virtual methods. +#if USE(SOUP) + char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override; +#endif + ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&) override; + void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override; + void didReceiveData(ResourceHandle*, const char*, unsigned, int) override; + void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedLength) override; + void didFinishLoading(ResourceHandle*, double) override; + void didFail(ResourceHandle*, const ResourceError&) override; + void wasBlocked(ResourceHandle*) override; + void cannotShowURL(ResourceHandle*) override; + + ThreadIdentifier m_thread { 0 }; + Lock m_initializeRunLoopConditionMutex; + Condition m_initializeRunLoopCondition; + RunLoop* m_runLoop { nullptr }; + Lock m_terminateRunLoopConditionMutex; + Condition m_terminateRunLoopCondition; + RefPtr<ResourceHandle> m_resource; +#if USE(SOUP) + std::unique_ptr<SoupNetworkSession> m_session; +#endif }; -class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient { - WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED; - public: - ResourceHandleStreamingClient(WebKitWebSrc*, const ResourceRequest&); - virtual ~ResourceHandleStreamingClient(); - - // StreamingClient virtual methods. - virtual bool loadFailed() const; - virtual void setDefersLoading(bool); - - private: - // ResourceHandleClient virtual methods. - virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize); - virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&); - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); - virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int); - virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedLength); - virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); - virtual void didFail(ResourceHandle*, const ResourceError&); - virtual void wasBlocked(ResourceHandle*); - virtual void cannotShowURL(ResourceHandle*); - - RefPtr<ResourceHandle> m_resource; +enum MainThreadSourceNotification { + Start = 1 << 0, + Stop = 1 << 1, + NeedData = 1 << 2, + EnoughData = 1 << 3, + Seek = 1 << 4 }; #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate)) struct _WebKitWebSrcPrivate { GstAppSrc* appsrc; GstPad* srcpad; - gchar* uri; + CString originalURI; + CString redirectedURI; + bool keepAlive; + GUniquePtr<GstStructure> extraHeaders; + bool compress; + GUniquePtr<gchar> httpMethod; WebCore::MediaPlayer* player; - StreamingClient* client; + RefPtr<PlatformMediaResourceLoader> loader; + RefPtr<PlatformMediaResource> resource; + RefPtr<ResourceHandleStreamingClient> client; - CORSAccessCheckResult corsAccessCheck; + bool didPassAccessControlCheck; guint64 offset; guint64 size; gboolean seekable; - gboolean paused; + bool paused; + bool isSeeking; guint64 requestedOffset; - guint startID; - guint stopID; - guint needDataID; - guint enoughDataID; - guint seekID; - + bool createdInMainThread; + RefPtr<MainThreadNotifier<MainThreadSourceNotification>> notifier; GRefPtr<GstBuffer> buffer; - - // icecast stuff - gboolean iradioMode; - gchar* iradioName; - gchar* iradioGenre; - gchar* iradioUrl; - gchar* iradioTitle; }; enum { - PROP_IRADIO_MODE = 1, - PROP_IRADIO_NAME, - PROP_IRADIO_GENRE, - PROP_IRADIO_URL, - PROP_IRADIO_TITLE, - PROP_LOCATION + PROP_0, + PROP_LOCATION, + PROP_RESOLVED_LOCATION, + PROP_KEEP_ALIVE, + PROP_EXTRA_HEADERS, + PROP_COMPRESS, + PROP_METHOD }; static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", @@ -180,15 +196,24 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange) static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); -static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData); -static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData); -static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData); +static void webKitWebSrcNeedData(WebKitWebSrc*); +static void webKitWebSrcEnoughData(WebKitWebSrc*); +static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64); static GstAppSrcCallbacks appsrcCallbacks = { - webKitWebSrcNeedDataCb, - webKitWebSrcEnoughDataCb, - webKitWebSrcSeekDataCb, - { 0 } + // need_data + [](GstAppSrc*, guint, gpointer userData) { + webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData)); + }, + // enough_data + [](GstAppSrc*, gpointer userData) { + webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData)); + }, + // seek_data + [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean { + return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset); + }, + { nullptr } }; #define webkit_web_src_parent_class parent_class @@ -213,57 +238,32 @@ static void webkit_web_src_class_init(WebKitWebSrcClass* klass) gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris", "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); - // icecast stuff - g_object_class_install_property(oklass, - PROP_IRADIO_MODE, - g_param_spec_boolean("iradio-mode", - "iradio-mode", - "Enable internet radio mode (extraction of shoutcast/icecast metadata)", - FALSE, - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_NAME, - g_param_spec_string("iradio-name", - "iradio-name", - "Name of the stream", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_GENRE, - g_param_spec_string("iradio-genre", - "iradio-genre", - "Genre of the stream", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_URL, - g_param_spec_string("iradio-url", - "iradio-url", - "Homepage URL for radio stream", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_TITLE, - g_param_spec_string("iradio-title", - "iradio-title", - "Name of currently playing song", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - /* Allows setting the uri using the 'location' property, which is used * for example by gst_element_make_from_uri() */ - g_object_class_install_property(oklass, - PROP_LOCATION, - g_param_spec_string("location", - "location", - "Location to read from", - 0, - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(oklass, PROP_LOCATION, + g_param_spec_string("location", "location", "Location to read from", + nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_RESOLVED_LOCATION, + g_param_spec_string("resolved-location", "Resolved location", "The location resolved by the server", + nullptr, static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_KEEP_ALIVE, + g_param_spec_boolean("keep-alive", "keep-alive", "Use HTTP persistent connections", + FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_EXTRA_HEADERS, + g_param_spec_boxed("extra-headers", "Extra Headers", "Extra headers to append to the HTTP request", + GST_TYPE_STRUCTURE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_COMPRESS, + g_param_spec_boolean("compress", "Compress", "Allow compressed content encodings", + FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_METHOD, + g_param_spec_string("method", "method", "The HTTP method to use (default: GET)", + nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + eklass->change_state = webKitWebSrcChangeState; g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate)); @@ -274,8 +274,12 @@ static void webkit_web_src_init(WebKitWebSrc* src) WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src); src->priv = priv; + new (priv) WebKitWebSrcPrivate(); + + priv->createdInMainThread = isMainThread(); + priv->notifier = MainThreadNotifier<MainThreadSourceNotification>::create(); - priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0)); + priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", nullptr)); if (!priv->appsrc) { GST_ERROR_OBJECT(src, "Failed to create appsrc"); return; @@ -292,7 +296,7 @@ static void webkit_web_src_init(WebKitWebSrc* src) GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT); gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent); - gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0); + gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, nullptr); gst_app_src_set_emit_signals(priv->appsrc, FALSE); gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE); @@ -313,28 +317,32 @@ static void webkit_web_src_init(WebKitWebSrc* src) // likely that libsoup already provides new data before // the queue is really empty. // This might need tweaking for ports not using libsoup. - g_object_set(priv->appsrc, "min-percent", 20, NULL); + g_object_set(priv->appsrc, "min-percent", 20, nullptr); - gst_app_src_set_caps(priv->appsrc, 0); + gst_base_src_set_automatic_eos(GST_BASE_SRC(priv->appsrc), FALSE); + + gst_app_src_set_caps(priv->appsrc, nullptr); gst_app_src_set_size(priv->appsrc, -1); } static void webKitWebSrcDispose(GObject* object) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(object); - WebKitWebSrcPrivate* priv = src->priv; + WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv; + if (priv->notifier) { + priv->notifier->invalidate(); + priv->notifier = nullptr; + } - priv->player = 0; + priv->player = nullptr; GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object)); } static void webKitWebSrcFinalize(GObject* object) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(object); - WebKitWebSrcPrivate* priv = src->priv; + WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv; - g_free(priv->uri); + priv->~WebKitWebSrcPrivate(); GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); } @@ -342,16 +350,24 @@ static void webKitWebSrcFinalize(GObject* object) static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec) { WebKitWebSrc* src = WEBKIT_WEB_SRC(object); - WebKitWebSrcPrivate* priv = src->priv; switch (propID) { - case PROP_IRADIO_MODE: { - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - priv->iradioMode = g_value_get_boolean(value); + case PROP_LOCATION: + gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), nullptr); + break; + case PROP_KEEP_ALIVE: + src->priv->keepAlive = g_value_get_boolean(value); + break; + case PROP_EXTRA_HEADERS: { + const GstStructure* s = gst_value_get_structure(value); + src->priv->extraHeaders.reset(s ? gst_structure_copy(s) : nullptr); break; } - case PROP_LOCATION: - gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0); + case PROP_COMPRESS: + src->priv->compress = g_value_get_boolean(value); + break; + case PROP_METHOD: + src->priv->httpMethod.reset(g_value_dup_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec); @@ -364,25 +380,25 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value WebKitWebSrc* src = WEBKIT_WEB_SRC(object); WebKitWebSrcPrivate* priv = src->priv; - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); switch (propID) { - case PROP_IRADIO_MODE: - g_value_set_boolean(value, priv->iradioMode); + case PROP_LOCATION: + g_value_set_string(value, priv->originalURI.data()); break; - case PROP_IRADIO_NAME: - g_value_set_string(value, priv->iradioName); + case PROP_RESOLVED_LOCATION: + g_value_set_string(value, priv->redirectedURI.isNull() ? priv->originalURI.data() : priv->redirectedURI.data()); break; - case PROP_IRADIO_GENRE: - g_value_set_string(value, priv->iradioGenre); + case PROP_KEEP_ALIVE: + g_value_set_boolean(value, priv->keepAlive); break; - case PROP_IRADIO_URL: - g_value_set_string(value, priv->iradioUrl); + case PROP_EXTRA_HEADERS: + gst_value_set_structure(value, priv->extraHeaders.get()); break; - case PROP_IRADIO_TITLE: - g_value_set_string(value, priv->iradioTitle); + case PROP_COMPRESS: + g_value_set_boolean(value, priv->compress); break; - case PROP_LOCATION: - g_value_set_string(value, priv->uri); + case PROP_METHOD: + g_value_set_string(value, priv->httpMethod.get()); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec); @@ -390,123 +406,159 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value } } -static void removeTimeoutSources(WebKitWebSrc* src) +static void webKitWebSrcStop(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; - if (priv->startID) - g_source_remove(priv->startID); - priv->startID = 0; - - if (priv->needDataID) - g_source_remove(priv->needDataID); - priv->needDataID = 0; - - if (priv->enoughDataID) - g_source_remove(priv->enoughDataID); - priv->enoughDataID = 0; - - if (priv->seekID) - g_source_remove(priv->seekID); - priv->seekID = 0; -} - -static gboolean webKitWebSrcStop(WebKitWebSrc* src) -{ - WebKitWebSrcPrivate* priv = src->priv; - - ASSERT(isMainThread()); - - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - - bool seeking = priv->seekID; - - removeTimeoutSources(src); - priv->stopID = 0; + if (priv->resource || (priv->loader && !priv->keepAlive)) { + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier->cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek); + priv->notifier->notify(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] { + WebKitWebSrcPrivate* priv = protector->priv; + + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get())); + if (priv->resource) { + priv->resource->stop(); + priv->resource->setClient(nullptr); + priv->resource = nullptr; + } + + if (!keepAlive) + priv->loader = nullptr; + }); + } if (priv->client) { - delete priv->client; - priv->client = 0; + priv->client->invalidate(); + priv->client = nullptr; } + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + + bool wasSeeking = std::exchange(priv->isSeeking, false); + if (priv->buffer) { unmapGstBuffer(priv->buffer.get()); priv->buffer.clear(); } - priv->paused = FALSE; - - g_free(priv->iradioName); - priv->iradioName = 0; - - g_free(priv->iradioGenre); - priv->iradioGenre = 0; - - g_free(priv->iradioUrl); - priv->iradioUrl = 0; - - g_free(priv->iradioTitle); - priv->iradioTitle = 0; + priv->paused = false; priv->offset = 0; priv->seekable = FALSE; - if (!seeking) { + if (!wasSeeking) { priv->size = 0; priv->requestedOffset = 0; - priv->player = 0; + priv->player = nullptr; } locker.unlock(); if (priv->appsrc) { - gst_app_src_set_caps(priv->appsrc, 0); - if (!seeking) + gst_app_src_set_caps(priv->appsrc, nullptr); + if (!wasSeeking) gst_app_src_set_size(priv->appsrc, -1); } GST_DEBUG_OBJECT(src, "Stopped request"); +} + +static bool webKitWebSrcSetExtraHeader(GQuark fieldId, const GValue* value, gpointer userData) +{ + GUniquePtr<gchar> fieldContent; + + if (G_VALUE_HOLDS_STRING(value)) + fieldContent.reset(g_value_dup_string(value)); + else { + GValue dest = G_VALUE_INIT; + + g_value_init(&dest, G_TYPE_STRING); + if (g_value_transform(value, &dest)) + fieldContent.reset(g_value_dup_string(&dest)); + } + + const gchar* fieldName = g_quark_to_string(fieldId); + if (!fieldContent.get()) { + GST_ERROR("extra-headers field '%s' contains no value or can't be converted to a string", fieldName); + return false; + } - return FALSE; + GST_DEBUG("Appending extra header: \"%s: %s\"", fieldName, fieldContent.get()); + ResourceRequest* request = static_cast<ResourceRequest*>(userData); + request->setHTTPHeaderField(fieldName, fieldContent.get()); + return true; } -static gboolean webKitWebSrcStart(WebKitWebSrc* src) +static gboolean webKitWebSrcProcessExtraHeaders(GQuark fieldId, const GValue* value, gpointer userData) { - WebKitWebSrcPrivate* priv = src->priv; + if (G_VALUE_TYPE(value) == GST_TYPE_ARRAY) { + unsigned size = gst_value_array_get_size(value); - ASSERT(isMainThread()); + for (unsigned i = 0; i < size; i++) { + if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_array_get_value(value, i), userData)) + return FALSE; + } + return TRUE; + } - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + if (G_VALUE_TYPE(value) == GST_TYPE_LIST) { + unsigned size = gst_value_list_get_size(value); + + for (unsigned i = 0; i < size; i++) { + if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_list_get_value(value, i), userData)) + return FALSE; + } + return TRUE; + } + + return webKitWebSrcSetExtraHeader(fieldId, value, userData); +} + +static void webKitWebSrcStart(WebKitWebSrc* src) +{ + WebKitWebSrcPrivate* priv = src->priv; - priv->startID = 0; - priv->corsAccessCheck = CORSNoCheck; + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - if (!priv->uri) { + priv->didPassAccessControlCheck = false; + + if (priv->originalURI.isNull()) { GST_ERROR_OBJECT(src, "No URI provided"); locker.unlock(); webKitWebSrcStop(src); - return FALSE; + return; } ASSERT(!priv->client); - URL url = URL(URL(), priv->uri); + GST_DEBUG_OBJECT(src, "Fetching %s", priv->originalURI.data()); + URL url = URL(URL(), priv->originalURI.data()); ResourceRequest request(url); request.setAllowCookies(true); request.setFirstPartyForCookies(url); + priv->size = 0; + if (priv->player) request.setHTTPReferrer(priv->player->referrer()); + if (priv->httpMethod.get()) + request.setHTTPMethod(priv->httpMethod.get()); + #if USE(SOUP) - // Let's disable HTTP Accept-Encoding here as we don't want the received response to be - // encoded in any way as we need to rely on the proper size of the returned data on + // By default, HTTP Accept-Encoding is disabled here as we don't + // want the received response to be encoded in any way as we need + // to rely on the proper size of the returned data on // didReceiveResponse. // If Accept-Encoding is used, the server may send the data in encoded format and // request.expectedContentLength() will have the "wrong" size (the size of the // compressed data), even though the data received in didReceiveData is uncompressed. - request.setAcceptEncoding(false); + // This is however useful to enable for adaptive streaming + // scenarios, when the demuxer needs to download playlists. + if (!priv->compress) + request.setAcceptEncoding(false); #endif // Let Apple web servers know we want to access their nice movie trailers. @@ -516,36 +568,55 @@ static gboolean webKitWebSrcStart(WebKitWebSrc* src) if (priv->requestedOffset) { GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset)); - request.setHTTPHeaderField("Range", val.get()); + request.setHTTPHeaderField(HTTPHeaderName::Range, val.get()); } priv->offset = priv->requestedOffset; - if (priv->iradioMode) - request.setHTTPHeaderField("icy-metadata", "1"); + if (!priv->keepAlive) { + GST_DEBUG_OBJECT(src, "Persistent connection support disabled"); + request.setHTTPHeaderField(HTTPHeaderName::Connection, "close"); + } - // Needed to use DLNA streaming servers - request.setHTTPHeaderField("transferMode.dlna", "Streaming"); + if (priv->extraHeaders) + gst_structure_foreach(priv->extraHeaders.get(), webKitWebSrcProcessExtraHeaders, &request); - if (priv->player) { - if (CachedResourceLoader* loader = priv->player->cachedResourceLoader()) - priv->client = new CachedResourceStreamingClient(src, loader, request, priv->player->mediaPlayerClient()->mediaPlayerCORSMode()); - } + // We always request Icecast/Shoutcast metadata, just in case ... + request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1"); - if (!priv->client) - priv->client = new ResourceHandleStreamingClient(src, request); + if (!priv->player || !priv->createdInMainThread) { + priv->client = ResourceHandleStreamingClient::create(src, WTFMove(request)); + if (priv->client->loadFailed()) { + GST_ERROR_OBJECT(src, "Failed to setup streaming client"); + locker.unlock(); + webKitWebSrcStop(src); + } else + GST_DEBUG_OBJECT(src, "Started request"); + return; + } - if (!priv->client || priv->client->loadFailed()) { - GST_ERROR_OBJECT(src, "Failed to setup streaming client"); - if (priv->client) { - delete priv->client; - priv->client = 0; + locker.unlock(); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier->notify(MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] { + WebKitWebSrcPrivate* priv = protector->priv; + + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get())); + if (!priv->loader) + priv->loader = priv->player->createResourceLoader(); + + PlatformMediaResourceLoader::LoadOptions loadOptions = 0; + if (request.url().protocolIsBlob()) + loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData; + priv->resource = priv->loader->requestResource(ResourceRequest(request), loadOptions); + if (priv->resource) { + priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get(), ResourceRequest(request))); + GST_DEBUG_OBJECT(protector.get(), "Started request"); + } else { + GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client"); + priv->loader = nullptr; + locker.unlock(); + webKitWebSrcStop(protector.get()); } - locker.unlock(); - webKitWebSrcStop(src); - return FALSE; - } - GST_DEBUG_OBJECT(src, "Started request"); - return FALSE; + }); } static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition) @@ -559,7 +630,7 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat if (!priv->appsrc) { gst_element_post_message(element, gst_missing_element_message_new(element, "appsrc")); - GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc")); + GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (nullptr), ("no appsrc")); return GST_STATE_CHANGE_FAILURE; } break; @@ -573,18 +644,19 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat return ret; } - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + { GST_DEBUG_OBJECT(src, "READY->PAUSED"); - priv->startID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + webKitWebSrcStart(src); break; + } case GST_STATE_CHANGE_PAUSED_TO_READY: + { GST_DEBUG_OBJECT(src, "PAUSED->READY"); - // cancel pending sources - removeTimeoutSources(src); - priv->stopID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + webKitWebSrcStop(src); break; + } default: break; } @@ -601,10 +673,10 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ case GST_QUERY_DURATION: { GstFormat format; - gst_query_parse_duration(query, &format, NULL); + gst_query_parse_duration(query, &format, nullptr); GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format)); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); if (format == GST_FORMAT_BYTES && src->priv->size > 0) { gst_query_set_duration(query, format, src->priv->size); result = TRUE; @@ -612,8 +684,19 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ break; } case GST_QUERY_URI: { - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - gst_query_set_uri(query, src->priv->uri); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + gst_query_set_uri(query, src->priv->originalURI.data()); + if (!src->priv->redirectedURI.isNull()) + gst_query_set_uri_redirection(query, src->priv->redirectedURI.data()); + result = TRUE; + break; + } + case GST_QUERY_SCHEDULING: { + GstSchedulingFlags flags; + int minSize, maxSize, align; + + gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align); + gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align); result = TRUE; break; } @@ -632,7 +715,7 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ static bool urlHasSupportedProtocol(const URL& url) { - return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIs("blob")); + return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIsBlob()); } // uri handler interface @@ -644,7 +727,7 @@ static GstURIType webKitWebSrcUriGetType(GType) const gchar* const* webKitWebSrcGetProtocols(GType) { - static const char* protocols[] = {"http", "https", "blob", 0 }; + static const char* protocols[] = {"http", "https", "blob", nullptr }; return protocols; } @@ -653,8 +736,8 @@ static gchar* webKitWebSrcGetUri(GstURIHandler* handler) WebKitWebSrc* src = WEBKIT_WEB_SRC(handler); gchar* ret; - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - ret = g_strdup(src->priv->uri); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + ret = g_strdup(src->priv->originalURI.data()); return ret; } @@ -668,11 +751,10 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr return FALSE; } - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - - g_free(priv->uri); - priv->uri = 0; + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + priv->redirectedURI = CString(); + priv->originalURI = CString(); if (!uri) return TRUE; @@ -682,7 +764,7 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr return FALSE; } - priv->uri = g_strdup(url.string().utf8().data()); + priv->originalURI = url.string().utf8(); return TRUE; } @@ -696,152 +778,122 @@ static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer) iface->set_uri = webKitWebSrcSetUri; } -// appsrc callbacks - -static gboolean webKitWebSrcNeedDataMainCb(WebKitWebSrc* src) -{ - WebKitWebSrcPrivate* priv = src->priv; - - ASSERT(isMainThread()); - - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - // already stopped - if (!priv->needDataID) - return FALSE; - - priv->paused = FALSE; - priv->needDataID = 0; - locker.unlock(); - - if (priv->client) - priv->client->setDefersLoading(false); - return FALSE; -} - -static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) +static void webKitWebSrcNeedData(WebKitWebSrc* src) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); WebKitWebSrcPrivate* priv = src->priv; - GST_DEBUG_OBJECT(src, "Need more data: %u", length); + GST_DEBUG_OBJECT(src, "Need more data"); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (priv->needDataID || !priv->paused) { - return; + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (!priv->paused) + return; + priv->paused = false; + if (priv->client) { + priv->client->setDefersLoading(false); + return; + } } - priv->needDataID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier->notify(MainThreadSourceNotification::NeedData, [protector] { + WebKitWebSrcPrivate* priv = protector->priv; + if (priv->resource) + priv->resource->setDefersLoading(false); + }); } -static gboolean webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src) +static void webKitWebSrcEnoughData(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); - - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - // already stopped - if (!priv->enoughDataID) - return FALSE; - - priv->paused = TRUE; - priv->enoughDataID = 0; - locker.unlock(); - - if (priv->client) - priv->client->setDefersLoading(true); - return FALSE; -} - -static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - GST_DEBUG_OBJECT(src, "Have enough data"); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (priv->enoughDataID || priv->paused) { - return; + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (priv->paused) + return; + priv->paused = true; + if (priv->client) { + priv->client->setDefersLoading(true); + return; + } } - priv->enoughDataID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier->notify(MainThreadSourceNotification::EnoughData, [protector] { + WebKitWebSrcPrivate* priv = protector->priv; + if (priv->resource) + priv->resource->setDefersLoading(true); + }); } -static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src) +static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset) { WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (offset == priv->offset && priv->requestedOffset == priv->offset) + return TRUE; - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - // already stopped - if (!priv->seekID) - return FALSE; - locker.unlock(); + if (!priv->seekable) + return FALSE; - webKitWebSrcStop(src); - webKitWebSrcStart(src); - - return FALSE; -} - -static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; + priv->isSeeking = true; + priv->requestedOffset = offset; + } - GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (offset == priv->offset && priv->requestedOffset == priv->offset) + GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset); + if (priv->client) { + webKitWebSrcStop(src); + webKitWebSrcStart(src); return TRUE; + } - if (!priv->seekable) - return FALSE; - - GST_DEBUG_OBJECT(src, "Doing range-request seek"); - priv->requestedOffset = offset; - - if (priv->seekID) - g_source_remove(priv->seekID); - priv->seekID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier->notify(MainThreadSourceNotification::Seek, [protector] { + webKitWebSrcStop(protector.get()); + webKitWebSrcStart(protector.get()); + }); return TRUE; } void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player) { ASSERT(player); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + ASSERT(src->priv->createdInMainThread); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); src->priv->player = player; } bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src) { - return src->priv->corsAccessCheck == CORSSuccess; + return src->priv->didPassAccessControlCheck; } -StreamingClient::StreamingClient(WebKitWebSrc* src) - : m_src(static_cast<GstElement*>(gst_object_ref(src))) +StreamingClient::StreamingClient(WebKitWebSrc* src, ResourceRequest&& request) + : m_src(GST_ELEMENT(src)) + , m_request(WTFMove(request)) { } StreamingClient::~StreamingClient() { - gst_object_unref(m_src); } char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); WebKitWebSrcPrivate* priv = src->priv; ASSERT(!priv->buffer); GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize); - mapGstBuffer(buffer); + mapGstBuffer(buffer, GST_MAP_WRITE); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); priv->buffer = adoptGRef(buffer); locker.unlock(); @@ -849,29 +901,27 @@ char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize return getGstBufferDataPointer(buffer); } -void StreamingClient::handleResponseReceived(const ResourceResponse& response, CORSAccessCheckResult corsAccessCheck) +void StreamingClient::handleResponseReceived(const ResourceResponse& response) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); WebKitWebSrcPrivate* priv = src->priv; GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode()); - if (response.httpStatusCode() >= 400 || corsAccessCheck == CORSFailure) { - // Received error code or CORS check failed - if (corsAccessCheck == CORSFailure) - GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Cross-origin stream load denied by Cross-Origin Resource Sharing policy."), (nullptr)); - else - GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr)); + auto responseURI = response.url().string().utf8(); + if (priv->originalURI != responseURI) + priv->redirectedURI = WTFMove(responseURI); + + if (response.httpStatusCode() >= 400) { + GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr)); gst_app_src_end_of_stream(priv->appsrc); webKitWebSrcStop(src); return; } - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - priv->corsAccessCheck = corsAccessCheck; - - if (priv->seekID) { + if (priv->isSeeking) { GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response"); return; } @@ -896,43 +946,9 @@ void StreamingClient::handleResponseReceived(const ResourceResponse& response, C length += priv->requestedOffset; priv->size = length >= 0 ? length : 0; - priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField("Accept-Ranges").utf8().data()); - - // Wait until we unlock to send notifications - g_object_freeze_notify(G_OBJECT(src)); - - GstTagList* tags = gst_tag_list_new_empty(); - String value = response.httpHeaderField("icy-name"); - if (!value.isEmpty()) { - g_free(priv->iradioName); - priv->iradioName = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-name"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL); - } - value = response.httpHeaderField("icy-genre"); - if (!value.isEmpty()) { - g_free(priv->iradioGenre); - priv->iradioGenre = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-genre"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL); - } - value = response.httpHeaderField("icy-url"); - if (!value.isEmpty()) { - g_free(priv->iradioUrl); - priv->iradioUrl = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-url"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL); - } - value = response.httpHeaderField("icy-title"); - if (!value.isEmpty()) { - g_free(priv->iradioTitle); - priv->iradioTitle = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-title"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL); - } + priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data()); locker.unlock(); - g_object_thaw_notify(G_OBJECT(src)); // notify size/duration if (length > 0) { @@ -940,33 +956,30 @@ void StreamingClient::handleResponseReceived(const ResourceResponse& response, C } else gst_app_src_set_size(priv->appsrc, -1); - // icecast stuff - value = response.httpHeaderField("icy-metaint"); - if (!value.isEmpty()) { - gchar* endptr = 0; - gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10); - - if (endptr && *endptr == '\0' && icyMetaInt > 0) { - GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL)); - - gst_app_src_set_caps(priv->appsrc, caps.get()); - } - } else - gst_app_src_set_caps(priv->appsrc, 0); - - // notify tags - if (gst_tag_list_is_empty(tags)) - gst_tag_list_unref(tags); - else - gst_pad_push_event(priv->srcpad, gst_event_new_tag(tags)); + gst_app_src_set_caps(priv->appsrc, nullptr); + + // Emit a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY event to let GStreamer know about the HTTP headers sent and received. + GstStructure* httpHeaders = gst_structure_new_empty("http-headers"); + gst_structure_set(httpHeaders, "uri", G_TYPE_STRING, priv->originalURI.data(), nullptr); + if (!priv->redirectedURI.isNull()) + gst_structure_set(httpHeaders, "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr); + GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers")); + for (const auto& header : m_request.httpHeaderFields()) + gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr); + gst_structure_set(httpHeaders, "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr); + headers.reset(gst_structure_new_empty("response-headers")); + for (const auto& header : response.httpHeaderFields()) + gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr); + gst_structure_set(httpHeaders, "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr); + gst_pad_push_event(GST_BASE_SRC_PAD(priv->appsrc), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, httpHeaders)); } void StreamingClient::handleDataReceived(const char* data, int length) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); WebKitWebSrcPrivate* priv = src->priv; - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); GST_LOG_OBJECT(src, "Have %lld bytes of data", priv->buffer ? static_cast<long long>(gst_buffer_get_size(priv->buffer.get())) : length); @@ -975,7 +988,7 @@ void StreamingClient::handleDataReceived(const char* data, int length) if (priv->buffer) unmapGstBuffer(priv->buffer.get()); - if (priv->seekID) { + if (priv->isSeeking) { GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data"); priv->buffer.clear(); return; @@ -1025,111 +1038,135 @@ void StreamingClient::handleDataReceived(const char* data, int length) GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef()); if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) - GST_ELEMENT_ERROR(src, CORE, FAILED, (0), (0)); + GST_ELEMENT_ERROR(src, CORE, FAILED, (nullptr), (nullptr)); } void StreamingClient::handleNotifyFinished() { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); WebKitWebSrcPrivate* priv = src->priv; GST_DEBUG_OBJECT(src, "Have EOS"); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (!priv->seekID) { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (!priv->isSeeking) { locker.unlock(); gst_app_src_end_of_stream(priv->appsrc); } } -CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, CachedResourceLoader* resourceLoader, const ResourceRequest& request, MediaPlayerClient::CORSMode corsMode) - : StreamingClient(src) +CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, ResourceRequest&& request) + : StreamingClient(src, WTFMove(request)) { - DataBufferingPolicy bufferingPolicy = request.url().protocolIs("blob") ? BufferData : DoNotBufferData; - RequestOriginPolicy corsPolicy = corsMode != MediaPlayerClient::Unspecified ? PotentiallyCrossOriginEnabled : UseDefaultOriginRestrictionsForType; - StoredCredentials allowCredentials = corsMode == MediaPlayerClient::UseCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials; - ResourceLoaderOptions options(SendCallbacks, DoNotSniffContent, bufferingPolicy, allowCredentials, DoNotAskClientForCrossOriginCredentials, DoSecurityCheck, corsPolicy); - - CachedResourceRequest cacheRequest(request, options); - - if (corsMode != MediaPlayerClient::Unspecified) { - m_origin = resourceLoader->document() ? resourceLoader->document()->securityOrigin() : nullptr; - updateRequestForAccessControl(cacheRequest.mutableResourceRequest(), m_origin.get(), allowCredentials); - } - - // TODO: Decide whether to use preflight mode for cross-origin requests (see http://wkbug.com/131484). - m_resource = resourceLoader->requestRawResource(cacheRequest); - if (m_resource) - m_resource->addClient(this); } CachedResourceStreamingClient::~CachedResourceStreamingClient() { - if (m_resource) { - m_resource->removeClient(this); - m_resource = 0; - } } -bool CachedResourceStreamingClient::loadFailed() const +#if USE(SOUP) +char* CachedResourceStreamingClient::getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) { - return !m_resource; + return createReadBuffer(requestedSize, actualSize); } +#endif -void CachedResourceStreamingClient::setDefersLoading(bool defers) +void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response) { - if (m_resource) - m_resource->setDefersLoading(defers); + WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src.get())->priv; + priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck(); + handleResponseReceived(response); } -char* CachedResourceStreamingClient::getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize) +void CachedResourceStreamingClient::dataReceived(PlatformMediaResource&, const char* data, int length) { - return createReadBuffer(requestedSize, actualSize); + handleDataReceived(data, length); } -void CachedResourceStreamingClient::responseReceived(CachedResource* resource, const ResourceResponse& response) +void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error) { - CORSAccessCheckResult corsAccessCheck = CORSNoCheck; - if (m_origin) - corsAccessCheck = (m_origin->canRequest(response.url()) || resource->passesAccessControlCheck(m_origin.get())) ? CORSSuccess : CORSFailure; - handleResponseReceived(response, corsAccessCheck); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr)); + gst_app_src_end_of_stream(src->priv->appsrc); + webKitWebSrcStop(src); } -void CachedResourceStreamingClient::dataReceived(CachedResource*, const char* data, int length) +void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error) { - handleDataReceived(data, length); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + + if (!error.isCancellation()) { + GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data()); + GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr)); + } + + gst_app_src_end_of_stream(src->priv->appsrc); } -void CachedResourceStreamingClient::notifyFinished(CachedResource* resource) +void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&) { - if (resource->loadFailedOrCanceled()) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + handleNotifyFinished(); +} - if (!resource->wasCanceled()) { - const ResourceError& error = resource->resourceError(); - GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data()); - GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0)); +ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, ResourceRequest&& request) + : StreamingClient(src, WTFMove(request)) +{ + LockHolder locker(m_initializeRunLoopConditionMutex); + m_thread = createThread("ResourceHandleStreamingClient", [this] { + { + LockHolder locker(m_initializeRunLoopConditionMutex); + m_runLoop = &RunLoop::current(); +#if USE(SOUP) + m_session = std::make_unique<SoupNetworkSession>(); + m_resource = ResourceHandle::create(*m_session, m_request, this, true, false); +#else + // FIXME: This create will hit an assert in debug builds. See https://bugs.webkit.org/show_bug.cgi?id=167003. + m_resource = ResourceHandle::create(nullptr, m_request, this, true, false); +#endif + m_initializeRunLoopCondition.notifyOne(); } - gst_app_src_end_of_stream(src->priv->appsrc); - return; - } + if (!m_resource) + return; - handleNotifyFinished(); + m_runLoop->dispatch([this] { m_resource->setDefersLoading(false); }); + m_runLoop->run(); + }); + m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex); } -ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest& request) - : StreamingClient(src) +ResourceHandleStreamingClient::~ResourceHandleStreamingClient() { - m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false); + if (m_thread) { + detachThread(m_thread); + m_thread = 0; + } } -ResourceHandleStreamingClient::~ResourceHandleStreamingClient() +void ResourceHandleStreamingClient::cleanupAndStopRunLoop() +{ + m_resource->clearClient(); + m_resource->cancel(); + m_resource = nullptr; +#if USE(SOUP) + m_session = nullptr; +#endif + m_runLoop->stop(); +} + +void ResourceHandleStreamingClient::invalidate() { - if (m_resource) { - m_resource->cancel(); - m_resource.release(); - m_resource = 0; + if (m_runLoop == &RunLoop::current()) { + cleanupAndStopRunLoop(); + return; } + + LockHolder locker(m_terminateRunLoopConditionMutex); + m_runLoop->dispatch([this, protectedThis = makeRef(*this)] { + cleanupAndStopRunLoop(); + LockHolder locker(m_terminateRunLoopConditionMutex); + m_terminateRunLoopCondition.notifyOne(); + }); + m_terminateRunLoopCondition.wait(m_terminateRunLoopConditionMutex); } bool ResourceHandleStreamingClient::loadFailed() const @@ -1139,31 +1176,40 @@ bool ResourceHandleStreamingClient::loadFailed() const void ResourceHandleStreamingClient::setDefersLoading(bool defers) { - if (m_resource) - m_resource->setDefersLoading(defers); + m_runLoop->dispatch([this, protectedThis = makeRef(*this), defers] { + if (m_resource) + m_resource->setDefersLoading(defers); + }); } +#if USE(SOUP) char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) { return createReadBuffer(requestedSize, actualSize); } +#endif -void ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&) +ResourceRequest ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&) { + return WTFMove(request); } -void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, ResourceResponse&& response) { - handleResponseReceived(response, CORSNoCheck); + if (m_resource) + handleResponseReceived(response); } -void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* data, unsigned length, int) +void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int) { ASSERT_NOT_REACHED(); } -void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */) +void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&& buffer, int /* encodedLength */) { + if (!m_resource) + return; + // This pattern is suggested by SharedBuffer.h. const char* segment; unsigned position = 0; @@ -1175,44 +1221,45 @@ void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*, double) { - handleNotifyFinished(); + if (m_resource) + handleNotifyFinished(); } void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data()); - GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0)); + GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr)); gst_app_src_end_of_stream(src->priv->appsrc); } void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); GUniquePtr<gchar> uri; GST_ERROR_OBJECT(src, "Request was blocked"); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - uri.reset(g_strdup(src->priv->uri)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + uri.reset(g_strdup(src->priv->originalURI.data())); locker.unlock(); - GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (0)); + GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (nullptr)); } void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); GUniquePtr<gchar> uri; GST_ERROR_OBJECT(src, "Cannot show URL"); - WTF::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - uri.reset(g_strdup(src->priv->uri)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + uri.reset(g_strdup(src->priv->originalURI.data())); locker.unlock(); - GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (0)); + GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (nullptr)); } #endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp new file mode 100644 index 000000000..dc697089f --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp @@ -0,0 +1,260 @@ +/* GStreamer ClearKey common encryption decryptor + * + * Copyright (C) 2016 Metrological + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#include "config.h" +#include "WebKitClearKeyDecryptorGStreamer.h" + +#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) + +#include "GRefPtrGStreamer.h" +#include <gcrypt.h> +#include <gst/base/gstbytereader.h> +#include <wtf/RunLoop.h> + +#define CLEARKEY_SIZE 16 + +#define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate)) +struct _WebKitMediaClearKeyDecryptPrivate { + GRefPtr<GstBuffer> key; + gcry_cipher_hd_t handle; +}; + +static void webKitMediaClearKeyDecryptorFinalize(GObject*); +static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent*); +static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt*); +static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples); +static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt*); + +GST_DEBUG_CATEGORY_STATIC(webkit_media_clear_key_decrypt_debug_category); +#define GST_CAT_DEFAULT webkit_media_clear_key_decrypt_debug_category + +static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS("application/x-cenc, original-media-type=(string)video/x-h264, protection-system=(string)" CLEAR_KEY_PROTECTION_SYSTEM_UUID "; " + "application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" CLEAR_KEY_PROTECTION_SYSTEM_UUID)); + +static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-h264; audio/mpeg")); + +#define webkit_media_clear_key_decrypt_parent_class parent_class +G_DEFINE_TYPE(WebKitMediaClearKeyDecrypt, webkit_media_clear_key_decrypt, WEBKIT_TYPE_MEDIA_CENC_DECRYPT); + +static void webkit_media_clear_key_decrypt_class_init(WebKitMediaClearKeyDecryptClass* klass) +{ + GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); + gobjectClass->finalize = webKitMediaClearKeyDecryptorFinalize; + + GstElementClass* elementClass = GST_ELEMENT_CLASS(klass); + gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate)); + gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&srcTemplate)); + + gst_element_class_set_static_metadata(elementClass, + "Decrypt content encrypted using ISOBMFF ClearKey Common Encryption", + GST_ELEMENT_FACTORY_KLASS_DECRYPTOR, + "Decrypts media that has been encrypted using ISOBMFF ClearKey Common Encryption.", + "Philippe Normand <philn@igalia.com>"); + + GST_DEBUG_CATEGORY_INIT(webkit_media_clear_key_decrypt_debug_category, + "webkitclearkey", 0, "ClearKey decryptor"); + + WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass); + cencClass->protectionSystemId = CLEAR_KEY_PROTECTION_SYSTEM_UUID; + cencClass->handleKeyResponse = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorHandleKeyResponse); + cencClass->setupCipher = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorSetupCipher); + cencClass->decrypt = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorDecrypt); + cencClass->releaseCipher = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorReleaseCipher); + + g_type_class_add_private(klass, sizeof(WebKitMediaClearKeyDecryptPrivate)); +} + +static void webkit_media_clear_key_decrypt_init(WebKitMediaClearKeyDecrypt* self) +{ + WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self); + + if (!gcry_check_version(GCRYPT_VERSION)) + GST_ERROR_OBJECT(self, "Libgcrypt failed to initialize"); + + // Allocate a pool of 16k secure memory. This make the secure memory + // available and also drops privileges where needed. + gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0); + + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + + self->priv = priv; + new (priv) WebKitMediaClearKeyDecryptPrivate(); +} + +static void webKitMediaClearKeyDecryptorFinalize(GObject* object) +{ + WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object); + WebKitMediaClearKeyDecryptPrivate* priv = self->priv; + + priv->~WebKitMediaClearKeyDecryptPrivate(); + + GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); +} + +static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent* event) +{ + WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); + const GstStructure* structure = gst_event_get_structure(event); + + if (!gst_structure_has_name(structure, "drm-cipher")) + return FALSE; + + const GValue* value = gst_structure_get_value(structure, "key"); + priv->key.clear(); + priv->key = adoptGRef(gst_buffer_copy(gst_value_get_buffer(value))); + return TRUE; +} + +static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt* self) +{ + WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); + gcry_error_t error; + + ASSERT(priv->key); + if (!priv->key) { + GST_ERROR_OBJECT(self, "Decryption key not provided"); + return false; + } + + error = gcry_cipher_open(&(priv->handle), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE); + if (error) { + GST_ERROR_OBJECT(self, "Failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error)); + return false; + } + + GstMapInfo keyMap; + if (!gst_buffer_map(priv->key.get(), &keyMap, GST_MAP_READ)) { + GST_ERROR_OBJECT(self, "Failed to map decryption key"); + return false; + } + + ASSERT(keyMap.size == CLEARKEY_SIZE); + error = gcry_cipher_setkey(priv->handle, keyMap.data, keyMap.size); + gst_buffer_unmap(priv->key.get(), &keyMap); + if (error) { + GST_ERROR_OBJECT(self, "gcry_cipher_setkey failed: %s", gpg_strerror(error)); + return false; + } + + return true; +} + +static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer) +{ + GstMapInfo ivMap; + if (!gst_buffer_map(ivBuffer, &ivMap, GST_MAP_READ)) { + GST_ERROR_OBJECT(self, "Failed to map IV"); + return false; + } + + uint8_t ctr[CLEARKEY_SIZE]; + if (ivMap.size == 8) { + memset(ctr + 8, 0, 8); + memcpy(ctr, ivMap.data, 8); + } else { + ASSERT(ivMap.size == CLEARKEY_SIZE); + memcpy(ctr, ivMap.data, CLEARKEY_SIZE); + } + gst_buffer_unmap(ivBuffer, &ivMap); + + WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); + gcry_error_t error = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE); + if (error) { + GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(error)); + return false; + } + + GstMapInfo map; + gboolean bufferMapped = gst_buffer_map(buffer, &map, static_cast<GstMapFlags>(GST_MAP_READWRITE)); + if (!bufferMapped) { + GST_ERROR_OBJECT(self, "Failed to map buffer"); + return false; + } + + GstMapInfo subSamplesMap; + gboolean subsamplesBufferMapped = gst_buffer_map(subSamplesBuffer, &subSamplesMap, GST_MAP_READ); + if (!subsamplesBufferMapped) { + GST_ERROR_OBJECT(self, "Failed to map subsample buffer"); + gst_buffer_unmap(buffer, &map); + return false; + } + + GstByteReader* reader = gst_byte_reader_new(subSamplesMap.data, subSamplesMap.size); + unsigned position = 0; + unsigned sampleIndex = 0; + + GST_DEBUG_OBJECT(self, "position: %d, size: %zu", position, map.size); + + while (position < map.size) { + guint16 nBytesClear = 0; + guint32 nBytesEncrypted = 0; + + if (sampleIndex < subSampleCount) { + if (!gst_byte_reader_get_uint16_be(reader, &nBytesClear) + || !gst_byte_reader_get_uint32_be(reader, &nBytesEncrypted)) { + GST_DEBUG_OBJECT(self, "unsupported"); + gst_byte_reader_free(reader); + gst_buffer_unmap(buffer, &map); + gst_buffer_unmap(subSamplesBuffer, &subSamplesMap); + return false; + } + + sampleIndex++; + } else { + nBytesClear = 0; + nBytesEncrypted = map.size - position; + } + + GST_TRACE_OBJECT(self, "%d bytes clear (todo=%zu)", nBytesClear, map.size - position); + position += nBytesClear; + if (nBytesEncrypted) { + GST_TRACE_OBJECT(self, "%d bytes encrypted (todo=%zu)", nBytesEncrypted, map.size - position); + error = gcry_cipher_decrypt(priv->handle, map.data + position, nBytesEncrypted, 0, 0); + if (error) { + GST_ERROR_OBJECT(self, "decryption failed: %s", gpg_strerror(error)); + gst_byte_reader_free(reader); + gst_buffer_unmap(buffer, &map); + gst_buffer_unmap(subSamplesBuffer, &subSamplesMap); + return false; + } + position += nBytesEncrypted; + } + } + + gst_byte_reader_free(reader); + gst_buffer_unmap(buffer, &map); + gst_buffer_unmap(subSamplesBuffer, &subSamplesMap); + return true; +} + +static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt* self) +{ + WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); + gcry_cipher_close(priv->handle); +} + +#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h new file mode 100644 index 000000000..30cfa299b --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h @@ -0,0 +1,57 @@ +/* GStreamer ClearKey common encryption decryptor + * + * Copyright (C) 2016 Metrological + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) + +#include "WebKitCommonEncryptionDecryptorGStreamer.h" + +#define CLEAR_KEY_PROTECTION_SYSTEM_UUID "58147ec8-0423-4659-92e6-f52c5ce8c3cc" +#define CLEAR_KEY_PROTECTION_SYSTEM_ID "org.w3.clearkey" + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_MEDIA_CK_DECRYPT (webkit_media_clear_key_decrypt_get_type()) +#define WEBKIT_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecrypt)) +#define WEBKIT_MEDIA_CK_DECRYPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptClass)) +#define WEBKIT_IS_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT)) +#define WEBKIT_IS_MEDIA_CK_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT)) + +typedef struct _WebKitMediaClearKeyDecrypt WebKitMediaClearKeyDecrypt; +typedef struct _WebKitMediaClearKeyDecryptClass WebKitMediaClearKeyDecryptClass; +typedef struct _WebKitMediaClearKeyDecryptPrivate WebKitMediaClearKeyDecryptPrivate; + +GType webkit_media_clear_key_decrypt_get_type(void); + +struct _WebKitMediaClearKeyDecrypt { + WebKitMediaCommonEncryptionDecrypt parent; + + WebKitMediaClearKeyDecryptPrivate* priv; +}; + +struct _WebKitMediaClearKeyDecryptClass { + WebKitMediaCommonEncryptionDecryptClass parentClass; +}; + +G_END_DECLS + +#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp new file mode 100644 index 000000000..389808050 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp @@ -0,0 +1,362 @@ +/* GStreamer ClearKey common encryption decryptor + * + * Copyright (C) 2013 YouView TV Ltd. <alex.ashley@youview.com> + * Copyright (C) 2016 Metrological + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#include "config.h" +#include "WebKitCommonEncryptionDecryptorGStreamer.h" + +#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) + +#include "GRefPtrGStreamer.h" +#include <wtf/Condition.h> +#include <wtf/RunLoop.h> + +#define WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptPrivate)) +struct _WebKitMediaCommonEncryptionDecryptPrivate { + GRefPtr<GstEvent> protectionEvent; + + bool keyReceived; + Lock mutex; + Condition condition; +}; + +static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement*, GstStateChange transition); +static void webKitMediaCommonEncryptionDecryptorFinalize(GObject*); +static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform*, GstPadDirection, GstCaps*, GstCaps*); +static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform*, GstBuffer*); +static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform*, GstEvent*); + +static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*); +static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*); + +GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category); +#define GST_CAT_DEFAULT webkit_media_common_encryption_decrypt_debug_category + +#define webkit_media_common_encryption_decrypt_parent_class parent_class +G_DEFINE_TYPE(WebKitMediaCommonEncryptionDecrypt, webkit_media_common_encryption_decrypt, GST_TYPE_BASE_TRANSFORM); + +static void webkit_media_common_encryption_decrypt_class_init(WebKitMediaCommonEncryptionDecryptClass* klass) +{ + GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); + gobjectClass->finalize = webKitMediaCommonEncryptionDecryptorFinalize; + + GST_DEBUG_CATEGORY_INIT(webkit_media_common_encryption_decrypt_debug_category, + "webkitcenc", 0, "Common Encryption base class"); + + GstElementClass* elementClass = GST_ELEMENT_CLASS(klass); + elementClass->change_state = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptorChangeState); + + GstBaseTransformClass* baseTransformClass = GST_BASE_TRANSFORM_CLASS(klass); + baseTransformClass->transform_ip = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformInPlace); + baseTransformClass->transform_caps = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformCaps); + baseTransformClass->transform_ip_on_passthrough = FALSE; + baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptSinkEventHandler); + + klass->setupCipher = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptDefaultSetupCipher); + klass->releaseCipher = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptDefaultReleaseCipher); + + g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate)); +} + +static void webkit_media_common_encryption_decrypt_init(WebKitMediaCommonEncryptionDecrypt* self) +{ + WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); + + self->priv = priv; + new (priv) WebKitMediaCommonEncryptionDecryptPrivate(); + + GstBaseTransform* base = GST_BASE_TRANSFORM(self); + gst_base_transform_set_in_place(base, TRUE); + gst_base_transform_set_passthrough(base, FALSE); + gst_base_transform_set_gap_aware(base, FALSE); +} + +static void webKitMediaCommonEncryptionDecryptorFinalize(GObject* object) +{ + WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(object); + WebKitMediaCommonEncryptionDecryptPrivate* priv = self->priv; + + priv->~WebKitMediaCommonEncryptionDecryptPrivate(); + GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); +} + +static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform* base, GstPadDirection direction, GstCaps* caps, GstCaps* filter) +{ + if (direction == GST_PAD_UNKNOWN) + return nullptr; + + GST_DEBUG_OBJECT(base, "direction: %s, caps: %" GST_PTR_FORMAT " filter: %" GST_PTR_FORMAT, (direction == GST_PAD_SRC) ? "src" : "sink", caps, filter); + + GstCaps* transformedCaps = gst_caps_new_empty(); + WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base); + WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self); + + unsigned size = gst_caps_get_size(caps); + for (unsigned i = 0; i < size; ++i) { + GstStructure* incomingStructure = gst_caps_get_structure(caps, i); + GRefPtr<GstStructure> outgoingStructure = nullptr; + + if (direction == GST_PAD_SINK) { + if (!gst_structure_has_field(incomingStructure, "original-media-type")) + continue; + + outgoingStructure = adoptGRef(gst_structure_copy(incomingStructure)); + gst_structure_set_name(outgoingStructure.get(), gst_structure_get_string(outgoingStructure.get(), "original-media-type")); + + // Filter out the DRM related fields from the down-stream caps. + for (int j = 0; j < gst_structure_n_fields(incomingStructure); ++j) { + const gchar* fieldName = gst_structure_nth_field_name(incomingStructure, j); + + if (g_str_has_prefix(fieldName, "protection-system") + || g_str_has_prefix(fieldName, "original-media-type")) + gst_structure_remove_field(outgoingStructure.get(), fieldName); + } + } else { + outgoingStructure = adoptGRef(gst_structure_copy(incomingStructure)); + // Filter out the video related fields from the up-stream caps, + // because they are not relevant to the input caps of this element and + // can cause caps negotiation failures with adaptive bitrate streams. + for (int index = gst_structure_n_fields(outgoingStructure.get()) - 1; index >= 0; --index) { + const gchar* fieldName = gst_structure_nth_field_name(outgoingStructure.get(), index); + GST_TRACE("Check field \"%s\" for removal", fieldName); + + if (!g_strcmp0(fieldName, "base-profile") + || !g_strcmp0(fieldName, "codec_data") + || !g_strcmp0(fieldName, "height") + || !g_strcmp0(fieldName, "framerate") + || !g_strcmp0(fieldName, "level") + || !g_strcmp0(fieldName, "pixel-aspect-ratio") + || !g_strcmp0(fieldName, "profile") + || !g_strcmp0(fieldName, "rate") + || !g_strcmp0(fieldName, "width")) { + gst_structure_remove_field(outgoingStructure.get(), fieldName); + GST_TRACE("Removing field %s", fieldName); + } + } + + gst_structure_set(outgoingStructure.get(), "protection-system", G_TYPE_STRING, klass->protectionSystemId, + "original-media-type", G_TYPE_STRING, gst_structure_get_name(incomingStructure), nullptr); + + gst_structure_set_name(outgoingStructure.get(), "application/x-cenc"); + } + + bool duplicate = false; + unsigned size = gst_caps_get_size(transformedCaps); + + for (unsigned index = 0; !duplicate && index < size; ++index) { + GstStructure* structure = gst_caps_get_structure(transformedCaps, index); + if (gst_structure_is_equal(structure, outgoingStructure.get())) + duplicate = true; + } + + if (!duplicate) + gst_caps_append_structure(transformedCaps, outgoingStructure.leakRef()); + } + + if (filter) { + GstCaps* intersection; + + GST_DEBUG_OBJECT(base, "Using filter caps %" GST_PTR_FORMAT, filter); + intersection = gst_caps_intersect_full(transformedCaps, filter, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref(transformedCaps); + transformedCaps = intersection; + } + + GST_DEBUG_OBJECT(base, "returning %" GST_PTR_FORMAT, transformedCaps); + return transformedCaps; +} + +static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer) +{ + WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base); + WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); + LockHolder locker(priv->mutex); + + // The key might not have been received yet. Wait for it. + if (!priv->keyReceived) { + GST_DEBUG_OBJECT(self, "key not available yet, waiting for it"); + if (GST_STATE(GST_ELEMENT(self)) < GST_STATE_PAUSED || (GST_STATE_TARGET(GST_ELEMENT(self)) != GST_STATE_VOID_PENDING && GST_STATE_TARGET(GST_ELEMENT(self)) < GST_STATE_PAUSED)) { + GST_ERROR_OBJECT(self, "can't process key requests in less than PAUSED state"); + return GST_FLOW_NOT_SUPPORTED; + } + priv->condition.waitFor(priv->mutex, Seconds(5), [priv] { + return priv->keyReceived; + }); + if (!priv->keyReceived) { + GST_ERROR_OBJECT(self, "key not available"); + return GST_FLOW_NOT_SUPPORTED; + } + GST_DEBUG_OBJECT(self, "key received, continuing"); + } + + GstProtectionMeta* protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer)); + if (!protectionMeta) { + GST_ERROR_OBJECT(self, "Failed to get GstProtection metadata from buffer %p", buffer); + return GST_FLOW_NOT_SUPPORTED; + } + + unsigned ivSize; + if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) { + GST_ERROR_OBJECT(self, "Failed to get iv_size"); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + + gboolean encrypted; + if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) { + GST_ERROR_OBJECT(self, "Failed to get encrypted flag"); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + + if (!ivSize || !encrypted) { + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_OK; + } + + GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info); + + unsigned subSampleCount; + if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) { + GST_ERROR_OBJECT(self, "Failed to get subsample_count"); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + + const GValue* value; + GstBuffer* subSamplesBuffer = nullptr; + if (subSampleCount) { + value = gst_structure_get_value(protectionMeta->info, "subsamples"); + if (!value) { + GST_ERROR_OBJECT(self, "Failed to get subsamples"); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + subSamplesBuffer = gst_value_get_buffer(value); + } + + WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self); + if (!klass->setupCipher(self)) { + GST_ERROR_OBJECT(self, "Failed to configure cipher"); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + + value = gst_structure_get_value(protectionMeta->info, "iv"); + if (!value) { + GST_ERROR_OBJECT(self, "Failed to get IV for sample"); + klass->releaseCipher(self); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + + GstBuffer* ivBuffer = gst_value_get_buffer(value); + GST_TRACE_OBJECT(self, "decrypting"); + if (!klass->decrypt(self, ivBuffer, buffer, subSampleCount, subSamplesBuffer)) { + GST_ERROR_OBJECT(self, "Decryption failed"); + klass->releaseCipher(self); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_NOT_SUPPORTED; + } + + klass->releaseCipher(self); + gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); + return GST_FLOW_OK; +} + + +static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform* trans, GstEvent* event) +{ + WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(trans); + WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); + WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self); + gboolean result = FALSE; + + switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_PROTECTION: { + const char* systemId = nullptr; + + gst_event_parse_protection(event, &systemId, nullptr, nullptr); + GST_TRACE_OBJECT(self, "received protection event for %s", systemId); + + if (!g_strcmp0(systemId, klass->protectionSystemId)) { + GST_DEBUG_OBJECT(self, "sending protection event to the pipeline"); + gst_element_post_message(GST_ELEMENT(self), + gst_message_new_element(GST_OBJECT(self), + gst_structure_new("drm-key-needed", "event", GST_TYPE_EVENT, event, nullptr))); + } + + gst_event_unref(event); + result = TRUE; + break; + } + case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: { + if (klass->handleKeyResponse(self, event)) { + GST_DEBUG_OBJECT(self, "key received"); + priv->keyReceived = true; + priv->condition.notifyOne(); + } + + gst_event_unref(event); + result = TRUE; + break; + } + default: + result = GST_BASE_TRANSFORM_CLASS(parent_class)->sink_event(trans, event); + break; + } + + return result; +} + +static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement* element, GstStateChange transition) +{ + WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element); + WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG_OBJECT(self, "PAUSED->READY"); + priv->condition.notifyOne(); + break; + default: + break; + } + + GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + + // Add post-transition code here. + + return result; +} + + +static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*) +{ + return true; +} + + +static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*) +{ +} + +#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h new file mode 100644 index 000000000..dcae82790 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h @@ -0,0 +1,64 @@ +/* GStreamer ClearKey common encryption decryptor + * + * Copyright (C) 2013 YouView TV Ltd. <alex.ashley@youview.com> + * Copyright (C) 2016 Metrological + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) + +#include <gst/base/gstbasetransform.h> +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_MEDIA_CENC_DECRYPT (webkit_media_common_encryption_decrypt_get_type()) +#define WEBKIT_MEDIA_CENC_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecrypt)) +#define WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptClass)) +#define WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptClass)) + +#define WEBKIT_IS_MEDIA_CENC_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT)) +#define WEBKIT_IS_MEDIA_CENC_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CENC_DECRYPT)) + +typedef struct _WebKitMediaCommonEncryptionDecrypt WebKitMediaCommonEncryptionDecrypt; +typedef struct _WebKitMediaCommonEncryptionDecryptClass WebKitMediaCommonEncryptionDecryptClass; +typedef struct _WebKitMediaCommonEncryptionDecryptPrivate WebKitMediaCommonEncryptionDecryptPrivate; + +GType webkit_media_common_encryption_decrypt_get_type(void); + +struct _WebKitMediaCommonEncryptionDecrypt { + GstBaseTransform parent; + + WebKitMediaCommonEncryptionDecryptPrivate* priv; +}; + +struct _WebKitMediaCommonEncryptionDecryptClass { + GstBaseTransformClass parentClass; + + const char* protectionSystemId; + gboolean (*handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, GstEvent* event); + gboolean (*setupCipher)(WebKitMediaCommonEncryptionDecrypt*); + gboolean (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSamplesCount, GstBuffer* subSamplesBuffer); + void (*releaseCipher)(WebKitMediaCommonEncryptionDecrypt*); +}; + +G_END_DECLS + +#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp new file mode 100644 index 000000000..c4f2b06bc --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp @@ -0,0 +1,1188 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "AppendPipeline.h" + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "AudioTrackPrivateGStreamer.h" +#include "GRefPtrGStreamer.h" +#include "GStreamerMediaDescription.h" +#include "GStreamerMediaSample.h" +#include "GStreamerUtilities.h" +#include "InbandTextTrackPrivateGStreamer.h" +#include "MediaDescription.h" +#include "SourceBufferPrivateGStreamer.h" +#include "VideoTrackPrivateGStreamer.h" + +#include <gst/app/gstappsink.h> +#include <gst/app/gstappsrc.h> +#include <gst/gst.h> +#include <gst/pbutils/pbutils.h> +#include <gst/video/video.h> +#include <wtf/Condition.h> +#include <wtf/glib/GLibUtilities.h> + +GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug); +#define GST_CAT_DEFAULT webkit_mse_debug + +namespace WebCore { + +static const char* dumpAppendState(AppendPipeline::AppendState appendState) +{ + switch (appendState) { + case AppendPipeline::AppendState::Invalid: + return "Invalid"; + case AppendPipeline::AppendState::NotStarted: + return "NotStarted"; + case AppendPipeline::AppendState::Ongoing: + return "Ongoing"; + case AppendPipeline::AppendState::KeyNegotiation: + return "KeyNegotiation"; + case AppendPipeline::AppendState::DataStarve: + return "DataStarve"; + case AppendPipeline::AppendState::Sampling: + return "Sampling"; + case AppendPipeline::AppendState::LastSample: + return "LastSample"; + case AppendPipeline::AppendState::Aborting: + return "Aborting"; + default: + return "(unknown)"; + } +} + +static void appendPipelineAppsrcNeedData(GstAppSrc*, guint, AppendPipeline*); +static void appendPipelineDemuxerPadAdded(GstElement*, GstPad*, AppendPipeline*); +static void appendPipelineDemuxerPadRemoved(GstElement*, GstPad*, AppendPipeline*); +static void appendPipelineAppsinkCapsChanged(GObject*, GParamSpec*, AppendPipeline*); +static GstPadProbeReturn appendPipelineAppsrcDataLeaving(GstPad*, GstPadProbeInfo*, AppendPipeline*); +#if !LOG_DISABLED +static GstPadProbeReturn appendPipelinePadProbeDebugInformation(GstPad*, GstPadProbeInfo*, struct PadProbeInformation*); +#endif +static GstPadProbeReturn appendPipelineDemuxerBlackHolePadProbe(GstPad*, GstPadProbeInfo*, gpointer); +static GstFlowReturn appendPipelineAppsinkNewSample(GstElement*, AppendPipeline*); +static void appendPipelineAppsinkEOS(GstElement*, AppendPipeline*); + +static void appendPipelineNeedContextMessageCallback(GstBus*, GstMessage* message, AppendPipeline* appendPipeline) +{ + GST_TRACE("received callback"); + appendPipeline->handleNeedContextSyncMessage(message); +} + +static void appendPipelineApplicationMessageCallback(GstBus*, GstMessage* message, AppendPipeline* appendPipeline) +{ + appendPipeline->handleApplicationMessage(message); +} + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +static void appendPipelineElementMessageCallback(GstBus*, GstMessage* message, AppendPipeline* appendPipeline) +{ + appendPipeline->handleElementMessage(message); +} +#endif + +AppendPipeline::AppendPipeline(Ref<MediaSourceClientGStreamerMSE> mediaSourceClient, Ref<SourceBufferPrivateGStreamer> sourceBufferPrivate, MediaPlayerPrivateGStreamerMSE& playerPrivate) + : m_mediaSourceClient(mediaSourceClient.get()) + , m_sourceBufferPrivate(sourceBufferPrivate.get()) + , m_playerPrivate(&playerPrivate) + , m_id(0) + , m_appsrcAtLeastABufferLeft(false) + , m_appsrcNeedDataReceived(false) + , m_appsrcDataLeavingProbeId(0) + , m_appendState(AppendState::NotStarted) + , m_abortPending(false) + , m_streamType(Unknown) +{ + ASSERT(WTF::isMainThread()); + + GST_TRACE("Creating AppendPipeline (%p)", this); + + // FIXME: give a name to the pipeline, maybe related with the track it's managing. + // The track name is still unknown at this time, though. + m_pipeline = gst_pipeline_new(nullptr); + + m_bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get()))); + gst_bus_add_signal_watch(m_bus.get()); + gst_bus_enable_sync_message_emission(m_bus.get()); + + g_signal_connect(m_bus.get(), "sync-message::need-context", G_CALLBACK(appendPipelineNeedContextMessageCallback), this); + g_signal_connect(m_bus.get(), "message::application", G_CALLBACK(appendPipelineApplicationMessageCallback), this); +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + g_signal_connect(m_bus.get(), "message::element", G_CALLBACK(appendPipelineElementMessageCallback), this); +#endif + + // We assign the created instances here instead of adoptRef() because gst_bin_add_many() + // below will already take the initial reference and we need an additional one for us. + m_appsrc = gst_element_factory_make("appsrc", nullptr); + m_demux = gst_element_factory_make("qtdemux", nullptr); + m_appsink = gst_element_factory_make("appsink", nullptr); + + gst_app_sink_set_emit_signals(GST_APP_SINK(m_appsink.get()), TRUE); + gst_base_sink_set_sync(GST_BASE_SINK(m_appsink.get()), FALSE); + + GRefPtr<GstPad> appsinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink")); + g_signal_connect(appsinkPad.get(), "notify::caps", G_CALLBACK(appendPipelineAppsinkCapsChanged), this); + + setAppsrcDataLeavingProbe(); + +#if !LOG_DISABLED + GRefPtr<GstPad> demuxerPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "sink")); + m_demuxerDataEnteringPadProbeInformation.appendPipeline = this; + m_demuxerDataEnteringPadProbeInformation.description = "demuxer data entering"; + m_demuxerDataEnteringPadProbeInformation.probeId = gst_pad_add_probe(demuxerPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelinePadProbeDebugInformation), &m_demuxerDataEnteringPadProbeInformation, nullptr); + m_appsinkDataEnteringPadProbeInformation.appendPipeline = this; + m_appsinkDataEnteringPadProbeInformation.description = "appsink data entering"; + m_appsinkDataEnteringPadProbeInformation.probeId = gst_pad_add_probe(appsinkPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelinePadProbeDebugInformation), &m_appsinkDataEnteringPadProbeInformation, nullptr); +#endif + + // These signals won't be connected outside of the lifetime of "this". + g_signal_connect(m_appsrc.get(), "need-data", G_CALLBACK(appendPipelineAppsrcNeedData), this); + g_signal_connect(m_demux.get(), "pad-added", G_CALLBACK(appendPipelineDemuxerPadAdded), this); + g_signal_connect(m_demux.get(), "pad-removed", G_CALLBACK(appendPipelineDemuxerPadRemoved), this); + g_signal_connect(m_appsink.get(), "new-sample", G_CALLBACK(appendPipelineAppsinkNewSample), this); + g_signal_connect(m_appsink.get(), "eos", G_CALLBACK(appendPipelineAppsinkEOS), this); + + // Add_many will take ownership of a reference. That's why we used an assignment before. + gst_bin_add_many(GST_BIN(m_pipeline.get()), m_appsrc.get(), m_demux.get(), nullptr); + gst_element_link(m_appsrc.get(), m_demux.get()); + + gst_element_set_state(m_pipeline.get(), GST_STATE_READY); +}; + +AppendPipeline::~AppendPipeline() +{ + ASSERT(WTF::isMainThread()); + + { + LockHolder locker(m_newSampleLock); + setAppendState(AppendState::Invalid); + m_newSampleCondition.notifyOne(); + } + + { + LockHolder locker(m_padAddRemoveLock); + m_playerPrivate = nullptr; + m_padAddRemoveCondition.notifyOne(); + } + + GST_TRACE("Destroying AppendPipeline (%p)", this); + + // FIXME: Maybe notify appendComplete here? + + if (m_pipeline) { + ASSERT(m_bus); + gst_bus_remove_signal_watch(m_bus.get()); + gst_element_set_state(m_pipeline.get(), GST_STATE_NULL); + m_pipeline = nullptr; + } + + if (m_appsrc) { + removeAppsrcDataLeavingProbe(); + g_signal_handlers_disconnect_by_data(m_appsrc.get(), this); + m_appsrc = nullptr; + } + + if (m_demux) { +#if !LOG_DISABLED + GRefPtr<GstPad> demuxerPad = adoptGRef(gst_element_get_static_pad(m_demux.get(), "sink")); + gst_pad_remove_probe(demuxerPad.get(), m_demuxerDataEnteringPadProbeInformation.probeId); +#endif + + g_signal_handlers_disconnect_by_data(m_demux.get(), this); + m_demux = nullptr; + } + + if (m_appsink) { + GRefPtr<GstPad> appsinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink")); + g_signal_handlers_disconnect_by_data(appsinkPad.get(), this); + g_signal_handlers_disconnect_by_data(m_appsink.get(), this); + +#if !LOG_DISABLED + gst_pad_remove_probe(appsinkPad.get(), m_appsinkDataEnteringPadProbeInformation.probeId); +#endif + + m_appsink = nullptr; + } + + m_appsinkCaps = nullptr; + m_demuxerSrcPadCaps = nullptr; +}; + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +void AppendPipeline::dispatchPendingDecryptionKey() +{ + ASSERT(m_decryptor); + ASSERT(m_pendingKey); + ASSERT(m_appendState == KeyNegotiation); + GST_TRACE("dispatching key to append pipeline %p", this); + gst_element_send_event(m_pipeline.get(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB, + gst_structure_new("drm-cipher", "key", GST_TYPE_BUFFER, m_pendingKey.get(), nullptr))); + m_pendingKey.clear(); + setAppendState(AppendState::Ongoing); +} + +void AppendPipeline::dispatchDecryptionKey(GstBuffer* buffer) +{ + if (m_appendState == AppendState::KeyNegotiation) { + GST_TRACE("append pipeline %p in key negotiation", this); + m_pendingKey = buffer; + if (m_decryptor) + dispatchPendingDecryptionKey(); + else + GST_TRACE("no decryptor yet, waiting for it"); + } else + GST_TRACE("append pipeline %p not in key negotiation", this); +} +#endif + +void AppendPipeline::clearPlayerPrivate() +{ + ASSERT(WTF::isMainThread()); + GST_DEBUG("cleaning private player"); + + { + LockHolder locker(m_newSampleLock); + // Make sure that AppendPipeline won't process more data from now on and + // instruct handleNewSample to abort itself from now on as well. + setAppendState(AppendState::Invalid); + + // Awake any pending handleNewSample operation in the streaming thread. + m_newSampleCondition.notifyOne(); + } + + { + LockHolder locker(m_padAddRemoveLock); + m_playerPrivate = nullptr; + m_padAddRemoveCondition.notifyOne(); + } + + // And now that no handleNewSample operations will remain stalled waiting + // for the main thread, stop the pipeline. + if (m_pipeline) + gst_element_set_state(m_pipeline.get(), GST_STATE_NULL); +} + +void AppendPipeline::handleNeedContextSyncMessage(GstMessage* message) +{ + const gchar* contextType = nullptr; + gst_message_parse_context_type(message, &contextType); + GST_TRACE("context type: %s", contextType); + if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id")) + setAppendState(AppendPipeline::AppendState::KeyNegotiation); + + // MediaPlayerPrivateGStreamerBase will take care of setting up encryption. + if (m_playerPrivate) + m_playerPrivate->handleSyncMessage(message); +} + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +void AppendPipeline::handleElementMessage(GstMessage* message) +{ + ASSERT(WTF::isMainThread()); + + const GstStructure* structure = gst_message_get_structure(message); + GST_TRACE("%s message from %s", gst_structure_get_name(structure), GST_MESSAGE_SRC_NAME(message)); + if (m_playerPrivate && gst_structure_has_name(structure, "drm-key-needed")) { + setAppendState(AppendPipeline::AppendState::KeyNegotiation); + + GST_DEBUG("sending drm-key-needed message from %s to the player", GST_MESSAGE_SRC_NAME(message)); + GRefPtr<GstEvent> event; + gst_structure_get(structure, "event", GST_TYPE_EVENT, &event.outPtr(), nullptr); + m_playerPrivate->handleProtectionEvent(event.get()); + } +} +#endif + +void AppendPipeline::handleApplicationMessage(GstMessage* message) +{ + ASSERT(WTF::isMainThread()); + + const GstStructure* structure = gst_message_get_structure(message); + + if (gst_structure_has_name(structure, "appsrc-need-data")) { + handleAppsrcNeedDataReceived(); + return; + } + + if (gst_structure_has_name(structure, "appsrc-buffer-left")) { + handleAppsrcAtLeastABufferLeft(); + return; + } + + if (gst_structure_has_name(structure, "demuxer-connect-to-appsink")) { + GRefPtr<GstPad> demuxerSrcPad; + gst_structure_get(structure, "demuxer-src-pad", G_TYPE_OBJECT, &demuxerSrcPad.outPtr(), nullptr); + ASSERT(demuxerSrcPad); + connectDemuxerSrcPadToAppsink(demuxerSrcPad.get()); + return; + } + + if (gst_structure_has_name(structure, "appsink-caps-changed")) { + appsinkCapsChanged(); + return; + } + + if (gst_structure_has_name(structure, "appsink-new-sample")) { + GRefPtr<GstSample> newSample; + gst_structure_get(structure, "new-sample", GST_TYPE_SAMPLE, &newSample.outPtr(), nullptr); + + appsinkNewSample(newSample.get()); + return; + } + + if (gst_structure_has_name(structure, "appsink-eos")) { + appsinkEOS(); + return; + } + + ASSERT_NOT_REACHED(); +} + +void AppendPipeline::handleAppsrcNeedDataReceived() +{ + if (!m_appsrcAtLeastABufferLeft) { + GST_TRACE("discarding until at least a buffer leaves appsrc"); + return; + } + + ASSERT(m_appendState == AppendState::Ongoing || m_appendState == AppendState::Sampling); + ASSERT(!m_appsrcNeedDataReceived); + + GST_TRACE("received need-data from appsrc"); + + m_appsrcNeedDataReceived = true; + checkEndOfAppend(); +} + +void AppendPipeline::handleAppsrcAtLeastABufferLeft() +{ + m_appsrcAtLeastABufferLeft = true; + GST_TRACE("received buffer-left from appsrc"); +#if LOG_DISABLED + removeAppsrcDataLeavingProbe(); +#endif +} + +gint AppendPipeline::id() +{ + ASSERT(WTF::isMainThread()); + + if (m_id) + return m_id; + + static gint s_totalAudio = 0; + static gint s_totalVideo = 0; + static gint s_totalText = 0; + + switch (m_streamType) { + case Audio: + m_id = ++s_totalAudio; + break; + case Video: + m_id = ++s_totalVideo; + break; + case Text: + m_id = ++s_totalText; + break; + case Unknown: + case Invalid: + GST_ERROR("Trying to get id for a pipeline of Unknown/Invalid type"); + ASSERT_NOT_REACHED(); + break; + } + + GST_DEBUG("streamType=%d, id=%d", static_cast<int>(m_streamType), m_id); + + return m_id; +} + +void AppendPipeline::setAppendState(AppendState newAppendState) +{ + ASSERT(WTF::isMainThread()); + // Valid transitions: + // NotStarted-->Ongoing-->DataStarve-->NotStarted + // | | `->Aborting-->NotStarted + // | `->Sampling-···->Sampling-->LastSample-->NotStarted + // | | `->Aborting-->NotStarted + // | `->KeyNegotiation-->Ongoing-->[...] + // `->Aborting-->NotStarted + AppendState oldAppendState = m_appendState; + AppendState nextAppendState = AppendState::Invalid; + + if (oldAppendState != newAppendState) + GST_TRACE("%s --> %s", dumpAppendState(oldAppendState), dumpAppendState(newAppendState)); + + bool ok = false; + + switch (oldAppendState) { + case AppendState::NotStarted: + switch (newAppendState) { + case AppendState::Ongoing: + ok = true; + gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING); + break; + case AppendState::NotStarted: + ok = true; + if (m_pendingBuffer) { + GST_TRACE("pushing pending buffer %p", m_pendingBuffer.get()); + gst_app_src_push_buffer(GST_APP_SRC(appsrc()), m_pendingBuffer.leakRef()); + nextAppendState = AppendState::Ongoing; + } + break; + case AppendState::Aborting: + ok = true; + nextAppendState = AppendState::NotStarted; + break; + case AppendState::Invalid: + ok = true; + break; + default: + break; + } + break; + case AppendState::KeyNegotiation: + switch (newAppendState) { + case AppendState::Ongoing: + case AppendState::Invalid: + ok = true; + break; + default: + break; + } + break; + case AppendState::Ongoing: + switch (newAppendState) { + case AppendState::KeyNegotiation: + case AppendState::Sampling: + case AppendState::Invalid: + ok = true; + break; + case AppendState::DataStarve: + ok = true; + GST_DEBUG("received all pending samples"); + m_sourceBufferPrivate->didReceiveAllPendingSamples(); + if (m_abortPending) + nextAppendState = AppendState::Aborting; + else + nextAppendState = AppendState::NotStarted; + break; + default: + break; + } + break; + case AppendState::DataStarve: + switch (newAppendState) { + case AppendState::NotStarted: + case AppendState::Invalid: + ok = true; + break; + case AppendState::Aborting: + ok = true; + nextAppendState = AppendState::NotStarted; + break; + default: + break; + } + break; + case AppendState::Sampling: + switch (newAppendState) { + case AppendState::Sampling: + case AppendState::Invalid: + ok = true; + break; + case AppendState::LastSample: + ok = true; + GST_DEBUG("received all pending samples"); + m_sourceBufferPrivate->didReceiveAllPendingSamples(); + if (m_abortPending) + nextAppendState = AppendState::Aborting; + else + nextAppendState = AppendState::NotStarted; + break; + default: + break; + } + break; + case AppendState::LastSample: + switch (newAppendState) { + case AppendState::NotStarted: + case AppendState::Invalid: + ok = true; + break; + case AppendState::Aborting: + ok = true; + nextAppendState = AppendState::NotStarted; + break; + default: + break; + } + break; + case AppendState::Aborting: + switch (newAppendState) { + case AppendState::NotStarted: + ok = true; + resetPipeline(); + m_abortPending = false; + nextAppendState = AppendState::NotStarted; + break; + case AppendState::Invalid: + ok = true; + break; + default: + break; + } + break; + case AppendState::Invalid: + ok = true; + break; + } + + if (ok) + m_appendState = newAppendState; + else + GST_ERROR("Invalid append state transition %s --> %s", dumpAppendState(oldAppendState), dumpAppendState(newAppendState)); + + ASSERT(ok); + + if (nextAppendState != AppendState::Invalid) + setAppendState(nextAppendState); +} + +void AppendPipeline::parseDemuxerSrcPadCaps(GstCaps* demuxerSrcPadCaps) +{ + ASSERT(WTF::isMainThread()); + + m_demuxerSrcPadCaps = adoptGRef(demuxerSrcPadCaps); + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Unknown; + + GstStructure* structure = gst_caps_get_structure(m_demuxerSrcPadCaps.get(), 0); + bool sizeConfigured = false; + +#if GST_CHECK_VERSION(1, 5, 3) && (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) + if (gst_structure_has_name(structure, "application/x-cenc")) { + // Any previous decryptor should have been removed from the pipeline by disconnectFromAppSinkFromStreamingThread() + ASSERT(!m_decryptor); + + m_decryptor = WebCore::createGstDecryptor(gst_structure_get_string(structure, "protection-system")); + if (!m_decryptor) { + GST_ERROR("decryptor not found for caps: %" GST_PTR_FORMAT, m_demuxerSrcPadCaps.get()); + return; + } + + const gchar* originalMediaType = gst_structure_get_string(structure, "original-media-type"); + + if (!MediaPlayerPrivateGStreamerMSE::supportsCodecs(originalMediaType)) { + m_presentationSize = WebCore::FloatSize(); + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Invalid; + } else if (g_str_has_prefix(originalMediaType, "video/")) { + int width = 0; + int height = 0; + float finalHeight = 0; + + if (gst_structure_get_int(structure, "width", &width) && gst_structure_get_int(structure, "height", &height)) { + int ratioNumerator = 1; + int ratioDenominator = 1; + + gst_structure_get_fraction(structure, "pixel-aspect-ratio", &ratioNumerator, &ratioDenominator); + finalHeight = height * ((float) ratioDenominator / (float) ratioNumerator); + } + + m_presentationSize = WebCore::FloatSize(width, finalHeight); + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Video; + } else { + m_presentationSize = WebCore::FloatSize(); + if (g_str_has_prefix(originalMediaType, "audio/")) + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Audio; + else if (g_str_has_prefix(originalMediaType, "text/")) + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Text; + } + sizeConfigured = true; + } +#endif + + if (!sizeConfigured) { + const char* structureName = gst_structure_get_name(structure); + GstVideoInfo info; + + if (!MediaPlayerPrivateGStreamerMSE::supportsCodecs(structureName)) { + m_presentationSize = WebCore::FloatSize(); + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Invalid; + } else if (g_str_has_prefix(structureName, "video/") && gst_video_info_from_caps(&info, demuxerSrcPadCaps)) { + float width, height; + + width = info.width; + height = info.height * ((float) info.par_d / (float) info.par_n); + + m_presentationSize = WebCore::FloatSize(width, height); + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Video; + } else { + m_presentationSize = WebCore::FloatSize(); + if (g_str_has_prefix(structureName, "audio/")) + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Audio; + else if (g_str_has_prefix(structureName, "text/")) + m_streamType = WebCore::MediaSourceStreamTypeGStreamer::Text; + } + } +} + +void AppendPipeline::appsinkCapsChanged() +{ + ASSERT(WTF::isMainThread()); + + if (!m_appsink) + return; + + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink")); + GRefPtr<GstCaps> caps = adoptGRef(gst_pad_get_current_caps(pad.get())); + + if (!caps) + return; + + // This means that we're right after a new track has appeared. Otherwise, it's a caps change inside the same track. + bool previousCapsWereNull = !m_appsinkCaps; + + if (m_appsinkCaps != caps) { + m_appsinkCaps = WTFMove(caps); + if (m_playerPrivate && previousCapsWereNull) + m_playerPrivate->trackDetected(this, m_oldTrack, m_track); + didReceiveInitializationSegment(); + gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING); + } +} + +void AppendPipeline::checkEndOfAppend() +{ + ASSERT(WTF::isMainThread()); + + if (!m_appsrcNeedDataReceived || (m_appendState != AppendState::Ongoing && m_appendState != AppendState::Sampling)) + return; + + GST_TRACE("end of append data mark was received"); + + switch (m_appendState) { + case AppendState::Ongoing: + GST_TRACE("DataStarve"); + m_appsrcNeedDataReceived = false; + setAppendState(AppendState::DataStarve); + break; + case AppendState::Sampling: + GST_TRACE("LastSample"); + m_appsrcNeedDataReceived = false; + setAppendState(AppendState::LastSample); + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +void AppendPipeline::appsinkNewSample(GstSample* sample) +{ + ASSERT(WTF::isMainThread()); + + { + LockHolder locker(m_newSampleLock); + + // Ignore samples if we're not expecting them. Refuse processing if we're in Invalid state. + if (m_appendState != AppendState::Ongoing && m_appendState != AppendState::Sampling) { + GST_WARNING("Unexpected sample, appendState=%s", dumpAppendState(m_appendState)); + // FIXME: Return ERROR and find a more robust way to detect that all the + // data has been processed, so we don't need to resort to these hacks. + // All in all, return OK, even if it's not the proper thing to do. We don't want to break the demuxer. + m_flowReturn = GST_FLOW_OK; + m_newSampleCondition.notifyOne(); + return; + } + + RefPtr<GStreamerMediaSample> mediaSample = WebCore::GStreamerMediaSample::create(sample, m_presentationSize, trackId()); + + GST_TRACE("append: trackId=%s PTS=%f presentationSize=%.0fx%.0f", mediaSample->trackID().string().utf8().data(), mediaSample->presentationTime().toFloat(), mediaSample->presentationSize().width(), mediaSample->presentationSize().height()); + + // If we're beyond the duration, ignore this sample and the remaining ones. + MediaTime duration = m_mediaSourceClient->duration(); + if (duration.isValid() && !duration.indefiniteTime() && mediaSample->presentationTime() > duration) { + GST_DEBUG("Detected sample (%f) beyond the duration (%f), declaring LastSample", mediaSample->presentationTime().toFloat(), duration.toFloat()); + setAppendState(AppendState::LastSample); + m_flowReturn = GST_FLOW_OK; + m_newSampleCondition.notifyOne(); + return; + } + + // Add a gap sample if a gap is detected before the first sample. + if (mediaSample->decodeTime() == MediaTime::zeroTime() + && mediaSample->presentationTime() > MediaTime::zeroTime() + && mediaSample->presentationTime() <= MediaTime::createWithDouble(0.1)) { + GST_DEBUG("Adding gap offset"); + mediaSample->applyPtsOffset(MediaTime::zeroTime()); + } + + m_sourceBufferPrivate->didReceiveSample(*mediaSample); + setAppendState(AppendState::Sampling); + m_flowReturn = GST_FLOW_OK; + m_newSampleCondition.notifyOne(); + } + + checkEndOfAppend(); +} + +void AppendPipeline::appsinkEOS() +{ + ASSERT(WTF::isMainThread()); + + switch (m_appendState) { + case AppendState::Aborting: + // Ignored. Operation completion will be managed by the Aborting->NotStarted transition. + return; + case AppendState::Ongoing: + // Finish Ongoing and Sampling states. + setAppendState(AppendState::DataStarve); + break; + case AppendState::Sampling: + setAppendState(AppendState::LastSample); + break; + default: + GST_DEBUG("Unexpected EOS"); + break; + } +} + +void AppendPipeline::didReceiveInitializationSegment() +{ + ASSERT(WTF::isMainThread()); + + WebCore::SourceBufferPrivateClient::InitializationSegment initializationSegment; + + GST_DEBUG("Notifying SourceBuffer for track %s", (m_track) ? m_track->id().string().utf8().data() : nullptr); + initializationSegment.duration = m_mediaSourceClient->duration(); + + switch (m_streamType) { + case Audio: { + WebCore::SourceBufferPrivateClient::InitializationSegment::AudioTrackInformation info; + info.track = static_cast<AudioTrackPrivateGStreamer*>(m_track.get()); + info.description = WebCore::GStreamerMediaDescription::create(m_demuxerSrcPadCaps.get()); + initializationSegment.audioTracks.append(info); + break; + } + case Video: { + WebCore::SourceBufferPrivateClient::InitializationSegment::VideoTrackInformation info; + info.track = static_cast<VideoTrackPrivateGStreamer*>(m_track.get()); + info.description = WebCore::GStreamerMediaDescription::create(m_demuxerSrcPadCaps.get()); + initializationSegment.videoTracks.append(info); + break; + } + default: + GST_ERROR("Unsupported stream type or codec"); + break; + } + + m_sourceBufferPrivate->didReceiveInitializationSegment(initializationSegment); +} + +AtomicString AppendPipeline::trackId() +{ + ASSERT(WTF::isMainThread()); + + if (!m_track) + return AtomicString(); + + return m_track->id(); +} + +void AppendPipeline::resetPipeline() +{ + ASSERT(WTF::isMainThread()); + GST_DEBUG("resetting pipeline"); + m_appsrcAtLeastABufferLeft = false; + setAppsrcDataLeavingProbe(); + + { + LockHolder locker(m_newSampleLock); + m_newSampleCondition.notifyOne(); + gst_element_set_state(m_pipeline.get(), GST_STATE_READY); + gst_element_get_state(m_pipeline.get(), nullptr, nullptr, 0); + } + +#if (!(LOG_DISABLED || defined(GST_DISABLE_GST_DEBUG))) + { + static unsigned i = 0; + // This is here for debugging purposes. It does not make sense to have it as class member. + WTF::String dotFileName = String::format("reset-pipeline-%d", ++i); + gst_debug_bin_to_dot_file(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data()); + } +#endif + +} + +void AppendPipeline::setAppsrcDataLeavingProbe() +{ + if (m_appsrcDataLeavingProbeId) + return; + + GST_TRACE("setting appsrc data leaving probe"); + + GRefPtr<GstPad> appsrcPad = adoptGRef(gst_element_get_static_pad(m_appsrc.get(), "src")); + m_appsrcDataLeavingProbeId = gst_pad_add_probe(appsrcPad.get(), GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelineAppsrcDataLeaving), this, nullptr); +} + +void AppendPipeline::removeAppsrcDataLeavingProbe() +{ + if (!m_appsrcDataLeavingProbeId) + return; + + GST_TRACE("removing appsrc data leaving probe"); + + GRefPtr<GstPad> appsrcPad = adoptGRef(gst_element_get_static_pad(m_appsrc.get(), "src")); + gst_pad_remove_probe(appsrcPad.get(), m_appsrcDataLeavingProbeId); + m_appsrcDataLeavingProbeId = 0; +} + +void AppendPipeline::abort() +{ + ASSERT(WTF::isMainThread()); + GST_DEBUG("aborting"); + + m_pendingBuffer = nullptr; + + // Abort already ongoing. + if (m_abortPending) + return; + + m_abortPending = true; + if (m_appendState == AppendState::NotStarted) + setAppendState(AppendState::Aborting); + // Else, the automatic state transitions will take care when the ongoing append finishes. +} + +GstFlowReturn AppendPipeline::pushNewBuffer(GstBuffer* buffer) +{ + GstFlowReturn result; + + if (m_abortPending) { + m_pendingBuffer = adoptGRef(buffer); + result = GST_FLOW_OK; + } else { + setAppendState(AppendPipeline::AppendState::Ongoing); + GST_TRACE("pushing new buffer %p", buffer); + result = gst_app_src_push_buffer(GST_APP_SRC(appsrc()), buffer); + } + + return result; +} + +void AppendPipeline::reportAppsrcAtLeastABufferLeft() +{ + GST_TRACE("buffer left appsrc, reposting to bus"); + GstStructure* structure = gst_structure_new_empty("appsrc-buffer-left"); + GstMessage* message = gst_message_new_application(GST_OBJECT(m_appsrc.get()), structure); + gst_bus_post(m_bus.get(), message); +} + +void AppendPipeline::reportAppsrcNeedDataReceived() +{ + GST_TRACE("received need-data signal at appsrc, reposting to bus"); + GstStructure* structure = gst_structure_new_empty("appsrc-need-data"); + GstMessage* message = gst_message_new_application(GST_OBJECT(m_appsrc.get()), structure); + gst_bus_post(m_bus.get(), message); +} + +GstFlowReturn AppendPipeline::handleNewAppsinkSample(GstElement* appsink) +{ + ASSERT(!WTF::isMainThread()); + + // Even if we're disabled, it's important to pull the sample out anyway to + // avoid deadlocks when changing to GST_STATE_NULL having a non empty appsink. + GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(appsink))); + LockHolder locker(m_newSampleLock); + + if (!m_playerPrivate || m_appendState == AppendState::Invalid) { + GST_WARNING("AppendPipeline has been disabled, ignoring this sample"); + return GST_FLOW_ERROR; + } + + GstStructure* structure = gst_structure_new("appsink-new-sample", "new-sample", GST_TYPE_SAMPLE, sample.get(), nullptr); + GstMessage* message = gst_message_new_application(GST_OBJECT(appsink), structure); + gst_bus_post(m_bus.get(), message); + GST_TRACE("appsink-new-sample message posted to bus"); + + m_newSampleCondition.wait(m_newSampleLock); + // We've been awaken because the sample was processed or because of + // an exceptional condition (entered in Invalid state, destructor, etc.). + // We can't reliably delete info here, appendPipelineAppsinkNewSampleMainThread will do it. + + return m_flowReturn; +} + +void AppendPipeline::connectDemuxerSrcPadToAppsinkFromAnyThread(GstPad* demuxerSrcPad) +{ + if (!m_appsink) + return; + + GST_DEBUG("connecting to appsink"); + + if (m_demux->numsrcpads > 1) { + GST_WARNING("Only one stream per SourceBuffer is allowed! Ignoring stream %d by adding a black hole probe.", m_demux->numsrcpads); + gulong probeId = gst_pad_add_probe(demuxerSrcPad, GST_PAD_PROBE_TYPE_BUFFER, reinterpret_cast<GstPadProbeCallback>(appendPipelineDemuxerBlackHolePadProbe), nullptr, nullptr); + g_object_set_data(G_OBJECT(demuxerSrcPad), "blackHoleProbeId", GULONG_TO_POINTER(probeId)); + return; + } + + GRefPtr<GstPad> appsinkSinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink")); + + // Only one stream per demuxer is supported. + ASSERT(!gst_pad_is_linked(appsinkSinkPad.get())); + + gint64 timeLength = 0; + if (gst_element_query_duration(m_demux.get(), GST_FORMAT_TIME, &timeLength) + && static_cast<guint64>(timeLength) != GST_CLOCK_TIME_NONE) + m_initialDuration = MediaTime(GST_TIME_AS_USECONDS(timeLength), G_USEC_PER_SEC); + else + m_initialDuration = MediaTime::positiveInfiniteTime(); + + if (WTF::isMainThread()) + connectDemuxerSrcPadToAppsink(demuxerSrcPad); + else { + // Call connectDemuxerSrcPadToAppsink() in the main thread and wait. + LockHolder locker(m_padAddRemoveLock); + if (!m_playerPrivate) + return; + + GstStructure* structure = gst_structure_new("demuxer-connect-to-appsink", "demuxer-src-pad", G_TYPE_OBJECT, demuxerSrcPad, nullptr); + GstMessage* message = gst_message_new_application(GST_OBJECT(m_demux.get()), structure); + gst_bus_post(m_bus.get(), message); + GST_TRACE("demuxer-connect-to-appsink message posted to bus"); + + m_padAddRemoveCondition.wait(m_padAddRemoveLock); + } + + // Must be done in the thread we were called from (usually streaming thread). + bool isData = (m_streamType == WebCore::MediaSourceStreamTypeGStreamer::Audio) + || (m_streamType == WebCore::MediaSourceStreamTypeGStreamer::Video) + || (m_streamType == WebCore::MediaSourceStreamTypeGStreamer::Text); + + if (isData) { + // FIXME: Only add appsink one time. This method can be called several times. + GRefPtr<GstObject> parent = adoptGRef(gst_element_get_parent(m_appsink.get())); + if (!parent) + gst_bin_add(GST_BIN(m_pipeline.get()), m_appsink.get()); + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) + if (m_decryptor) { + gst_object_ref(m_decryptor.get()); + gst_bin_add(GST_BIN(m_pipeline.get()), m_decryptor.get()); + + GRefPtr<GstPad> decryptorSinkPad = adoptGRef(gst_element_get_static_pad(m_decryptor.get(), "sink")); + gst_pad_link(demuxerSrcPad, decryptorSinkPad.get()); + + GRefPtr<GstPad> decryptorSrcPad = adoptGRef(gst_element_get_static_pad(m_decryptor.get(), "src")); + gst_pad_link(decryptorSrcPad.get(), appsinkSinkPad.get()); + + gst_element_sync_state_with_parent(m_appsink.get()); + gst_element_sync_state_with_parent(m_decryptor.get()); + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + if (m_pendingKey) + dispatchPendingDecryptionKey(); +#endif + } else { +#endif + gst_pad_link(demuxerSrcPad, appsinkSinkPad.get()); + gst_element_sync_state_with_parent(m_appsink.get()); +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) + } +#endif + gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED); + } +} + +void AppendPipeline::connectDemuxerSrcPadToAppsink(GstPad* demuxerSrcPad) +{ + ASSERT(WTF::isMainThread()); + GST_DEBUG("Connecting to appsink"); + + LockHolder locker(m_padAddRemoveLock); + GRefPtr<GstPad> sinkSinkPad = adoptGRef(gst_element_get_static_pad(m_appsink.get(), "sink")); + + // Only one stream per demuxer is supported. + ASSERT(!gst_pad_is_linked(sinkSinkPad.get())); + + GRefPtr<GstCaps> caps = adoptGRef(gst_pad_get_current_caps(GST_PAD(demuxerSrcPad))); + + if (!caps || m_appendState == AppendState::Invalid || !m_playerPrivate) { + m_padAddRemoveCondition.notifyOne(); + return; + } + +#ifndef GST_DISABLE_GST_DEBUG + { + GUniquePtr<gchar> strcaps(gst_caps_to_string(caps.get())); + GST_DEBUG("%s", strcaps.get()); + } +#endif + + if (m_initialDuration > m_mediaSourceClient->duration() + || (m_mediaSourceClient->duration().isInvalid() && m_initialDuration > MediaTime::zeroTime())) + m_mediaSourceClient->durationChanged(m_initialDuration); + + m_oldTrack = m_track; + + parseDemuxerSrcPadCaps(gst_caps_ref(caps.get())); + + switch (m_streamType) { + case WebCore::MediaSourceStreamTypeGStreamer::Audio: + if (m_playerPrivate) + m_track = WebCore::AudioTrackPrivateGStreamer::create(m_playerPrivate->pipeline(), id(), sinkSinkPad.get()); + break; + case WebCore::MediaSourceStreamTypeGStreamer::Video: + if (m_playerPrivate) + m_track = WebCore::VideoTrackPrivateGStreamer::create(m_playerPrivate->pipeline(), id(), sinkSinkPad.get()); + break; + case WebCore::MediaSourceStreamTypeGStreamer::Text: + m_track = WebCore::InbandTextTrackPrivateGStreamer::create(id(), sinkSinkPad.get()); + break; + case WebCore::MediaSourceStreamTypeGStreamer::Invalid: + { + GUniquePtr<gchar> strcaps(gst_caps_to_string(caps.get())); + GST_DEBUG("Unsupported track codec: %s", strcaps.get()); + } + // This is going to cause an error which will detach the SourceBuffer and tear down this + // AppendPipeline, so we need the padAddRemove lock released before continuing. + m_track = nullptr; + m_padAddRemoveCondition.notifyOne(); + locker.unlockEarly(); + didReceiveInitializationSegment(); + return; + default: + // No useful data, but notify anyway to complete the append operation. + GST_DEBUG("Received all pending samples (no data)"); + m_sourceBufferPrivate->didReceiveAllPendingSamples(); + break; + } + + m_padAddRemoveCondition.notifyOne(); +} + +void AppendPipeline::disconnectDemuxerSrcPadFromAppsinkFromAnyThread(GstPad* demuxerSrcPad) +{ + // Must be done in the thread we were called from (usually streaming thread). + if (!gst_pad_is_linked(demuxerSrcPad)) { + gulong probeId = GPOINTER_TO_ULONG(g_object_get_data(G_OBJECT(demuxerSrcPad), "blackHoleProbeId")); + if (probeId) { + GST_DEBUG("Disconnecting black hole probe."); + g_object_set_data(G_OBJECT(demuxerSrcPad), "blackHoleProbeId", nullptr); + gst_pad_remove_probe(demuxerSrcPad, probeId); + } else + GST_WARNING("Not disconnecting demuxer src pad because it wasn't linked"); + return; + } + + GST_DEBUG("Disconnecting appsink"); + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) + if (m_decryptor) { + gst_element_unlink(m_decryptor.get(), m_appsink.get()); + gst_element_unlink(m_demux.get(), m_decryptor.get()); + gst_element_set_state(m_decryptor.get(), GST_STATE_NULL); + gst_bin_remove(GST_BIN(m_pipeline.get()), m_decryptor.get()); + } else +#endif + gst_element_unlink(m_demux.get(), m_appsink.get()); +} + +static void appendPipelineAppsinkCapsChanged(GObject* appsinkPad, GParamSpec*, AppendPipeline* appendPipeline) +{ + GstStructure* structure = gst_structure_new_empty("appsink-caps-changed"); + GstMessage* message = gst_message_new_application(GST_OBJECT(appsinkPad), structure); + gst_bus_post(appendPipeline->bus(), message); + GST_TRACE("appsink-caps-changed message posted to bus"); +} + +static GstPadProbeReturn appendPipelineAppsrcDataLeaving(GstPad*, GstPadProbeInfo* info, AppendPipeline* appendPipeline) +{ + ASSERT(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER); + + GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info); + gsize bufferSize = gst_buffer_get_size(buffer); + + GST_TRACE("buffer of size %" G_GSIZE_FORMAT " going thru", bufferSize); + + appendPipeline->reportAppsrcAtLeastABufferLeft(); + + return GST_PAD_PROBE_OK; +} + +#if !LOG_DISABLED +static GstPadProbeReturn appendPipelinePadProbeDebugInformation(GstPad*, GstPadProbeInfo* info, struct PadProbeInformation* padProbeInformation) +{ + ASSERT(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER); + GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info); + GST_TRACE("%s: buffer of size %" G_GSIZE_FORMAT " going thru", padProbeInformation->description, gst_buffer_get_size(buffer)); + return GST_PAD_PROBE_OK; +} +#endif + +static GstPadProbeReturn appendPipelineDemuxerBlackHolePadProbe(GstPad*, GstPadProbeInfo* info, gpointer) +{ + ASSERT(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER); + GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info); + GST_TRACE("buffer of size %" G_GSIZE_FORMAT " ignored", gst_buffer_get_size(buffer)); + return GST_PAD_PROBE_DROP; +} + +static void appendPipelineAppsrcNeedData(GstAppSrc*, guint, AppendPipeline* appendPipeline) +{ + appendPipeline->reportAppsrcNeedDataReceived(); +} + +static void appendPipelineDemuxerPadAdded(GstElement*, GstPad* demuxerSrcPad, AppendPipeline* appendPipeline) +{ + appendPipeline->connectDemuxerSrcPadToAppsinkFromAnyThread(demuxerSrcPad); +} + +static void appendPipelineDemuxerPadRemoved(GstElement*, GstPad* demuxerSrcPad, AppendPipeline* appendPipeline) +{ + appendPipeline->disconnectDemuxerSrcPadFromAppsinkFromAnyThread(demuxerSrcPad); +} + +static GstFlowReturn appendPipelineAppsinkNewSample(GstElement* appsink, AppendPipeline* appendPipeline) +{ + return appendPipeline->handleNewAppsinkSample(appsink); +} + +static void appendPipelineAppsinkEOS(GstElement*, AppendPipeline* appendPipeline) +{ + if (WTF::isMainThread()) + appendPipeline->appsinkEOS(); + else { + GstStructure* structure = gst_structure_new_empty("appsink-eos"); + GstMessage* message = gst_message_new_application(GST_OBJECT(appendPipeline->appsink()), structure); + gst_bus_post(appendPipeline->bus(), message); + GST_TRACE("appsink-eos message posted to bus"); + } + + GST_DEBUG("%s main thread", (WTF::isMainThread()) ? "Is" : "Not"); +} + + + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h new file mode 100644 index 000000000..301265eb9 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "GRefPtrGStreamer.h" +#include "MediaPlayerPrivateGStreamerMSE.h" +#include "MediaSourceClientGStreamerMSE.h" +#include "SourceBufferPrivateGStreamer.h" + +#include <gst/gst.h> +#include <wtf/Condition.h> + +namespace WebCore { + +#if !LOG_DISABLED +struct PadProbeInformation { + AppendPipeline* appendPipeline; + const char* description; + gulong probeId; +}; +#endif + +class AppendPipeline : public ThreadSafeRefCounted<AppendPipeline> { +public: + enum class AppendState { Invalid, NotStarted, Ongoing, KeyNegotiation, DataStarve, Sampling, LastSample, Aborting }; + + AppendPipeline(Ref<MediaSourceClientGStreamerMSE>, Ref<SourceBufferPrivateGStreamer>, MediaPlayerPrivateGStreamerMSE&); + virtual ~AppendPipeline(); + + void handleNeedContextSyncMessage(GstMessage*); + void handleApplicationMessage(GstMessage*); +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + void handleElementMessage(GstMessage*); +#endif + + gint id(); + AppendState appendState() { return m_appendState; } + void setAppendState(AppendState); + + GstFlowReturn handleNewAppsinkSample(GstElement*); + GstFlowReturn pushNewBuffer(GstBuffer*); +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + void dispatchDecryptionKey(GstBuffer*); +#endif + + // Takes ownership of caps. + void parseDemuxerSrcPadCaps(GstCaps*); + void appsinkCapsChanged(); + void appsinkNewSample(GstSample*); + void appsinkEOS(); + void didReceiveInitializationSegment(); + AtomicString trackId(); + void abort(); + + void clearPlayerPrivate(); + Ref<SourceBufferPrivateGStreamer> sourceBufferPrivate() { return m_sourceBufferPrivate.get(); } + GstBus* bus() { return m_bus.get(); } + GstElement* pipeline() { return m_pipeline.get(); } + GstElement* appsrc() { return m_appsrc.get(); } + GstElement* appsink() { return m_appsink.get(); } + GstCaps* demuxerSrcPadCaps() { return m_demuxerSrcPadCaps.get(); } + GstCaps* appsinkCaps() { return m_appsinkCaps.get(); } + RefPtr<WebCore::TrackPrivateBase> track() { return m_track; } + WebCore::MediaSourceStreamTypeGStreamer streamType() { return m_streamType; } + + void disconnectDemuxerSrcPadFromAppsinkFromAnyThread(GstPad*); + void connectDemuxerSrcPadToAppsinkFromAnyThread(GstPad*); + void connectDemuxerSrcPadToAppsink(GstPad*); + + void reportAppsrcAtLeastABufferLeft(); + void reportAppsrcNeedDataReceived(); + +private: + void resetPipeline(); + void checkEndOfAppend(); + void handleAppsrcAtLeastABufferLeft(); + void handleAppsrcNeedDataReceived(); + void removeAppsrcDataLeavingProbe(); + void setAppsrcDataLeavingProbe(); +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + void dispatchPendingDecryptionKey(); +#endif + +private: + Ref<MediaSourceClientGStreamerMSE> m_mediaSourceClient; + Ref<SourceBufferPrivateGStreamer> m_sourceBufferPrivate; + MediaPlayerPrivateGStreamerMSE* m_playerPrivate; + + // (m_mediaType, m_id) is unique. + gint m_id; + + MediaTime m_initialDuration; + + GstFlowReturn m_flowReturn; + + GRefPtr<GstElement> m_pipeline; + GRefPtr<GstBus> m_bus; + GRefPtr<GstElement> m_appsrc; + GRefPtr<GstElement> m_demux; +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA) + GRefPtr<GstElement> m_decryptor; +#endif + // The demuxer has one src stream only, so only one appsink is needed and linked to it. + GRefPtr<GstElement> m_appsink; + + Lock m_newSampleLock; + Condition m_newSampleCondition; + Lock m_padAddRemoveLock; + Condition m_padAddRemoveCondition; + + GRefPtr<GstCaps> m_appsinkCaps; + GRefPtr<GstCaps> m_demuxerSrcPadCaps; + FloatSize m_presentationSize; + + bool m_appsrcAtLeastABufferLeft; + bool m_appsrcNeedDataReceived; + + gulong m_appsrcDataLeavingProbeId; +#if !LOG_DISABLED + struct PadProbeInformation m_demuxerDataEnteringPadProbeInformation; + struct PadProbeInformation m_appsinkDataEnteringPadProbeInformation; +#endif + + // Keeps track of the states of append processing, to avoid performing actions inappropriate for the current state + // (eg: processing more samples when the last one has been detected, etc.). See setAppendState() for valid + // transitions. + AppendState m_appendState; + + // Aborts can only be completed when the normal sample detection has finished. Meanwhile, the willing to abort is + // expressed in this field. + bool m_abortPending; + + WebCore::MediaSourceStreamTypeGStreamer m_streamType; + RefPtr<WebCore::TrackPrivateBase> m_oldTrack; + RefPtr<WebCore::TrackPrivateBase> m_track; + + GRefPtr<GstBuffer> m_pendingBuffer; +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + GRefPtr<GstBuffer> m_pendingKey; +#endif +}; + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp new file mode 100644 index 000000000..776a0be9b --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerMediaDescription.h" + +#include "GUniquePtrGStreamer.h" + +#include <gst/pbutils/pbutils.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/WTFString.h> + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +namespace WebCore { + +AtomicString GStreamerMediaDescription::codec() const +{ + GUniquePtr<gchar> description(gst_pb_utils_get_codec_description(m_caps.get())); + String codecName(description.get()); + + // Report "H.264 (Main Profile)" and "H.264 (High Profile)" just as "H.264" to allow changes between both variants + // go unnoticed to the SourceBuffer layer. + if (codecName.startsWith("H.264")) { + size_t braceStart = codecName.find(" ("); + size_t braceEnd = codecName.find(")"); + if (braceStart != notFound && braceEnd != notFound) + codecName.remove(braceStart, braceEnd-braceStart); + } + + return codecName; +} + +bool GStreamerMediaDescription::isVideo() const +{ + GstStructure* structure = gst_caps_get_structure(m_caps.get(), 0); + const gchar* name = gst_structure_get_name(structure); + + return g_str_has_prefix(name, "video/"); +} + +bool GStreamerMediaDescription::isAudio() const +{ + GstStructure* structure = gst_caps_get_structure(m_caps.get(), 0); + const gchar* name = gst_structure_get_name(structure); + + return g_str_has_prefix(name, "audio/"); +} + +bool GStreamerMediaDescription::isText() const +{ + // FIXME: Implement proper text track support. + return false; +} + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h new file mode 100644 index 000000000..84e263caa --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaDescription.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "GRefPtrGStreamer.h" +#include "MediaDescription.h" + +#include <gst/gst.h> + +namespace WebCore { + +class GStreamerMediaDescription : public MediaDescription { +public: + static Ref<GStreamerMediaDescription> create(GstCaps* caps) + { + return adoptRef(*new GStreamerMediaDescription(caps)); + } + + virtual ~GStreamerMediaDescription() = default; + + AtomicString codec() const override; + bool isVideo() const override; + bool isAudio() const override; + bool isText() const override; + +private: + GStreamerMediaDescription(GstCaps* caps) + : MediaDescription() + , m_caps(caps) + { + } + + GRefPtr<GstCaps> m_caps; +}; + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp new file mode 100644 index 000000000..86d4329df --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerMediaSample.h" + +#include "GStreamerUtilities.h" + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +namespace WebCore { + +GStreamerMediaSample::GStreamerMediaSample(GstSample* sample, const FloatSize& presentationSize, const AtomicString& trackId) + : MediaSample() + , m_pts(MediaTime::zeroTime()) + , m_dts(MediaTime::zeroTime()) + , m_duration(MediaTime::zeroTime()) + , m_trackId(trackId) + , m_size(0) + , m_presentationSize(presentationSize) + , m_flags(MediaSample::IsSync) +{ + + if (!sample) + return; + + GstBuffer* buffer = gst_sample_get_buffer(sample); + if (!buffer) + return; + + auto createMediaTime = + [](GstClockTime time) -> MediaTime { + return MediaTime(GST_TIME_AS_USECONDS(time), G_USEC_PER_SEC); + }; + + if (GST_BUFFER_PTS_IS_VALID(buffer)) + m_pts = createMediaTime(GST_BUFFER_PTS(buffer)); + if (GST_BUFFER_DTS_IS_VALID(buffer)) + m_dts = createMediaTime(GST_BUFFER_DTS(buffer)); + if (GST_BUFFER_DURATION_IS_VALID(buffer)) + m_duration = createMediaTime(GST_BUFFER_DURATION(buffer)); + + m_size = gst_buffer_get_size(buffer); + m_sample = sample; + + if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)) + m_flags = MediaSample::None; + + if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DECODE_ONLY)) + m_flags = static_cast<MediaSample::SampleFlags>(m_flags | MediaSample::IsNonDisplaying); +} + +Ref<GStreamerMediaSample> GStreamerMediaSample::createFakeSample(GstCaps*, MediaTime pts, MediaTime dts, MediaTime duration, const FloatSize& presentationSize, const AtomicString& trackId) +{ + GStreamerMediaSample* gstreamerMediaSample = new GStreamerMediaSample(nullptr, presentationSize, trackId); + gstreamerMediaSample->m_pts = pts; + gstreamerMediaSample->m_dts = dts; + gstreamerMediaSample->m_duration = duration; + gstreamerMediaSample->m_flags = MediaSample::IsNonDisplaying; + return adoptRef(*gstreamerMediaSample); +} + +void GStreamerMediaSample::applyPtsOffset(MediaTime timestampOffset) +{ + if (m_pts > timestampOffset) { + m_duration = m_duration + (m_pts - timestampOffset); + m_pts = timestampOffset; + } +} + +void GStreamerMediaSample::offsetTimestampsBy(const MediaTime& timestampOffset) +{ + if (!timestampOffset) + return; + m_pts += timestampOffset; + m_dts += timestampOffset; + GstBuffer* buffer = gst_sample_get_buffer(m_sample.get()); + if (buffer) { + GST_BUFFER_PTS(buffer) = toGstClockTime(m_pts.toFloat()); + GST_BUFFER_DTS(buffer) = toGstClockTime(m_dts.toFloat()); + } +} + +Ref<MediaSample> GStreamerMediaSample::createNonDisplayingCopy() const +{ + if (!m_sample) + return createFakeSample(nullptr, m_pts, m_dts, m_duration, m_presentationSize, m_trackId); + + GstBuffer* buffer = gst_sample_get_buffer(m_sample.get()); + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DECODE_ONLY); + + GstCaps* caps = gst_sample_get_caps(m_sample.get()); + GstSegment* segment = gst_sample_get_segment(m_sample.get()); + const GstStructure* originalInfo = gst_sample_get_info(m_sample.get()); + GstStructure* info = originalInfo ? gst_structure_copy(originalInfo) : nullptr; + GRefPtr<GstSample> sample = adoptGRef(gst_sample_new(buffer, caps, segment, info)); + + return adoptRef(*new GStreamerMediaSample(sample.get(), m_presentationSize, m_trackId)); +} + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h new file mode 100644 index 000000000..49e12b5c3 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/GStreamerMediaSample.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "FloatSize.h" +#include "GRefPtrGStreamer.h" +#include "MediaSample.h" +#include <gst/gst.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class GStreamerMediaSample : public MediaSample { +public: + static Ref<GStreamerMediaSample> create(GstSample* sample, const FloatSize& presentationSize, const AtomicString& trackId) + { + return adoptRef(*new GStreamerMediaSample(sample, presentationSize, trackId)); + } + + static Ref<GStreamerMediaSample> createFakeSample(GstCaps*, MediaTime pts, MediaTime dts, MediaTime duration, const FloatSize& presentationSize, const AtomicString& trackId); + + void applyPtsOffset(MediaTime); + MediaTime presentationTime() const override { return m_pts; } + MediaTime decodeTime() const override { return m_dts; } + MediaTime duration() const override { return m_duration; } + AtomicString trackID() const override { return m_trackId; } + void setTrackID(const String& trackId) override { m_trackId = trackId; } + size_t sizeInBytes() const override { return m_size; } + GstSample* sample() const { return m_sample.get(); } + FloatSize presentationSize() const override { return m_presentationSize; } + void offsetTimestampsBy(const MediaTime&) override; + void setTimestamps(const MediaTime&, const MediaTime&) override { } + bool isDivisable() const override { return false; } + std::pair<RefPtr<MediaSample>, RefPtr<MediaSample>> divide(const MediaTime&) override { return { nullptr, nullptr }; } + Ref<MediaSample> createNonDisplayingCopy() const override; + SampleFlags flags() const override { return m_flags; } + PlatformSample platformSample() override { return PlatformSample(); } + void dump(PrintStream&) const override { } + +private: + GStreamerMediaSample(GstSample*, const FloatSize& presentationSize, const AtomicString& trackId); + virtual ~GStreamerMediaSample() = default; + + MediaTime m_pts; + MediaTime m_dts; + MediaTime m_duration; + AtomicString m_trackId; + size_t m_size; + GRefPtr<GstSample> m_sample; + FloatSize m_presentationSize; + MediaSample::SampleFlags m_flags; +}; + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp new file mode 100644 index 000000000..4614eb9b9 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp @@ -0,0 +1,860 @@ +/* + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Collabora Ltd. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> + * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2016 Igalia S.L + * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "MediaPlayerPrivateGStreamerMSE.h" + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "AppendPipeline.h" +#include "AudioTrackPrivateGStreamer.h" +#include "GStreamerUtilities.h" +#include "InbandTextTrackPrivateGStreamer.h" +#include "MIMETypeRegistry.h" +#include "MediaDescription.h" +#include "MediaPlayer.h" +#include "NotImplemented.h" +#include "SourceBufferPrivateGStreamer.h" +#include "TimeRanges.h" +#include "URL.h" +#include "VideoTrackPrivateGStreamer.h" + +#include <fnmatch.h> +#include <gst/app/gstappsink.h> +#include <gst/app/gstappsrc.h> +#include <gst/gst.h> +#include <gst/pbutils/pbutils.h> +#include <gst/video/video.h> +#include <wtf/Condition.h> +#include <wtf/NeverDestroyed.h> + +static const char* dumpReadyState(WebCore::MediaPlayer::ReadyState readyState) +{ + switch (readyState) { + case WebCore::MediaPlayer::HaveNothing: return "HaveNothing"; + case WebCore::MediaPlayer::HaveMetadata: return "HaveMetadata"; + case WebCore::MediaPlayer::HaveCurrentData: return "HaveCurrentData"; + case WebCore::MediaPlayer::HaveFutureData: return "HaveFutureData"; + case WebCore::MediaPlayer::HaveEnoughData: return "HaveEnoughData"; + default: return "(unknown)"; + } +} + +GST_DEBUG_CATEGORY(webkit_mse_debug); +#define GST_CAT_DEFAULT webkit_mse_debug + +namespace WebCore { + +void MediaPlayerPrivateGStreamerMSE::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) { + registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateGStreamerMSE>(player); }, + getSupportedTypes, supportsType, nullptr, nullptr, nullptr, supportsKeySystem); + } +} + +bool initializeGStreamerAndRegisterWebKitMSEElement() +{ + if (UNLIKELY(!initializeGStreamer())) + return false; + + registerWebKitGStreamerElements(); + + GST_DEBUG_CATEGORY_INIT(webkit_mse_debug, "webkitmse", 0, "WebKit MSE media player"); + + GRefPtr<GstElementFactory> WebKitMediaSrcFactory = adoptGRef(gst_element_factory_find("webkitmediasrc")); + if (UNLIKELY(!WebKitMediaSrcFactory)) + gst_element_register(nullptr, "webkitmediasrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_SRC); + return true; +} + +bool MediaPlayerPrivateGStreamerMSE::isAvailable() +{ + if (UNLIKELY(!initializeGStreamerAndRegisterWebKitMSEElement())) + return false; + + GRefPtr<GstElementFactory> factory = adoptGRef(gst_element_factory_find("playbin")); + return factory; +} + +MediaPlayerPrivateGStreamerMSE::MediaPlayerPrivateGStreamerMSE(MediaPlayer* player) + : MediaPlayerPrivateGStreamer(player) +{ + GST_TRACE("creating the player (%p)", this); +} + +MediaPlayerPrivateGStreamerMSE::~MediaPlayerPrivateGStreamerMSE() +{ + GST_TRACE("destroying the player (%p)", this); + + for (auto iterator : m_appendPipelinesMap) + iterator.value->clearPlayerPrivate(); + + if (m_source) { + webKitMediaSrcSetMediaPlayerPrivate(WEBKIT_MEDIA_SRC(m_source.get()), nullptr); + g_signal_handlers_disconnect_by_data(m_source.get(), this); + } + + if (m_playbackPipeline) + m_playbackPipeline->setWebKitMediaSrc(nullptr); +} + +void MediaPlayerPrivateGStreamerMSE::load(const String& urlString) +{ + if (!urlString.startsWith("mediasource")) { + // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate. + m_networkState = MediaPlayer::FormatError; + m_player->networkStateChanged(); + return; + } + + if (UNLIKELY(!initializeGStreamerAndRegisterWebKitMSEElement())) + return; + + if (!m_playbackPipeline) + m_playbackPipeline = PlaybackPipeline::create(); + + MediaPlayerPrivateGStreamer::load(urlString); +} + +void MediaPlayerPrivateGStreamerMSE::load(const String& url, MediaSourcePrivateClient* mediaSource) +{ + m_mediaSource = mediaSource; + load(String::format("mediasource%s", url.utf8().data())); +} + +void MediaPlayerPrivateGStreamerMSE::pause() +{ + m_paused = true; + MediaPlayerPrivateGStreamer::pause(); +} + +MediaTime MediaPlayerPrivateGStreamerMSE::durationMediaTime() const +{ + if (UNLIKELY(!m_pipeline || m_errorOccured)) + return MediaTime(); + + return m_mediaTimeDuration; +} + +void MediaPlayerPrivateGStreamerMSE::seek(float time) +{ + if (UNLIKELY(!m_pipeline || m_errorOccured)) + return; + + GST_INFO("[Seek] seek attempt to %f secs", time); + + // Avoid useless seeking. + float current = currentMediaTime().toFloat(); + if (time == current) { + if (!m_seeking) + timeChanged(); + return; + } + + if (isLiveStream()) + return; + + if (m_seeking && m_seekIsPending) { + m_seekTime = time; + return; + } + + GST_DEBUG("Seeking from %f to %f seconds", current, time); + + float prevSeekTime = m_seekTime; + m_seekTime = time; + + if (!doSeek()) { + m_seekTime = prevSeekTime; + GST_WARNING("Seeking to %f failed", time); + return; + } + + m_isEndReached = false; + GST_DEBUG("m_seeking=%s, m_seekTime=%f", m_seeking ? "true" : "false", m_seekTime); +} + +void MediaPlayerPrivateGStreamerMSE::configurePlaySink() +{ + MediaPlayerPrivateGStreamer::configurePlaySink(); + + GRefPtr<GstElement> playsink = adoptGRef(gst_bin_get_by_name(GST_BIN(m_pipeline.get()), "playsink")); + if (playsink) { + // The default value (0) means "send events to all the sinks", instead + // of "only to the first that returns true". This is needed for MSE seek. + g_object_set(G_OBJECT(playsink.get()), "send-event-mode", 0, nullptr); + } +} + +bool MediaPlayerPrivateGStreamerMSE::changePipelineState(GstState newState) +{ + if (seeking()) { + GST_DEBUG("Rejected state change to %s while seeking", + gst_element_state_get_name(newState)); + return true; + } + + return MediaPlayerPrivateGStreamer::changePipelineState(newState); +} + +void MediaPlayerPrivateGStreamerMSE::notifySeekNeedsDataForTime(const MediaTime& seekTime) +{ + // Reenqueue samples needed to resume playback in the new position. + m_mediaSource->seekToTime(seekTime); + + GST_DEBUG("MSE seek to %f finished", seekTime.toDouble()); + + if (!m_gstSeekCompleted) { + m_gstSeekCompleted = true; + maybeFinishSeek(); + } +} + +bool MediaPlayerPrivateGStreamerMSE::doSeek(gint64, float, GstSeekFlags) +{ + // Use doSeek() instead. If anybody is calling this version of doSeek(), something is wrong. + ASSERT_NOT_REACHED(); + return false; +} + +bool MediaPlayerPrivateGStreamerMSE::doSeek() +{ + GstClockTime position = toGstClockTime(m_seekTime); + MediaTime seekTime = MediaTime::createWithDouble(m_seekTime); + double rate = m_player->rate(); + GstSeekFlags seekType = static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE); + + // Always move to seeking state to report correct 'currentTime' while pending for actual seek to complete. + m_seeking = true; + + // Check if playback pipeline is ready for seek. + GstState state, newState; + GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &newState, 0); + if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) { + GST_DEBUG("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult)); + webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true); + m_seeking = false; + return false; + } + if ((getStateResult == GST_STATE_CHANGE_ASYNC + && !(state == GST_STATE_PLAYING && newState == GST_STATE_PAUSED)) + || state < GST_STATE_PAUSED + || m_isEndReached + || !m_gstSeekCompleted) { + CString reason = "Unknown reason"; + if (getStateResult == GST_STATE_CHANGE_ASYNC) { + reason = String::format("In async change %s --> %s", + gst_element_state_get_name(state), + gst_element_state_get_name(newState)).utf8(); + } else if (state < GST_STATE_PAUSED) + reason = "State less than PAUSED"; + else if (m_isEndReached) + reason = "End reached"; + else if (!m_gstSeekCompleted) + reason = "Previous seek is not finished yet"; + + GST_DEBUG("[Seek] Delaying the seek: %s", reason.data()); + + m_seekIsPending = true; + + if (m_isEndReached) { + GST_DEBUG("[Seek] reset pipeline"); + m_resetPipeline = true; + m_seeking = false; + if (!changePipelineState(GST_STATE_PAUSED)) + loadingFailed(MediaPlayer::Empty); + else + m_seeking = true; + } + + return m_seeking; + } + + // Stop accepting new samples until actual seek is finished. + webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), false); + + // Correct seek time if it helps to fix a small gap. + if (!isTimeBuffered(seekTime)) { + // Look if a near future time (<0.1 sec.) is buffered and change the seek target time. + if (m_mediaSource) { + const MediaTime miniGap = MediaTime::createWithDouble(0.1); + MediaTime nearest = m_mediaSource->buffered()->nearest(seekTime); + if (nearest.isValid() && nearest > seekTime && (nearest - seekTime) <= miniGap && isTimeBuffered(nearest + miniGap)) { + GST_DEBUG("[Seek] Changed the seek target time from %f to %f, a near point in the future", seekTime.toFloat(), nearest.toFloat()); + seekTime = nearest; + } + } + } + + // Check if MSE has samples for requested time and defer actual seek if needed. + if (!isTimeBuffered(seekTime)) { + GST_DEBUG("[Seek] Delaying the seek: MSE is not ready"); + GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED); + if (setStateResult == GST_STATE_CHANGE_FAILURE) { + GST_DEBUG("[Seek] Cannot seek, failed to pause playback pipeline."); + webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true); + m_seeking = false; + return false; + } + m_readyState = MediaPlayer::HaveMetadata; + notifySeekNeedsDataForTime(seekTime); + ASSERT(!m_mseSeekCompleted); + return true; + } + + // Complete previous MSE seek if needed. + if (!m_mseSeekCompleted) { + m_mediaSource->monitorSourceBuffers(); + ASSERT(m_mseSeekCompleted); + // Note: seekCompleted will recursively call us. + return m_seeking; + } + + GST_DEBUG("We can seek now"); + + gint64 startTime = position, endTime = GST_CLOCK_TIME_NONE; + if (rate < 0) { + startTime = 0; + endTime = position; + } + + if (!rate) + rate = 1; + + GST_DEBUG("Actual seek to %" GST_TIME_FORMAT ", end time: %" GST_TIME_FORMAT ", rate: %f", GST_TIME_ARGS(startTime), GST_TIME_ARGS(endTime), rate); + + // This will call notifySeekNeedsData() after some time to tell that the pipeline is ready for sample enqueuing. + webKitMediaSrcPrepareSeek(WEBKIT_MEDIA_SRC(m_source.get()), seekTime); + + m_gstSeekCompleted = false; + if (!gst_element_seek(m_pipeline.get(), rate, GST_FORMAT_TIME, seekType, GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime)) { + webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true); + m_seeking = false; + m_gstSeekCompleted = true; + GST_DEBUG("doSeek(): gst_element_seek() failed, returning false"); + return false; + } + + // The samples will be enqueued in notifySeekNeedsData(). + GST_DEBUG("doSeek(): gst_element_seek() succeeded, returning true"); + return true; +} + +void MediaPlayerPrivateGStreamerMSE::maybeFinishSeek() +{ + if (!m_seeking || !m_mseSeekCompleted || !m_gstSeekCompleted) + return; + + GstState state, newState; + GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &newState, 0); + + if (getStateResult == GST_STATE_CHANGE_ASYNC + && !(state == GST_STATE_PLAYING && newState == GST_STATE_PAUSED)) { + GST_DEBUG("[Seek] Delaying seek finish"); + return; + } + + if (m_seekIsPending) { + GST_DEBUG("[Seek] Committing pending seek to %f", m_seekTime); + m_seekIsPending = false; + if (!doSeek()) { + GST_WARNING("[Seek] Seeking to %f failed", m_seekTime); + m_cachedPosition = -1; + } + return; + } + + GST_DEBUG("[Seek] Seeked to %f", m_seekTime); + + webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true); + m_seeking = false; + m_cachedPosition = -1; + // The pipeline can still have a pending state. In this case a position query will fail. + // Right now we can use m_seekTime as a fallback. + m_canFallBackToLastFinishedSeekPosition = true; + timeChanged(); +} + +void MediaPlayerPrivateGStreamerMSE::updatePlaybackRate() +{ + notImplemented(); +} + +bool MediaPlayerPrivateGStreamerMSE::seeking() const +{ + return m_seeking; +} + +// FIXME: MediaPlayerPrivateGStreamer manages the ReadyState on its own. We shouldn't change it manually. +void MediaPlayerPrivateGStreamerMSE::setReadyState(MediaPlayer::ReadyState readyState) +{ + if (readyState == m_readyState) + return; + + if (seeking()) { + GST_DEBUG("Skip ready state change(%s -> %s) due to seek\n", dumpReadyState(m_readyState), dumpReadyState(readyState)); + return; + } + + GST_DEBUG("Ready State Changed manually from %u to %u", m_readyState, readyState); + MediaPlayer::ReadyState oldReadyState = m_readyState; + m_readyState = readyState; + GST_DEBUG("m_readyState: %s -> %s", dumpReadyState(oldReadyState), dumpReadyState(m_readyState)); + + if (oldReadyState < MediaPlayer::HaveCurrentData && m_readyState >= MediaPlayer::HaveCurrentData) { + GST_DEBUG("[Seek] Reporting load state changed to trigger seek continuation"); + loadStateChanged(); + } + m_player->readyStateChanged(); + + GstState pipelineState; + GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &pipelineState, nullptr, 250 * GST_NSECOND); + bool isPlaying = (getStateResult == GST_STATE_CHANGE_SUCCESS && pipelineState == GST_STATE_PLAYING); + + if (m_readyState == MediaPlayer::HaveMetadata && oldReadyState > MediaPlayer::HaveMetadata && isPlaying) { + GST_TRACE("Changing pipeline to PAUSED..."); + bool ok = changePipelineState(GST_STATE_PAUSED); + GST_TRACE("Changed pipeline to PAUSED: %s", ok ? "Success" : "Error"); + } +} + +void MediaPlayerPrivateGStreamerMSE::waitForSeekCompleted() +{ + if (!m_seeking) + return; + + GST_DEBUG("Waiting for MSE seek completed"); + m_mseSeekCompleted = false; +} + +void MediaPlayerPrivateGStreamerMSE::seekCompleted() +{ + if (m_mseSeekCompleted) + return; + + GST_DEBUG("MSE seek completed"); + m_mseSeekCompleted = true; + + doSeek(); + + if (!seeking() && m_readyState >= MediaPlayer::HaveFutureData) + changePipelineState(GST_STATE_PLAYING); + + if (!seeking()) + m_player->timeChanged(); +} + +void MediaPlayerPrivateGStreamerMSE::setRate(float) +{ + notImplemented(); +} + +std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateGStreamerMSE::buffered() const +{ + return m_mediaSource ? m_mediaSource->buffered() : std::make_unique<PlatformTimeRanges>(); +} + +void MediaPlayerPrivateGStreamerMSE::sourceChanged() +{ + m_source = nullptr; + g_object_get(m_pipeline.get(), "source", &m_source.outPtr(), nullptr); + + ASSERT(WEBKIT_IS_MEDIA_SRC(m_source.get())); + + m_playbackPipeline->setWebKitMediaSrc(WEBKIT_MEDIA_SRC(m_source.get())); + + MediaSourceGStreamer::open(*m_mediaSource.get(), *this); + g_signal_connect_swapped(m_source.get(), "video-changed", G_CALLBACK(videoChangedCallback), this); + g_signal_connect_swapped(m_source.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this); + g_signal_connect_swapped(m_source.get(), "text-changed", G_CALLBACK(textChangedCallback), this); + webKitMediaSrcSetMediaPlayerPrivate(WEBKIT_MEDIA_SRC(m_source.get()), this); +} + +void MediaPlayerPrivateGStreamerMSE::updateStates() +{ + if (UNLIKELY(!m_pipeline || m_errorOccured)) + return; + + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + GstState state, pending; + + GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND); + + bool shouldUpdatePlaybackState = false; + switch (getStateResult) { + case GST_STATE_CHANGE_SUCCESS: { + GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + + // Do nothing if on EOS and state changed to READY to avoid recreating the player + // on HTMLMediaElement and properly generate the video 'ended' event. + if (m_isEndReached && state == GST_STATE_READY) + break; + + m_resetPipeline = (state <= GST_STATE_READY); + if (m_resetPipeline) + m_mediaTimeDuration = MediaTime::zeroTime(); + + // Update ready and network states. + switch (state) { + case GST_STATE_NULL: + m_readyState = MediaPlayer::HaveNothing; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_networkState = MediaPlayer::Empty; + break; + case GST_STATE_READY: + m_readyState = MediaPlayer::HaveMetadata; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_networkState = MediaPlayer::Empty; + break; + case GST_STATE_PAUSED: + case GST_STATE_PLAYING: + if (seeking()) { + m_readyState = MediaPlayer::HaveMetadata; + // FIXME: Should we manage NetworkState too? + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + } else if (m_buffering) { + if (m_bufferingPercentage == 100) { + GST_DEBUG("[Buffering] Complete."); + m_buffering = false; + m_readyState = MediaPlayer::HaveEnoughData; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading; + } else { + m_readyState = MediaPlayer::HaveCurrentData; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_networkState = MediaPlayer::Loading; + } + } else if (m_downloadFinished) { + m_readyState = MediaPlayer::HaveEnoughData; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_networkState = MediaPlayer::Loaded; + } else { + m_readyState = MediaPlayer::HaveFutureData; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_networkState = MediaPlayer::Loading; + } + + if (m_eosMarked && state == GST_STATE_PLAYING) + m_eosPending = true; + + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + // Sync states where needed. + if (state == GST_STATE_PAUSED) { + if (!m_volumeAndMuteInitialized) { + notifyPlayerOfVolumeChange(); + notifyPlayerOfMute(); + m_volumeAndMuteInitialized = true; + } + + if (!seeking() && !m_buffering && !m_paused && m_playbackRate) { + GST_DEBUG("[Buffering] Restarting playback."); + changePipelineState(GST_STATE_PLAYING); + } + } else if (state == GST_STATE_PLAYING) { + m_paused = false; + + if ((m_buffering && !isLiveStream()) || !m_playbackRate) { + GST_DEBUG("[Buffering] Pausing stream for buffering."); + changePipelineState(GST_STATE_PAUSED); + } + } else + m_paused = true; + + if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) { + shouldUpdatePlaybackState = true; + GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state)); + } + + break; + } + case GST_STATE_CHANGE_ASYNC: + GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + // Change in progress. + break; + case GST_STATE_CHANGE_FAILURE: + GST_WARNING("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + // Change failed. + return; + case GST_STATE_CHANGE_NO_PREROLL: + GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); + + // Live pipelines go in PAUSED without prerolling. + m_isStreaming = true; + + if (state == GST_STATE_READY) { + m_readyState = MediaPlayer::HaveNothing; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + } else if (state == GST_STATE_PAUSED) { + m_readyState = MediaPlayer::HaveEnoughData; + GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState)); + m_paused = true; + } else if (state == GST_STATE_PLAYING) + m_paused = false; + + if (!m_paused && m_playbackRate) + changePipelineState(GST_STATE_PLAYING); + + m_networkState = MediaPlayer::Loading; + break; + default: + GST_DEBUG("Else : %d", getStateResult); + break; + } + + m_requestedState = GST_STATE_VOID_PENDING; + + if (shouldUpdatePlaybackState) + m_player->playbackStateChanged(); + + if (m_networkState != oldNetworkState) { + GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState); + m_player->networkStateChanged(); + } + if (m_readyState != oldReadyState) { + GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState); + m_player->readyStateChanged(); + } + + if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) { + updatePlaybackRate(); + maybeFinishSeek(); + } +} +void MediaPlayerPrivateGStreamerMSE::asyncStateChangeDone() +{ + if (UNLIKELY(!m_pipeline || m_errorOccured)) + return; + + if (m_seeking) + maybeFinishSeek(); + else + updateStates(); +} + +bool MediaPlayerPrivateGStreamerMSE::isTimeBuffered(const MediaTime &time) const +{ + bool result = m_mediaSource && m_mediaSource->buffered()->contain(time); + GST_DEBUG("Time %f buffered? %s", time.toDouble(), result ? "Yes" : "No"); + return result; +} + +void MediaPlayerPrivateGStreamerMSE::setMediaSourceClient(Ref<MediaSourceClientGStreamerMSE> client) +{ + m_mediaSourceClient = client.ptr(); +} + +RefPtr<MediaSourceClientGStreamerMSE> MediaPlayerPrivateGStreamerMSE::mediaSourceClient() +{ + return m_mediaSourceClient; +} + +void MediaPlayerPrivateGStreamerMSE::durationChanged() +{ + if (!m_mediaSourceClient) { + GST_DEBUG("m_mediaSourceClient is null, doing nothing"); + return; + } + + MediaTime previousDuration = m_mediaTimeDuration; + m_mediaTimeDuration = m_mediaSourceClient->duration(); + + GST_TRACE("previous=%f, new=%f", previousDuration.toFloat(), m_mediaTimeDuration.toFloat()); + + // Avoid emiting durationchanged in the case where the previous duration was 0 because that case is already handled + // by the HTMLMediaElement. + if (m_mediaTimeDuration != previousDuration && m_mediaTimeDuration.isValid() && previousDuration.isValid()) { + m_player->durationChanged(); + m_playbackPipeline->notifyDurationChanged(); + m_mediaSource->durationChanged(m_mediaTimeDuration); + } +} + +static HashSet<String, ASCIICaseInsensitiveHash>& mimeTypeCache() +{ + static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> cache = []() + { + initializeGStreamerAndRegisterWebKitMSEElement(); + HashSet<String, ASCIICaseInsensitiveHash> set; + const char* mimeTypes[] = { + "video/mp4", + "audio/mp4" + }; + for (auto& type : mimeTypes) + set.add(type); + return set; + }(); + return cache; +} + +void MediaPlayerPrivateGStreamerMSE::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types) +{ + types = mimeTypeCache(); +} + +void MediaPlayerPrivateGStreamerMSE::trackDetected(RefPtr<AppendPipeline> appendPipeline, RefPtr<WebCore::TrackPrivateBase> oldTrack, RefPtr<WebCore::TrackPrivateBase> newTrack) +{ + ASSERT(appendPipeline->track() == newTrack); + + GstCaps* caps = appendPipeline->appsinkCaps(); + ASSERT(caps); + GST_DEBUG("track ID: %s, caps: %" GST_PTR_FORMAT, newTrack->id().string().latin1().data(), caps); + + GstStructure* structure = gst_caps_get_structure(caps, 0); + const gchar* mediaType = gst_structure_get_name(structure); + GstVideoInfo info; + + if (g_str_has_prefix(mediaType, "video/") && gst_video_info_from_caps(&info, caps)) { + float width, height; + + width = info.width; + height = info.height * ((float) info.par_d / (float) info.par_n); + m_videoSize.setWidth(width); + m_videoSize.setHeight(height); + } + + if (!oldTrack) + m_playbackPipeline->attachTrack(appendPipeline->sourceBufferPrivate(), newTrack, structure, caps); + else + m_playbackPipeline->reattachTrack(appendPipeline->sourceBufferPrivate(), newTrack); +} + +bool MediaPlayerPrivateGStreamerMSE::supportsCodecs(const String& codecs) +{ + static Vector<const char*> supportedCodecs = { "avc*", "mp4a*", "mpeg", "x-h264" }; + Vector<String> codecEntries; + codecs.split(',', false, codecEntries); + + for (String codec : codecEntries) { + bool isCodecSupported = false; + + // If the codec is named like a mimetype (eg: video/avc) remove the "video/" part. + size_t slashIndex = codec.find('/'); + if (slashIndex != WTF::notFound) + codec = codec.substring(slashIndex+1); + + const char* codecData = codec.utf8().data(); + for (const auto& pattern : supportedCodecs) { + isCodecSupported = !fnmatch(pattern, codecData, 0); + if (isCodecSupported) + break; + } + if (!isCodecSupported) + return false; + } + + return true; +} + +MediaPlayer::SupportsType MediaPlayerPrivateGStreamerMSE::supportsType(const MediaEngineSupportParameters& parameters) +{ + MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported; + if (!parameters.isMediaSource) + return result; + + // Disable VPX/Opus on MSE for now, mp4/avc1 seems way more reliable currently. + if (parameters.type.endsWith("webm")) + return result; + + // YouTube TV provides empty types for some videos and we want to be selected as best media engine for them. + if (parameters.type.isEmpty()) { + result = MediaPlayer::MayBeSupported; + return result; + } + + // Spec says we should not return "probably" if the codecs string is empty. + if (mimeTypeCache().contains(parameters.type)) { + if (parameters.codecs.isEmpty()) + result = MediaPlayer::MayBeSupported; + else + result = supportsCodecs(parameters.codecs) ? MediaPlayer::IsSupported : MediaPlayer::IsNotSupported; + } + + return extendedSupportsType(parameters, result); +} + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) +void MediaPlayerPrivateGStreamerMSE::dispatchDecryptionKey(GstBuffer* buffer) +{ + for (auto it : m_appendPipelinesMap) + it.value->dispatchDecryptionKey(buffer); +} +#endif + +void MediaPlayerPrivateGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus status) +{ + if (status != MediaSourcePrivate::EosNoError) + return; + + GST_DEBUG("Marking end of stream"); + m_eosMarked = true; + updateStates(); +} + +MediaTime MediaPlayerPrivateGStreamerMSE::currentMediaTime() const +{ + MediaTime position = MediaPlayerPrivateGStreamer::currentMediaTime(); + + if (m_eosPending && (paused() || (position >= durationMediaTime()))) { + if (m_networkState != MediaPlayer::Loaded) { + m_networkState = MediaPlayer::Loaded; + m_player->networkStateChanged(); + } + + m_eosPending = false; + m_isEndReached = true; + m_cachedPosition = m_mediaTimeDuration.toFloat(); + m_durationAtEOS = m_mediaTimeDuration.toFloat(); + m_player->timeChanged(); + } + return position; +} + +float MediaPlayerPrivateGStreamerMSE::maxTimeSeekable() const +{ + if (UNLIKELY(m_errorOccured)) + return 0; + + GST_DEBUG("maxTimeSeekable"); + float result = durationMediaTime().toFloat(); + // Infinite duration means live stream. + if (std::isinf(result)) { + MediaTime maxBufferedTime = buffered()->maximumBufferedTime(); + // Return the highest end time reported by the buffered attribute. + result = maxBufferedTime.isValid() ? maxBufferedTime.toFloat() : 0; + } + + return result; +} + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h new file mode 100644 index 000000000..0d3ebb902 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Collabora Ltd. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009, 2010, 2016 Igalia S.L + * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "GRefPtrGStreamer.h" +#include "MediaPlayerPrivateGStreamer.h" +#include "MediaSample.h" +#include "MediaSourceGStreamer.h" +#include "PlaybackPipeline.h" +#include "WebKitMediaSourceGStreamer.h" + +namespace WebCore { + +class MediaSourceClientGStreamerMSE; +class AppendPipeline; +class PlaybackPipeline; + +class MediaPlayerPrivateGStreamerMSE : public MediaPlayerPrivateGStreamer { + WTF_MAKE_NONCOPYABLE(MediaPlayerPrivateGStreamerMSE); WTF_MAKE_FAST_ALLOCATED; + + friend class MediaSourceClientGStreamerMSE; + +public: + explicit MediaPlayerPrivateGStreamerMSE(MediaPlayer*); + virtual ~MediaPlayerPrivateGStreamerMSE(); + + static void registerMediaEngine(MediaEngineRegistrar); + + void load(const String&) override; + void load(const String&, MediaSourcePrivateClient*) override; + + void setDownloadBuffering() override { }; + + bool isLiveStream() const override { return false; } + MediaTime currentMediaTime() const override; + + void pause() override; + bool seeking() const override; + void seek(float) override; + void configurePlaySink() override; + bool changePipelineState(GstState) override; + + void durationChanged() override; + MediaTime durationMediaTime() const override; + + void setRate(float) override; + std::unique_ptr<PlatformTimeRanges> buffered() const override; + float maxTimeSeekable() const override; + + void sourceChanged() override; + + void setReadyState(MediaPlayer::ReadyState); + void waitForSeekCompleted(); + void seekCompleted(); + MediaSourcePrivateClient* mediaSourcePrivateClient() { return m_mediaSource.get(); } + + void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus); + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + void dispatchDecryptionKey(GstBuffer*) override; +#endif + + void trackDetected(RefPtr<AppendPipeline>, RefPtr<WebCore::TrackPrivateBase> oldTrack, RefPtr<WebCore::TrackPrivateBase> newTrack); + void notifySeekNeedsDataForTime(const MediaTime&); + + static bool supportsCodecs(const String& codecs); + +private: + static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&); + static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&); + + static bool isAvailable(); + + // FIXME: Reduce code duplication. + void updateStates() override; + + bool doSeek(gint64, float, GstSeekFlags) override; + bool doSeek(); + void maybeFinishSeek(); + void updatePlaybackRate() override; + void asyncStateChangeDone() override; + + // FIXME: Implement. + unsigned long totalVideoFrames() override { return 0; } + unsigned long droppedVideoFrames() override { return 0; } + unsigned long corruptedVideoFrames() override { return 0; } + MediaTime totalFrameDelay() override { return MediaTime::zeroTime(); } + bool isTimeBuffered(const MediaTime&) const; + + bool isMediaSource() const override { return true; } + + void setMediaSourceClient(Ref<MediaSourceClientGStreamerMSE>); + RefPtr<MediaSourceClientGStreamerMSE> mediaSourceClient(); + + HashMap<RefPtr<SourceBufferPrivateGStreamer>, RefPtr<AppendPipeline>> m_appendPipelinesMap; + bool m_eosMarked = false; + mutable bool m_eosPending = false; + bool m_gstSeekCompleted = true; + RefPtr<MediaSourcePrivateClient> m_mediaSource; + RefPtr<MediaSourceClientGStreamerMSE> m_mediaSourceClient; + MediaTime m_mediaTimeDuration; + bool m_mseSeekCompleted = true; + RefPtr<PlaybackPipeline> m_playbackPipeline; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp new file mode 100644 index 000000000..441401e6a --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "MediaSourceClientGStreamerMSE.h" + +#include "AppendPipeline.h" +#include "MediaPlayerPrivateGStreamerMSE.h" +#include "WebKitMediaSourceGStreamer.h" +#include <gst/gst.h> + +GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug); +#define GST_CAT_DEFAULT webkit_mse_debug + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +namespace WebCore { + +Ref<MediaSourceClientGStreamerMSE> MediaSourceClientGStreamerMSE::create(MediaPlayerPrivateGStreamerMSE& playerPrivate) +{ + ASSERT(WTF::isMainThread()); + + // No return adoptRef(new MediaSourceClientGStreamerMSE(playerPrivate)) because the ownership has already been transferred to MediaPlayerPrivateGStreamerMSE. + Ref<MediaSourceClientGStreamerMSE> client(adoptRef(*new MediaSourceClientGStreamerMSE(playerPrivate))); + playerPrivate.setMediaSourceClient(client.get()); + return client; +} + +MediaSourceClientGStreamerMSE::MediaSourceClientGStreamerMSE(MediaPlayerPrivateGStreamerMSE& playerPrivate) + : m_playerPrivate(&playerPrivate) + , m_duration(MediaTime::invalidTime()) +{ + ASSERT(WTF::isMainThread()); +} + +MediaSourceClientGStreamerMSE::~MediaSourceClientGStreamerMSE() +{ + ASSERT(WTF::isMainThread()); +} + +MediaSourcePrivate::AddStatus MediaSourceClientGStreamerMSE::addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, const ContentType&) +{ + ASSERT(WTF::isMainThread()); + + if (!m_playerPrivate) + return MediaSourcePrivate::AddStatus::NotSupported; + + ASSERT(m_playerPrivate->m_playbackPipeline); + ASSERT(sourceBufferPrivate); + + RefPtr<AppendPipeline> appendPipeline = adoptRef(new AppendPipeline(*this, *sourceBufferPrivate, *m_playerPrivate)); + GST_TRACE("Adding SourceBuffer to AppendPipeline: this=%p sourceBuffer=%p appendPipeline=%p", this, sourceBufferPrivate.get(), appendPipeline.get()); + m_playerPrivate->m_appendPipelinesMap.add(sourceBufferPrivate, appendPipeline); + + return m_playerPrivate->m_playbackPipeline->addSourceBuffer(sourceBufferPrivate); +} + +const MediaTime& MediaSourceClientGStreamerMSE::duration() +{ + ASSERT(WTF::isMainThread()); + + return m_duration; +} + +void MediaSourceClientGStreamerMSE::durationChanged(const MediaTime& duration) +{ + ASSERT(WTF::isMainThread()); + + GST_TRACE("duration: %f", duration.toFloat()); + if (!duration.isValid() || duration.isPositiveInfinite() || duration.isNegativeInfinite()) + return; + + m_duration = duration; + if (m_playerPrivate) + m_playerPrivate->durationChanged(); +} + +void MediaSourceClientGStreamerMSE::abort(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate) +{ + ASSERT(WTF::isMainThread()); + + GST_DEBUG("aborting"); + + if (!m_playerPrivate) + return; + + RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate); + + ASSERT(appendPipeline); + + appendPipeline->abort(); +} + +void MediaSourceClientGStreamerMSE::resetParserState(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate) +{ + ASSERT(WTF::isMainThread()); + + GST_DEBUG("resetting parser state"); + + if (!m_playerPrivate) + return; + + RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate); + + ASSERT(appendPipeline); + + appendPipeline->abort(); +} + +bool MediaSourceClientGStreamerMSE::append(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, const unsigned char* data, unsigned length) +{ + ASSERT(WTF::isMainThread()); + + GST_DEBUG("Appending %u bytes", length); + + if (!m_playerPrivate) + return false; + + RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate); + + ASSERT(appendPipeline); + + void* bufferData = fastMalloc(length); + GstBuffer* buffer = gst_buffer_new_wrapped_full(static_cast<GstMemoryFlags>(0), bufferData, length, 0, length, bufferData, fastFree); + gst_buffer_fill(buffer, 0, data, length); + + return appendPipeline->pushNewBuffer(buffer) == GST_FLOW_OK; +} + +void MediaSourceClientGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus status) +{ + ASSERT(WTF::isMainThread()); + + if (!m_playerPrivate) + return; + + m_playerPrivate->markEndOfStream(status); +} + +void MediaSourceClientGStreamerMSE::removedFromMediaSource(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate) +{ + ASSERT(WTF::isMainThread()); + + if (!m_playerPrivate) + return; + + ASSERT(m_playerPrivate->m_playbackPipeline); + + RefPtr<AppendPipeline> appendPipeline = m_playerPrivate->m_appendPipelinesMap.get(sourceBufferPrivate); + + ASSERT(appendPipeline); + + appendPipeline->clearPlayerPrivate(); + m_playerPrivate->m_appendPipelinesMap.remove(sourceBufferPrivate); + // AppendPipeline destructor will take care of cleaning up when appropriate. + + m_playerPrivate->m_playbackPipeline->removeSourceBuffer(sourceBufferPrivate); +} + +void MediaSourceClientGStreamerMSE::flush(AtomicString trackId) +{ + ASSERT(WTF::isMainThread()); + + if (m_playerPrivate) + m_playerPrivate->m_playbackPipeline->flush(trackId); +} + +void MediaSourceClientGStreamerMSE::enqueueSample(PassRefPtr<MediaSample> prpSample) +{ + ASSERT(WTF::isMainThread()); + + if (m_playerPrivate) + m_playerPrivate->m_playbackPipeline->enqueueSample(prpSample); +} + +GRefPtr<WebKitMediaSrc> MediaSourceClientGStreamerMSE::webKitMediaSrc() +{ + ASSERT(WTF::isMainThread()); + + if (!m_playerPrivate) + return nullptr; + + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(m_playerPrivate->m_source.get()); + + ASSERT(WEBKIT_IS_MEDIA_SRC(source)); + + return source; +} + +void MediaSourceClientGStreamerMSE::clearPlayerPrivate() +{ + ASSERT(WTF::isMainThread()); + + m_playerPrivate = nullptr; +} + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h new file mode 100644 index 000000000..c3d4ac7bc --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceClientGStreamerMSE.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "GRefPtrGStreamer.h" +#include "MediaSourcePrivate.h" +#include "MediaSourcePrivateClient.h" +#include "WebKitMediaSourceGStreamer.h" +#include <wtf/MediaTime.h> + +namespace WebCore { + +class ContentType; +class MediaPlayerPrivateGStreamerMSE; +class MediaSample; +class SourceBufferPrivateGStreamer; + +class MediaSourceClientGStreamerMSE : public RefCounted<MediaSourceClientGStreamerMSE> { +public: + static Ref<MediaSourceClientGStreamerMSE> create(MediaPlayerPrivateGStreamerMSE&); + virtual ~MediaSourceClientGStreamerMSE(); + + // From MediaSourceGStreamer. + MediaSourcePrivate::AddStatus addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>, const ContentType&); + void durationChanged(const MediaTime&); + void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus); + + // From SourceBufferPrivateGStreamer. + void abort(RefPtr<SourceBufferPrivateGStreamer>); + void resetParserState(RefPtr<SourceBufferPrivateGStreamer>); + bool append(RefPtr<SourceBufferPrivateGStreamer>, const unsigned char*, unsigned); + void removedFromMediaSource(RefPtr<SourceBufferPrivateGStreamer>); + void flush(AtomicString); + void enqueueSample(PassRefPtr<MediaSample>); + + void clearPlayerPrivate(); + + const MediaTime& duration(); + GRefPtr<WebKitMediaSrc> webKitMediaSrc(); + +private: + MediaSourceClientGStreamerMSE(MediaPlayerPrivateGStreamerMSE&); + + MediaPlayerPrivateGStreamerMSE* m_playerPrivate; + MediaTime m_duration; +}; + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp new file mode 100644 index 000000000..92095b610 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Orange + * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * Copyright (C) 2015, 2016 Igalia, S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaSourceGStreamer.h" + +#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) + +#include "ContentType.h" +#include "MediaPlayerPrivateGStreamer.h" +#include "MediaPlayerPrivateGStreamerMSE.h" +#include "MediaSourceClientGStreamerMSE.h" +#include "NotImplemented.h" +#include "SourceBufferPrivateGStreamer.h" +#include "TimeRanges.h" +#include "WebKitMediaSourceGStreamer.h" +#include <wtf/PassRefPtr.h> +#include <wtf/glib/GRefPtr.h> + +namespace WebCore { + +void MediaSourceGStreamer::open(MediaSourcePrivateClient& mediaSource, MediaPlayerPrivateGStreamerMSE& playerPrivate) +{ + mediaSource.setPrivateAndOpen(adoptRef(*new MediaSourceGStreamer(mediaSource, playerPrivate))); +} + +MediaSourceGStreamer::MediaSourceGStreamer(MediaSourcePrivateClient& mediaSource, MediaPlayerPrivateGStreamerMSE& playerPrivate) + : MediaSourcePrivate() + , m_client(MediaSourceClientGStreamerMSE::create(playerPrivate)) + , m_mediaSource(mediaSource) + , m_playerPrivate(playerPrivate) +{ +} + +MediaSourceGStreamer::~MediaSourceGStreamer() +{ + for (auto& sourceBufferPrivate : m_sourceBuffers) + sourceBufferPrivate->clearMediaSource(); +} + +MediaSourceGStreamer::AddStatus MediaSourceGStreamer::addSourceBuffer(const ContentType& contentType, RefPtr<SourceBufferPrivate>& sourceBufferPrivate) +{ + sourceBufferPrivate = SourceBufferPrivateGStreamer::create(this, m_client.get(), contentType); + RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivateGStreamer = static_cast<SourceBufferPrivateGStreamer*>(sourceBufferPrivate.get()); + m_sourceBuffers.add(sourceBufferPrivateGStreamer); + return m_client->addSourceBuffer(sourceBufferPrivateGStreamer, contentType); +} + +void MediaSourceGStreamer::removeSourceBuffer(SourceBufferPrivate* sourceBufferPrivate) +{ + RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivateGStreamer = static_cast<SourceBufferPrivateGStreamer*>(sourceBufferPrivate); + ASSERT(m_sourceBuffers.contains(sourceBufferPrivateGStreamer)); + + sourceBufferPrivateGStreamer->clearMediaSource(); + m_sourceBuffers.remove(sourceBufferPrivateGStreamer); + m_activeSourceBuffers.remove(sourceBufferPrivateGStreamer.get()); +} + +void MediaSourceGStreamer::durationChanged() +{ + m_client->durationChanged(m_mediaSource->duration()); +} + +void MediaSourceGStreamer::markEndOfStream(EndOfStreamStatus status) +{ + m_client->markEndOfStream(status); +} + +void MediaSourceGStreamer::unmarkEndOfStream() +{ + notImplemented(); +} + +MediaPlayer::ReadyState MediaSourceGStreamer::readyState() const +{ + return m_playerPrivate.readyState(); +} + +void MediaSourceGStreamer::setReadyState(MediaPlayer::ReadyState state) +{ + m_playerPrivate.setReadyState(state); +} + +void MediaSourceGStreamer::waitForSeekCompleted() +{ + m_playerPrivate.waitForSeekCompleted(); +} + +void MediaSourceGStreamer::seekCompleted() +{ + m_playerPrivate.seekCompleted(); +} + +void MediaSourceGStreamer::sourceBufferPrivateDidChangeActiveState(SourceBufferPrivateGStreamer* sourceBufferPrivate, bool isActive) +{ + if (!isActive) + m_activeSourceBuffers.remove(sourceBufferPrivate); + else if (!m_activeSourceBuffers.contains(sourceBufferPrivate)) + m_activeSourceBuffers.add(sourceBufferPrivate); +} + +std::unique_ptr<PlatformTimeRanges> MediaSourceGStreamer::buffered() +{ + return m_mediaSource->buffered(); +} + +} +#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h new file mode 100644 index 000000000..c9a09fa04 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourceGStreamer.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Orange + * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * Copyright (C) 2015, 2016 Igalia, S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) +#include "MediaSourcePrivate.h" + +#include <wtf/Forward.h> +#include <wtf/HashSet.h> + +typedef struct _WebKitMediaSrc WebKitMediaSrc; + +namespace WebCore { + +class SourceBufferPrivateGStreamer; +class MediaSourceClientGStreamerMSE; +class MediaPlayerPrivateGStreamerMSE; +class PlatformTimeRanges; + +// FIXME: Should this be called MediaSourcePrivateGStreamer? +class MediaSourceGStreamer final : public MediaSourcePrivate { +public: + static void open(MediaSourcePrivateClient&, MediaPlayerPrivateGStreamerMSE&); + virtual ~MediaSourceGStreamer(); + + MediaSourceClientGStreamerMSE& client() { return m_client.get(); } + AddStatus addSourceBuffer(const ContentType&, RefPtr<SourceBufferPrivate>&) override; + void removeSourceBuffer(SourceBufferPrivate*); + + void durationChanged() override; + void markEndOfStream(EndOfStreamStatus) override; + void unmarkEndOfStream() override; + + MediaPlayer::ReadyState readyState() const override; + void setReadyState(MediaPlayer::ReadyState) override; + + void waitForSeekCompleted() override; + void seekCompleted() override; + + void sourceBufferPrivateDidChangeActiveState(SourceBufferPrivateGStreamer*, bool); + + std::unique_ptr<PlatformTimeRanges> buffered(); + +private: + MediaSourceGStreamer(MediaSourcePrivateClient&, MediaPlayerPrivateGStreamerMSE&); + + HashSet<RefPtr<SourceBufferPrivateGStreamer>> m_sourceBuffers; + HashSet<SourceBufferPrivateGStreamer*> m_activeSourceBuffers; + Ref<MediaSourceClientGStreamerMSE> m_client; + Ref<MediaSourcePrivateClient> m_mediaSource; + MediaPlayerPrivateGStreamerMSE& m_playerPrivate; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp new file mode 100644 index 000000000..95df6d947 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "PlaybackPipeline.h" + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "AudioTrackPrivateGStreamer.h" +#include "GStreamerMediaSample.h" +#include "GStreamerUtilities.h" +#include "MediaSample.h" +#include "SourceBufferPrivateGStreamer.h" +#include "VideoTrackPrivateGStreamer.h" + +#include <gst/app/gstappsrc.h> +#include <gst/gst.h> +#include <wtf/MainThread.h> +#include <wtf/RefCounted.h> +#include <wtf/glib/GMutexLocker.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/AtomicString.h> + +GST_DEBUG_CATEGORY_EXTERN(webkit_mse_debug); +#define GST_CAT_DEFAULT webkit_mse_debug + +static Stream* getStreamByTrackId(WebKitMediaSrc*, AtomicString); +static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc*, WebCore::SourceBufferPrivateGStreamer*); + +static Stream* getStreamByTrackId(WebKitMediaSrc* source, AtomicString trackIdString) +{ + // WebKitMediaSrc should be locked at this point. + for (Stream* stream : source->priv->streams) { + if (stream->type != WebCore::Invalid + && ((stream->audioTrack && stream->audioTrack->id() == trackIdString) + || (stream->videoTrack && stream->videoTrack->id() == trackIdString) ) ) { + return stream; + } + } + return nullptr; +} + +static Stream* getStreamBySourceBufferPrivate(WebKitMediaSrc* source, WebCore::SourceBufferPrivateGStreamer* sourceBufferPrivate) +{ + for (Stream* stream : source->priv->streams) { + if (stream->sourceBuffer == sourceBufferPrivate) + return stream; + } + return nullptr; +} + +// FIXME: Use gst_app_src_push_sample() instead when we switch to the appropriate GStreamer version. +static GstFlowReturn pushSample(GstAppSrc* appsrc, GstSample* sample) +{ + g_return_val_if_fail(GST_IS_SAMPLE(sample), GST_FLOW_ERROR); + + GstCaps* caps = gst_sample_get_caps(sample); + if (caps) + gst_app_src_set_caps(appsrc, caps); + else + GST_WARNING_OBJECT(appsrc, "received sample without caps"); + + GstBuffer* buffer = gst_sample_get_buffer(sample); + if (UNLIKELY(!buffer)) { + GST_WARNING_OBJECT(appsrc, "received sample without buffer"); + return GST_FLOW_OK; + } + + // gst_app_src_push_buffer() steals the reference, we need an additional one. + return gst_app_src_push_buffer(appsrc, gst_buffer_ref(buffer)); +} + +namespace WebCore { + +void PlaybackPipeline::setWebKitMediaSrc(WebKitMediaSrc* webKitMediaSrc) +{ + GST_DEBUG("webKitMediaSrc=%p", webKitMediaSrc); + m_webKitMediaSrc = webKitMediaSrc; +} + +WebKitMediaSrc* PlaybackPipeline::webKitMediaSrc() +{ + return m_webKitMediaSrc.get(); +} + +MediaSourcePrivate::AddStatus PlaybackPipeline::addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate) +{ + WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv; + + if (priv->allTracksConfigured) { + GST_ERROR_OBJECT(m_webKitMediaSrc.get(), "Adding new source buffers after first data not supported yet"); + return MediaSourcePrivate::NotSupported; + } + + GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "State %d", int(GST_STATE(m_webKitMediaSrc.get()))); + + Stream* stream = new Stream{ }; + stream->parent = m_webKitMediaSrc.get(); + stream->appsrc = gst_element_factory_make("appsrc", nullptr); + stream->appsrcNeedDataFlag = false; + stream->sourceBuffer = sourceBufferPrivate.get(); + + // No track has been attached yet. + stream->type = Invalid; + stream->parser = nullptr; + stream->caps = nullptr; + stream->audioTrack = nullptr; + stream->videoTrack = nullptr; + stream->presentationSize = WebCore::FloatSize(); + stream->lastEnqueuedTime = MediaTime::invalidTime(); + + gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &enabledAppsrcCallbacks, stream->parent, nullptr); + gst_app_src_set_emit_signals(GST_APP_SRC(stream->appsrc), FALSE); + gst_app_src_set_stream_type(GST_APP_SRC(stream->appsrc), GST_APP_STREAM_TYPE_SEEKABLE); + + gst_app_src_set_max_bytes(GST_APP_SRC(stream->appsrc), 2 * WTF::MB); + g_object_set(G_OBJECT(stream->appsrc), "block", FALSE, "min-percent", 20, nullptr); + + GST_OBJECT_LOCK(m_webKitMediaSrc.get()); + priv->streams.prepend(stream); + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + + gst_bin_add(GST_BIN(m_webKitMediaSrc.get()), stream->appsrc); + gst_element_sync_state_with_parent(stream->appsrc); + + return MediaSourcePrivate::Ok; +} + +void PlaybackPipeline::removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate) +{ + ASSERT(WTF::isMainThread()); + + GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Element removed from MediaSource"); + GST_OBJECT_LOCK(m_webKitMediaSrc.get()); + WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv; + Stream* stream = nullptr; + Deque<Stream*>::iterator streamPosition = priv->streams.begin(); + + for (; streamPosition != priv->streams.end(); ++streamPosition) { + if ((*streamPosition)->sourceBuffer == sourceBufferPrivate.get()) { + stream = *streamPosition; + break; + } + } + if (stream) + priv->streams.remove(streamPosition); + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + + if (stream) + webKitMediaSrcFreeStream(m_webKitMediaSrc.get(), stream); +} + +void PlaybackPipeline::attachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate, GstStructure* structure, GstCaps* caps) +{ + WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get(); + + GST_OBJECT_LOCK(webKitMediaSrc); + Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get()); + GST_OBJECT_UNLOCK(webKitMediaSrc); + + ASSERT(stream); + + GST_OBJECT_LOCK(webKitMediaSrc); + unsigned padId = stream->parent->priv->numberOfPads; + stream->parent->priv->numberOfPads++; + GST_OBJECT_UNLOCK(webKitMediaSrc); + + const gchar* mediaType = gst_structure_get_name(structure); + + GST_DEBUG_OBJECT(webKitMediaSrc, "Configured track %s: appsrc=%s, padId=%u, mediaType=%s", trackPrivate->id().string().utf8().data(), GST_ELEMENT_NAME(stream->appsrc), padId, mediaType); + + GUniquePtr<gchar> parserBinName(g_strdup_printf("streamparser%u", padId)); + + if (!g_strcmp0(mediaType, "video/x-h264")) { + GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h264", "alignment", G_TYPE_STRING, "au", nullptr)); + GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr); + g_object_set(capsfilter, "caps", filterCaps.get(), nullptr); + + stream->parser = gst_bin_new(parserBinName.get()); + + GstElement* parser = gst_element_factory_make("h264parse", nullptr); + gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr); + gst_element_link_pads(parser, "src", capsfilter, "sink"); + + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink")); + gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get())); + + pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src")); + gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get())); + } else if (!g_strcmp0(mediaType, "video/x-h265")) { + GRefPtr<GstCaps> filterCaps = adoptGRef(gst_caps_new_simple("video/x-h265", "alignment", G_TYPE_STRING, "au", nullptr)); + GstElement* capsfilter = gst_element_factory_make("capsfilter", nullptr); + g_object_set(capsfilter, "caps", filterCaps.get(), nullptr); + + stream->parser = gst_bin_new(parserBinName.get()); + + GstElement* parser = gst_element_factory_make("h265parse", nullptr); + gst_bin_add_many(GST_BIN(stream->parser), parser, capsfilter, nullptr); + gst_element_link_pads(parser, "src", capsfilter, "sink"); + + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink")); + gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get())); + + pad = adoptGRef(gst_element_get_static_pad(capsfilter, "src")); + gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get())); + } else if (!g_strcmp0(mediaType, "audio/mpeg")) { + gint mpegversion = -1; + gst_structure_get_int(structure, "mpegversion", &mpegversion); + + GstElement* parser = nullptr; + if (mpegversion == 1) + parser = gst_element_factory_make("mpegaudioparse", nullptr); + else if (mpegversion == 2 || mpegversion == 4) + parser = gst_element_factory_make("aacparse", nullptr); + else + ASSERT_NOT_REACHED(); + + stream->parser = gst_bin_new(parserBinName.get()); + gst_bin_add(GST_BIN(stream->parser), parser); + + GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(parser, "sink")); + gst_element_add_pad(stream->parser, gst_ghost_pad_new("sink", pad.get())); + + pad = adoptGRef(gst_element_get_static_pad(parser, "src")); + gst_element_add_pad(stream->parser, gst_ghost_pad_new("src", pad.get())); + } else if (!g_strcmp0(mediaType, "video/x-vp9")) + stream->parser = nullptr; + else { + GST_ERROR_OBJECT(stream->parent, "Unsupported media format: %s", mediaType); + return; + } + + GST_OBJECT_LOCK(webKitMediaSrc); + stream->type = Unknown; + GST_OBJECT_UNLOCK(webKitMediaSrc); + + GRefPtr<GstPad> sourcePad; + if (stream->parser) { + gst_bin_add(GST_BIN(stream->parent), stream->parser); + gst_element_sync_state_with_parent(stream->parser); + + GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(stream->parser, "sink")); + sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src")); + gst_pad_link(sourcePad.get(), sinkPad.get()); + sourcePad = adoptGRef(gst_element_get_static_pad(stream->parser, "src")); + } else { + GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Stream of type %s doesn't require a parser bin", mediaType); + sourcePad = adoptGRef(gst_element_get_static_pad(stream->appsrc, "src")); + } + ASSERT(sourcePad); + + // FIXME: Is padId the best way to identify the Stream? What about trackId? + g_object_set_data(G_OBJECT(sourcePad.get()), "padId", GINT_TO_POINTER(padId)); + webKitMediaSrcLinkParser(sourcePad.get(), caps, stream); + + ASSERT(stream->parent->priv->mediaPlayerPrivate); + int signal = -1; + + GST_OBJECT_LOCK(webKitMediaSrc); + if (g_str_has_prefix(mediaType, "audio")) { + stream->type = Audio; + stream->parent->priv->numberOfAudioStreams++; + signal = SIGNAL_AUDIO_CHANGED; + stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get())); + } else if (g_str_has_prefix(mediaType, "video")) { + stream->type = Video; + stream->parent->priv->numberOfVideoStreams++; + signal = SIGNAL_VIDEO_CHANGED; + stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get())); + } else if (g_str_has_prefix(mediaType, "text")) { + stream->type = Text; + stream->parent->priv->numberOfTextStreams++; + signal = SIGNAL_TEXT_CHANGED; + + // FIXME: Support text tracks. + } + GST_OBJECT_UNLOCK(webKitMediaSrc); + + if (signal != -1) + g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr); +} + +void PlaybackPipeline::reattachTrack(RefPtr<SourceBufferPrivateGStreamer> sourceBufferPrivate, RefPtr<TrackPrivateBase> trackPrivate) +{ + GST_DEBUG("Re-attaching track"); + + // FIXME: Maybe remove this method. Now the caps change is managed by gst_appsrc_push_sample() in enqueueSample() + // and flushAndEnqueueNonDisplayingSamples(). + + WebKitMediaSrc* webKitMediaSrc = m_webKitMediaSrc.get(); + + GST_OBJECT_LOCK(webKitMediaSrc); + Stream* stream = getStreamBySourceBufferPrivate(webKitMediaSrc, sourceBufferPrivate.get()); + GST_OBJECT_UNLOCK(webKitMediaSrc); + + ASSERT(stream && stream->type != Invalid); + + // The caps change is managed by gst_appsrc_push_sample() in enqueueSample() and + // flushAndEnqueueNonDisplayingSamples(), so the caps aren't set from here. + GRefPtr<GstCaps> appsrcCaps = adoptGRef(gst_app_src_get_caps(GST_APP_SRC(stream->appsrc))); + const gchar* mediaType = gst_structure_get_name(gst_caps_get_structure(appsrcCaps.get(), 0)); + int signal = -1; + + GST_OBJECT_LOCK(webKitMediaSrc); + if (g_str_has_prefix(mediaType, "audio")) { + ASSERT(stream->type == Audio); + signal = SIGNAL_AUDIO_CHANGED; + stream->audioTrack = RefPtr<WebCore::AudioTrackPrivateGStreamer>(static_cast<WebCore::AudioTrackPrivateGStreamer*>(trackPrivate.get())); + } else if (g_str_has_prefix(mediaType, "video")) { + ASSERT(stream->type == Video); + signal = SIGNAL_VIDEO_CHANGED; + stream->videoTrack = RefPtr<WebCore::VideoTrackPrivateGStreamer>(static_cast<WebCore::VideoTrackPrivateGStreamer*>(trackPrivate.get())); + } else if (g_str_has_prefix(mediaType, "text")) { + ASSERT(stream->type == Text); + signal = SIGNAL_TEXT_CHANGED; + + // FIXME: Support text tracks. + } + GST_OBJECT_UNLOCK(webKitMediaSrc); + + if (signal != -1) + g_signal_emit(G_OBJECT(stream->parent), webKitMediaSrcSignals[signal], 0, nullptr); +} + +void PlaybackPipeline::notifyDurationChanged() +{ + gst_element_post_message(GST_ELEMENT(m_webKitMediaSrc.get()), gst_message_new_duration_changed(GST_OBJECT(m_webKitMediaSrc.get()))); + // WebKitMediaSrc will ask MediaPlayerPrivateGStreamerMSE for the new duration later, when somebody asks for it. +} + +void PlaybackPipeline::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus) +{ + WebKitMediaSrcPrivate* priv = m_webKitMediaSrc->priv; + + GST_DEBUG_OBJECT(m_webKitMediaSrc.get(), "Have EOS"); + + GST_OBJECT_LOCK(m_webKitMediaSrc.get()); + bool allTracksConfigured = priv->allTracksConfigured; + if (!allTracksConfigured) + priv->allTracksConfigured = true; + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + + if (!allTracksConfigured) { + gst_element_no_more_pads(GST_ELEMENT(m_webKitMediaSrc.get())); + webKitMediaSrcDoAsyncDone(m_webKitMediaSrc.get()); + } + + Vector<GstAppSrc*> appsrcs; + + GST_OBJECT_LOCK(m_webKitMediaSrc.get()); + for (Stream* stream : priv->streams) { + if (stream->appsrc) + appsrcs.append(GST_APP_SRC(stream->appsrc)); + } + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + + for (GstAppSrc* appsrc : appsrcs) + gst_app_src_end_of_stream(appsrc); +} + +void PlaybackPipeline::flush(AtomicString trackId) +{ + ASSERT(WTF::isMainThread()); + + GST_DEBUG("flush: trackId=%s", trackId.string().utf8().data()); + + GST_OBJECT_LOCK(m_webKitMediaSrc.get()); + Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId); + + if (!stream) { + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); + return; + } + + stream->lastEnqueuedTime = MediaTime::invalidTime(); + GST_OBJECT_UNLOCK(m_webKitMediaSrc.get()); +} + +void PlaybackPipeline::enqueueSample(RefPtr<MediaSample> mediaSample) +{ + ASSERT(WTF::isMainThread()); + + AtomicString trackId = mediaSample->trackID(); + + GST_TRACE("enqueing sample trackId=%s PTS=%f presentationSize=%.0fx%.0f at %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT, + trackId.string().utf8().data(), mediaSample->presentationTime().toFloat(), + mediaSample->presentationSize().width(), mediaSample->presentationSize().height(), + GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->presentationTime().toDouble())), + GST_TIME_ARGS(WebCore::toGstClockTime(mediaSample->duration().toDouble()))); + + Stream* stream = getStreamByTrackId(m_webKitMediaSrc.get(), trackId); + + if (!stream) { + GST_WARNING("No stream!"); + return; + } + + if (!stream->sourceBuffer->isReadyForMoreSamples(trackId)) { + GST_DEBUG("enqueueSample: skip adding new sample for trackId=%s, SB is not ready yet", trackId.string().utf8().data()); + return; + } + + GstElement* appsrc = stream->appsrc; + MediaTime lastEnqueuedTime = stream->lastEnqueuedTime; + + GStreamerMediaSample* sample = static_cast<GStreamerMediaSample*>(mediaSample.get()); + if (sample->sample() && gst_sample_get_buffer(sample->sample())) { + GRefPtr<GstSample> gstSample = sample->sample(); + GstBuffer* buffer = gst_sample_get_buffer(gstSample.get()); + lastEnqueuedTime = sample->presentationTime(); + + GST_BUFFER_FLAG_UNSET(buffer, GST_BUFFER_FLAG_DECODE_ONLY); + pushSample(GST_APP_SRC(appsrc), gstSample.get()); + // gst_app_src_push_sample() uses transfer-none for gstSample. + + stream->lastEnqueuedTime = lastEnqueuedTime; + } +} + +GstElement* PlaybackPipeline::pipeline() +{ + if (!m_webKitMediaSrc || !GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get()))) + return nullptr; + + return GST_ELEMENT_PARENT(GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get()))); +} + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h new file mode 100644 index 000000000..08f0e60d3 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/PlaybackPipeline.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +// PlaybackPipeline is (sort of) a friend class of WebKitMediaSourceGStreamer. + +#include "WebKitMediaSourceGStreamer.h" +#include "WebKitMediaSourceGStreamerPrivate.h" + +#include <gst/gst.h> +#include <wtf/Condition.h> +#include <wtf/glib/GRefPtr.h> + +namespace WTF { +template<> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc*); +template<> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc*); +template<> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc*); +}; + +namespace WebCore { + +class ContentType; +class SourceBufferPrivateGStreamer; +class MediaSourceGStreamer; + +class PlaybackPipeline: public RefCounted<PlaybackPipeline> { +public: + static Ref<PlaybackPipeline> create() + { + return adoptRef(*new PlaybackPipeline()); + } + + virtual ~PlaybackPipeline() = default; + + void setWebKitMediaSrc(WebKitMediaSrc*); + WebKitMediaSrc* webKitMediaSrc(); + + MediaSourcePrivate::AddStatus addSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>); + void removeSourceBuffer(RefPtr<SourceBufferPrivateGStreamer>); + void attachTrack(RefPtr<SourceBufferPrivateGStreamer>, RefPtr<TrackPrivateBase>, GstStructure*, GstCaps*); + void reattachTrack(RefPtr<SourceBufferPrivateGStreamer>, RefPtr<TrackPrivateBase>); + void notifyDurationChanged(); + + // From MediaSourceGStreamer. + void markEndOfStream(MediaSourcePrivate::EndOfStreamStatus); + + // From SourceBufferPrivateGStreamer. + void flush(AtomicString); + void enqueueSample(RefPtr<MediaSample>); + + GstElement* pipeline(); +private: + PlaybackPipeline() = default; + GRefPtr<WebKitMediaSrc> m_webKitMediaSrc; +}; + +} // namespace WebCore. + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp new file mode 100644 index 000000000..e4b107f70 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Orange + * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * Copyright (C) 2015, 2016 Igalia, S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SourceBufferPrivateGStreamer.h" + +#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) + +#include "ContentType.h" +#include "GStreamerUtilities.h" +#include "MediaPlayerPrivateGStreamerMSE.h" +#include "MediaSample.h" +#include "MediaSourceClientGStreamerMSE.h" +#include "MediaSourceGStreamer.h" +#include "NotImplemented.h" +#include "WebKitMediaSourceGStreamer.h" + +namespace WebCore { + +Ref<SourceBufferPrivateGStreamer> SourceBufferPrivateGStreamer::create(MediaSourceGStreamer* mediaSource, Ref<MediaSourceClientGStreamerMSE> client, const ContentType& contentType) +{ + return adoptRef(*new SourceBufferPrivateGStreamer(mediaSource, client.get(), contentType)); +} + +SourceBufferPrivateGStreamer::SourceBufferPrivateGStreamer(MediaSourceGStreamer* mediaSource, Ref<MediaSourceClientGStreamerMSE> client, const ContentType& contentType) + : SourceBufferPrivate() + , m_mediaSource(mediaSource) + , m_type(contentType) + , m_client(client.get()) +{ +} + +void SourceBufferPrivateGStreamer::setClient(SourceBufferPrivateClient* client) +{ + m_sourceBufferPrivateClient = client; +} + +void SourceBufferPrivateGStreamer::append(const unsigned char* data, unsigned length) +{ + ASSERT(m_mediaSource); + + if (!m_sourceBufferPrivateClient) + return; + + if (m_client->append(this, data, length)) + return; + + m_sourceBufferPrivateClient->sourceBufferPrivateAppendComplete(SourceBufferPrivateClient::ReadStreamFailed); +} + +void SourceBufferPrivateGStreamer::abort() +{ + m_client->abort(this); +} + +void SourceBufferPrivateGStreamer::resetParserState() +{ + m_client->resetParserState(this); +} + +void SourceBufferPrivateGStreamer::removedFromMediaSource() +{ + if (m_mediaSource) + m_mediaSource->removeSourceBuffer(this); + m_client->removedFromMediaSource(this); +} + +MediaPlayer::ReadyState SourceBufferPrivateGStreamer::readyState() const +{ + return m_mediaSource->readyState(); +} + +void SourceBufferPrivateGStreamer::setReadyState(MediaPlayer::ReadyState state) +{ + m_mediaSource->setReadyState(state); +} + +void SourceBufferPrivateGStreamer::flush(const AtomicString& trackId) +{ + m_client->flush(trackId); +} + +void SourceBufferPrivateGStreamer::enqueueSample(Ref<MediaSample>&& sample, const AtomicString&) +{ + m_notifyWhenReadyForMoreSamples = false; + + m_client->enqueueSample(WTFMove(sample)); +} + +bool SourceBufferPrivateGStreamer::isReadyForMoreSamples(const AtomicString&) +{ + return m_isReadyForMoreSamples; +} + +void SourceBufferPrivateGStreamer::setReadyForMoreSamples(bool isReady) +{ + ASSERT(WTF::isMainThread()); + m_isReadyForMoreSamples = isReady; +} + +void SourceBufferPrivateGStreamer::notifyReadyForMoreSamples() +{ + ASSERT(WTF::isMainThread()); + setReadyForMoreSamples(true); + if (m_notifyWhenReadyForMoreSamples) + m_sourceBufferPrivateClient->sourceBufferPrivateDidBecomeReadyForMoreSamples(m_trackId); +} + +void SourceBufferPrivateGStreamer::setActive(bool isActive) +{ + if (m_mediaSource) + m_mediaSource->sourceBufferPrivateDidChangeActiveState(this, isActive); +} + +void SourceBufferPrivateGStreamer::stopAskingForMoreSamples(const AtomicString&) +{ + notImplemented(); +} + +void SourceBufferPrivateGStreamer::notifyClientWhenReadyForMoreSamples(const AtomicString& trackId) +{ + ASSERT(WTF::isMainThread()); + m_notifyWhenReadyForMoreSamples = true; + m_trackId = trackId; +} + +void SourceBufferPrivateGStreamer::didReceiveInitializationSegment(const SourceBufferPrivateClient::InitializationSegment& initializationSegment) +{ + if (m_sourceBufferPrivateClient) + m_sourceBufferPrivateClient->sourceBufferPrivateDidReceiveInitializationSegment(initializationSegment); +} + +void SourceBufferPrivateGStreamer::didReceiveSample(MediaSample& sample) +{ + if (m_sourceBufferPrivateClient) + m_sourceBufferPrivateClient->sourceBufferPrivateDidReceiveSample(sample); +} + +void SourceBufferPrivateGStreamer::didReceiveAllPendingSamples() +{ + if (m_sourceBufferPrivateClient) + m_sourceBufferPrivateClient->sourceBufferPrivateAppendComplete(SourceBufferPrivateClient::AppendSucceeded); +} + +} +#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h new file mode 100644 index 000000000..5671310ff --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Orange + * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * Copyright (C) 2015, 2016 Igalia, S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) + +#include "ContentType.h" +#include "MediaPlayerPrivateGStreamerMSE.h" +#include "SourceBufferPrivate.h" +#include "SourceBufferPrivateClient.h" +#include "WebKitMediaSourceGStreamer.h" + +namespace WebCore { + +class MediaSourceGStreamer; + +class SourceBufferPrivateGStreamer final : public SourceBufferPrivate { + +public: + static Ref<SourceBufferPrivateGStreamer> create(MediaSourceGStreamer*, Ref<MediaSourceClientGStreamerMSE>, const ContentType&); + virtual ~SourceBufferPrivateGStreamer() = default; + + void clearMediaSource() { m_mediaSource = nullptr; } + + void setClient(SourceBufferPrivateClient*) final; + void append(const unsigned char*, unsigned) final; + void abort() final; + void resetParserState() final; + void removedFromMediaSource() final; + MediaPlayer::ReadyState readyState() const final; + void setReadyState(MediaPlayer::ReadyState) final; + + void flush(const AtomicString&) final; + void enqueueSample(Ref<MediaSample>&&, const AtomicString&) final; + bool isReadyForMoreSamples(const AtomicString&) final; + void setActive(bool) final; + void stopAskingForMoreSamples(const AtomicString&) final; + void notifyClientWhenReadyForMoreSamples(const AtomicString&) final; + + void setReadyForMoreSamples(bool); + void notifyReadyForMoreSamples(); + + void didReceiveInitializationSegment(const SourceBufferPrivateClient::InitializationSegment&); + void didReceiveSample(MediaSample&); + void didReceiveAllPendingSamples(); + +private: + SourceBufferPrivateGStreamer(MediaSourceGStreamer*, Ref<MediaSourceClientGStreamerMSE>, const ContentType&); + friend class MediaSourceClientGStreamerMSE; + + MediaSourceGStreamer* m_mediaSource; + ContentType m_type; + Ref<MediaSourceClientGStreamerMSE> m_client; + SourceBufferPrivateClient* m_sourceBufferPrivateClient; + bool m_isReadyForMoreSamples = true; + bool m_notifyWhenReadyForMoreSamples = false; + AtomicString m_trackId; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp new file mode 100644 index 000000000..52ca66867 --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * Copyright (C) 2013 Collabora Ltd. + * Copyright (C) 2013 Orange + * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * Copyright (C) 2015, 2016 Igalia, S.L + * + * 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 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 "WebKitMediaSourceGStreamer.h" + +#include "PlaybackPipeline.h" + +#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) + +#include "AudioTrackPrivateGStreamer.h" +#include "GStreamerUtilities.h" +#include "MediaDescription.h" +#include "MediaPlayerPrivateGStreamerMSE.h" +#include "MediaSample.h" +#include "MediaSourceGStreamer.h" +#include "NotImplemented.h" +#include "SourceBufferPrivateGStreamer.h" +#include "TimeRanges.h" +#include "VideoTrackPrivateGStreamer.h" +#include "WebKitMediaSourceGStreamerPrivate.h" + +#include <gst/app/app.h> +#include <gst/app/gstappsrc.h> +#include <gst/gst.h> +#include <gst/pbutils/missing-plugins.h> +#include <gst/pbutils/pbutils.h> +#include <gst/video/video.h> +#include <wtf/Condition.h> +#include <wtf/MainThread.h> +#include <wtf/glib/GMutexLocker.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> + +GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug); +#define GST_CAT_DEFAULT webkit_media_src_debug + +#define webkit_media_src_parent_class parent_class +#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element"); + +static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC, + GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY); + +static void enabledAppsrcNeedData(GstAppSrc*, guint, gpointer); +static void enabledAppsrcEnoughData(GstAppSrc*, gpointer); +static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer); + +static void disabledAppsrcNeedData(GstAppSrc*, guint, gpointer) { }; +static void disabledAppsrcEnoughData(GstAppSrc*, gpointer) { }; +static gboolean disabledAppsrcSeekData(GstAppSrc*, guint64, gpointer) +{ + return FALSE; +}; + +GstAppSrcCallbacks enabledAppsrcCallbacks = { + enabledAppsrcNeedData, + enabledAppsrcEnoughData, + enabledAppsrcSeekData, + { 0 } +}; + +GstAppSrcCallbacks disabledAppsrcCallbacks = { + disabledAppsrcNeedData, + disabledAppsrcEnoughData, + disabledAppsrcSeekData, + { 0 } +}; + +static Stream* getStreamByAppsrc(WebKitMediaSrc*, GstElement*); + +static void enabledAppsrcNeedData(GstAppSrc* appsrc, guint, gpointer userData) +{ + WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData); + ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc)); + + GST_OBJECT_LOCK(webKitMediaSrc); + OnSeekDataAction appsrcSeekDataNextAction = webKitMediaSrc->priv->appsrcSeekDataNextAction; + Stream* appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc)); + bool allAppsrcNeedDataAfterSeek = false; + + if (webKitMediaSrc->priv->appsrcSeekDataCount > 0) { + if (appsrcStream && !appsrcStream->appsrcNeedDataFlag) { + ++webKitMediaSrc->priv->appsrcNeedDataCount; + appsrcStream->appsrcNeedDataFlag = true; + } + int numAppsrcs = webKitMediaSrc->priv->streams.size(); + if (webKitMediaSrc->priv->appsrcSeekDataCount == numAppsrcs && webKitMediaSrc->priv->appsrcNeedDataCount == numAppsrcs) { + GST_DEBUG("All needDatas completed"); + allAppsrcNeedDataAfterSeek = true; + webKitMediaSrc->priv->appsrcSeekDataCount = 0; + webKitMediaSrc->priv->appsrcNeedDataCount = 0; + webKitMediaSrc->priv->appsrcSeekDataNextAction = Nothing; + + for (Stream* stream : webKitMediaSrc->priv->streams) + stream->appsrcNeedDataFlag = false; + } + } + GST_OBJECT_UNLOCK(webKitMediaSrc); + + if (allAppsrcNeedDataAfterSeek) { + GST_DEBUG("All expected appsrcSeekData() and appsrcNeedData() calls performed. Running next action (%d)", static_cast<int>(appsrcSeekDataNextAction)); + + switch (appsrcSeekDataNextAction) { + case MediaSourceSeekToTime: { + GstStructure* structure = gst_structure_new_empty("seek-needs-data"); + GstMessage* message = gst_message_new_application(GST_OBJECT(appsrc), structure); + gst_bus_post(webKitMediaSrc->priv->bus.get(), message); + GST_TRACE("seek-needs-data message posted to the bus"); + break; + } + case Nothing: + break; + } + } else if (appsrcSeekDataNextAction == Nothing) { + LockHolder locker(webKitMediaSrc->priv->streamLock); + + GST_OBJECT_LOCK(webKitMediaSrc); + + // Search again for the Stream, just in case it was removed between the previous lock and this one. + appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc)); + + if (appsrcStream && appsrcStream->type != WebCore::Invalid) { + GstStructure* structure = gst_structure_new("ready-for-more-samples", "appsrc-stream", G_TYPE_POINTER, appsrcStream, nullptr); + GstMessage* message = gst_message_new_application(GST_OBJECT(appsrc), structure); + gst_bus_post(webKitMediaSrc->priv->bus.get(), message); + GST_TRACE("ready-for-more-samples message posted to the bus"); + } + + GST_OBJECT_UNLOCK(webKitMediaSrc); + } +} + +static void enabledAppsrcEnoughData(GstAppSrc *appsrc, gpointer userData) +{ + // No need to lock on webKitMediaSrc, we're on the main thread and nobody is going to remove the stream in the meantime. + ASSERT(WTF::isMainThread()); + + WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData); + ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc)); + Stream* stream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc)); + + // This callback might have been scheduled from a child thread before the stream was removed. + // Then, the removal code might have run, and later this callback. + // This check solves the race condition. + if (!stream || stream->type == WebCore::Invalid) + return; + + stream->sourceBuffer->setReadyForMoreSamples(false); +} + +static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer userData) +{ + ASSERT(WTF::isMainThread()); + + WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData); + + ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc)); + + GST_OBJECT_LOCK(webKitMediaSrc); + webKitMediaSrc->priv->appsrcSeekDataCount++; + GST_OBJECT_UNLOCK(webKitMediaSrc); + + return TRUE; +} + +static Stream* getStreamByAppsrc(WebKitMediaSrc* source, GstElement* appsrc) +{ + for (Stream* stream : source->priv->streams) { + if (stream->appsrc == appsrc) + return stream; + } + return nullptr; +} + +G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN, + G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit); + WEBKIT_MEDIA_SRC_CATEGORY_INIT); + +guint webKitMediaSrcSignals[LAST_SIGNAL] = { 0 }; + +static void webkit_media_src_class_init(WebKitMediaSrcClass* klass) +{ + GObjectClass* oklass = G_OBJECT_CLASS(klass); + GstElementClass* eklass = GST_ELEMENT_CLASS(klass); + + oklass->finalize = webKitMediaSrcFinalize; + oklass->set_property = webKitMediaSrcSetProperty; + oklass->get_property = webKitMediaSrcGetProperty; + + gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate)); + + gst_element_class_set_static_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>, Sebastian Dröge <sebastian@centricular.com>, Enrique Ocaña González <eocanha@igalia.com>"); + + // Allows setting the uri using the 'location' property, which is used for example by gst_element_make_from_uri(). + g_object_class_install_property(oklass, + PROP_LOCATION, + g_param_spec_string("location", "location", "Location to read from", nullptr, + GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(oklass, + PROP_N_AUDIO, + g_param_spec_int("n-audio", "Number Audio", "Total number of audio streams", + 0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(oklass, + PROP_N_VIDEO, + g_param_spec_int("n-video", "Number Video", "Total number of video streams", + 0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(oklass, + PROP_N_TEXT, + g_param_spec_int("n-text", "Number Text", "Total number of text streams", + 0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + + webKitMediaSrcSignals[SIGNAL_VIDEO_CHANGED] = + g_signal_new("video-changed", G_TYPE_FROM_CLASS(oklass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(WebKitMediaSrcClass, videoChanged), nullptr, nullptr, + g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); + webKitMediaSrcSignals[SIGNAL_AUDIO_CHANGED] = + g_signal_new("audio-changed", G_TYPE_FROM_CLASS(oklass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(WebKitMediaSrcClass, audioChanged), nullptr, nullptr, + g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); + webKitMediaSrcSignals[SIGNAL_TEXT_CHANGED] = + g_signal_new("text-changed", G_TYPE_FROM_CLASS(oklass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(WebKitMediaSrcClass, textChanged), nullptr, nullptr, + g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); + + eklass->change_state = webKitMediaSrcChangeState; + + g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate)); +} + +static void webkit_media_src_init(WebKitMediaSrc* source) +{ + source->priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(source); + new (source->priv) WebKitMediaSrcPrivate(); + source->priv->seekTime = MediaTime::invalidTime(); + source->priv->appsrcSeekDataCount = 0; + source->priv->appsrcNeedDataCount = 0; + source->priv->appsrcSeekDataNextAction = Nothing; + + // No need to reset Stream.appsrcNeedDataFlag because there are no Streams at this point yet. +} + +void webKitMediaSrcFinalize(GObject* object) +{ + ASSERT(WTF::isMainThread()); + + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object); + WebKitMediaSrcPrivate* priv = source->priv; + + Deque<Stream*> oldStreams; + source->priv->streams.swap(oldStreams); + + for (Stream* stream : oldStreams) + webKitMediaSrcFreeStream(source, stream); + + priv->seekTime = MediaTime::invalidTime(); + + if (priv->mediaPlayerPrivate) + webKitMediaSrcSetMediaPlayerPrivate(source, nullptr); + + // We used a placement new for construction, the destructor won't be called automatically. + priv->~_WebKitMediaSrcPrivate(); + + GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); +} + +void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec) +{ + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object); + + switch (propId) { + case PROP_LOCATION: + gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(source), g_value_get_string(value), nullptr); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); + break; + } +} + +void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec) +{ + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object); + WebKitMediaSrcPrivate* priv = source->priv; + + GST_OBJECT_LOCK(source); + switch (propId) { + case PROP_LOCATION: + g_value_set_string(value, priv->location.get()); + break; + case PROP_N_AUDIO: + g_value_set_int(value, priv->numberOfAudioStreams); + break; + case PROP_N_VIDEO: + g_value_set_int(value, priv->numberOfVideoStreams); + break; + case PROP_N_TEXT: + g_value_set_int(value, priv->numberOfTextStreams); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); + break; + } + GST_OBJECT_UNLOCK(source); +} + +void webKitMediaSrcDoAsyncStart(WebKitMediaSrc* source) +{ + source->priv->asyncStart = true; + GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source), + gst_message_new_async_start(GST_OBJECT(source))); +} + +void webKitMediaSrcDoAsyncDone(WebKitMediaSrc* source) +{ + WebKitMediaSrcPrivate* priv = source->priv; + if (priv->asyncStart) { + GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source), + gst_message_new_async_done(GST_OBJECT(source), GST_CLOCK_TIME_NONE)); + priv->asyncStart = false; + } +} + +GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition) +{ + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(element); + WebKitMediaSrcPrivate* priv = source->priv; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + priv->allTracksConfigured = false; + webKitMediaSrcDoAsyncStart(source); + break; + default: + break; + } + + GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE)) { + GST_WARNING_OBJECT(source, "State change failed"); + webKitMediaSrcDoAsyncDone(source); + return result; + } + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + result = GST_STATE_CHANGE_ASYNC; + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + webKitMediaSrcDoAsyncDone(source); + priv->allTracksConfigured = false; + break; + default: + break; + } + + return result; +} + +gint64 webKitMediaSrcGetSize(WebKitMediaSrc* webKitMediaSrc) +{ + gint64 duration = 0; + for (Stream* stream : webKitMediaSrc->priv->streams) + duration = std::max<gint64>(duration, gst_app_src_get_size(GST_APP_SRC(stream->appsrc))); + return duration; +} + +gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query) +{ + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent)); + gboolean result = FALSE; + + switch (GST_QUERY_TYPE(query)) { + case GST_QUERY_DURATION: { + GstFormat format; + gst_query_parse_duration(query, &format, nullptr); + + GST_DEBUG_OBJECT(source, "duration query in format %s", gst_format_get_name(format)); + GST_OBJECT_LOCK(source); + switch (format) { + case GST_FORMAT_TIME: { + if (source->priv && source->priv->mediaPlayerPrivate) { + float duration = source->priv->mediaPlayerPrivate->durationMediaTime().toFloat(); + if (duration > 0) { + gst_query_set_duration(query, format, WebCore::toGstClockTime(duration)); + GST_DEBUG_OBJECT(source, "Answering: duration=%" GST_TIME_FORMAT, GST_TIME_ARGS(WebCore::toGstClockTime(duration))); + result = TRUE; + } + } + break; + } + case GST_FORMAT_BYTES: { + if (source->priv) { + gint64 duration = webKitMediaSrcGetSize(source); + if (duration) { + gst_query_set_duration(query, format, duration); + GST_DEBUG_OBJECT(source, "size: %" G_GINT64_FORMAT, duration); + result = TRUE; + } + } + break; + } + default: + break; + } + + GST_OBJECT_UNLOCK(source); + break; + } + case GST_QUERY_URI: + if (source) { + GST_OBJECT_LOCK(source); + if (source->priv) + gst_query_set_uri(query, source->priv->location.get()); + GST_OBJECT_UNLOCK(source); + } + result = TRUE; + break; + default: { + GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad))); + // Forward the query to the proxy target pad. + if (target) + result = gst_pad_query(target.get(), query); + break; + } + } + + return result; +} + +void webKitMediaSrcUpdatePresentationSize(GstCaps* caps, Stream* stream) +{ + GstStructure* structure = gst_caps_get_structure(caps, 0); + const gchar* structureName = gst_structure_get_name(structure); + GstVideoInfo info; + + GST_OBJECT_LOCK(stream->parent); + if (g_str_has_prefix(structureName, "video/") && gst_video_info_from_caps(&info, caps)) { + float width, height; + + // FIXME: Correct?. + width = info.width; + height = info.height * ((float) info.par_d / (float) info.par_n); + stream->presentationSize = WebCore::FloatSize(width, height); + } else + stream->presentationSize = WebCore::FloatSize(); + + gst_caps_ref(caps); + stream->caps = adoptGRef(caps); + GST_OBJECT_UNLOCK(stream->parent); +} + +void webKitMediaSrcLinkStreamToSrcPad(GstPad* sourcePad, Stream* stream) +{ + unsigned padId = static_cast<unsigned>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sourcePad), "padId"))); + GST_DEBUG_OBJECT(stream->parent, "linking stream to src pad (id: %u)", padId); + + GUniquePtr<gchar> padName(g_strdup_printf("src_%u", padId)); + GstPad* ghostpad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, padName.get(), sourcePad); + + gst_pad_set_query_function(ghostpad, webKitMediaSrcQueryWithParent); + + gst_pad_set_active(ghostpad, TRUE); + gst_element_add_pad(GST_ELEMENT(stream->parent), ghostpad); + + if (stream->decodebinSinkPad) { + GST_DEBUG_OBJECT(stream->parent, "A decodebin was previously used for this source, trying to reuse it."); + // FIXME: error checking here. Not sure what to do if linking + // fails though, because decodebin is out of this source + // element's scope, in theory. + gst_pad_link(ghostpad, stream->decodebinSinkPad); + } +} + +void webKitMediaSrcLinkParser(GstPad* sourcePad, GstCaps* caps, Stream* stream) +{ + ASSERT(caps && stream->parent); + if (!caps || !stream->parent) { + GST_ERROR("Unable to link parser"); + return; + } + + webKitMediaSrcUpdatePresentationSize(caps, stream); + + // FIXME: drop webKitMediaSrcLinkStreamToSrcPad() and move its code here. + if (!gst_pad_is_linked(sourcePad)) { + GST_DEBUG_OBJECT(stream->parent, "pad not linked yet"); + webKitMediaSrcLinkStreamToSrcPad(sourcePad, stream); + } + + webKitMediaSrcCheckAllTracksConfigured(stream->parent); +} + +void webKitMediaSrcFreeStream(WebKitMediaSrc* source, Stream* stream) +{ + if (stream->appsrc) { + // Don't trigger callbacks from this appsrc to avoid using the stream anymore. + gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &disabledAppsrcCallbacks, nullptr, nullptr); + gst_app_src_end_of_stream(GST_APP_SRC(stream->appsrc)); + } + + if (stream->type != WebCore::Invalid) { + GST_DEBUG("Freeing track-related info on stream %p", stream); + + LockHolder locker(source->priv->streamLock); + + if (stream->caps) + stream->caps = nullptr; + + if (stream->audioTrack) + stream->audioTrack = nullptr; + if (stream->videoTrack) + stream->videoTrack = nullptr; + + int signal = -1; + switch (stream->type) { + case WebCore::Audio: + signal = SIGNAL_AUDIO_CHANGED; + break; + case WebCore::Video: + signal = SIGNAL_VIDEO_CHANGED; + break; + case WebCore::Text: + signal = SIGNAL_TEXT_CHANGED; + break; + default: + break; + } + stream->type = WebCore::Invalid; + + if (signal != -1) + g_signal_emit(G_OBJECT(source), webKitMediaSrcSignals[signal], 0, nullptr); + + source->priv->streamCondition.notifyOne(); + } + + GST_DEBUG("Releasing stream: %p", stream); + delete stream; +} + +void webKitMediaSrcCheckAllTracksConfigured(WebKitMediaSrc* webKitMediaSrc) +{ + bool allTracksConfigured = false; + + GST_OBJECT_LOCK(webKitMediaSrc); + if (!webKitMediaSrc->priv->allTracksConfigured) { + allTracksConfigured = true; + for (Stream* stream : webKitMediaSrc->priv->streams) { + if (stream->type == WebCore::Invalid) { + allTracksConfigured = false; + break; + } + } + if (allTracksConfigured) + webKitMediaSrc->priv->allTracksConfigured = true; + } + GST_OBJECT_UNLOCK(webKitMediaSrc); + + if (allTracksConfigured) { + GST_DEBUG("All tracks attached. Completing async state change operation."); + gst_element_no_more_pads(GST_ELEMENT(webKitMediaSrc)); + webKitMediaSrcDoAsyncDone(webKitMediaSrc); + } +} + +// Uri handler interface. +GstURIType webKitMediaSrcUriGetType(GType) +{ + return GST_URI_SRC; +} + +const gchar* const* webKitMediaSrcGetProtocols(GType) +{ + static const char* protocols[] = {"mediasourceblob", nullptr }; + return protocols; +} + +gchar* webKitMediaSrcGetUri(GstURIHandler* handler) +{ + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler); + gchar* result; + + GST_OBJECT_LOCK(source); + result = g_strdup(source->priv->location.get()); + GST_OBJECT_UNLOCK(source); + return result; +} + +gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**) +{ + WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler); + + if (GST_STATE(source) >= GST_STATE_PAUSED) { + GST_ERROR_OBJECT(source, "URI can only be set in states < PAUSED"); + return FALSE; + } + + GST_OBJECT_LOCK(source); + WebKitMediaSrcPrivate* priv = source->priv; + priv->location = nullptr; + if (!uri) { + GST_OBJECT_UNLOCK(source); + return TRUE; + } + + WebCore::URL url(WebCore::URL(), uri); + + priv->location = GUniquePtr<gchar>(g_strdup(url.string().utf8().data())); + GST_OBJECT_UNLOCK(source); + return TRUE; +} + +void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer) +{ + GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface; + + iface->get_type = webKitMediaSrcUriGetType; + iface->get_protocols = webKitMediaSrcGetProtocols; + iface->get_uri = webKitMediaSrcGetUri; + iface->set_uri = webKitMediaSrcSetUri; +} + +static void seekNeedsDataMainThread(WebKitMediaSrc* source) +{ + GST_DEBUG("Buffering needed before seek"); + + ASSERT(WTF::isMainThread()); + + GST_OBJECT_LOCK(source); + MediaTime seekTime = source->priv->seekTime; + WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate; + + if (!mediaPlayerPrivate) { + GST_OBJECT_UNLOCK(source); + return; + } + + for (Stream* stream : source->priv->streams) { + if (stream->type != WebCore::Invalid) + stream->sourceBuffer->setReadyForMoreSamples(true); + } + GST_OBJECT_UNLOCK(source); + mediaPlayerPrivate->notifySeekNeedsDataForTime(seekTime); +} + +static void notifyReadyForMoreSamplesMainThread(WebKitMediaSrc* source, Stream* appsrcStream) +{ + GST_OBJECT_LOCK(source); + + auto it = std::find(source->priv->streams.begin(), source->priv->streams.end(), appsrcStream); + if (it == source->priv->streams.end()) { + GST_OBJECT_UNLOCK(source); + return; + } + + WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate; + if (mediaPlayerPrivate && !mediaPlayerPrivate->seeking()) + appsrcStream->sourceBuffer->notifyReadyForMoreSamples(); + + GST_OBJECT_UNLOCK(source); +} + +static void applicationMessageCallback(GstBus*, GstMessage* message, WebKitMediaSrc* source) +{ + ASSERT(WTF::isMainThread()); + ASSERT(GST_MESSAGE_TYPE(message) == GST_MESSAGE_APPLICATION); + + const GstStructure* structure = gst_message_get_structure(message); + + if (gst_structure_has_name(structure, "seek-needs-data")) { + seekNeedsDataMainThread(source); + return; + } + + if (gst_structure_has_name(structure, "ready-for-more-samples")) { + Stream* appsrcStream = nullptr; + gst_structure_get(structure, "appsrc-stream", G_TYPE_POINTER, &appsrcStream, nullptr); + ASSERT(appsrcStream); + + notifyReadyForMoreSamplesMainThread(source, appsrcStream); + return; + } + + ASSERT_NOT_REACHED(); +} + +void webKitMediaSrcSetMediaPlayerPrivate(WebKitMediaSrc* source, WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate) +{ + GST_OBJECT_LOCK(source); + if (source->priv->mediaPlayerPrivate && source->priv->mediaPlayerPrivate != mediaPlayerPrivate && source->priv->bus) + g_signal_handlers_disconnect_by_func(source->priv->bus.get(), gpointer(applicationMessageCallback), source); + + // Set to nullptr on MediaPlayerPrivateGStreamer destruction, never a dangling pointer. + source->priv->mediaPlayerPrivate = mediaPlayerPrivate; + source->priv->bus = mediaPlayerPrivate ? adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(mediaPlayerPrivate->pipeline()))) : nullptr; + if (source->priv->bus) { + // MediaPlayerPrivateGStreamer has called gst_bus_add_signal_watch() at this point, so we can subscribe. + g_signal_connect(source->priv->bus.get(), "message::application", G_CALLBACK(applicationMessageCallback), source); + } + GST_OBJECT_UNLOCK(source); +} + +void webKitMediaSrcSetReadyForSamples(WebKitMediaSrc* source, bool isReady) +{ + if (source) { + GST_OBJECT_LOCK(source); + for (Stream* stream : source->priv->streams) + stream->sourceBuffer->setReadyForMoreSamples(isReady); + GST_OBJECT_UNLOCK(source); + } +} + +void webKitMediaSrcPrepareSeek(WebKitMediaSrc* source, const MediaTime& time) +{ + GST_OBJECT_LOCK(source); + source->priv->seekTime = time; + source->priv->appsrcSeekDataCount = 0; + source->priv->appsrcNeedDataCount = 0; + + for (Stream* stream : source->priv->streams) { + stream->appsrcNeedDataFlag = false; + // Don't allow samples away from the seekTime to be enqueued. + stream->lastEnqueuedTime = time; + } + + // The pending action will be performed in enabledAppsrcSeekData(). + source->priv->appsrcSeekDataNextAction = MediaSourceSeekToTime; + GST_OBJECT_UNLOCK(source); +} + +namespace WTF { +template <> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc* ptr) +{ + ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr))); + return GRefPtr<WebKitMediaSrc>(ptr, GRefPtrAdopt); +} + +template <> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr) +{ + if (ptr) + gst_object_ref_sink(GST_OBJECT(ptr)); + + return ptr; +} + +template <> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr) +{ + if (ptr) + gst_object_unref(ptr); +} +}; + +#endif // USE(GSTREAMER) + diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.h index 78892c3e3..79086054c 100644 --- a/Source/WebCore/platform/graphics/gstreamer/WebKitMediaSourceGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.h @@ -2,6 +2,9 @@ * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> * Copyright (C) 2013 Collabora Ltd. * Copyright (C) 2013 Orange + * Copyright (C) 2014, 2015 Sebastian Dröge <sebastian@centricular.com> + * Copyright (C) 2015, 2016 Metrological Group B.V. + * Copyright (C) 2015, 2016 Igalia, S.L * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,13 +21,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef WebKitMediaSourceGStreamer_h -#define WebKitMediaSourceGStreamer_h +#pragma once + #if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) +#include "GRefPtrGStreamer.h" #include "MediaPlayer.h" +#include "MediaSource.h" +#include "MediaSourcePrivate.h" +#include "SourceBufferPrivate.h" +#include "SourceBufferPrivateClient.h" #include <gst/gst.h> +namespace WebCore { + +class MediaPlayerPrivateGStreamerMSE; + +enum MediaSourceStreamTypeGStreamer { Invalid, Unknown, Audio, Video, Text }; + +} + G_BEGIN_DECLS #define WEBKIT_TYPE_MEDIA_SRC (webkit_media_src_get_type ()) @@ -45,28 +61,20 @@ struct _WebKitMediaSrc { struct _WebKitMediaSrcClass { GstBinClass parentClass; + + // Notify app that number of audio/video/text streams changed. + void (*videoChanged)(WebKitMediaSrc*); + void (*audioChanged)(WebKitMediaSrc*); + void (*textChanged)(WebKitMediaSrc*); }; GType webkit_media_src_get_type(void); -void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc*, WebCore::MediaPlayer*); -void webKitMediaSrcSetPlayBin(WebKitMediaSrc*, GstElement*); -G_END_DECLS - -class MediaSourceClientGstreamer: public RefCounted<MediaSourceClientGstreamer> { - public: - MediaSourceClientGstreamer(WebKitMediaSrc*); - ~MediaSourceClientGstreamer(); +void webKitMediaSrcSetMediaPlayerPrivate(WebKitMediaSrc*, WebCore::MediaPlayerPrivateGStreamerMSE*); - void didReceiveDuration(double); - void didReceiveData(const char*, int, String); - void didFinishLoading(double); - void didFail(); - - private: - WebKitMediaSrc* m_src; -}; +void webKitMediaSrcPrepareSeek(WebKitMediaSrc*, const MediaTime&); +void webKitMediaSrcSetReadyForSamples(WebKitMediaSrc*, bool); +G_END_DECLS #endif // USE(GSTREAMER) -#endif diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h new file mode 100644 index 000000000..83b523f7d --- /dev/null +++ b/Source/WebCore/platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamerPrivate.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2016 Metrological Group B.V. + * Copyright (C) 2016 Igalia S.L + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE) + +#include "AudioTrackPrivateGStreamer.h" +#include "SourceBufferPrivateGStreamer.h" +#include "VideoTrackPrivateGStreamer.h" +#include "WebKitMediaSourceGStreamer.h" + +#include <gst/app/gstappsrc.h> +#include <gst/gst.h> +#include <wtf/Condition.h> +#include <wtf/RefPtr.h> +#include <wtf/glib/GRefPtr.h> + +namespace WebCore { + +class MediaPlayerPrivateGStreamerMSE; + +}; + +void webKitMediaSrcUriHandlerInit(gpointer, gpointer); + +#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate)) + +typedef struct _Stream Stream; + +struct _Stream { + // Fields filled when the Stream is created. + WebKitMediaSrc* parent; + + // AppSrc. + GstElement* appsrc; + GstPad* decodebinSinkPad; + WebCore::SourceBufferPrivateGStreamer* sourceBuffer; + + // Fields filled when the track is attached. + WebCore::MediaSourceStreamTypeGStreamer type; + // Might be 0, e.g. for VP8/VP9. + GstElement* parser; + GRefPtr<GstCaps> caps; + RefPtr<WebCore::AudioTrackPrivateGStreamer> audioTrack; + RefPtr<WebCore::VideoTrackPrivateGStreamer> videoTrack; + WebCore::FloatSize presentationSize; + + // This helps WebKitMediaSrcPrivate.appsrcNeedDataCount, ensuring that needDatas are + // counted only once per each appsrc. + bool appsrcNeedDataFlag; + + // Used to enforce continuity in the appended data and avoid breaking the decoder. + MediaTime lastEnqueuedTime; +}; + +enum { + PROP_0, + PROP_LOCATION, + PROP_N_AUDIO, + PROP_N_VIDEO, + PROP_N_TEXT, + PROP_LAST +}; + +enum { + SIGNAL_VIDEO_CHANGED, + SIGNAL_AUDIO_CHANGED, + SIGNAL_TEXT_CHANGED, + LAST_SIGNAL +}; + +enum OnSeekDataAction { + Nothing, + MediaSourceSeekToTime +}; + +struct _WebKitMediaSrcPrivate { + // Used to coordinate the release of Stream track info. + Lock streamLock; + Condition streamCondition; + + Deque<Stream*> streams; + GUniquePtr<gchar> location; + int numberOfAudioStreams; + int numberOfVideoStreams; + int numberOfTextStreams; + bool asyncStart; + bool allTracksConfigured; + unsigned numberOfPads; + + MediaTime seekTime; + + // On seek, we wait for all the seekDatas, then for all the needDatas, and then run the nextAction. + OnSeekDataAction appsrcSeekDataNextAction; + int appsrcSeekDataCount; + int appsrcNeedDataCount; + + GRefPtr<GstBus> bus; + WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate; +}; + +extern guint webKitMediaSrcSignals[LAST_SIGNAL]; +extern GstAppSrcCallbacks enabledAppsrcCallbacks; +extern GstAppSrcCallbacks disabledAppsrcCallbacks; + +void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData); +void webKitMediaSrcFinalize(GObject*); +void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*); +void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*); +void webKitMediaSrcDoAsyncStart(WebKitMediaSrc*); +void webKitMediaSrcDoAsyncDone(WebKitMediaSrc*); +GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange); +gint64 webKitMediaSrcGetSize(WebKitMediaSrc*); +gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); +void webKitMediaSrcUpdatePresentationSize(GstCaps*, Stream*); +void webKitMediaSrcLinkStreamToSrcPad(GstPad*, Stream*); +void webKitMediaSrcLinkParser(GstPad*, GstCaps*, Stream*); +void webKitMediaSrcFreeStream(WebKitMediaSrc*, Stream*); +void webKitMediaSrcCheckAllTracksConfigured(WebKitMediaSrc*); +GstURIType webKitMediaSrcUriGetType(GType); +const gchar* const* webKitMediaSrcGetProtocols(GType); +gchar* webKitMediaSrcGetUri(GstURIHandler*); +gboolean webKitMediaSrcSetUri(GstURIHandler*, const gchar*, GError**); + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp b/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp index 8f56ba6fb..4d94a6947 100644 --- a/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp +++ b/Source/WebCore/platform/graphics/gtk/ColorGtk.cpp @@ -26,27 +26,27 @@ namespace WebCore { Color::Color(const GdkColor& c) - : m_color(makeRGB(c.red >> 8, c.green >> 8, c.blue >> 8)) - , m_valid(true) { + setRGB(makeRGB(c.red >> 8, c.green >> 8, c.blue >> 8)); } #ifndef GTK_API_VERSION_2 Color::Color(const GdkRGBA& c) - : m_color(makeRGBA(static_cast<int>(c.red * 255), - static_cast<int>(c.green * 255), - static_cast<int>(c.blue * 255), - static_cast<int>(c.alpha * 255))) - , m_valid(true) { + setRGB(makeRGBA(static_cast<int>(c.red * 255), + static_cast<int>(c.green * 255), + static_cast<int>(c.blue * 255), + static_cast<int>(c.alpha * 255))); } Color::operator GdkRGBA() const { + if (isExtended()) + return { asExtended().red(), asExtended().green(), asExtended().blue(), asExtended().alpha() }; + double red, green, blue, alpha; getRGBA(red, green, blue, alpha); - GdkRGBA rgba = { red, green, blue, alpha }; - return rgba; + return { red, green, blue, alpha }; } #endif diff --git a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp index 4b28f0e0e..819441efa 100644 --- a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp +++ b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,8 +31,25 @@ #include "IntSize.h" #include <cairo.h> #include <gtk/gtk.h> +#include <mutex> +#include <wtf/NeverDestroyed.h> -using namespace WebCore; +namespace WebCore { + +const cairo_font_options_t* getDefaultCairoFontOptions() +{ + if (auto* screen = gdk_screen_get_default()) { + if (auto* options = gdk_screen_get_font_options(screen)) + return options; + } + + static LazyNeverDestroyed<cairo_font_options_t*> options; + static std::once_flag flag; + std::call_once(flag, [] { + options.construct(cairo_font_options_create()); + }); + return options; +} GdkPixbuf* cairoSurfaceToGdkPixbuf(cairo_surface_t* surface) { @@ -40,3 +57,4 @@ GdkPixbuf* cairoSurfaceToGdkPixbuf(cairo_surface_t* surface) return gdk_pixbuf_get_from_surface(surface, 0, 0, size.width(), size.height()); } +} diff --git a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h index 43a9a0900..e2aeaae22 100644 --- a/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h +++ b/Source/WebCore/platform/graphics/gtk/GdkCairoUtilities.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,9 +23,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GdkCairoUtilities_h -#define GdkCairoUtilities_h +#pragma once + +namespace WebCore { GdkPixbuf* cairoSurfaceToGdkPixbuf(cairo_surface_t*); -#endif // GdkCairoUtilities_h +} diff --git a/Source/WebCore/platform/graphics/gtk/IconGtk.cpp b/Source/WebCore/platform/graphics/gtk/IconGtk.cpp index 3e957b345..880c4c979 100644 --- a/Source/WebCore/platform/graphics/gtk/IconGtk.cpp +++ b/Source/WebCore/platform/graphics/gtk/IconGtk.cpp @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -36,7 +36,7 @@ #include <gtk/gtk.h> -#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> #include <wtf/text/CString.h> namespace WebCore { @@ -90,7 +90,7 @@ static String lookupIconName(String MIMEType) } // FIXME: Move the code to ChromeClient::iconForFiles(). -PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +RefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) { if (filenames.isEmpty()) return 0; @@ -113,13 +113,13 @@ PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) return 0; } -void Icon::paint(GraphicsContext* context, const IntRect& rect) +void Icon::paint(GraphicsContext& context, const FloatRect& rect) { - if (context->paintingDisabled()) + if (context.paintingDisabled()) return; // TODO: Scale/clip the image if necessary. - cairo_t* cr = context->platformContext()->cr(); + cairo_t* cr = context.platformContext()->cr(); cairo_save(cr); gdk_cairo_set_source_pixbuf(cr, m_icon, rect.x(), rect.y()); cairo_paint(cr); diff --git a/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp b/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp index 78a60c1ef..b2fe2a909 100644 --- a/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp +++ b/Source/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp @@ -25,14 +25,14 @@ #include "MIMETypeRegistry.h" #include <cairo.h> #include <gtk/gtk.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/Base64.h> #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> namespace WebCore { -static bool encodeImage(cairo_surface_t* surface, const String& mimeType, const double* quality, GUniqueOutPtr<gchar>& buffer, gsize& bufferSize) +static bool encodeImage(cairo_surface_t* surface, const String& mimeType, std::optional<double> quality, GUniqueOutPtr<gchar>& buffer, gsize& bufferSize) { // List of supported image encoding types comes from the GdkPixbuf documentation. // http://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-File-saving.html#gdk-pixbuf-save-to-bufferv @@ -75,7 +75,7 @@ static bool encodeImage(cairo_surface_t* surface, const String& mimeType, const return !error; } -String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const +String ImageBuffer::toDataURL(const String& mimeType, std::optional<double> quality, CoordinateSystem) const { ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); diff --git a/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp b/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp index 6208f7dc1..49ccc9118 100644 --- a/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp +++ b/Source/WebCore/platform/graphics/gtk/ImageGtk.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,49 +26,31 @@ #include "config.h" #include "BitmapImage.h" -#include "FileSystem.h" #include "GUniquePtrGtk.h" #include "GdkCairoUtilities.h" #include "SharedBuffer.h" -#include <wtf/gobject/GUniquePtr.h> -#include <wtf/text/CString.h> #include <cairo.h> #include <gtk/gtk.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> namespace WebCore { -static char* getPathToImageResource(char* resource) +static PassRefPtr<Image> loadImageFromGResource(const char* iconName) { - if (g_getenv("WEBKIT_TOP_LEVEL")) - return g_build_filename(g_getenv("WEBKIT_TOP_LEVEL"), "Source", "WebCore", "Resources", resource, NULL); - - return g_build_filename(sharedResourcesPath().data(), "images", resource, NULL); -} - -static CString getThemeIconFileName(const char* name, int size) -{ - GtkIconInfo* iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), - name, size, GTK_ICON_LOOKUP_NO_SVG); - // Try to fallback on MISSING_IMAGE. - if (!iconInfo) - iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), - GTK_STOCK_MISSING_IMAGE, size, - GTK_ICON_LOOKUP_NO_SVG); - if (iconInfo) { - GUniquePtr<GtkIconInfo> info(iconInfo); - return CString(gtk_icon_info_get_filename(info.get())); - } - - // No icon was found, this can happen if not GTK theme is set. In - // that case an empty Image will be created. - return CString(); + RefPtr<BitmapImage> icon = BitmapImage::create(); + GUniquePtr<char> path(g_strdup_printf("/org/webkitgtk/resources/images/%s", iconName)); + GRefPtr<GBytes> data = adoptGRef(g_resources_lookup_data(path.get(), G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr)); + ASSERT(data); + icon->setData(SharedBuffer::create(static_cast<const unsigned char*>(g_bytes_get_data(data.get(), nullptr)), g_bytes_get_size(data.get())), true); + return icon.release(); } -static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(CString name) +static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(const char* filename) { GUniqueOutPtr<gchar> content; gsize length; - if (!g_file_get_contents(name.data(), &content.outPtr(), &length, 0)) + if (!g_file_get_contents(filename, &content.outPtr(), &length, nullptr)) return SharedBuffer::create(); return SharedBuffer::create(content.get(), length); @@ -78,33 +60,23 @@ void BitmapImage::invalidatePlatformData() { } -PassRefPtr<Image> loadImageFromFile(CString fileName) +static PassRefPtr<Image> loadMissingImageIconFromTheme(const char* name) { - RefPtr<BitmapImage> img = BitmapImage::create(); - if (!fileName.isNull()) { - RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(fileName); - img->setData(buffer.release(), true); - } - return img.release(); -} - -PassRefPtr<Image> Image::loadPlatformResource(const char* name) -{ - CString fileName; - if (!strcmp("missingImage", name)) - fileName = getThemeIconFileName(GTK_STOCK_MISSING_IMAGE, 16); - if (fileName.isNull()) { - GUniquePtr<gchar> imageName(g_strdup_printf("%s.png", name)); - GUniquePtr<gchar> glibFileName(getPathToImageResource(imageName.get())); - fileName = glibFileName.get(); + int iconSize = g_str_has_suffix(name, "@2x") ? 32 : 16; + RefPtr<BitmapImage> icon = BitmapImage::create(); + GUniquePtr<GtkIconInfo> iconInfo(gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), GTK_STOCK_MISSING_IMAGE, iconSize, GTK_ICON_LOOKUP_NO_SVG)); + if (iconInfo) { + RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(gtk_icon_info_get_filename(iconInfo.get())); + icon->setData(buffer.release(), true); + return icon.release(); } - return loadImageFromFile(fileName); + return loadImageFromGResource(name); } -PassRefPtr<Image> Image::loadPlatformThemeIcon(const char* name, int size) +PassRefPtr<Image> Image::loadPlatformResource(const char* name) { - return loadImageFromFile(getThemeIconFileName(name, size)); + return g_str_has_prefix(name, "missingImage") ? loadMissingImageIconFromTheme(name) : loadImageFromGResource(name); } GdkPixbuf* BitmapImage::getGdkPixbuf() diff --git a/Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp b/Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp deleted file mode 100644 index c4021581e..000000000 --- a/Source/WebCore/platform/graphics/gtk/IntPointGtk.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "IntPoint.h" - -#include <gdk/gdk.h> - -namespace WebCore { - -IntPoint::IntPoint(const GdkPoint& p) - : m_x(p.x) - , m_y(p.y) -{ -} - -IntPoint::operator GdkPoint() const -{ - GdkPoint p = { x(), y() }; - return p; -} - -} - -// vim: ts=4 sw=4 et diff --git a/Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp b/Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp deleted file mode 100644 index 1f312819a..000000000 --- a/Source/WebCore/platform/graphics/gtk/IntRectGtk.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "IntRect.h" - -#include <gdk/gdk.h> - -#ifdef GTK_API_VERSION_2 -namespace WebCore { - -IntRect::IntRect(const GdkRectangle& r) - : m_location(IntPoint(r.x, r.y)) - , m_size(r.width, r.height) -{ -} - -IntRect::operator GdkRectangle() const -{ - GdkRectangle r = { x(), y(), width(), height() }; - return r; -} - -} -#endif diff --git a/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp new file mode 100644 index 000000000..e97f9c204 --- /dev/null +++ b/Source/WebCore/platform/graphics/harfbuzz/ComplexTextControllerHarfBuzz.cpp @@ -0,0 +1,36 @@ +/* +* Copyright (C) 2017 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" +#include "ComplexTextController.h" + +namespace WebCore { + +void ComplexTextController::collectComplexTextRunsForCharacters(const UChar*, unsigned, unsigned, const Font*) +{ + // FIXME: Implement this. + ASSERT_NOT_REACHED(); +} + +} diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp index efc94f3f9..f03cace8e 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.cpp @@ -32,8 +32,8 @@ #include "HarfBuzzFace.h" #include "FontPlatformData.h" -#include "hb-ot.h" -#include "hb.h" +#include <hb-ot.h> +#include <hb.h> namespace WebCore { @@ -74,7 +74,7 @@ typedef HashMap<uint64_t, RefPtr<FaceCacheEntry>, WTF::IntHash<uint64_t>, WTF::U static HarfBuzzFaceCache* harfBuzzFaceCache() { - DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfBuzzFaceCache, ()); + DEPRECATED_DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfBuzzFaceCache, ()); return &s_harfBuzzFaceCache; } diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h index 8066528b5..63179fe81 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFace.h @@ -36,7 +36,6 @@ #include <wtf/HashMap.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> namespace WebCore { diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp index 9ddf36a7e..61486ca0e 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp @@ -32,10 +32,11 @@ #include "config.h" #include "HarfBuzzFace.h" +#include "CairoUtilities.h" +#include "Font.h" #include "FontPlatformData.h" #include "GlyphBuffer.h" #include "HarfBuzzShaper.h" -#include "SimpleFontData.h" #include "TextEncoding.h" #include <cairo-ft.h> #include <cairo.h> @@ -44,6 +45,7 @@ #include FT_TRUETYPE_TABLES_H #include <hb.h> #include <wtf/text/CString.h> +#include <wtf/text/StringView.h> namespace WebCore { @@ -56,21 +58,6 @@ struct HarfBuzzFontData { cairo_scaled_font_t* m_cairoScaledFont; }; -class CairoFtFaceLocker { -public: - CairoFtFaceLocker(cairo_scaled_font_t* cairoScaledFont) : m_scaledFont(cairoScaledFont) { }; - FT_Face lock() - { - return cairo_ft_scaled_font_lock_face(m_scaledFont); - }; - ~CairoFtFaceLocker() - { - cairo_ft_scaled_font_unlock_face(m_scaledFont); - } -private: - cairo_scaled_font_t* m_scaledFont; -}; - static hb_position_t floatToHarfBuzzPosition(float value) { return static_cast<hb_position_t>(value * (1 << 16)); @@ -113,7 +100,7 @@ static hb_bool_t harfBuzzGetGlyph(hb_font_t*, void* fontData, hb_codepoint_t uni cairo_glyph_t* glyphs = 0; int numGlyphs = 0; UChar ch = unicode; - CString utf8Codepoint = UTF8Encoding().encode(&ch, 1, QuestionMarksForUnencodables); + CString utf8Codepoint = UTF8Encoding().encode(StringView(&ch, 1), QuestionMarksForUnencodables); if (cairo_scaled_font_text_to_glyphs(scaledFont, 0, 0, utf8Codepoint.data(), utf8Codepoint.length(), &glyphs, &numGlyphs, 0, 0, 0) != CAIRO_STATUS_SUCCESS) return false; if (!numGlyphs) @@ -177,9 +164,9 @@ static hb_blob_t* harfBuzzCairoGetTable(hb_face_t*, hb_tag_t tag, void* userData return 0; CairoFtFaceLocker cairoFtFaceLocker(scaledFont); - FT_Face ftFont = cairoFtFaceLocker.lock(); + FT_Face ftFont = cairoFtFaceLocker.ftFace(); if (!ftFont) - return 0; + return nullptr; FT_ULong tableSize = 0; FT_Error error = FT_Load_Sfnt_Table(ftFont, tag, 0, 0, &tableSize); diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp index d4985c7a2..9e6f44867 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.cpp @@ -31,17 +31,15 @@ #include "config.h" #include "HarfBuzzShaper.h" -#include "Font.h" +#include "FontCascade.h" #include "HarfBuzzFace.h" #include "SurrogatePairAwareTextIterator.h" -#include "TextRun.h" -#include "hb-icu.h" +#include <hb-icu.h> #include <unicode/normlzr.h> #include <unicode/uchar.h> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> -#include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> +#include <wtf/text/StringView.h> namespace WebCore { @@ -73,7 +71,7 @@ static inline float harfBuzzPositionToFloat(hb_position_t value) return static_cast<float>(value) / (1 << 16); } -HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script) +HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const Font* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script) : m_fontData(fontData) , m_startIndex(startIndex) , m_numCharacters(numCharacters) @@ -85,6 +83,11 @@ HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigne void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer) { m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); + if (!m_numGlyphs) { + // HarfBuzzShaper::fillGlyphBuffer gets offsets()[0] + m_offsets.resize(1); + return; + } m_glyphs.resize(m_numGlyphs); m_advances.resize(m_numGlyphs); m_glyphToCharacterIndexes.resize(m_numGlyphs); @@ -98,7 +101,7 @@ void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t m_offsets[index] = FloatPoint(offsetX, offsetY); } -int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) +unsigned HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) { ASSERT(targetX <= m_width); float currentX = 0; @@ -157,9 +160,9 @@ float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) return position; } -static void normalizeCharacters(const TextRun& run, UChar* destination, int length) +static void normalizeCharacters(const TextRun& run, UChar* destination, unsigned length) { - int position = 0; + unsigned position = 0; bool error = false; const UChar* source; String stringFor8BitRun; @@ -171,12 +174,12 @@ static void normalizeCharacters(const TextRun& run, UChar* destination, int leng while (position < length) { UChar32 character; - int nextPosition = position; + unsigned nextPosition = position; U16_NEXT(source, nextPosition, length, character); // Don't normalize tabs as they are not treated as spaces for word-end. - if (Font::treatAsSpace(character) && character != '\t') + if (FontCascade::treatAsSpace(character) && character != '\t') character = ' '; - else if (Font::treatAsZeroWidthSpaceInComplexScript(character)) + else if (FontCascade::treatAsZeroWidthSpaceInComplexScript(character)) character = zeroWidthSpace; U16_APPEND(destination, position, length, character, error); ASSERT_UNUSED(error, !error); @@ -184,7 +187,7 @@ static void normalizeCharacters(const TextRun& run, UChar* destination, int leng } } -HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run) +HarfBuzzShaper::HarfBuzzShaper(const FontCascade* font, const TextRun& run) : m_font(font) , m_normalizedBufferLength(0) , m_run(run) @@ -193,8 +196,6 @@ HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run) , m_padPerWordBreak(0) , m_padError(0) , m_letterSpacing(font->letterSpacing()) - , m_fromIndex(0) - , m_toIndex(m_run.length()) { m_normalizedBuffer = std::make_unique<UChar[]>(m_run.length() + 1); m_normalizedBufferLength = m_run.length(); @@ -207,19 +208,19 @@ HarfBuzzShaper::~HarfBuzzShaper() { } -static void normalizeSpacesAndMirrorChars(const UChar* source, UChar* destination, int length, HarfBuzzShaper::NormalizeMode normalizeMode) +static void normalizeSpacesAndMirrorChars(const UChar* source, UChar* destination, unsigned length, HarfBuzzShaper::NormalizeMode normalizeMode) { - int position = 0; + unsigned position = 0; bool error = false; // Iterate characters in source and mirror character if needed. while (position < length) { UChar32 character; - int nextPosition = position; + unsigned nextPosition = position; U16_NEXT(source, nextPosition, length, character); // Don't normalize tabs as they are not treated as spaces for word-end - if (Font::treatAsSpace(character) && character != '\t') + if (FontCascade::treatAsSpace(character) && character != '\t') character = ' '; - else if (Font::treatAsZeroWidthSpace(character)) + else if (FontCascade::treatAsZeroWidthSpace(character)) character = zeroWidthSpace; else if (normalizeMode == HarfBuzzShaper::NormalizeMirrorChars) character = u_charMirror(character); @@ -257,7 +258,7 @@ void HarfBuzzShaper::setNormalizedBuffer(NormalizeMode normalizeMode) } else runCharacters = m_run.characters16(); - for (int i = 0; i < m_run.length(); ++i) { + for (unsigned i = 0; i < m_run.length(); ++i) { UChar ch = runCharacters[i]; if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { icu::Normalizer::normalize(icu::UnicodeString(runCharacters, @@ -329,18 +330,9 @@ void HarfBuzzShaper::setPadding(int padding) m_padPerWordBreak = 0; } - -void HarfBuzzShaper::setDrawRange(int from, int to) -{ - ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); - ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); - m_fromIndex = from; - m_toIndex = to; -} - void HarfBuzzShaper::setFontFeatures() { - const FontDescription& description = m_font->fontDescription(); + const auto& description = m_font->fontDescription(); if (description.orientation() == Vertical) { static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast<unsigned>(-1) }; static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast<unsigned>(-1) }; @@ -350,30 +342,28 @@ void HarfBuzzShaper::setFontFeatures() hb_feature_t kerning = { HarfBuzzFace::kernTag, 0, 0, static_cast<unsigned>(-1) }; switch (description.kerning()) { - case FontDescription::NormalKerning: + case Kerning::Normal: kerning.value = 1; m_features.append(kerning); break; - case FontDescription::NoneKerning: + case Kerning::NoShift: kerning.value = 0; m_features.append(kerning); break; - case FontDescription::AutoKerning: + case Kerning::Auto: break; default: ASSERT_NOT_REACHED(); } - FontFeatureSettings* settings = description.featureSettings(); - if (!settings) - return; + const FontFeatureSettings& settings = description.featureSettings(); - unsigned numFeatures = settings->size(); + unsigned numFeatures = settings.size(); for (unsigned i = 0; i < numFeatures; ++i) { hb_feature_t feature; - const UChar* tag = settings->at(i).tag().characters(); + auto& tag = settings[i].tag(); feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); - feature.value = settings->at(i).value(); + feature.value = settings[i].value(); feature.start = 0; feature.end = static_cast<unsigned>(-1); m_features.append(feature); @@ -413,7 +403,7 @@ bool HarfBuzzShaper::collectHarfBuzzRuns() if (!iterator.consume(character, clusterLength)) return false; - const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character, false).fontData; + const Font* nextFontData = m_font->glyphDataForCharacter(character, false).font; UErrorCode errorCode = U_ZERO_ERROR; UScriptCode nextScript = uscript_getScript(character, &errorCode); if (U_FAILURE(errorCode)) @@ -421,19 +411,21 @@ bool HarfBuzzShaper::collectHarfBuzzRuns() do { const UChar* currentCharacterPosition = iterator.characters(); - const SimpleFontData* currentFontData = nextFontData; + const Font* currentFontData = nextFontData; + if (!currentFontData) + currentFontData = &m_font->primaryFont(); UScriptCode currentScript = nextScript; for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) { - if (Font::treatAsZeroWidthSpace(character)) + if (FontCascade::treatAsZeroWidthSpace(character)) continue; if (U_GET_GC_MASK(character) & U_GC_M_MASK) { - int markLength = clusterLength; + unsigned markLength = clusterLength; const UChar* markCharactersEnd = iterator.characters() + clusterLength; while (markCharactersEnd < normalizedBufferEnd) { UChar32 nextCharacter; - int nextCharacterLength = 0; + unsigned nextCharacterLength = 0; U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter); if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) break; @@ -445,9 +437,9 @@ bool HarfBuzzShaper::collectHarfBuzzRuns() clusterLength = markLength; continue; } - nextFontData = m_font->glyphDataForCharacter(character, false).fontData; + nextFontData = m_font->glyphDataForCharacter(character, false).font; } else - nextFontData = m_font->glyphDataForCharacter(character, false).fontData; + nextFontData = m_font->glyphDataForCharacter(character, false).font; nextScript = uscript_getScript(character, &errorCode); if (U_FAILURE(errorCode)) @@ -458,11 +450,11 @@ bool HarfBuzzShaper::collectHarfBuzzRuns() nextScript = currentScript; currentCharacterPosition = iterator.characters(); } - unsigned numCharactersOfCurrentRun = iterator.currentCharacter() - startIndexOfCurrentRun; + unsigned numCharactersOfCurrentRun = iterator.currentIndex() - startIndexOfCurrentRun; hb_script_t script = hb_icu_script_to_script(currentScript); - m_harfBuzzRuns.append(HarfBuzzRun::create(currentFontData, startIndexOfCurrentRun, numCharactersOfCurrentRun, m_run.direction(), script)); + m_harfBuzzRuns.append(std::make_unique<HarfBuzzRun>(currentFontData, startIndexOfCurrentRun, numCharactersOfCurrentRun, m_run.direction(), script)); currentFontData = nextFontData; - startIndexOfCurrentRun = iterator.currentCharacter(); + startIndexOfCurrentRun = iterator.currentIndex(); } while (iterator.consume(character, clusterLength)); return !m_harfBuzzRuns.isEmpty(); @@ -477,9 +469,7 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection) for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); - const SimpleFontData* currentFontData = currentRun->fontData(); - if (currentFontData->isSVGFont()) - return false; + const Font* currentFontData = currentRun->fontData(); hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); if (shouldSetDirection) @@ -494,9 +484,10 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection) hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->startIndex()])) { - String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper(); - currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData; - hb_buffer_add_utf16(harfBuzzBuffer.get(), reinterpret_cast<const uint16_t*>(upperText.characters()), currentRun->numCharacters(), 0, currentRun->numCharacters()); + String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).convertToUppercaseWithoutLocale(); + currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).font; + const UChar* characters = StringView(upperText).upconvertedCharacters(); + hb_buffer_add_utf16(harfBuzzBuffer.get(), reinterpret_cast<const uint16_t*>(characters), currentRun->numCharacters(), 0, currentRun->numCharacters()); } else hb_buffer_add_utf16(harfBuzzBuffer.get(), reinterpret_cast<const uint16_t*>(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters()); @@ -523,7 +514,7 @@ bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection) void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) { - const SimpleFontData* currentFontData = currentRun->fontData(); + const Font* currentFontData = currentRun->fontData(); hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0); @@ -545,7 +536,7 @@ void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb glyphToCharacterIndexes[i] = glyphInfos[i].cluster; - if (isClusterEnd && !Font::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex])) + if (isClusterEnd && !FontCascade::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex])) spacing += m_letterSpacing; if (isClusterEnd && isWordEnd(currentCharacterIndex)) @@ -587,15 +578,13 @@ void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, Ha float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x(); float glyphAdvanceY = nextOffset.y() - currentOffset.y(); if (m_run.rtl()) { - if (currentCharacterIndex > m_toIndex) + if (currentCharacterIndex > m_run.length()) m_startOffset.move(glyphAdvanceX, glyphAdvanceY); - else if (currentCharacterIndex >= m_fromIndex) - glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY)); + else + glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY), currentCharacterIndex); } else { - if (currentCharacterIndex < m_fromIndex) - m_startOffset.move(glyphAdvanceX, glyphAdvanceY); - else if (currentCharacterIndex < m_toIndex) - glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY)); + if (currentCharacterIndex < m_run.length()) + glyphBuffer->add(glyphs[i], currentRun->fontData(), createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY), currentCharacterIndex); } } } @@ -655,7 +644,7 @@ int HarfBuzzShaper::offsetForPosition(float targetX) return charactersSoFar; } -FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to) +FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, unsigned from, unsigned to) { float currentX = 0; float fromX = 0; @@ -663,23 +652,34 @@ FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int bool foundFromX = false; bool foundToX = false; + std::optional<unsigned> fromIndex = from; + std::optional<unsigned> toIndex = to; + if (m_run.rtl()) currentX = m_totalWidth; for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { if (m_run.rtl()) currentX -= m_harfBuzzRuns[i]->width(); - int numCharacters = m_harfBuzzRuns[i]->numCharacters(); - if (!foundFromX && from >= 0 && from < numCharacters) { - fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; + unsigned numCharacters = m_harfBuzzRuns[i]->numCharacters(); + if (!foundFromX && fromIndex.value() < numCharacters) { + fromX = m_harfBuzzRuns[i]->xPositionForOffset(fromIndex.value()) + currentX; foundFromX = true; - } else - from -= numCharacters; + } else { + if (fromIndex && fromIndex.value() >= numCharacters) + fromIndex.value() -= numCharacters; + else + fromIndex = std::nullopt; + } - if (!foundToX && to >= 0 && to < numCharacters) { - toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; + if (!foundToX && toIndex.value() < numCharacters) { + toX = m_harfBuzzRuns[i]->xPositionForOffset(toIndex.value()) + currentX; foundToX = true; - } else - to -= numCharacters; + } else { + if (toIndex && toIndex.value() >= numCharacters) + toIndex.value() -= numCharacters; + else + toIndex = std::nullopt; + } if (foundFromX && foundToX) break; diff --git a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h index 5747e4304..593c87cd2 100644 --- a/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h +++ b/Source/WebCore/platform/graphics/harfbuzz/HarfBuzzShaper.h @@ -37,15 +37,13 @@ #include "hb.h" #include <memory> #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> #include <wtf/unicode/CharacterNames.h> namespace WebCore { class Font; -class SimpleFontData; +class FontCascade; class HarfBuzzShaper { public: @@ -54,49 +52,43 @@ public: NormalizeMirrorChars }; - HarfBuzzShaper(const Font*, const TextRun&); + HarfBuzzShaper(const FontCascade*, const TextRun&); virtual ~HarfBuzzShaper(); - void setDrawRange(int from, int to); bool shape(GlyphBuffer* = 0); FloatPoint adjustStartPoint(const FloatPoint&); float totalWidth() { return m_totalWidth; } int offsetForPosition(float targetX); - FloatRect selectionRect(const FloatPoint&, int height, int from, int to); + FloatRect selectionRect(const FloatPoint&, int height, unsigned from, unsigned to); private: class HarfBuzzRun { public: - static PassOwnPtr<HarfBuzzRun> create(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, TextDirection direction, hb_script_t script) - { - return adoptPtr(new HarfBuzzRun(fontData, startIndex, numCharacters, direction, script)); - } + HarfBuzzRun(const Font*, unsigned startIndex, unsigned numCharacters, TextDirection, hb_script_t); void applyShapeResult(hb_buffer_t*); void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY); void setWidth(float width) { m_width = width; } - int characterIndexForXPosition(float targetX); + unsigned characterIndexForXPosition(float targetX); float xPositionForOffset(unsigned offset); - const SimpleFontData* fontData() { return m_fontData; } + const Font* fontData() { return m_fontData; } unsigned startIndex() const { return m_startIndex; } unsigned numCharacters() const { return m_numCharacters; } unsigned numGlyphs() const { return m_numGlyphs; } - uint16_t* glyphs() { return &m_glyphs[0]; } - float* advances() { return &m_advances[0]; } - FloatPoint* offsets() { return &m_offsets[0]; } - uint16_t* glyphToCharacterIndexes() { return &m_glyphToCharacterIndexes[0]; } + uint16_t* glyphs() { return m_glyphs.data(); } + float* advances() { return m_advances.data(); } + FloatPoint* offsets() { return m_offsets.data(); } + uint16_t* glyphToCharacterIndexes() { return m_glyphToCharacterIndexes.data(); } float width() { return m_width; } bool rtl() { return m_direction == RTL; } hb_script_t script() { return m_script; } private: - HarfBuzzRun(const SimpleFontData*, unsigned startIndex, unsigned numCharacters, TextDirection, hb_script_t); - - const SimpleFontData* m_fontData; + const Font* m_fontData; unsigned m_startIndex; - size_t m_numCharacters; + unsigned m_numCharacters; unsigned m_numGlyphs; TextDirection m_direction; hb_script_t m_script; @@ -128,7 +120,7 @@ private: GlyphBufferAdvance createGlyphBufferAdvance(float, float); - const Font* m_font; + const FontCascade* m_font; std::unique_ptr<UChar[]> m_normalizedBuffer; unsigned m_normalizedBufferLength; const TextRun& m_run; @@ -140,13 +132,10 @@ private: int m_letterSpacing; // Pixels to be added after each glyph. Vector<hb_feature_t, 4> m_features; - Vector<OwnPtr<HarfBuzzRun>, 16> m_harfBuzzRuns; + Vector<std::unique_ptr<HarfBuzzRun>, 16> m_harfBuzzRuns; FloatPoint m_startOffset; - int m_fromIndex; - int m_toIndex; - float m_totalWidth; }; diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp index f8321cac0..5da632035 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp @@ -25,20 +25,17 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "Extensions3DOpenGL.h" #include "GraphicsContext3D.h" -#include <wtf/Vector.h> #if PLATFORM(IOS) -#include "ANGLE/ShaderLang.h" #include <OpenGLES/ES2/glext.h> #elif PLATFORM(MAC) -#include "ANGLE/ShaderLang.h" #include <OpenGL/gl.h> -#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN) +#elif PLATFORM(GTK) || PLATFORM(WIN) #include "OpenGLShims.h" #endif @@ -48,8 +45,8 @@ namespace WebCore { -Extensions3DOpenGL::Extensions3DOpenGL(GraphicsContext3D* context) - : Extensions3DOpenGLCommon(context) +Extensions3DOpenGL::Extensions3DOpenGL(GraphicsContext3D* context, bool useIndexedGetString) + : Extensions3DOpenGLCommon(context, useIndexedGetString) { } @@ -86,7 +83,7 @@ Platform3DObject Extensions3DOpenGL::createVertexArrayOES() { m_context->makeContextCurrent(); GLuint array = 0; -#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) +#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) if (isVertexArrayObjectSupported()) glGenVertexArrays(1, &array); #elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object @@ -101,7 +98,7 @@ void Extensions3DOpenGL::deleteVertexArrayOES(Platform3DObject array) return; m_context->makeContextCurrent(); -#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) +#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) if (isVertexArrayObjectSupported()) glDeleteVertexArrays(1, &array); #elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object @@ -115,7 +112,7 @@ GC3Dboolean Extensions3DOpenGL::isVertexArrayOES(Platform3DObject array) return GL_FALSE; m_context->makeContextCurrent(); -#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) +#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) if (isVertexArrayObjectSupported()) return glIsVertexArray(array); #elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object @@ -126,12 +123,8 @@ GC3Dboolean Extensions3DOpenGL::isVertexArrayOES(Platform3DObject array) void Extensions3DOpenGL::bindVertexArrayOES(Platform3DObject array) { -#if PLATFORM(IOS) - UNUSED_PARAM(array); -#endif - m_context->makeContextCurrent(); -#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) +#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) if (isVertexArrayObjectSupported()) glBindVertexArray(array); #elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object @@ -141,12 +134,6 @@ void Extensions3DOpenGL::bindVertexArrayOES(Platform3DObject array) #endif } -void Extensions3DOpenGL::copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum) -{ - // FIXME: implement this function and add GL_CHROMIUM_copy_texture in supports(). - return; -} - void Extensions3DOpenGL::insertEventMarkerEXT(const String&) { // FIXME: implement this function and add GL_EXT_debug_marker in supports(). @@ -168,25 +155,50 @@ void Extensions3DOpenGL::popGroupMarkerEXT(void) bool Extensions3DOpenGL::supportsExtension(const String& name) { // GL_ANGLE_framebuffer_blit and GL_ANGLE_framebuffer_multisample are "fake". They are implemented using other - // extensions. In particular GL_EXT_framebuffer_blit and GL_EXT_framebuffer_multisample + // extensions. In particular GL_EXT_framebuffer_blit and GL_EXT_framebuffer_multisample/GL_APPLE_framebuffer_multisample. if (name == "GL_ANGLE_framebuffer_blit") return m_availableExtensions.contains("GL_EXT_framebuffer_blit"); if (name == "GL_ANGLE_framebuffer_multisample") +#if PLATFORM(IOS) + return m_availableExtensions.contains("GL_APPLE_framebuffer_multisample"); +#else return m_availableExtensions.contains("GL_EXT_framebuffer_multisample"); +#endif + + if (name == "GL_ANGLE_instanced_arrays") { + return (m_availableExtensions.contains("GL_ARB_instanced_arrays") || m_availableExtensions.contains("GL_EXT_instanced_arrays")) + && (m_availableExtensions.contains("GL_ARB_draw_instanced") || m_availableExtensions.contains("GL_EXT_draw_instanced")); + } + + if (name == "GL_EXT_sRGB") +#if PLATFORM(IOS) + return m_availableExtensions.contains("GL_EXT_sRGB"); +#else + return m_availableExtensions.contains("GL_EXT_texture_sRGB") && (m_availableExtensions.contains("GL_EXT_framebuffer_sRGB") || m_availableExtensions.contains("GL_ARB_framebuffer_sRGB")); +#endif + + if (name == "GL_EXT_frag_depth") +#if PLATFORM(MAC) + return true; +#else + return m_availableExtensions.contains("GL_EXT_frag_depth"); +#endif // Desktop GL always supports GL_OES_rgb8_rgba8. if (name == "GL_OES_rgb8_rgba8") return true; - // If GL_ARB_texture_float is available then we report GL_OES_texture_float, + // If GL_ARB_texture_float or GL_OES_texture_float is available then we report // GL_OES_texture_half_float, GL_OES_texture_float_linear and GL_OES_texture_half_float_linear as available. if (name == "GL_OES_texture_float" || name == "GL_OES_texture_half_float" || name == "GL_OES_texture_float_linear" || name == "GL_OES_texture_half_float_linear") - return m_availableExtensions.contains("GL_ARB_texture_float"); + return m_availableExtensions.contains("GL_ARB_texture_float") || m_availableExtensions.contains("GL_OES_texture_float"); // GL_OES_vertex_array_object if (name == "GL_OES_vertex_array_object") { -#if (PLATFORM(GTK) || PLATFORM(EFL)) +#if (PLATFORM(GTK)) return m_availableExtensions.contains("GL_ARB_vertex_array_object"); +#elif PLATFORM(IOS) + return m_availableExtensions.contains("GL_OES_vertex_array_object"); #else return m_availableExtensions.contains("GL_APPLE_vertex_array_object"); #endif @@ -199,12 +211,17 @@ bool Extensions3DOpenGL::supportsExtension(const String& name) // Desktop GL always supports UNSIGNED_INT indices if (name == "GL_OES_element_index_uint") return true; - + + if (name == "GL_EXT_shader_texture_lod") + return m_availableExtensions.contains("GL_EXT_shader_texture_lod"); + if (name == "GL_EXT_texture_filter_anisotropic") return m_availableExtensions.contains("GL_EXT_texture_filter_anisotropic"); if (name == "GL_EXT_draw_buffers") { -#if PLATFORM(MAC) +#if PLATFORM(IOS) + return m_availableExtensions.contains(name); +#elif PLATFORM(MAC) || PLATFORM(GTK) return m_availableExtensions.contains("GL_ARB_draw_buffers"); #else // FIXME: implement support for other platforms. @@ -223,20 +240,67 @@ bool Extensions3DOpenGL::supportsExtension(const String& name) void Extensions3DOpenGL::drawBuffersEXT(GC3Dsizei n, const GC3Denum* bufs) { // FIXME: implement support for other platforms. -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) ::glDrawBuffersARB(n, bufs); +#elif PLATFORM(GTK) + ::glDrawBuffers(n, bufs); #else UNUSED_PARAM(n); UNUSED_PARAM(bufs); #endif } +void Extensions3DOpenGL::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) +{ + m_context->makeContextCurrent(); +#if PLATFORM(GTK) + ::glDrawArraysInstanced(mode, first, count, primcount); +#elif PLATFORM(COCOA) + ::glDrawArraysInstancedARB(mode, first, count, primcount); +#else + UNUSED_PARAM(mode); + UNUSED_PARAM(first); + UNUSED_PARAM(count); + UNUSED_PARAM(primcount); +#endif +} + +void Extensions3DOpenGL::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount) +{ + m_context->makeContextCurrent(); +#if PLATFORM(GTK) + ::glDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)), primcount); +#elif PLATFORM(COCOA) + ::glDrawElementsInstancedARB(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)), primcount); +#else + UNUSED_PARAM(mode); + UNUSED_PARAM(count); + UNUSED_PARAM(type); + UNUSED_PARAM(offset); + UNUSED_PARAM(primcount); +#endif +} + +void Extensions3DOpenGL::vertexAttribDivisor(GC3Duint index, GC3Duint divisor) +{ + m_context->makeContextCurrent(); +#if PLATFORM(GTK) + ::glVertexAttribDivisor(index, divisor); +#elif PLATFORM(COCOA) + ::glVertexAttribDivisorARB(index, divisor); +#else + UNUSED_PARAM(index); + UNUSED_PARAM(divisor); +#endif +} + String Extensions3DOpenGL::getExtensions() { + ASSERT(!m_useIndexedGetString); return String(reinterpret_cast<const char*>(::glGetString(GL_EXTENSIONS))); } -#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) +#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) bool Extensions3DOpenGL::isVertexArrayObjectSupported() { static const bool supportsVertexArrayObject = supports("GL_OES_vertex_array_object"); @@ -246,4 +310,4 @@ bool Extensions3DOpenGL::isVertexArrayObjectSupported() } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h index 81c5e49d5..c3fe83eb5 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.h @@ -23,8 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Extensions3DOpenGL_h -#define Extensions3DOpenGL_h +#pragma once #include "Extensions3DOpenGLCommon.h" @@ -37,35 +36,35 @@ namespace WebCore { class Extensions3DOpenGL : public Extensions3DOpenGLCommon { WTF_MAKE_FAST_ALLOCATED; public: + // This class only needs to be instantiated by GraphicsContext3D implementations. + Extensions3DOpenGL(GraphicsContext3D*, bool useIndexedGetString); virtual ~Extensions3DOpenGL(); // Extensions3D methods. - virtual void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter); - virtual void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height); + void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter) override; + void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height) override; + + Platform3DObject createVertexArrayOES() override; + void deleteVertexArrayOES(Platform3DObject) override; + GC3Dboolean isVertexArrayOES(Platform3DObject) override; + void bindVertexArrayOES(Platform3DObject) override; + void insertEventMarkerEXT(const String&) override; + void pushGroupMarkerEXT(const String&) override; + void popGroupMarkerEXT(void) override; + void drawBuffersEXT(GC3Dsizei, const GC3Denum*) override; - virtual Platform3DObject createVertexArrayOES(); - virtual void deleteVertexArrayOES(Platform3DObject); - virtual GC3Dboolean isVertexArrayOES(Platform3DObject); - virtual void bindVertexArrayOES(Platform3DObject); - virtual void copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum); - virtual void insertEventMarkerEXT(const String&); - virtual void pushGroupMarkerEXT(const String&); - virtual void popGroupMarkerEXT(void); - virtual void drawBuffersEXT(GC3Dsizei, const GC3Denum*); + void drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) override; + void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount) override; + void vertexAttribDivisor(GC3Duint index, GC3Duint divisor) override; protected: - // This class only needs to be instantiated by GraphicsContext3D implementations. - friend class GraphicsContext3D; - Extensions3DOpenGL(GraphicsContext3D*); + bool supportsExtension(const WTF::String&) override; + String getExtensions() override; - virtual bool supportsExtension(const WTF::String&); - virtual String getExtensions(); -#if (PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)) private: +#if (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) bool isVertexArrayObjectSupported(); #endif }; } // namespace WebCore - -#endif // Extensions3DOpenGL_h diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp index 9b7c5ab65..d28d765df 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp @@ -26,7 +26,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "Extensions3DOpenGLCommon.h" #include "ANGLEWebKitBridge.h" @@ -34,14 +34,18 @@ #if PLATFORM(IOS) #include <OpenGLES/ES2/glext.h> +#include <OpenGLES/ES3/gl.h> #else #if USE(OPENGL_ES_2) #include "OpenGLESShims.h" #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #elif PLATFORM(MAC) +#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED #include <OpenGL/gl.h> -#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN) +#include <OpenGL/gl3.h> +#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED +#elif PLATFORM(GTK) || PLATFORM(WIN) #include "OpenGLShims.h" #endif #endif @@ -49,54 +53,39 @@ #include <wtf/MainThread.h> #include <wtf/Vector.h> -#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS)) -#undef NO_ERROR -#endif - namespace WebCore { -Extensions3DOpenGLCommon::Extensions3DOpenGLCommon(GraphicsContext3D* context) +Extensions3DOpenGLCommon::Extensions3DOpenGLCommon(GraphicsContext3D* context, bool useIndexedGetString) : m_initializedAvailableExtensions(false) , m_context(context) , m_isNVIDIA(false) , m_isAMD(false) , m_isIntel(false) - , m_maySupportMultisampling(true) + , m_isImagination(false) , m_requiresBuiltInFunctionEmulation(false) + , m_requiresRestrictedMaximumTextureSize(false) + , m_useIndexedGetString(useIndexedGetString) { m_vendor = String(reinterpret_cast<const char*>(::glGetString(GL_VENDOR))); + m_renderer = String(reinterpret_cast<const char*>(::glGetString(GL_RENDERER))); Vector<String> vendorComponents; - m_vendor.lower().split(' ', vendorComponents); + m_vendor.convertToASCIILowercase().split(' ', vendorComponents); if (vendorComponents.contains("nvidia")) m_isNVIDIA = true; if (vendorComponents.contains("ati") || vendorComponents.contains("amd")) m_isAMD = true; if (vendorComponents.contains("intel")) m_isIntel = true; + if (vendorComponents.contains("imagination")) + m_isImagination = true; -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) if (m_isAMD || m_isIntel) m_requiresBuiltInFunctionEmulation = true; - // Currently in Mac we only allow multisampling if the vendor is NVIDIA, - // or if the vendor is AMD/ATI and the system is 10.7.2 and above. - - bool systemSupportsMultisampling = true; -#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1080 - ASSERT(isMainThread()); - static SInt32 version; - if (!version) { - if (Gestalt(gestaltSystemVersion, &version) != noErr) - systemSupportsMultisampling = false; - } - // See https://bugs.webkit.org/show_bug.cgi?id=77922 for more details - if (systemSupportsMultisampling) - systemSupportsMultisampling = version >= 0x1072; -#endif // SNOW_LEOPARD and LION - - if (m_isAMD && !systemSupportsMultisampling) - m_maySupportMultisampling = false; + // Intel HD 3000 devices have problems with large textures. <rdar://problem/16649140> + m_requiresRestrictedMaximumTextureSize = m_renderer.startsWith("Intel HD Graphics 3000"); #endif } @@ -109,6 +98,12 @@ bool Extensions3DOpenGLCommon::supports(const String& name) if (!m_initializedAvailableExtensions) initializeAvailableExtensions(); + // We explicitly do not support this extension until + // we fix the following bug: + // https://bugs.webkit.org/show_bug.cgi?id=149734 + if (name == "GL_ANGLE_translated_shader_source") + return false; + return supportsExtension(name); } @@ -131,6 +126,22 @@ void Extensions3DOpenGLCommon::ensureEnabled(const String& name) m_context->getIntegerv(Extensions3D::MAX_DRAW_BUFFERS_EXT, &ANGLEResources.MaxDrawBuffers); compiler.setResources(ANGLEResources); } + } else if (name == "GL_EXT_shader_texture_lod") { + // Enable support in ANGLE (if not enabled already) + ANGLEWebKitBridge& compiler = m_context->m_compiler; + ShBuiltInResources ANGLEResources = compiler.getResources(); + if (!ANGLEResources.EXT_shader_texture_lod) { + ANGLEResources.EXT_shader_texture_lod = 1; + compiler.setResources(ANGLEResources); + } + } else if (name == "GL_EXT_frag_depth") { + // Enable support in ANGLE (if not enabled already) + ANGLEWebKitBridge& compiler = m_context->m_compiler; + ShBuiltInResources ANGLEResources = compiler.getResources(); + if (!ANGLEResources.EXT_frag_depth) { + ANGLEResources.EXT_frag_depth = 1; + compiler.setResources(ANGLEResources); + } } } @@ -174,22 +185,20 @@ String Extensions3DOpenGLCommon::getTranslatedShaderSourceANGLE(Platform3DObject String translatedShaderSource; String shaderInfoLog; - int extraCompileOptions = SH_MAP_LONG_VARIABLE_NAMES | SH_CLAMP_INDIRECT_ARRAY_BOUNDS | SH_UNFOLD_SHORT_CIRCUIT | SH_ENFORCE_PACKING_RESTRICTIONS; + int extraCompileOptions = SH_CLAMP_INDIRECT_ARRAY_BOUNDS | SH_UNFOLD_SHORT_CIRCUIT | SH_INIT_OUTPUT_VARIABLES | SH_ENFORCE_PACKING_RESTRICTIONS | SH_LIMIT_EXPRESSION_COMPLEXITY | SH_LIMIT_CALL_STACK_DEPTH; if (m_requiresBuiltInFunctionEmulation) - extraCompileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; + extraCompileOptions |= SH_EMULATE_ABS_INT_FUNCTION; - Vector<ANGLEShaderSymbol> symbols; + Vector<std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>> symbols; bool isValid = compiler.compileShaderSource(entry.source.utf8().data(), shaderType, translatedShaderSource, shaderInfoLog, symbols, extraCompileOptions); entry.log = shaderInfoLog; entry.isValid = isValid; - size_t numSymbols = symbols.size(); - for (size_t i = 0; i < numSymbols; ++i) { - ANGLEShaderSymbol shaderSymbol = symbols[i]; - GraphicsContext3D::SymbolInfo symbolInfo(shaderSymbol.dataType, shaderSymbol.size, shaderSymbol.mappedName, shaderSymbol.precision, shaderSymbol.staticUse); - entry.symbolMap(shaderSymbol.symbolType).set(shaderSymbol.name, symbolInfo); + for (const std::pair<ANGLEShaderSymbolType, sh::ShaderVariable>& pair : symbols) { + const std::string& name = pair.second.name; + entry.symbolMap(pair.first).set(String(name.c_str(), name.length()), pair.second); } if (!isValid) @@ -200,11 +209,30 @@ String Extensions3DOpenGLCommon::getTranslatedShaderSourceANGLE(Platform3DObject void Extensions3DOpenGLCommon::initializeAvailableExtensions() { - String extensionsString = getExtensions(); - Vector<String> availableExtensions; - extensionsString.split(" ", availableExtensions); - for (size_t i = 0; i < availableExtensions.size(); ++i) - m_availableExtensions.add(availableExtensions[i]); +#if PLATFORM(MAC) || (PLATFORM(GTK) && !USE(OPENGL_ES_2)) + if (m_useIndexedGetString) { + GLint numExtensions = 0; + ::glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + for (GLint i = 0; i < numExtensions; ++i) + m_availableExtensions.add(glGetStringi(GL_EXTENSIONS, i)); + + if (!m_availableExtensions.contains(ASCIILiteral("GL_ARB_texture_storage"))) { + GLint majorVersion; + glGetIntegerv(GL_MAJOR_VERSION, &majorVersion); + GLint minorVersion; + glGetIntegerv(GL_MINOR_VERSION, &minorVersion); + if (majorVersion > 4 || (majorVersion == 4 && minorVersion >= 2)) + m_availableExtensions.add(ASCIILiteral("GL_ARB_texture_storage")); + } + } else +#endif + { + String extensionsString = getExtensions(); + Vector<String> availableExtensions; + extensionsString.split(' ', availableExtensions); + for (size_t i = 0; i < availableExtensions.size(); ++i) + m_availableExtensions.add(availableExtensions[i]); + } m_initializedAvailableExtensions = true; } @@ -225,4 +253,4 @@ void Extensions3DOpenGLCommon::getnUniformivEXT(GC3Duint, int, GC3Dsizei, int *) } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h index 4cc8dbac1..7c614fb40 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.h @@ -24,8 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Extensions3DOpenGLCommon_h -#define Extensions3DOpenGLCommon_h +#pragma once #include "Extensions3D.h" @@ -40,36 +39,37 @@ public: virtual ~Extensions3DOpenGLCommon(); // Extensions3D methods. - virtual bool supports(const String&); - virtual void ensureEnabled(const String&); - virtual bool isEnabled(const String&); - virtual int getGraphicsResetStatusARB(); + bool supports(const String&) override; + void ensureEnabled(const String&) override; + bool isEnabled(const String&) override; + int getGraphicsResetStatusARB() override; - virtual Platform3DObject createVertexArrayOES() = 0; - virtual void deleteVertexArrayOES(Platform3DObject) = 0; - virtual GC3Dboolean isVertexArrayOES(Platform3DObject) = 0; - virtual void bindVertexArrayOES(Platform3DObject) = 0; + Platform3DObject createVertexArrayOES() override = 0; + void deleteVertexArrayOES(Platform3DObject) override = 0; + GC3Dboolean isVertexArrayOES(Platform3DObject) override = 0; + void bindVertexArrayOES(Platform3DObject) override = 0; - virtual void drawBuffersEXT(GC3Dsizei, const GC3Denum*) = 0; + void drawBuffersEXT(GC3Dsizei, const GC3Denum*) override = 0; - virtual String getTranslatedShaderSourceANGLE(Platform3DObject); + String getTranslatedShaderSourceANGLE(Platform3DObject) override; // EXT Robustness - uses getGraphicsResetStatusARB() - virtual void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data); - virtual void getnUniformfvEXT(GC3Duint program, int location, GC3Dsizei bufSize, float *params); - virtual void getnUniformivEXT(GC3Duint program, int location, GC3Dsizei bufSize, int *params); + void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data) override; + void getnUniformfvEXT(GC3Duint program, int location, GC3Dsizei bufSize, float *params) override; + void getnUniformivEXT(GC3Duint program, int location, GC3Dsizei bufSize, int *params) override; - virtual bool isNVIDIA() { return m_isNVIDIA; } - virtual bool isAMD() { return m_isAMD; } - virtual bool isIntel() { return m_isIntel; } - virtual String vendor() { return m_vendor; } + bool isNVIDIA() override { return m_isNVIDIA; } + bool isAMD() override { return m_isAMD; } + bool isIntel() override { return m_isIntel; } + bool isImagination() override { return m_isImagination; } + String vendor() override { return m_vendor; } - virtual bool maySupportMultisampling() { return m_maySupportMultisampling; } - virtual bool requiresBuiltInFunctionEmulation() { return m_requiresBuiltInFunctionEmulation; } + bool requiresBuiltInFunctionEmulation() override { return m_requiresBuiltInFunctionEmulation; } + bool requiresRestrictedMaximumTextureSize() override { return m_requiresRestrictedMaximumTextureSize; } protected: friend class Extensions3DOpenGLES; - Extensions3DOpenGLCommon(GraphicsContext3D*); + Extensions3DOpenGLCommon(GraphicsContext3D*, bool useIndexedGetString); virtual bool supportsExtension(const String&) = 0; virtual String getExtensions() = 0; @@ -84,12 +84,14 @@ protected: bool m_isNVIDIA; bool m_isAMD; bool m_isIntel; - bool m_maySupportMultisampling; + bool m_isImagination; bool m_requiresBuiltInFunctionEmulation; + bool m_requiresRestrictedMaximumTextureSize; + + bool m_useIndexedGetString { false }; String m_vendor; + String m_renderer; }; } // namespace WebCore - -#endif // Extensions3DOpenGLCommon_h diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp index dc9e59252..958e09383 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. * Copyright (C) 2012 Research In Motion Limited. All rights reserved. + * Copyright (C) 2014 Collabora Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,21 +26,23 @@ */ #include "config.h" -#if USE(3D_GRAPHICS) + +#if USE(OPENGL_ES_2) #include "Extensions3DOpenGLES.h" +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" #include "NotImplemented.h" #include <EGL/egl.h> -#include <wtf/Vector.h> namespace WebCore { -Extensions3DOpenGLES::Extensions3DOpenGLES(GraphicsContext3D* context) - : Extensions3DOpenGLCommon(context) +Extensions3DOpenGLES::Extensions3DOpenGLES(GraphicsContext3D* context, bool useIndexedGetString) + : Extensions3DOpenGLCommon(context, useIndexedGetString) , m_contextResetStatus(GL_NO_ERROR) , m_supportsOESvertexArrayObject(false) , m_supportsIMGMultisampledRenderToTexture(false) + , m_supportsANGLEinstancedArrays(false) , m_glFramebufferTexture2DMultisampleIMG(0) , m_glRenderbufferStorageMultisampleIMG(0) , m_glBindVertexArrayOES(0) @@ -50,6 +53,9 @@ Extensions3DOpenGLES::Extensions3DOpenGLES(GraphicsContext3D* context) , m_glReadnPixelsEXT(0) , m_glGetnUniformfvEXT(0) , m_glGetnUniformivEXT(0) + , m_glVertexAttribDivisorANGLE(nullptr) + , m_glDrawArraysInstancedANGLE(nullptr) + , m_glDrawElementsInstancedANGLE(nullptr) { } @@ -73,7 +79,7 @@ void Extensions3DOpenGLES::renderbufferStorageMultisampleIMG(unsigned long targe m_context->synthesizeGLError(GL_INVALID_OPERATION); } -void Extensions3DOpenGLES::blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter) +void Extensions3DOpenGLES::blitFramebuffer(long /* srcX0 */, long /* srcY0 */, long /* srcX1 */, long /* srcY1 */, long /* dstX0 */, long /* dstY0 */, long /* dstX1 */, long /* dstY1 */, unsigned long /* mask */, unsigned long /* filter */) { notImplemented(); } @@ -86,11 +92,6 @@ void Extensions3DOpenGLES::renderbufferStorageMultisample(unsigned long target, notImplemented(); } -void Extensions3DOpenGLES::copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum) -{ - notImplemented(); -} - void Extensions3DOpenGLES::insertEventMarkerEXT(const String&) { notImplemented(); @@ -156,9 +157,10 @@ void Extensions3DOpenGLES::bindVertexArrayOES(Platform3DObject array) m_context->synthesizeGLError(GL_INVALID_OPERATION); } -void Extensions3DOpenGLES::drawBuffersEXT(GC3Dsizei n, const GC3Denum* bufs) +void Extensions3DOpenGLES::drawBuffersEXT(GC3Dsizei /* n */, const GC3Denum* /* bufs */) { // FIXME: implement the support. + notImplemented(); } int Extensions3DOpenGLES::getGraphicsResetStatusARB() @@ -184,9 +186,9 @@ int Extensions3DOpenGLES::getGraphicsResetStatusARB() return false; } -void Extensions3DOpenGLES::setEXTContextLostCallback(PassOwnPtr<GraphicsContext3D::ContextLostCallback> callback) +void Extensions3DOpenGLES::setEXTContextLostCallback(std::unique_ptr<GraphicsContext3D::ContextLostCallback> callback) { - m_contextLostCallback = callback; + m_contextLostCallback = WTFMove(callback); } void Extensions3DOpenGLES::readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data) @@ -228,6 +230,39 @@ void Extensions3DOpenGLES::getnUniformivEXT(GC3Duint program, int location, GC3D m_context->synthesizeGLError(GL_INVALID_OPERATION); } +void Extensions3DOpenGLES::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) +{ + if (!m_glDrawArraysInstancedANGLE) { + m_context->synthesizeGLError(GL_INVALID_OPERATION); + return; + } + + m_context->makeContextCurrent(); + m_glDrawArraysInstancedANGLE(mode, first, count, primcount); +} + +void Extensions3DOpenGLES::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount) +{ + if (!m_glDrawElementsInstancedANGLE) { + m_context->synthesizeGLError(GL_INVALID_OPERATION); + return; + } + + m_context->makeContextCurrent(); + m_glDrawElementsInstancedANGLE(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)), primcount); +} + +void Extensions3DOpenGLES::vertexAttribDivisor(GC3Duint index, GC3Duint divisor) +{ + if (!m_glVertexAttribDivisorANGLE) { + m_context->synthesizeGLError(GL_INVALID_OPERATION); + return; + } + + m_context->makeContextCurrent(); + m_glVertexAttribDivisorANGLE(index, divisor); +} + bool Extensions3DOpenGLES::supportsExtension(const String& name) { if (m_availableExtensions.contains(name)) { @@ -238,14 +273,19 @@ bool Extensions3DOpenGLES::supportsExtension(const String& name) m_glIsVertexArrayOES = reinterpret_cast<PFNGLISVERTEXARRAYOESPROC>(eglGetProcAddress("glIsVertexArrayOES")); m_supportsOESvertexArrayObject = true; } else if (!m_supportsIMGMultisampledRenderToTexture && name == "GL_IMG_multisampled_render_to_texture") { - m_glFramebufferTexture2DMultisampleIMG = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG>(eglGetProcAddress("glFramebufferTexture2DMultisampleIMG")); - m_glRenderbufferStorageMultisampleIMG = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG>(eglGetProcAddress("glRenderbufferStorageMultisampleIMG")); + m_glFramebufferTexture2DMultisampleIMG = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMGPROC>(eglGetProcAddress("glFramebufferTexture2DMultisampleIMG")); + m_glRenderbufferStorageMultisampleIMG = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMGPROC>(eglGetProcAddress("glRenderbufferStorageMultisampleIMG")); m_supportsIMGMultisampledRenderToTexture = true; } else if (!m_glGetGraphicsResetStatusEXT && name == "GL_EXT_robustness") { m_glGetGraphicsResetStatusEXT = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSEXTPROC>(eglGetProcAddress("glGetGraphicsResetStatusEXT")); m_glReadnPixelsEXT = reinterpret_cast<PFNGLREADNPIXELSEXTPROC>(eglGetProcAddress("glReadnPixelsEXT")); m_glGetnUniformfvEXT = reinterpret_cast<PFNGLGETNUNIFORMFVEXTPROC>(eglGetProcAddress("glGetnUniformfvEXT")); m_glGetnUniformivEXT = reinterpret_cast<PFNGLGETNUNIFORMIVEXTPROC>(eglGetProcAddress("glGetnUniformivEXT")); + } else if (!m_supportsANGLEinstancedArrays && name == "GL_ANGLE_instanced_arrays") { + m_glVertexAttribDivisorANGLE = reinterpret_cast<PFNGLVERTEXATTRIBDIVISORANGLEPROC>(eglGetProcAddress("glVertexAttribDivisorANGLE")); + m_glDrawArraysInstancedANGLE = reinterpret_cast<PFNGLDRAWARRAYSINSTANCEDANGLEPROC >(eglGetProcAddress("glDrawArraysInstancedANGLE")); + m_glDrawElementsInstancedANGLE = reinterpret_cast<PFNGLDRAWELEMENTSINSTANCEDANGLEPROC >(eglGetProcAddress("glDrawElementsInstancedANGLE")); + m_supportsANGLEinstancedArrays = true; } else if (name == "GL_EXT_draw_buffers") { // FIXME: implement the support. return false; @@ -263,4 +303,6 @@ String Extensions3DOpenGLES::getExtensions() } // namespace WebCore -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) + +#endif // USE(OPENGL_ES_2) diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h index 7c40566e9..316e5e9b5 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLES.h @@ -24,11 +24,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Extensions3DOpenGLES_h -#define Extensions3DOpenGLES_h +#pragma once #include "Extensions3DOpenGLCommon.h" +#if USE(OPENGL_ES_2) + #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -61,6 +62,8 @@ namespace WebCore { class Extensions3DOpenGLES : public Extensions3DOpenGLCommon { public: + // This class only needs to be instantiated by GraphicsContext3D implementations. + Extensions3DOpenGLES(GraphicsContext3D*, bool useIndexedGetString); virtual ~Extensions3DOpenGLES(); virtual void framebufferTexture2DMultisampleIMG(unsigned long target, unsigned long attachment, unsigned long textarget, unsigned int texture, int level, unsigned long samples); @@ -69,7 +72,6 @@ public: // Extension3D methods virtual void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter); virtual void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height); - virtual void copyTextureCHROMIUM(GC3Denum, Platform3DObject, Platform3DObject, GC3Dint, GC3Denum); virtual void insertEventMarkerEXT(const String&); virtual void pushGroupMarkerEXT(const String&); virtual void popGroupMarkerEXT(void); @@ -80,9 +82,13 @@ public: virtual void bindVertexArrayOES(Platform3DObject); virtual void drawBuffersEXT(GC3Dsizei, const GC3Denum*); + virtual void drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount); + virtual void drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount); + virtual void vertexAttribDivisor(GC3Duint index, GC3Duint divisor); + // EXT Robustness - reset virtual int getGraphicsResetStatusARB(); - void setEXTContextLostCallback(PassOwnPtr<GraphicsContext3D::ContextLostCallback>); + void setEXTContextLostCallback(std::unique_ptr<GraphicsContext3D::ContextLostCallback>); // EXT Robustness - etc virtual void readnPixelsEXT(int x, int y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, GC3Dsizei bufSize, void *data); @@ -90,10 +96,6 @@ public: virtual void getnUniformivEXT(GC3Duint program, int location, GC3Dsizei bufSize, int *params); protected: - // This class only needs to be instantiated by GraphicsContext3D implementations. - friend class GraphicsContext3D; - Extensions3DOpenGLES(GraphicsContext3D*); - virtual bool supportsExtension(const String&); virtual String getExtensions(); @@ -101,9 +103,10 @@ protected: bool m_supportsOESvertexArrayObject; bool m_supportsIMGMultisampledRenderToTexture; + bool m_supportsANGLEinstancedArrays; - PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG m_glFramebufferTexture2DMultisampleIMG; - PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG m_glRenderbufferStorageMultisampleIMG; + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMGPROC m_glFramebufferTexture2DMultisampleIMG; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMGPROC m_glRenderbufferStorageMultisampleIMG; PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES; PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES; PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES; @@ -112,10 +115,13 @@ protected: PFNGLREADNPIXELSEXTPROC m_glReadnPixelsEXT; PFNGLGETNUNIFORMFVEXTPROC m_glGetnUniformfvEXT; PFNGLGETNUNIFORMIVEXTPROC m_glGetnUniformivEXT; + PFNGLVERTEXATTRIBDIVISORANGLEPROC m_glVertexAttribDivisorANGLE; + PFNGLDRAWARRAYSINSTANCEDANGLEPROC m_glDrawArraysInstancedANGLE; + PFNGLDRAWELEMENTSINSTANCEDANGLEPROC m_glDrawElementsInstancedANGLE; - OwnPtr<GraphicsContext3D::ContextLostCallback> m_contextLostCallback; + std::unique_ptr<GraphicsContext3D::ContextLostCallback> m_contextLostCallback; }; } // namespace WebCore -#endif // Extensions3DOpenGLES_h +#endif // USE(OPENGL_ES_2) diff --git a/Source/WebCore/platform/graphics/opengl/GLDefs.h b/Source/WebCore/platform/graphics/opengl/GLDefs.h new file mode 100644 index 000000000..9a2c2fffd --- /dev/null +++ b/Source/WebCore/platform/graphics/opengl/GLDefs.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GLDefs_h +#define GLDefs_h + +#define GL_GLEXT_PROTOTYPES 1 + +#if USE(OPENGL_ES_2) +#include "Extensions3DOpenGLES.h" +#include "OpenGLESShims.h" +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#else +#include "Extensions3DOpenGL.h" +#include "OpenGLShims.h" +#include <GL/gl.h> +#include <GL/glext.h> +#if USE(GLX) +#define GLX_GLXEXT_PROTOTYPES 1 +#include <GL/glx.h> +#include <GL/glxext.h> +#endif +#endif + +#if USE(EGL) +#define EGL_EGLEXT_PROTOTYPES 1 +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + +namespace WebCore { + +typedef uint32_t PlatformBufferHandle; + +#if USE(GLX) +typedef GLXContext PlatformContext; +typedef GLXFBConfig PlatformSurfaceConfig; +typedef GLXDrawable PlatformDrawable; +#elif USE(EGL) +typedef EGLContext PlatformContext; +typedef EGLConfig PlatformSurfaceConfig; +typedef EGLSurface PlatformDrawable; +#else +typedef void* PlatformContext; +typedef void* PlatformSurfaceConfig; +typedef void* PlatformDrawable; +#endif + +} + +#endif diff --git a/Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp new file mode 100644 index 000000000..5219a2391 --- /dev/null +++ b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(GRAPHICS_CONTEXT_3D) +#include "GLPlatformContext.h" + +#if USE(GLX) +#include "GLXContext.h" +#elif USE(EGL) +#include "EGLContext.h" +#endif + +#include "NotImplemented.h" + +namespace WebCore { + +#if USE(OPENGL_ES_2) +static PFNGLGETGRAPHICSRESETSTATUSEXTPROC glGetGraphicsResetStatus = 0; +#else +static PFNGLGETGRAPHICSRESETSTATUSARBPROC glGetGraphicsResetStatus = 0; +#endif + +class GLCurrentContextWrapper : public GLPlatformContext { + +public: + GLCurrentContextWrapper() + : GLPlatformContext() + { +#if USE(GLX) + m_contextHandle = glXGetCurrentContext(); +#elif USE(EGL) + m_contextHandle = eglGetCurrentContext(); +#endif + } + + virtual ~GLCurrentContextWrapper() { } + + bool isCurrentContext() const override + { + return true; + } +}; + +static std::unique_ptr<GLPlatformContext> createOffScreenContext() +{ +#if USE(GLX) + return std::make_unique<GLXOffScreenContext>(); +#elif USE(EGL) + return std::make_unique<EGLOffScreenContext>(); +#else + return nullptr; +#endif +} + +static HashSet<String> parseExtensions(const String& extensionsString) +{ + Vector<String> extNames; + extensionsString.split(' ', extNames); + HashSet<String> splitExtNames; + unsigned size = extNames.size(); + for (unsigned i = 0; i < size; ++i) + splitExtNames.add(extNames[i]); + extNames.clear(); + + return splitExtNames; +} + +static void resolveResetStatusExtension() +{ + static bool resolvedRobustnessExtension = false; + if (!resolvedRobustnessExtension) { + resolvedRobustnessExtension = true; +#if USE(OPENGL_ES_2) + glGetGraphicsResetStatus = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSEXTPROC>(eglGetProcAddress("glGetGraphicsResetStatusEXT")); +#elif USE(EGL) + glGetGraphicsResetStatus = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSARBPROC>(eglGetProcAddress("glGetGraphicsResetStatusARB")); +#elif USE(GLX) + glGetGraphicsResetStatus = reinterpret_cast<PFNGLGETGRAPHICSRESETSTATUSARBPROC>(glXGetProcAddressARB(reinterpret_cast<const GLubyte*>("glGetGraphicsResetStatusARB"))); +#endif + } +} + +std::unique_ptr<GLPlatformContext> GLPlatformContext::createContext(GraphicsContext3D::RenderStyle renderStyle) +{ +#if !USE(OPENGL_ES_2) + if (!initializeOpenGLShims()) + return nullptr; +#endif + + switch (renderStyle) { + case GraphicsContext3D::RenderOffscreen: + return createOffScreenContext(); + case GraphicsContext3D::RenderToCurrentGLContext: + return std::make_unique<GLCurrentContextWrapper>(); + case GraphicsContext3D::RenderDirectlyToHostWindow: + ASSERT_NOT_REACHED(); + break; + } + + return nullptr; +} + +bool GLPlatformContext::supportsGLExtension(const String& name) +{ + static HashSet<String> supportedExtensions; + + if (!supportedExtensions.size()) { + String rawExtensions = reinterpret_cast<const char*>(::glGetString(GL_EXTENSIONS)); + supportedExtensions = parseExtensions(rawExtensions); + } + + if (supportedExtensions.contains(name)) + return true; + + return false; +} + +#if USE(EGL) +bool GLPlatformContext::supportsEGLExtension(EGLDisplay display, const String& name) +{ + static HashSet<String> supportedExtensions; + + if (!supportedExtensions.size()) { + if (display == EGL_NO_DISPLAY) + return false; + + String rawExtensions = reinterpret_cast<const char*>(eglQueryString(display, EGL_EXTENSIONS)); + supportedExtensions = parseExtensions(rawExtensions); + } + + if (supportedExtensions.contains(name)) + return true; + + return false; +} +#endif + +#if USE(GLX) +bool GLPlatformContext::supportsGLXExtension(Display* display, const String& name) +{ + static HashSet<String> supportedExtensions; + + if (!supportedExtensions.size()) { + if (!display) + return false; + + String rawExtensions = glXQueryExtensionsString(display, DefaultScreen(display)); + supportedExtensions = parseExtensions(rawExtensions); + } + + if (supportedExtensions.contains(name)) + return true; + + return false; +} +#endif + +GLPlatformContext::GLPlatformContext() + : m_contextHandle(0) + , m_resetLostContext(false) +{ +} + +GLPlatformContext::~GLPlatformContext() +{ +} + +bool GLPlatformContext::makeCurrent(GLPlatformSurface* surface) +{ + m_contextLost = false; + + if (isCurrentContext() && (!surface || surface->isCurrentDrawable())) + return true; + + GLPlatformContext* currentContext = 0; + + if (!surface || (surface && !surface->drawable())) + platformReleaseCurrent(); + else if (platformMakeCurrent(surface)) { + currentContext = this; + surface->onMakeCurrent(); + } + + if (m_resetLostContext) { + resolveResetStatusExtension(); + + if (glGetGraphicsResetStatus) { + GLenum status = glGetGraphicsResetStatus(); + + switch (status) { + case PLATFORMCONTEXT_NO_ERROR: + break; + case PLATFORMCONTEXT_GUILTY_CONTEXT_RESET: + m_contextLost = true; + break; + case PLATFORMCONTEXT_INNOCENT_CONTEXT_RESET: + break; + case PLATFORMCONTEXT_UNKNOWN_CONTEXT_RESET: + m_contextLost = true; + break; + default: + break; + } + } + } + + return currentContext; +} + +bool GLPlatformContext::isValid() const +{ + return !m_contextLost; +} + +void GLPlatformContext::releaseCurrent() +{ + if (isCurrentContext()) + platformReleaseCurrent(); +} + +PlatformContext GLPlatformContext::handle() const +{ + return m_contextHandle; +} + +bool GLPlatformContext::initialize(GLPlatformSurface*, PlatformContext) +{ + return true; +} + +bool GLPlatformContext::platformMakeCurrent(GLPlatformSurface*) +{ + return true; +} + +void GLPlatformContext::platformReleaseCurrent() +{ + notImplemented(); +} + +void GLPlatformContext::destroy() +{ + m_contextHandle = 0; + m_resetLostContext = false; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/opengl/GLPlatformContext.h b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.h new file mode 100644 index 000000000..a999cc0cf --- /dev/null +++ b/Source/WebCore/platform/graphics/opengl/GLPlatformContext.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GLPlatformContext_h +#define GLPlatformContext_h + +#include "GLDefs.h" +#include "GLPlatformSurface.h" +#include "GraphicsContext3D.h" +#include <wtf/Noncopyable.h> + +// Encapsulates an OpenGL context, hiding platform specific management. +namespace WebCore { + +class GLPlatformContext { + WTF_MAKE_NONCOPYABLE(GLPlatformContext); + +public: + // From http://www.khronos.org/registry/gles/extensions/EXT/EXT_robustness.txt + enum PlatformContextReset { + PLATFORMCONTEXT_NO_ERROR = 0x0000, + PLATFORMCONTEXT_GUILTY_CONTEXT_RESET = 0x8253, + PLATFORMCONTEXT_INNOCENT_CONTEXT_RESET = 0x8254, + PLATFORMCONTEXT_UNKNOWN_CONTEXT_RESET = 0x8255, + }; + + static std::unique_ptr<GLPlatformContext> createContext(GraphicsContext3D::RenderStyle); + + static bool supportsGLExtension(const String&); + +#if USE(EGL) + static bool supportsEGLExtension(EGLDisplay, const String&); +#endif + +#if USE(GLX) + static bool supportsGLXExtension(Display*, const String&); +#endif + + virtual ~GLPlatformContext(); + + virtual bool initialize(GLPlatformSurface*, PlatformContext = 0); + + // Makes this and surface as current context and drawable. + // Calling this function with no surface is same as calling releaseCurrent. + // Does nothing if this is already current Context. + bool makeCurrent(GLPlatformSurface* = 0); + + // Sets Current Context and Drawable as Null. + // Doesn't have any effect if this is not the current Context. + void releaseCurrent(); + + virtual PlatformContext handle() const; + + virtual bool isCurrentContext() const = 0; + + bool isValid() const; + + // Destroys any GL resources associated with this context. + virtual void destroy(); + +protected: + GLPlatformContext(); + virtual bool platformMakeCurrent(GLPlatformSurface*); + virtual void platformReleaseCurrent(); + PlatformContext m_contextHandle; + bool m_resetLostContext; + bool m_contextLost; +}; + +} // namespace WebCore + +#endif // GLNativeContext_H diff --git a/Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h b/Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h new file mode 100644 index 000000000..2d99d6f2d --- /dev/null +++ b/Source/WebCore/platform/graphics/opengl/GLPlatformSurface.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GLPlatformSurface_h +#define GLPlatformSurface_h + +#include "GLDefs.h" +#include "IntRect.h" +#include <wtf/Noncopyable.h> + +// Encapsulates a surface that can be rendered to with GL, hiding platform +// specific management. +namespace WebCore { + +class GLPlatformSurface { + WTF_MAKE_NONCOPYABLE(GLPlatformSurface); + +public: + enum Attributes { + Default = 0x00, // No Alpha channel. Only R,G,B values set. + SupportAlpha = 0x01, + DoubleBuffered = 0x02 + }; + + typedef unsigned SurfaceAttributes; + // Creates a GL surface used for offscreen rendering. + static std::unique_ptr<GLPlatformSurface> createOffScreenSurface(SurfaceAttributes = GLPlatformSurface::Default); + + virtual ~GLPlatformSurface(); + + const IntRect& geometry() const; + + // Get the underlying platform specific buffer handle. + // The handle will be null if surface doesn't support + // buffer sharing. + PlatformBufferHandle handle() const; + + PlatformDrawable drawable() const; + + virtual SurfaceAttributes attributes() const; + + virtual void swapBuffers(); + + virtual bool isCurrentDrawable() const = 0; + + virtual void onMakeCurrent(); + + // Convenience Function to update surface backbuffer with texture contents. + // Note that the function doesn't track or restore any GL states. + // Function does the following(in order): + // a) Blits texture contents to back buffer. + // b) Calls Swap Buffers. + virtual void updateContents(const uint32_t); + + virtual void setGeometry(const IntRect&); + + virtual PlatformSurfaceConfig configuration(); + + virtual void destroy(); + +protected: + GLPlatformSurface(SurfaceAttributes); + + PlatformDrawable m_drawable; + PlatformBufferHandle m_bufferHandle; + IntRect m_rect; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp index 02e1b9ec8..25c9a221c 100644 --- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp +++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,7 +26,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" #if PLATFORM(IOS) @@ -44,14 +44,21 @@ #include <wtf/MainThread.h> #include <wtf/text/CString.h> +#if USE(ACCELERATE) +#include <Accelerate/Accelerate.h> +#endif + #if PLATFORM(IOS) #import <OpenGLES/ES2/glext.h> // From <OpenGLES/glext.h> #define GL_RGBA32F_ARB 0x8814 #define GL_RGB32F_ARB 0x8815 #elif PLATFORM(MAC) +#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED #include <OpenGL/gl.h> -#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN) +#include <OpenGL/gl3.h> +#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED +#elif PLATFORM(GTK) || PLATFORM(WIN) #include "OpenGLShims.h" #endif @@ -65,7 +72,32 @@ void GraphicsContext3D::releaseShaderCompiler() void GraphicsContext3D::readPixelsAndConvertToBGRAIfNecessary(int x, int y, int width, int height, unsigned char* pixels) { - ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); + // NVIDIA drivers have a bug where calling readPixels in BGRA can return the wrong values for the alpha channel when the alpha is off for the context. + if (!m_attrs.alpha && getExtensions().isNVIDIA()) { + ::glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +#if USE(ACCELERATE) + vImage_Buffer src; + src.height = height; + src.width = width; + src.rowBytes = width * 4; + src.data = pixels; + + vImage_Buffer dest; + dest.height = height; + dest.width = width; + dest.rowBytes = width * 4; + dest.data = pixels; + + // Swap pixel channels from RGBA to BGRA. + const uint8_t map[4] = { 2, 1, 0, 3 }; + vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags); +#else + int totalBytes = width * height * 4; + for (int i = 0; i < totalBytes; i += 4) + std::swap(pixels[i], pixels[i + 2]); +#endif + } else + ::glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); } void GraphicsContext3D::validateAttributes() @@ -89,9 +121,9 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) // We don't allow the logic where stencil is required and depth is not. // See GraphicsContext3D::validateAttributes. - Extensions3D* extensions = getExtensions(); + Extensions3D& extensions = getExtensions(); // Use a 24 bit depth buffer where we know we have it. - if (extensions->supports("GL_EXT_packed_depth_stencil")) + if (extensions.supports("GL_EXT_packed_depth_stencil")) internalDepthStencilFormat = GL_DEPTH24_STENCIL8_EXT; else #if PLATFORM(IOS) @@ -110,7 +142,11 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) sampleCount = maxSampleCount; ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleColorBuffer); +#if PLATFORM(IOS) + ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, GL_RGBA8_OES, width, height); +#else ::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, sampleCount, m_internalColorFormat, width, height); +#endif ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_multisampleColorBuffer); if (m_attrs.stencil || m_attrs.depth) { ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_multisampleDepthStencilBuffer); @@ -143,9 +179,31 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture); ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); ::glBindTexture(GL_TEXTURE_2D, 0); +#if USE(COORDINATED_GRAPHICS_THREADED) + ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture); + ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); + ::glBindTexture(GL_TEXTURE_2D, 0); +#endif } #endif + attachDepthAndStencilBufferIfNeeded(internalDepthStencilFormat, width, height); + + bool mustRestoreFBO = true; + if (m_attrs.antialias) { + ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); + if (m_state.boundFBO == m_multisampleFBO) + mustRestoreFBO = false; + } else { + if (m_state.boundFBO == m_fbo) + mustRestoreFBO = false; + } + + return mustRestoreFBO; +} + +void GraphicsContext3D::attachDepthAndStencilBufferIfNeeded(GLuint internalDepthStencilFormat, int width, int height) +{ if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) { ::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthStencilBuffer); ::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalDepthStencilFormat, width, height); @@ -160,18 +218,6 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) // FIXME: cleanup notImplemented(); } - - bool mustRestoreFBO = true; - if (m_attrs.antialias) { - ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); - if (m_state.boundFBO == m_multisampleFBO) - mustRestoreFBO = false; - } else { - if (m_state.boundFBO == m_fbo) - mustRestoreFBO = false; - } - - return mustRestoreFBO; } void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect) @@ -181,11 +227,20 @@ void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect) TemporaryOpenGLSetting scopedDepth(GL_DEPTH_TEST, GL_FALSE); TemporaryOpenGLSetting scopedStencil(GL_STENCIL_TEST, GL_FALSE); +#if PLATFORM(IOS) + GLint boundFrameBuffer; + ::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundFrameBuffer); +#endif + ::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_multisampleFBO); ::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_fbo); #if PLATFORM(IOS) UNUSED_PARAM(rect); + ::glFlush(); ::glResolveMultisampleFramebufferAPPLE(); + const GLenum discards[] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT }; + ::glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards); + ::glBindFramebuffer(GL_FRAMEBUFFER, boundFrameBuffer); #else IntRect resolveRect = rect; if (rect.isEmpty()) @@ -236,10 +291,29 @@ void GraphicsContext3D::getIntegerv(GC3Denum pname, GC3Dint* value) *value /= 4; break; case MAX_VARYING_VECTORS: - ::glGetIntegerv(GL_MAX_VARYING_FLOATS, value); - *value /= 4; + if (isGLES2Compliant()) { + ASSERT(::glGetError() == GL_NO_ERROR); + ::glGetIntegerv(GL_MAX_VARYING_VECTORS, value); + if (::glGetError() == GL_INVALID_ENUM) { + ::glGetIntegerv(GL_MAX_VARYING_COMPONENTS, value); + *value /= 4; + } + } else { + ::glGetIntegerv(GL_MAX_VARYING_FLOATS, value); + *value /= 4; + } break; #endif + case MAX_TEXTURE_SIZE: + ::glGetIntegerv(MAX_TEXTURE_SIZE, value); + if (getExtensions().requiresRestrictedMaximumTextureSize()) + *value = std::min(4096, *value); + break; + case MAX_CUBE_MAP_TEXTURE_SIZE: + ::glGetIntegerv(MAX_CUBE_MAP_TEXTURE_SIZE, value); + if (getExtensions().requiresRestrictedMaximumTextureSize()) + *value = std::min(1024, *value); + break; default: ::glGetIntegerv(pname, value); } @@ -283,14 +357,15 @@ bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum inte return false; } + GC3Denum openGLFormat = format; GC3Denum openGLInternalFormat = internalformat; +#if !PLATFORM(IOS) if (type == GL_FLOAT) { if (format == GL_RGBA) openGLInternalFormat = GL_RGBA32F_ARB; else if (format == GL_RGB) openGLInternalFormat = GL_RGB32F_ARB; } else if (type == HALF_FLOAT_OES) { -#if !PLATFORM(IOS) if (format == GL_RGBA) openGLInternalFormat = GL_RGBA16F_ARB; else if (format == GL_RGB) @@ -302,9 +377,25 @@ bool GraphicsContext3D::texImage2D(GC3Denum target, GC3Dint level, GC3Denum inte else if (format == GL_LUMINANCE_ALPHA) openGLInternalFormat = GL_LUMINANCE_ALPHA16F_ARB; type = GL_HALF_FLOAT_ARB; + } + + ASSERT(format != Extensions3D::SRGB8_ALPHA8_EXT); + if (format == Extensions3D::SRGB_ALPHA_EXT) + openGLFormat = GL_RGBA; + else if (format == Extensions3D::SRGB_EXT) + openGLFormat = GL_RGB; #endif + + if (m_usingCoreProfile && openGLInternalFormat == ALPHA) { + // We are using a core profile. This means that GL_ALPHA, which is a valid format in WebGL for texImage2D + // is not supported in OpenGL. It needs to be backed with a GL_RED plane. We change the formats to GL_RED + // (both need to be GL_ALPHA in WebGL) and instruct the texture to swizzle the red component values with + // the the alpha component values. + openGLInternalFormat = openGLFormat = RED; + texParameteri(target, TEXTURE_SWIZZLE_A, RED); } - texImage2DDirect(target, level, openGLInternalFormat, width, height, border, format, type, pixels); + + texImage2DDirect(target, level, openGLInternalFormat, width, height, border, openGLFormat, type, pixels); return true; } @@ -328,12 +419,14 @@ void GraphicsContext3D::clearDepth(GC3Dclampf depth) #endif } -Extensions3D* GraphicsContext3D::getExtensions() +#if !PLATFORM(GTK) +Extensions3D& GraphicsContext3D::getExtensions() { if (!m_extensions) - m_extensions = adoptPtr(new Extensions3DOpenGL(this)); - return m_extensions.get(); + m_extensions = std::make_unique<Extensions3DOpenGL>(this, isGLES2Compliant()); + return *m_extensions; } +#endif void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, void* data) { @@ -351,31 +444,6 @@ void GraphicsContext3D::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsi ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); } -#if !PLATFORM(MAC) -void GraphicsContext3D::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) -{ - UNUSED_PARAM(mode); - UNUSED_PARAM(first); - UNUSED_PARAM(count); - UNUSED_PARAM(primcount); -} - -void GraphicsContext3D::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount) -{ - UNUSED_PARAM(mode); - UNUSED_PARAM(count); - UNUSED_PARAM(type); - UNUSED_PARAM(offset); - UNUSED_PARAM(primcount); -} - -void GraphicsContext3D::vertexAttribDivisor(GC3Duint index, GC3Duint divisor) -{ - UNUSED_PARAM(index); - UNUSED_PARAM(divisor); -} -#endif - } -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp index c1356a0e9..1536faf42 100644 --- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp +++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp @@ -13,10 +13,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,7 +28,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" #if PLATFORM(IOS) @@ -40,26 +40,26 @@ #else #include "Extensions3DOpenGL.h" #endif +#include "ANGLEWebKitBridge.h" #include "GraphicsContext.h" #include "ImageBuffer.h" #include "ImageData.h" #include "IntRect.h" #include "IntSize.h" #include "Logging.h" -#include "NotImplemented.h" #include "TemporaryOpenGLSetting.h" +#include "WebGLRenderingContextBase.h" #include <cstring> -#include <runtime/ArrayBuffer.h> -#include <runtime/ArrayBufferView.h> -#include <runtime/Float32Array.h> -#include <runtime/Int32Array.h> -#include <runtime/Uint8Array.h> +#include <wtf/HexNumber.h> #include <wtf/MainThread.h> +#include <wtf/ThreadSpecific.h> #include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> #include <yarr/RegularExpression.h> #if PLATFORM(IOS) #import <OpenGLES/ES2/glext.h> +#import <OpenGLES/ES3/gl.h> // From <OpenGLES/glext.h> #define GL_RGBA32F_ARB 0x8814 #define GL_RGB32F_ARB 0x8815 @@ -67,15 +67,35 @@ #if USE(OPENGL_ES_2) #include "OpenGLESShims.h" #elif PLATFORM(MAC) +#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED #include <OpenGL/gl.h> -#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN) +#include <OpenGL/gl3.h> +#include <OpenGL/gl3ext.h> +#undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED +#elif PLATFORM(GTK) || PLATFORM(WIN) #include "OpenGLShims.h" #endif #endif +using namespace WTF; + namespace WebCore { -static ShaderNameHash* currentNameHashMapForShader; +static ThreadSpecific<ShaderNameHash*>& getCurrentNameHashMapForShader() +{ + static std::once_flag onceFlag; + static ThreadSpecific<ShaderNameHash*>* sharedNameHash; + std::call_once(onceFlag, [] { + sharedNameHash = new ThreadSpecific<ShaderNameHash*>; + }); + + return *sharedNameHash; +} + +static void setCurrentNameHashMapForShader(ShaderNameHash* shaderNameHash) +{ + *getCurrentNameHashMapForShader() = shaderNameHash; +} // Hash function used by the ANGLE translator/compiler to do // symbol name mangling. Since this is a static method, before @@ -90,11 +110,10 @@ static uint64_t nameHashForShader(const char* name, size_t length) CString nameAsCString = CString(name); // Look up name in our local map. - if (currentNameHashMapForShader) { - ShaderNameHash::iterator result = currentNameHashMapForShader->find(nameAsCString); - if (result != currentNameHashMapForShader->end()) - return result->value; - } + ShaderNameHash*& currentNameHashMapForShader = *getCurrentNameHashMapForShader(); + ShaderNameHash::iterator findResult = currentNameHashMapForShader->find(nameAsCString); + if (findResult != currentNameHashMapForShader->end()) + return findResult->value; unsigned hashValue = nameAsCString.hash(); @@ -111,37 +130,39 @@ static uint64_t nameHashForShader(const char* name, size_t length) return result; } -PassRefPtr<GraphicsContext3D> GraphicsContext3D::createForCurrentGLContext() +RefPtr<GraphicsContext3D> GraphicsContext3D::createForCurrentGLContext() { - RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(Attributes(), 0, GraphicsContext3D::RenderToCurrentGLContext)); - return context->m_private ? context.release() : 0; + auto context = adoptRef(*new GraphicsContext3D({ }, 0, GraphicsContext3D::RenderToCurrentGLContext)); +#if USE(TEXTURE_MAPPER) + if (!context->m_texmapLayer) + return nullptr; +#else + if (!context->m_private) + return nullptr; +#endif + return WTFMove(context); } void GraphicsContext3D::validateDepthStencil(const char* packedDepthStencilExtension) { - Extensions3D* extensions = getExtensions(); + Extensions3D& extensions = getExtensions(); if (m_attrs.stencil) { - if (extensions->supports(packedDepthStencilExtension)) { - extensions->ensureEnabled(packedDepthStencilExtension); + if (extensions.supports(packedDepthStencilExtension)) { + extensions.ensureEnabled(packedDepthStencilExtension); // Force depth if stencil is true. m_attrs.depth = true; } else m_attrs.stencil = false; } if (m_attrs.antialias) { - if (!extensions->maySupportMultisampling() || !extensions->supports("GL_ANGLE_framebuffer_multisample") || isGLES2Compliant()) + if (!extensions.supports("GL_ANGLE_framebuffer_multisample") || isGLES2Compliant()) m_attrs.antialias = false; else - extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample"); + extensions.ensureEnabled("GL_ANGLE_framebuffer_multisample"); } } -bool GraphicsContext3D::isResourceSafe() -{ - return false; -} - -void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer, DrawingBuffer*) +void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer) { int rowBytes = m_currentWidth * 4; int totalBytes = rowBytes * m_currentHeight; @@ -165,8 +186,7 @@ void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer, paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight, imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context()); #else - paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight, - imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context()->platformContext()); + paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight, imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context().platformContext()); #endif #if PLATFORM(IOS) @@ -180,14 +200,14 @@ bool GraphicsContext3D::paintCompositedResultsToCanvas(ImageBuffer*) return false; } -PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData(DrawingBuffer*) +RefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData() { // Reading premultiplied alpha would involve unpremultiplying, which is // lossy. if (m_attrs.premultipliedAlpha) - return 0; + return nullptr; - RefPtr<ImageData> imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight)); + auto imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight)); unsigned char* pixels = imageData->data()->data(); int totalBytes = 4 * m_currentWidth * m_currentHeight; @@ -197,7 +217,7 @@ PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData(Drawin for (int i = 0; i < totalBytes; i += 4) std::swap(pixels[i], pixels[i + 2]); - return imageData.release(); + return imageData; } void GraphicsContext3D::prepareTexture() @@ -207,12 +227,28 @@ void GraphicsContext3D::prepareTexture() makeContextCurrent(); +#if !USE(COORDINATED_GRAPHICS_THREADED) TemporaryOpenGLSetting scopedScissor(GL_SCISSOR_TEST, GL_FALSE); TemporaryOpenGLSetting scopedDither(GL_DITHER, GL_FALSE); - +#endif + if (m_attrs.antialias) resolveMultisamplingIfNecessary(); +#if USE(COORDINATED_GRAPHICS_THREADED) + std::swap(m_texture, m_compositorTexture); + std::swap(m_texture, m_intermediateTexture); + ::glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + ::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_texture, 0); + glFlush(); + + if (m_state.boundFBO != m_fbo) + ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_state.boundFBO); + else + ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo); + return; +#endif + ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo); ::glActiveTexture(GL_TEXTURE0); ::glBindTexture(GL_TEXTURE_2D, m_compositorTexture); @@ -221,8 +257,7 @@ void GraphicsContext3D::prepareTexture() ::glActiveTexture(m_state.activeTexture); if (m_state.boundFBO != m_fbo) ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_state.boundFBO); - ::glFinish(); - m_layerComposited = true; + ::glFlush(); } void GraphicsContext3D::readRenderingResults(unsigned char *pixels, int pixelsSize) @@ -271,11 +306,6 @@ void GraphicsContext3D::reshape(int width, int height) markContextChanged(); -#if PLATFORM(EFL) && USE(GRAPHICS_SURFACE) - ::glFlush(); // Make sure all GL calls have been committed before resizing. - createGraphicsSurfaces(IntSize(width, height)); -#endif - m_currentWidth = width; m_currentHeight = height; @@ -334,6 +364,49 @@ void GraphicsContext3D::reshape(int width, int height) ::glFlush(); } +bool GraphicsContext3D::checkVaryingsPacking(Platform3DObject vertexShader, Platform3DObject fragmentShader) const +{ + ASSERT(m_shaderSourceMap.contains(vertexShader)); + ASSERT(m_shaderSourceMap.contains(fragmentShader)); + const auto& vertexEntry = m_shaderSourceMap.find(vertexShader)->value; + const auto& fragmentEntry = m_shaderSourceMap.find(fragmentShader)->value; + + HashMap<String, sh::ShaderVariable> combinedVaryings; + for (const auto& vertexSymbol : vertexEntry.varyingMap) { + const String& symbolName = vertexSymbol.key; + // The varying map includes variables for each index of an array variable. + // We only want a single variable to represent the array. + if (symbolName.endsWith("]")) + continue; + + // Don't count built in varyings. + if (symbolName == "gl_FragCoord" || symbolName == "gl_FrontFacing" || symbolName == "gl_PointCoord") + continue; + + const auto& fragmentSymbol = fragmentEntry.varyingMap.find(symbolName); + if (fragmentSymbol != fragmentEntry.varyingMap.end()) + combinedVaryings.add(symbolName, fragmentSymbol->value); + } + + size_t numVaryings = combinedVaryings.size(); + if (!numVaryings) + return true; + + std::vector<sh::ShaderVariable> variables; + variables.reserve(combinedVaryings.size()); + for (const auto& varyingSymbol : combinedVaryings.values()) + variables.push_back(varyingSymbol); + + GC3Dint maxVaryingVectors = 0; +#if !PLATFORM(IOS) && !((PLATFORM(WIN) || PLATFORM(GTK)) && USE(OPENGL_ES_2)) + GC3Dint maxVaryingFloats = 0; + ::glGetIntegerv(GL_MAX_VARYING_FLOATS, &maxVaryingFloats); + maxVaryingVectors = maxVaryingFloats / 4; +#else + ::glGetIntegerv(MAX_VARYING_VECTORS, &maxVaryingVectors); +#endif + return ShCheckVariablesWithinPackingLimits(maxVaryingVectors, variables); +} bool GraphicsContext3D::precisionsMatch(Platform3DObject vertexShader, Platform3DObject fragmentShader) const { @@ -342,13 +415,16 @@ bool GraphicsContext3D::precisionsMatch(Platform3DObject vertexShader, Platform3 const auto& vertexEntry = m_shaderSourceMap.find(vertexShader)->value; const auto& fragmentEntry = m_shaderSourceMap.find(fragmentShader)->value; - HashMap<String, ShPrecisionType> vertexSymbolPrecisionMap; + HashMap<String, sh::GLenum> vertexSymbolPrecisionMap; - for (const auto& entry : vertexEntry.uniformMap) - vertexSymbolPrecisionMap.add(entry.value.mappedName, entry.value.precision); + for (const auto& entry : vertexEntry.uniformMap) { + const std::string& mappedName = entry.value.mappedName; + vertexSymbolPrecisionMap.add(String(mappedName.c_str(), mappedName.length()), entry.value.precision); + } for (const auto& entry : fragmentEntry.uniformMap) { - const auto& vertexSymbol = vertexSymbolPrecisionMap.find(entry.value.mappedName); + const std::string& mappedName = entry.value.mappedName; + const auto& vertexSymbol = vertexSymbolPrecisionMap.find(String(mappedName.c_str(), mappedName.length())); if (vertexSymbol != vertexSymbolPrecisionMap.end() && vertexSymbol->value != entry.value.precision) return false; } @@ -373,6 +449,7 @@ void GraphicsContext3D::attachShader(Platform3DObject program, Platform3DObject ASSERT(program); ASSERT(shader); makeContextCurrent(); + m_shaderProgramSymbolCountMap.remove(program); ::glAttachShader(program, shader); } @@ -470,6 +547,38 @@ void GraphicsContext3D::bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsi ::glBufferSubData(target, offset, size, data); } +#if PLATFORM(MAC) || PLATFORM(IOS) +void* GraphicsContext3D::mapBufferRange(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr length, GC3Dbitfield access) +{ + makeContextCurrent(); + return ::glMapBufferRange(target, offset, length, access); +} + +GC3Dboolean GraphicsContext3D::unmapBuffer(GC3Denum target) +{ + makeContextCurrent(); + return ::glUnmapBuffer(target); +} + +void GraphicsContext3D::copyBufferSubData(GC3Denum readTarget, GC3Denum writeTarget, GC3Dintptr readOffset, GC3Dintptr writeOffset, GC3Dsizeiptr size) +{ + makeContextCurrent(); + ::glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); +} + +void GraphicsContext3D::texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height) +{ + makeContextCurrent(); + ::glTexStorage2D(target, levels, internalformat, width, height); +} + +void GraphicsContext3D::texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth) +{ + makeContextCurrent(); + ::glTexStorage3D(target, levels, internalformat, width, height, depth); +} +#endif + GC3Denum GraphicsContext3D::checkFramebufferStatus(GC3Denum target) { makeContextCurrent(); @@ -512,15 +621,15 @@ void GraphicsContext3D::compileShader(Platform3DObject shader) ANGLEResources.HashFunction = nameHashForShader; if (!nameHashMapForShaders) - nameHashMapForShaders = adoptPtr(new ShaderNameHash); - currentNameHashMapForShader = nameHashMapForShaders.get(); + nameHashMapForShaders = std::make_unique<ShaderNameHash>(); + setCurrentNameHashMapForShader(nameHashMapForShaders.get()); m_compiler.setResources(ANGLEResources); String translatedShaderSource = m_extensions->getTranslatedShaderSourceANGLE(shader); ANGLEResources.HashFunction = previousHashFunction; m_compiler.setResources(ANGLEResources); - currentNameHashMapForShader = nullptr; + setCurrentNameHashMapForShader(nullptr); if (!translatedShaderSource.length()) return; @@ -560,8 +669,6 @@ void GraphicsContext3D::compileShader(Platform3DObject shader) entry.isValid = false; LOG(WebGL, "Error: shader translator produced a shader that OpenGL would not compile."); } - - m_shaderSymbolCount = nullptr; } void GraphicsContext3D::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border) @@ -611,6 +718,7 @@ void GraphicsContext3D::detachShader(Platform3DObject program, Platform3DObject ASSERT(program); ASSERT(shader); makeContextCurrent(); + m_shaderProgramSymbolCountMap.remove(program); ::glDetachShader(program, shader); } @@ -630,12 +738,14 @@ void GraphicsContext3D::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count { makeContextCurrent(); ::glDrawArrays(mode, first, count); + checkGPUStatusIfNecessary(); } void GraphicsContext3D::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset) { makeContextCurrent(); ::glDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset))); + checkGPUStatusIfNecessary(); } void GraphicsContext3D::enable(GC3Denum cap) @@ -718,8 +828,15 @@ bool GraphicsContext3D::getActiveAttribImpl(Platform3DObject program, GC3Duint i bool GraphicsContext3D::getActiveAttrib(Platform3DObject program, GC3Duint index, ActiveInfo& info) { - ASSERT(!m_shaderSymbolCount || index < m_shaderSymbolCount->filteredToActualAttributeIndexMap.size()); - GC3Duint rawIndex = (m_shaderSymbolCount) ? m_shaderSymbolCount->filteredToActualAttributeIndexMap[index] : index; + GC3Dint symbolCount; + auto result = m_shaderProgramSymbolCountMap.find(program); + if (result == m_shaderProgramSymbolCountMap.end()) { + getNonBuiltInActiveSymbolCount(program, GraphicsContext3D::ACTIVE_ATTRIBUTES, &symbolCount); + result = m_shaderProgramSymbolCountMap.find(program); + } + + ActiveShaderSymbolCounts& symbolCounts = result->value; + GC3Duint rawIndex = (index < symbolCounts.filteredToActualAttributeIndexMap.size()) ? symbolCounts.filteredToActualAttributeIndexMap[index] : -1; return getActiveAttribImpl(program, rawIndex, info); } @@ -758,8 +875,15 @@ bool GraphicsContext3D::getActiveUniformImpl(Platform3DObject program, GC3Duint bool GraphicsContext3D::getActiveUniform(Platform3DObject program, GC3Duint index, ActiveInfo& info) { - ASSERT(!m_shaderSymbolCount || index < m_shaderSymbolCount->filteredToActualUniformIndexMap.size()); - GC3Duint rawIndex = (m_shaderSymbolCount) ? m_shaderSymbolCount->filteredToActualUniformIndexMap[index] : index; + GC3Dint symbolCount; + auto result = m_shaderProgramSymbolCountMap.find(program); + if (result == m_shaderProgramSymbolCountMap.end()) { + getNonBuiltInActiveSymbolCount(program, GraphicsContext3D::ACTIVE_UNIFORMS, &symbolCount); + result = m_shaderProgramSymbolCountMap.find(program); + } + + ActiveShaderSymbolCounts& symbolCounts = result->value; + GC3Duint rawIndex = (index < symbolCounts.filteredToActualUniformIndexMap.size()) ? symbolCounts.filteredToActualUniformIndexMap[index] : -1; return getActiveUniformImpl(program, rawIndex, info); } @@ -774,10 +898,21 @@ void GraphicsContext3D::getAttachedShaders(Platform3DObject program, GC3Dsizei m ::glGetAttachedShaders(program, maxCount, count, shaders); } +static String generateHashedName(const String& name) +{ + if (name.isEmpty()) + return name; + uint64_t number = nameHashForShader(name.utf8().data(), name.length()); + StringBuilder builder; + builder.appendLiteral("webgl_"); + appendUnsigned64AsHex(number, builder, Lowercase); + return builder.toString(); +} + String GraphicsContext3D::mappedSymbolName(Platform3DObject program, ANGLEShaderSymbolType symbolType, const String& name) { - GC3Dsizei count; - Platform3DObject shaders[2]; + GC3Dsizei count = 0; + Platform3DObject shaders[2] = { }; getAttachedShaders(program, 2, &count, shaders); for (GC3Dsizei i = 0; i < count; ++i) { @@ -787,9 +922,28 @@ String GraphicsContext3D::mappedSymbolName(Platform3DObject program, ANGLEShader const ShaderSymbolMap& symbolMap = result->value.symbolMap(symbolType); ShaderSymbolMap::const_iterator symbolEntry = symbolMap.find(name); - if (symbolEntry != symbolMap.end()) - return symbolEntry->value.mappedName; + if (symbolEntry != symbolMap.end()) { + const std::string& mappedName = symbolEntry->value.mappedName; + return String(mappedName.c_str(), mappedName.length()); + } } + + if (symbolType == SHADER_SYMBOL_TYPE_ATTRIBUTE && !name.isEmpty()) { + // Attributes are a special case: they may be requested before any shaders have been compiled, + // and aren't even required to be used in any shader program. + if (!nameHashMapForShaders) + nameHashMapForShaders = std::make_unique<ShaderNameHash>(); + setCurrentNameHashMapForShader(nameHashMapForShaders.get()); + + String generatedName = generateHashedName(name); + + setCurrentNameHashMapForShader(nullptr); + + m_possiblyUnusedAttributeMap.set(generatedName, name); + + return generatedName; + } + return name; } @@ -806,10 +960,20 @@ String GraphicsContext3D::originalSymbolName(Platform3DObject program, ANGLEShad const ShaderSymbolMap& symbolMap = result->value.symbolMap(symbolType); for (const auto& symbolEntry : symbolMap) { - if (symbolEntry.value.mappedName == name) + if (name == symbolEntry.value.mappedName.c_str()) return symbolEntry.key; } } + + if (symbolType == SHADER_SYMBOL_TYPE_ATTRIBUTE && !name.isEmpty()) { + // Attributes are a special case: they may be requested before any shaders have been compiled, + // and aren't even required to be used in any shader program. + + const auto& cached = m_possiblyUnusedAttributeMap.find(name); + if (cached != m_possiblyUnusedAttributeMap.end()) + return cached->value; + } + return name; } @@ -823,7 +987,7 @@ String GraphicsContext3D::mappedSymbolName(Platform3DObject shaders[2], size_t c const ShaderSymbolMap& symbolMap = result->value.symbolMap(static_cast<enum ANGLEShaderSymbolType>(symbolType)); for (const auto& symbolEntry : symbolMap) { - if (symbolEntry.value.mappedName == name) + if (name == symbolEntry.value.mappedName.c_str()) return symbolEntry.key; } } @@ -843,18 +1007,38 @@ int GraphicsContext3D::getAttribLocation(Platform3DObject program, const String& return ::glGetAttribLocation(program, mappedName.utf8().data()); } -GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes() +GraphicsContext3DAttributes GraphicsContext3D::getContextAttributes() { return m_attrs; } +bool GraphicsContext3D::moveErrorsToSyntheticErrorList() +{ + makeContextCurrent(); + bool movedAnError = false; + + // Set an arbitrary limit of 100 here to avoid creating a hang if + // a problem driver has a bug that causes it to never clear the error. + // Otherwise, we would just loop until we got NO_ERROR. + for (unsigned i = 0; i < 100; ++i) { + GC3Denum error = glGetError(); + if (error == NO_ERROR) + break; + m_syntheticErrors.add(error); + movedAnError = true; + } + + return movedAnError; +} + GC3Denum GraphicsContext3D::getError() { - if (m_syntheticErrors.size() > 0) { - ListHashSet<GC3Denum>::iterator iter = m_syntheticErrors.begin(); - GC3Denum err = *iter; - m_syntheticErrors.remove(iter); - return err; + if (!m_syntheticErrors.isEmpty()) { + // Need to move the current errors to the synthetic error list in case + // that error is already there, since the expected behavior of both + // glGetError and getError is to only report each error code once. + moveErrorsToSyntheticErrorList(); + return m_syntheticErrors.takeFirst(); } makeContextCurrent(); @@ -1228,6 +1412,57 @@ void GraphicsContext3D::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsize ::glViewport(x, y, width, height); } +Platform3DObject GraphicsContext3D::createVertexArray() +{ + makeContextCurrent(); + GLuint array = 0; +#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) + glGenVertexArrays(1, &array); +#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object + glGenVertexArraysAPPLE(1, &array); +#endif + return array; +} + +void GraphicsContext3D::deleteVertexArray(Platform3DObject array) +{ + if (!array) + return; + + makeContextCurrent(); +#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) + glDeleteVertexArrays(1, &array); +#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object + glDeleteVertexArraysAPPLE(1, &array); +#endif +} + +GC3Dboolean GraphicsContext3D::isVertexArray(Platform3DObject array) +{ + if (!array) + return GL_FALSE; + + makeContextCurrent(); +#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) + return glIsVertexArray(array); +#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object + return glIsVertexArrayAPPLE(array); +#endif + return GL_FALSE; +} + +void GraphicsContext3D::bindVertexArray(Platform3DObject array) +{ + makeContextCurrent(); +#if !USE(OPENGL_ES_2) && (PLATFORM(GTK) || PLATFORM(WIN) || PLATFORM(IOS)) + glBindVertexArray(array); +#elif defined(GL_APPLE_vertex_array_object) && GL_APPLE_vertex_array_object + glBindVertexArrayAPPLE(array); +#else + UNUSED_PARAM(array); +#endif +} + void GraphicsContext3D::getBooleanv(GC3Denum pname, GC3Dboolean* value) { makeContextCurrent(); @@ -1245,6 +1480,15 @@ void GraphicsContext3D::getFloatv(GC3Denum pname, GC3Dfloat* value) makeContextCurrent(); ::glGetFloatv(pname, value); } + +void GraphicsContext3D::getInteger64v(GC3Denum pname, GC3Dint64* value) +{ + UNUSED_PARAM(pname); + makeContextCurrent(); + *value = 0; + // FIXME 141178: Before enabling this we must first switch over to using gl3.h and creating and initialing the WebGL2 context using OpenGL ES 3.0. + // ::glGetInteger64v(pname, value); +} void GraphicsContext3D::getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum pname, GC3Dint* value) { @@ -1267,13 +1511,14 @@ void GraphicsContext3D::getNonBuiltInActiveSymbolCount(Platform3DObject program, return; makeContextCurrent(); - - if (m_shaderSymbolCount) { - *value = m_shaderSymbolCount->countForType(pname); + const auto& result = m_shaderProgramSymbolCountMap.find(program); + if (result != m_shaderProgramSymbolCountMap.end()) { + *value = result->value.countForType(pname); return; } - m_shaderSymbolCount = std::make_unique<ActiveShaderSymbolCounts>(); + m_shaderProgramSymbolCountMap.set(program, ActiveShaderSymbolCounts()); + ActiveShaderSymbolCounts& symbolCounts = m_shaderProgramSymbolCountMap.find(program)->value; // Retrieve the active attributes, build a filtered count, and a mapping of // our internal attributes indexes to the real unfiltered indexes inside OpenGL. @@ -1285,7 +1530,7 @@ void GraphicsContext3D::getNonBuiltInActiveSymbolCount(Platform3DObject program, if (info.name.startsWith("gl_")) continue; - m_shaderSymbolCount->filteredToActualAttributeIndexMap.append(i); + symbolCounts.filteredToActualAttributeIndexMap.append(i); } // Do the same for uniforms. @@ -1297,10 +1542,10 @@ void GraphicsContext3D::getNonBuiltInActiveSymbolCount(Platform3DObject program, if (info.name.startsWith("gl_")) continue; - m_shaderSymbolCount->filteredToActualUniformIndexMap.append(i); + symbolCounts.filteredToActualUniformIndexMap.append(i); } - *value = m_shaderSymbolCount->countForType(pname); + *value = symbolCounts.countForType(pname); } String GraphicsContext3D::getUnmangledInfoLog(Platform3DObject shaders[2], GC3Dsizei count, const String& log) @@ -1309,7 +1554,7 @@ String GraphicsContext3D::getUnmangledInfoLog(Platform3DObject shaders[2], GC3Ds JSC::Yarr::RegularExpression regExp("webgl_[0123456789abcdefABCDEF]+", TextCaseSensitive); - String processedLog; + StringBuilder processedLog; int startFrom = 0; int matchedLength = 0; @@ -1329,8 +1574,8 @@ String GraphicsContext3D::getUnmangledInfoLog(Platform3DObject shaders[2], GC3Ds processedLog.append(log.substring(startFrom, log.length() - startFrom)); - LOG(WebGL, "-->: %s", processedLog.utf8().data()); - return processedLog; + LOG(WebGL, "-->: %s", processedLog.toString().utf8().data()); + return processedLog.toString(); } String GraphicsContext3D::getProgramInfoLog(Platform3DObject program) @@ -1501,6 +1746,12 @@ void GraphicsContext3D::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xo type = GL_HALF_FLOAT_ARB; #endif + if (m_usingCoreProfile && format == ALPHA) { + // We are using a core profile. This means that GL_ALPHA, which is a valid format in WebGL for texSubImage2D + // is not supported in OpenGL. We are using GL_RED to back GL_ALPHA, so do it here as well. + format = RED; + } + // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size. ::glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels); } @@ -1606,6 +1857,10 @@ void GraphicsContext3D::deleteTexture(Platform3DObject texture) void GraphicsContext3D::synthesizeGLError(GC3Denum error) { + // Need to move the current errors to the synthetic error list to + // preserve the order of errors, so a caller to getError will get + // any errors from glError before the error we are synthesizing. + moveErrorsToSyntheticErrorList(); m_syntheticErrors.add(error); } @@ -1624,12 +1879,43 @@ bool GraphicsContext3D::layerComposited() const return m_layerComposited; } +void GraphicsContext3D::forceContextLost() +{ +#if ENABLE(WEBGL) + if (m_webglContext) + m_webglContext->forceLostContext(WebGLRenderingContextBase::RealLostContext); +#endif +} + +void GraphicsContext3D::recycleContext() +{ +#if ENABLE(WEBGL) + if (m_webglContext) + m_webglContext->recycleContext(); +#endif +} + void GraphicsContext3D::texImage2DDirect(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels) { makeContextCurrent(); ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); } +void GraphicsContext3D::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) +{ + getExtensions().drawArraysInstanced(mode, first, count, primcount); +} + +void GraphicsContext3D::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount) +{ + getExtensions().drawElementsInstanced(mode, count, type, offset, primcount); +} + +void GraphicsContext3D::vertexAttribDivisor(GC3Duint index, GC3Duint divisor) +{ + getExtensions().vertexAttribDivisor(index, divisor); +} + } -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp index 546b2d433..6b608c66d 100644 --- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp +++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLES.cpp @@ -13,10 +13,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,7 +28,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "GraphicsContext3D.h" @@ -37,6 +37,12 @@ #include "IntSize.h" #include "NotImplemented.h" +#if PLATFORM(WIN) +#include <GLSLANG/ShaderLang.h> +#else +#include <ANGLE/ShaderLang.h> +#endif + namespace WebCore { void GraphicsContext3D::releaseShaderCompiler() @@ -90,7 +96,7 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) // We don't allow the logic where stencil is required and depth is not. // See GraphicsContext3D::validateAttributes. - bool supportPackedDepthStencilBuffer = (m_attrs.stencil || m_attrs.depth) && getExtensions()->supports("GL_OES_packed_depth_stencil"); + bool supportPackedDepthStencilBuffer = (m_attrs.stencil || m_attrs.depth) && getExtensions().supports("GL_OES_packed_depth_stencil"); // Resize regular FBO. bool mustRestoreFBO = false; @@ -110,29 +116,64 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) ::glBindTexture(GL_TEXTURE_2D, 0); } - // We don't support antialiasing yet. See GraphicsContext3D::validateAttributes. - ASSERT(!m_attrs.antialias); - - if (m_attrs.stencil || m_attrs.depth) { - // Use a 24 bit depth buffer where we know we have it. - if (supportPackedDepthStencilBuffer) { - ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); - ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height); - if (m_attrs.stencil) - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); - if (m_attrs.depth) - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); - ::glBindRenderbuffer(GL_RENDERBUFFER, 0); - } else { - if (m_attrs.stencil) { - ::glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer); - ::glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer); +#if USE(COORDINATED_GRAPHICS_THREADED) + ::glBindTexture(GL_TEXTURE_2D, m_intermediateTexture); + ::glTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); + ::glBindTexture(GL_TEXTURE_2D, 0); +#endif + + Extensions3DOpenGLES& extensions = static_cast<Extensions3DOpenGLES&>(getExtensions()); + if (extensions.isImagination() && m_attrs.antialias) { + GLint maxSampleCount; + ::glGetIntegerv(Extensions3D::MAX_SAMPLES_IMG, &maxSampleCount); + GLint sampleCount = std::min(8, maxSampleCount); + + extensions.framebufferTexture2DMultisampleIMG(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0, sampleCount); + + if (m_attrs.stencil || m_attrs.depth) { + // Use a 24 bit depth buffer where we know we have it. + if (supportPackedDepthStencilBuffer) { + ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); + extensions.renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH24_STENCIL8_OES, width, height); + if (m_attrs.stencil) + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); + if (m_attrs.depth) + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); + } else { + if (m_attrs.stencil) { + ::glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer); + extensions.renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_STENCIL_INDEX8, width, height); + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer); + } + if (m_attrs.depth) { + ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); + extensions.renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH_COMPONENT16, width, height); + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); + } } - if (m_attrs.depth) { - ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); - ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); + ::glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + } else { + if (m_attrs.stencil || m_attrs.depth) { + // Use a 24 bit depth buffer where we know we have it. + if (supportPackedDepthStencilBuffer) { + ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); + ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height); + if (m_attrs.stencil) + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); + if (m_attrs.depth) + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); + } else { + if (m_attrs.stencil) { + ::glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer); + ::glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer); + } + if (m_attrs.depth) { + ::glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); + ::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); + } } ::glBindRenderbuffer(GL_RENDERBUFFER, 0); } @@ -145,7 +186,7 @@ bool GraphicsContext3D::reshapeFBOs(const IntSize& size) return mustRestoreFBO; } -void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect& rect) +void GraphicsContext3D::resolveMultisamplingIfNecessary(const IntRect&) { // FIXME: We don't support antialiasing yet. notImplemented(); @@ -187,11 +228,8 @@ void GraphicsContext3D::validateAttributes() { validateDepthStencil("GL_OES_packed_depth_stencil"); - if (m_attrs.antialias) { - Extensions3D* extensions = getExtensions(); - if (!extensions->supports("GL_IMG_multisampled_render_to_texture")) - m_attrs.antialias = false; - } + if (m_attrs.antialias && !getExtensions().supports("GL_IMG_multisampled_render_to_texture")) + m_attrs.antialias = false; } void GraphicsContext3D::depthRange(GC3Dclampf zNear, GC3Dclampf zFar) @@ -206,36 +244,171 @@ void GraphicsContext3D::clearDepth(GC3Dclampf depth) ::glClearDepthf(depth); } -void GraphicsContext3D::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount) +#if !PLATFORM(GTK) +Extensions3D& GraphicsContext3D::getExtensions() +{ + if (!m_extensions) + m_extensions = std::make_unique<Extensions3DOpenGLES>(this, isGLES2Compliant()); + return *m_extensions; +} +#endif + +#if PLATFORM(WIN) && !USE(CAIRO) +RefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3DAttributes attributes, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) { - UNUSED_PARAM(mode); - UNUSED_PARAM(first); - UNUSED_PARAM(count); - UNUSED_PARAM(primcount); + // This implementation doesn't currently support rendering directly to the HostWindow. + if (renderStyle == RenderDirectlyToHostWindow) + return nullptr; + + static bool initialized = false; + static bool success = true; + if (!initialized) { +#if !USE(OPENGL_ES_2) + success = initializeOpenGLShims(); +#endif + initialized = true; + } + if (!success) + return nullptr; + + return adoptRef(new GraphicsContext3D(attributes, hostWindow, renderStyle)); } -void GraphicsContext3D::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, GC3Dsizei primcount) +GraphicsContext3D::GraphicsContext3D(GraphicsContext3DAttributes attributes, HostWindow*, GraphicsContext3D::RenderStyle renderStyle) + : m_currentWidth(0) + , m_currentHeight(0) + , m_compiler(isGLES2Compliant() ? SH_ESSL_OUTPUT : SH_GLSL_COMPATIBILITY_OUTPUT) + , m_attrs(attributes) + , m_texture(0) + , m_fbo(0) + , m_depthStencilBuffer(0) + , m_multisampleFBO(0) + , m_multisampleDepthStencilBuffer(0) + , m_multisampleColorBuffer(0) + , m_private(std::make_unique<GraphicsContext3DPrivate>(this, renderStyle)) { - UNUSED_PARAM(mode); - UNUSED_PARAM(count); - UNUSED_PARAM(type); - UNUSED_PARAM(offset); - UNUSED_PARAM(primcount); + makeContextCurrent(); + + validateAttributes(); + + if (renderStyle == RenderOffscreen) { + // Create a texture to render into. + ::glGenTextures(1, &m_texture); + ::glBindTexture(GL_TEXTURE_2D, m_texture); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ::glBindTexture(GL_TEXTURE_2D, 0); + + // Create an FBO. + ::glGenFramebuffers(1, &m_fbo); + ::glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + m_state.boundFBO = m_fbo; + if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) + ::glGenRenderbuffers(1, &m_depthStencilBuffer); + + // Create a multisample FBO. + if (m_attrs.antialias) { + ::glGenFramebuffers(1, &m_multisampleFBO); + ::glBindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); + m_state.boundFBO = m_multisampleFBO; + ::glGenRenderbuffers(1, &m_multisampleColorBuffer); + if (m_attrs.stencil || m_attrs.depth) + ::glGenRenderbuffers(1, &m_multisampleDepthStencilBuffer); + } + } + + // ANGLE initialization. + ShBuiltInResources ANGLEResources; + ShInitBuiltInResources(&ANGLEResources); + + getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs); + getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors); + getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors); + getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits); + getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits); + getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits); + getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors); + + // Always set to 1 for OpenGL ES. + ANGLEResources.MaxDrawBuffers = 1; + + GC3Dint range[2], precision; + getShaderPrecisionFormat(GraphicsContext3D::FRAGMENT_SHADER, GraphicsContext3D::HIGH_FLOAT, range, &precision); + ANGLEResources.FragmentPrecisionHigh = (range[0] || range[1] || precision); + + m_compiler.setResources(ANGLEResources); + +#if !USE(OPENGL_ES_2) + ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + ::glEnable(GL_POINT_SPRITE); +#endif + + ::glClearColor(0, 0, 0, 0); } -void GraphicsContext3D::vertexAttribDivisor(GC3Duint index, GC3Duint divisor) +GraphicsContext3D::~GraphicsContext3D() { - UNUSED_PARAM(index); - UNUSED_PARAM(divisor); + makeContextCurrent(); + ::glDeleteTextures(1, &m_texture); + if (m_attrs.antialias) { + ::glDeleteRenderbuffers(1, &m_multisampleColorBuffer); + if (m_attrs.stencil || m_attrs.depth) + ::glDeleteRenderbuffers(1, &m_multisampleDepthStencilBuffer); + ::glDeleteFramebuffers(1, &m_multisampleFBO); + } else { + if (m_attrs.stencil || m_attrs.depth) + ::glDeleteRenderbuffers(1, &m_depthStencilBuffer); + } + ::glDeleteFramebuffers(1, &m_fbo); } -Extensions3D* GraphicsContext3D::getExtensions() +void GraphicsContext3D::setContextLostCallback(std::unique_ptr<ContextLostCallback>) { - if (!m_extensions) - m_extensions = adoptPtr(new Extensions3DOpenGLES(this)); - return m_extensions.get(); } +void GraphicsContext3D::setErrorMessageCallback(std::unique_ptr<ErrorMessageCallback>) +{ +} + +bool GraphicsContext3D::makeContextCurrent() +{ + if (!m_private) + return false; + return m_private->makeContextCurrent(); +} + +void GraphicsContext3D::checkGPUStatusIfNecessary() +{ +} + +PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D() +{ + return m_private->platformContext(); +} + +Platform3DObject GraphicsContext3D::platformTexture() const +{ + return m_texture; +} + +bool GraphicsContext3D::isGLES2Compliant() const +{ +#if USE(OPENGL_ES_2) + return true; +#else + return false; +#endif +} + +PlatformLayer* GraphicsContext3D::platformLayer() const +{ + return m_webGLLayer->platformLayer(); +} +#endif + } -#endif // USE(3D_GRAPHICS) +#endif // ENABLE(GRAPHICS_CONTEXT_3D) diff --git a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp index a0163770c..7438d9e14 100644 --- a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp +++ b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.cpp @@ -26,7 +26,7 @@ #include "config.h" -#if USE(3D_GRAPHICS) +#if ENABLE(GRAPHICS_CONTEXT_3D) #include "TemporaryOpenGLSetting.h" #if USE(OPENGL_ES_2) @@ -36,7 +36,7 @@ #include <OpenGLES/ES2/gl.h> #elif PLATFORM(MAC) #include <OpenGL/gl.h> -#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN) +#elif PLATFORM(GTK) || PLATFORM(WIN) #include "OpenGLShims.h" #endif diff --git a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h index 833f8b6be..cb51695b7 100644 --- a/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h +++ b/Source/WebCore/platform/graphics/opengl/TemporaryOpenGLSetting.h @@ -38,7 +38,7 @@ namespace WebCore { // value upon destruction, making it an alternative to checking, clearing, and resetting each flag // at all of a block's exit points. // -// Based on WTF::TemporaryChange<> +// Based on WTF::SetForScope<> class TemporaryOpenGLSetting { WTF_MAKE_NONCOPYABLE(TemporaryOpenGLSetting); diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp new file mode 100644 index 000000000..e1b56fab4 --- /dev/null +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Frederic Wang (fred.wang@free.fr). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OpenTypeCG.h" + +#include "OpenTypeTypes.h" + +namespace WebCore { +namespace OpenType { + +#if PLATFORM(WIN) +static const unsigned long kCTFontTableOS2 = 'OS/2'; +#endif + +bool fontHasMathTable(CTFontRef ctFont) +{ + RetainPtr<CFArrayRef> tableTags = adoptCF(CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions)); + if (!tableTags) + return false; + CFIndex numTables = CFArrayGetCount(tableTags.get()); + for (CFIndex index = 0; index < numTables; ++index) { + CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tableTags.get(), index); + if (tag == 'MATH') + return true; + } + return false; +} + +static inline short readShortFromTable(const UInt8* os2Data, CFIndex offset) +{ + return *(reinterpret_cast<const OpenType::Int16*>(os2Data + offset)); +} + +bool tryGetTypoMetrics(CTFontRef font, short& ascent, short& descent, short& lineGap) +{ + bool result = false; + if (auto os2Table = adoptCF(CTFontCopyTable(font, kCTFontTableOS2, kCTFontTableOptionNoOptions))) { + // For the structure of the OS/2 table, see + // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6OS2.html + const CFIndex fsSelectionOffset = 16 * 2 + 10 + 4 * 4 + 4 * 1; + const CFIndex sTypoAscenderOffset = fsSelectionOffset + 3 * 2; + const CFIndex sTypoDescenderOffset = sTypoAscenderOffset + 2; + const CFIndex sTypoLineGapOffset = sTypoDescenderOffset + 2; + if (CFDataGetLength(os2Table.get()) >= sTypoLineGapOffset + 2) { + const UInt8* os2Data = CFDataGetBytePtr(os2Table.get()); + // We test the use typo bit on the least significant byte of fsSelection. + const UInt8 useTypoMetricsMask = 1 << 7; + if (*(os2Data + fsSelectionOffset + 1) & useTypoMetricsMask) { + ascent = readShortFromTable(os2Data, sTypoAscenderOffset); + descent = readShortFromTable(os2Data, sTypoDescenderOffset); + lineGap = readShortFromTable(os2Data, sTypoLineGapOffset); + result = true; + } + } + } + return result; +} + +} // namespace OpenType +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeCG.h b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.h new file mode 100644 index 000000000..975cdaf05 --- /dev/null +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeCG.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Frederic Wang (fred.wang@free.fr). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OpenTypeCG_h +#define OpenTypeCG_h + +#include <CoreGraphics/CoreGraphics.h> +#include <CoreText/CoreText.h> + +#if PLATFORM(WIN) +#include "CoreTextSPIWin.h" +#endif + +namespace WebCore { +namespace OpenType { + +bool fontHasMathTable(CTFontRef); +bool tryGetTypoMetrics(CTFontRef, short& ascent, short& descent, short& lineGap); + +} // namespace OpenType +} // namespace WebCore +#endif // OpenTypeCG_h diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp new file mode 100644 index 000000000..c2d50a404 --- /dev/null +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2014 Frederic Wang (fred.wang@free.fr). All rights reserved. + * Copyright (C) 2016 Igalia S.L. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OpenTypeMathData.h" + +#include "Font.h" +#include "FontPlatformData.h" +#if ENABLE(OPENTYPE_MATH) +#include "OpenTypeTypes.h" +#endif +#include "SharedBuffer.h" + +using namespace std; + +namespace WebCore { + +#if ENABLE(OPENTYPE_MATH) +namespace OpenType { + +#if PLATFORM(COCOA) +const FourCharCode MATHTag = 'MATH'; +#else +const uint32_t MATHTag = OT_MAKE_TAG('M', 'A', 'T', 'H'); +#endif + +#pragma pack(1) + +struct MathValueRecord { + OpenType::Int16 value; + OpenType::Offset deviceTableOffset; +}; + +struct MathConstants { + OpenType::Int16 intConstants[OpenTypeMathData::ScriptScriptPercentScaleDown - OpenTypeMathData::ScriptPercentScaleDown + 1]; + OpenType::UInt16 uIntConstants[OpenTypeMathData::DisplayOperatorMinHeight - OpenTypeMathData::DelimitedSubFormulaMinHeight + 1]; + OpenType::MathValueRecord mathValuesConstants[OpenTypeMathData::RadicalKernAfterDegree - OpenTypeMathData::MathLeading + 1]; + OpenType::UInt16 radicalDegreeBottomRaisePercent; +}; + +struct MathItalicsCorrectionInfo : TableWithCoverage { + OpenType::Offset coverageOffset; + OpenType::UInt16 italicsCorrectionCount; + OpenType::MathValueRecord italicsCorrection[1]; // There are italicsCorrectionCount italic correction values. + + int16_t getItalicCorrection(const SharedBuffer& buffer, Glyph glyph) const + { + uint16_t count = uint16_t(italicsCorrectionCount); + if (!isValidEnd(buffer, &italicsCorrection[count])) + return 0; + + uint16_t offset = coverageOffset; + if (!offset) + return 0; + const CoverageTable* coverage = validateOffset<CoverageTable>(buffer, offset); + if (!coverage) + return 0; + + // We determine the index in the italicsCorrection table. + uint32_t i; + if (!getCoverageIndex(buffer, coverage, glyph, i) || i >= count) + return 0; + + return int16_t(italicsCorrection[i].value); + } +}; + +struct MathGlyphInfo : TableWithCoverage { + OpenType::Offset mathItalicsCorrectionInfoOffset; + OpenType::Offset mathTopAccentAttachmentOffset; + OpenType::Offset extendedShapeCoverageOffset; + OpenType::Offset mathKernInfoOffset; + + const MathItalicsCorrectionInfo* mathItalicsCorrectionInfo(const SharedBuffer& buffer) const + { + uint16_t offset = mathItalicsCorrectionInfoOffset; + if (offset) + return validateOffset<MathItalicsCorrectionInfo>(buffer, offset); + return nullptr; + } +}; + +struct GlyphAssembly : TableBase { + OpenType::MathValueRecord italicsCorrection; + OpenType::UInt16 partCount; + struct GlyphPartRecord { + OpenType::GlyphID glyph; + OpenType::UInt16 startConnectorLength; + OpenType::UInt16 endConnectorLength; + OpenType::UInt16 fullAdvance; + OpenType::UInt16 partFlags; + } partRecords[1]; // There are partCount GlyphPartRecord's. + + // PartFlags enumeration currently uses only one bit: + // 0x0001 If set, the part can be skipped or repeated. + // 0xFFFE Reserved. + enum { + PartFlagsExtender = 0x01 + }; + + void getAssemblyParts(const SharedBuffer& buffer, Vector<OpenTypeMathData::AssemblyPart>& assemblyParts) const + { + uint16_t count = partCount; + if (!isValidEnd(buffer, &partRecords[count])) + return; + assemblyParts.resize(count); + for (uint16_t i = 0; i < count; i++) { + assemblyParts[i].glyph = partRecords[i].glyph; + uint16_t flag = partRecords[i].partFlags; + assemblyParts[i].isExtender = flag & PartFlagsExtender; + } + } + +}; + +struct MathGlyphConstruction : TableBase { + OpenType::Offset glyphAssemblyOffset; + OpenType::UInt16 variantCount; + struct MathGlyphVariantRecord { + OpenType::GlyphID variantGlyph; + OpenType::UInt16 advanceMeasurement; + } mathGlyphVariantRecords[1]; // There are variantCount MathGlyphVariantRecord's. + + void getSizeVariants(const SharedBuffer& buffer, Vector<Glyph>& variants) const + { + uint16_t count = variantCount; + if (!isValidEnd(buffer, &mathGlyphVariantRecords[count])) + return; + variants.resize(count); + for (uint16_t i = 0; i < count; i++) + variants[i] = mathGlyphVariantRecords[i].variantGlyph; + } + + void getAssemblyParts(const SharedBuffer& buffer, Vector<OpenTypeMathData::AssemblyPart>& assemblyParts) const + { + uint16_t offset = glyphAssemblyOffset; + const GlyphAssembly* glyphAssembly = validateOffset<GlyphAssembly>(buffer, offset); + if (glyphAssembly) + glyphAssembly->getAssemblyParts(buffer, assemblyParts); + } +}; + +struct MathVariants : TableWithCoverage { + OpenType::UInt16 minConnectorOverlap; + OpenType::Offset verticalGlyphCoverageOffset; + OpenType::Offset horizontalGlyphCoverageOffset; + OpenType::UInt16 verticalGlyphCount; + OpenType::UInt16 horizontalGlyphCount; + OpenType::Offset mathGlyphConstructionsOffset[1]; // There are verticalGlyphCount vertical glyph contructions and horizontalGlyphCount vertical glyph contructions. + + const MathGlyphConstruction* mathGlyphConstruction(const SharedBuffer& buffer, Glyph glyph, bool isVertical) const + { + uint32_t count = uint16_t(verticalGlyphCount) + uint16_t(horizontalGlyphCount); + if (!isValidEnd(buffer, &mathGlyphConstructionsOffset[count])) + return nullptr; + + // We determine the coverage table for the specified glyph. + uint16_t coverageOffset = isVertical ? verticalGlyphCoverageOffset : horizontalGlyphCoverageOffset; + if (!coverageOffset) + return nullptr; + const CoverageTable* coverage = validateOffset<CoverageTable>(buffer, coverageOffset); + if (!coverage) + return nullptr; + + // We determine the index in the mathGlyphConstructionsOffset table. + uint32_t i; + if (!getCoverageIndex(buffer, coverage, glyph, i)) + return nullptr; + count = isVertical ? verticalGlyphCount : horizontalGlyphCount; + if (i >= count) + return nullptr; + if (!isVertical) + i += uint16_t(verticalGlyphCount); + + return validateOffset<MathGlyphConstruction>(buffer, mathGlyphConstructionsOffset[i]); + } +}; + +struct MATHTable : TableBase { + OpenType::Fixed version; + OpenType::Offset mathConstantsOffset; + OpenType::Offset mathGlyphInfoOffset; + OpenType::Offset mathVariantsOffset; + + const MathConstants* mathConstants(const SharedBuffer& buffer) const + { + uint16_t offset = mathConstantsOffset; + if (offset) + return validateOffset<MathConstants>(buffer, offset); + return nullptr; + } + + const MathGlyphInfo* mathGlyphInfo(const SharedBuffer& buffer) const + { + uint16_t offset = mathGlyphInfoOffset; + if (offset) + return validateOffset<MathGlyphInfo>(buffer, offset); + return nullptr; + } + + const MathVariants* mathVariants(const SharedBuffer& buffer) const + { + uint16_t offset = mathVariantsOffset; + if (offset) + return validateOffset<MathVariants>(buffer, offset); + return nullptr; + } +}; + +#pragma pack() + +} // namespace OpenType +#endif // ENABLE(OPENTYPE_MATH) + +#if ENABLE(OPENTYPE_MATH) +OpenTypeMathData::OpenTypeMathData(const FontPlatformData& font) +{ + m_mathBuffer = font.openTypeTable(OpenType::MATHTag); + const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer); + if (!math) { + m_mathBuffer = nullptr; + return; + } + + const OpenType::MathConstants* mathConstants = math->mathConstants(*m_mathBuffer); + if (!mathConstants) { + m_mathBuffer = nullptr; + return; + } + + const OpenType::MathVariants* mathVariants = math->mathVariants(*m_mathBuffer); + if (!mathVariants) + m_mathBuffer = nullptr; +#elif USE(HARFBUZZ) +OpenTypeMathData::OpenTypeMathData(const FontPlatformData& font) +{ + HarfBuzzFace* face = font.harfBuzzFace(); + if (face) { + m_mathFont.reset(face->createFont()); + if (!hb_ot_math_has_data(hb_font_get_face(m_mathFont.get()))) + m_mathFont.release(); + } +#else +OpenTypeMathData::OpenTypeMathData(const FontPlatformData&) +{ +#endif +} + +OpenTypeMathData::~OpenTypeMathData() +{ +} + +#if ENABLE(OPENTYPE_MATH) +float OpenTypeMathData::getMathConstant(const Font& font, MathConstant constant) const +{ + int32_t value = 0; + + const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer); + ASSERT(math); + const OpenType::MathConstants* mathConstants = math->mathConstants(*m_mathBuffer); + ASSERT(mathConstants); + + if (constant >= 0 && constant <= ScriptScriptPercentScaleDown) + value = int16_t(mathConstants->intConstants[constant]); + else if (constant >= DelimitedSubFormulaMinHeight && constant <= DisplayOperatorMinHeight) + value = uint16_t(mathConstants->uIntConstants[constant - DelimitedSubFormulaMinHeight]); + else if (constant >= MathLeading && constant <= RadicalKernAfterDegree) + value = int16_t(mathConstants->mathValuesConstants[constant - MathLeading].value); + else if (constant == RadicalDegreeBottomRaisePercent) + value = uint16_t(mathConstants->radicalDegreeBottomRaisePercent); + + if (constant == ScriptPercentScaleDown || constant == ScriptScriptPercentScaleDown || constant == RadicalDegreeBottomRaisePercent) + return value / 100.0; + + return value * font.sizePerUnit(); +#elif USE(HARFBUZZ) +float OpenTypeMathData::getMathConstant(const Font&, MathConstant constant) const +{ + hb_position_t value = hb_ot_math_get_constant(m_mathFont.get(), static_cast<hb_ot_math_constant_t>(constant)); + if (constant == ScriptPercentScaleDown || constant == ScriptScriptPercentScaleDown || constant == RadicalDegreeBottomRaisePercent) + return value / 100.0; + + return value / 65536.0; +#else +float OpenTypeMathData::getMathConstant(const Font&, MathConstant) const +{ + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +#if ENABLE(OPENTYPE_MATH) +float OpenTypeMathData::getItalicCorrection(const Font& font, Glyph glyph) const +{ + const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer); + ASSERT(math); + const OpenType::MathGlyphInfo* mathGlyphInfo = math->mathGlyphInfo(*m_mathBuffer); + if (!mathGlyphInfo) + return 0; + + const OpenType::MathItalicsCorrectionInfo* mathItalicsCorrectionInfo = mathGlyphInfo->mathItalicsCorrectionInfo(*m_mathBuffer); + if (!mathItalicsCorrectionInfo) + return 0; + + return mathItalicsCorrectionInfo->getItalicCorrection(*m_mathBuffer, glyph) * font.sizePerUnit(); +#elif USE(HARFBUZZ) +float OpenTypeMathData::getItalicCorrection(const Font&, Glyph glyph) const +{ + return hb_ot_math_get_glyph_italics_correction(m_mathFont.get(), glyph) / 65536.0; +#else +float OpenTypeMathData::getItalicCorrection(const Font&, Glyph) const +{ + ASSERT_NOT_REACHED(); + return 0; +#endif +} + +#if ENABLE(OPENTYPE_MATH) +void OpenTypeMathData::getMathVariants(Glyph glyph, bool isVertical, Vector<Glyph>& sizeVariants, Vector<AssemblyPart>& assemblyParts) const +{ + sizeVariants.clear(); + assemblyParts.clear(); + const OpenType::MATHTable* math = OpenType::validateTable<OpenType::MATHTable>(m_mathBuffer); + ASSERT(math); + const OpenType::MathVariants* mathVariants = math->mathVariants(*m_mathBuffer); + ASSERT(mathVariants); + + const OpenType::MathGlyphConstruction* mathGlyphConstruction = mathVariants->mathGlyphConstruction(*m_mathBuffer, glyph, isVertical); + if (!mathGlyphConstruction) + return; + + mathGlyphConstruction->getSizeVariants(*m_mathBuffer, sizeVariants); + mathGlyphConstruction->getAssemblyParts(*m_mathBuffer, assemblyParts); +#elif USE(HARFBUZZ) +void OpenTypeMathData::getMathVariants(Glyph glyph, bool isVertical, Vector<Glyph>& sizeVariants, Vector<AssemblyPart>& assemblyParts) const +{ + hb_direction_t direction = isVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR; + + sizeVariants.clear(); + hb_ot_math_glyph_variant_t variants[10]; + unsigned variantsSize = WTF_ARRAY_LENGTH(variants); + unsigned count; + unsigned offset = 0; + do { + count = variantsSize; + hb_ot_math_get_glyph_variants(m_mathFont.get(), glyph, direction, offset, &count, variants); + offset += count; + for (unsigned i = 0; i < count; i++) + sizeVariants.append(variants[i].glyph); + } while (count == variantsSize); + + assemblyParts.clear(); + hb_ot_math_glyph_part_t parts[10]; + unsigned partsSize = WTF_ARRAY_LENGTH(parts); + offset = 0; + do { + count = partsSize; + hb_ot_math_get_glyph_assembly(m_mathFont.get(), glyph, direction, offset, &count, parts, nullptr); + offset += count; + for (unsigned i = 0; i < count; i++) { + AssemblyPart assemblyPart; + assemblyPart.glyph = parts[i].glyph; + assemblyPart.isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER; + assemblyParts.append(assemblyPart); + } + } while (count == partsSize); +#else +void OpenTypeMathData::getMathVariants(Glyph, bool, Vector<Glyph>&, Vector<AssemblyPart>&) const +{ + ASSERT_NOT_REACHED(); +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h new file mode 100644 index 000000000..97d754d02 --- /dev/null +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeMathData.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014 Frederic Wang (fred.wang@free.fr). All rights reserved. + * Copyright (C) 2016 Igalia S.L. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Glyph.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +#if !ENABLE(OPENTYPE_MATH) && USE(HARFBUZZ) +#include <hb-ot.h> +#endif + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; +class Font; + +class OpenTypeMathData : public RefCounted<OpenTypeMathData> { +public: + static PassRefPtr<OpenTypeMathData> create(const FontPlatformData& font) + { + return adoptRef(new OpenTypeMathData(font)); + } + ~OpenTypeMathData(); + +#if ENABLE(OPENTYPE_MATH) + bool hasMathData() const { return m_mathBuffer; } +#elif USE(HARFBUZZ) + bool hasMathData() const { return m_mathFont.get(); } +#else + bool hasMathData() const { return false; } +#endif + + // These constants are defined in the MATH table. + // The implementation of OpenTypeMathData::getMathConstant assumes that they correspond to the indices of the MathContant table. + enum MathConstant { + ScriptPercentScaleDown, + ScriptScriptPercentScaleDown, + DelimitedSubFormulaMinHeight, + DisplayOperatorMinHeight, + MathLeading, + AxisHeight, + AccentBaseHeight, + FlattenedAccentBaseHeight, + SubscriptShiftDown, + SubscriptTopMax, + SubscriptBaselineDropMin, + SuperscriptShiftUp, + SuperscriptShiftUpCramped, + SuperscriptBottomMin, + SuperscriptBaselineDropMax, + SubSuperscriptGapMin, + SuperscriptBottomMaxWithSubscript, + SpaceAfterScript, + UpperLimitGapMin, + UpperLimitBaselineRiseMin, + LowerLimitGapMin, + LowerLimitBaselineDropMin, + StackTopShiftUp, + StackTopDisplayStyleShiftUp, + StackBottomShiftDown, + StackBottomDisplayStyleShiftDown, + StackGapMin, + StackDisplayStyleGapMin, + StretchStackTopShiftUp, + StretchStackBottomShiftDown, + StretchStackGapAboveMin, + StretchStackGapBelowMin, + FractionNumeratorShiftUp, + FractionNumeratorDisplayStyleShiftUp, + FractionDenominatorShiftDown, + FractionDenominatorDisplayStyleShiftDown, + FractionNumeratorGapMin, + FractionNumDisplayStyleGapMin, + FractionRuleThickness, + FractionDenominatorGapMin, + FractionDenomDisplayStyleGapMin, + SkewedFractionHorizontalGap, + SkewedFractionVerticalGap, + OverbarVerticalGap, + OverbarRuleThickness, + OverbarExtraAscender, + UnderbarVerticalGap, + UnderbarRuleThickness, + UnderbarExtraDescender, + RadicalVerticalGap, + RadicalDisplayStyleVerticalGap, + RadicalRuleThickness, + RadicalExtraAscender, + RadicalKernBeforeDegree, + RadicalKernAfterDegree, + RadicalDegreeBottomRaisePercent + }; + + struct AssemblyPart { + Glyph glyph; + bool isExtender; + }; + + float getMathConstant(const Font&, MathConstant) const; + float getItalicCorrection(const Font&, Glyph) const; + void getMathVariants(Glyph, bool isVertical, Vector<Glyph>& sizeVariants, Vector<AssemblyPart>& assemblyParts) const; + +private: + explicit OpenTypeMathData(const FontPlatformData&); + +#if ENABLE(OPENTYPE_MATH) + RefPtr<SharedBuffer> m_mathBuffer; +#elif USE(HARFBUZZ) + struct HbFontDeleter { + void operator()(hb_font_t* font) + { + hb_font_destroy(font); + } + }; + std::unique_ptr<hb_font_t, HbFontDeleter> m_mathFont; +#endif +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h b/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h index da9cea96c..e89fb5998 100644 --- a/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeTypes.h @@ -25,6 +25,10 @@ #ifndef OpenTypeTypes_h #define OpenTypeTypes_h +#if ENABLE(OPENTYPE_MATH) +#include "Glyph.h" +#endif + #include "SharedBuffer.h" namespace WebCore { @@ -100,6 +104,87 @@ protected: } }; +#if ENABLE(OPENTYPE_VERTICAL) || ENABLE(OPENTYPE_MATH) +struct CoverageTable : TableBase { + OpenType::UInt16 coverageFormat; +}; + +struct Coverage1Table : CoverageTable { + OpenType::UInt16 glyphCount; + OpenType::GlyphID glyphArray[1]; +}; + +struct Coverage2Table : CoverageTable { + OpenType::UInt16 rangeCount; + struct RangeRecord { + OpenType::GlyphID start; + OpenType::GlyphID end; + OpenType::UInt16 startCoverageIndex; + } ranges[1]; +}; +#endif // ENABLE(OPENTYPE_VERTICAL) || ENABLE(OPENTYPE_MATH) + +#if ENABLE(OPENTYPE_MATH) +struct TableWithCoverage : TableBase { +protected: + bool getCoverageIndex(const SharedBuffer& buffer, const CoverageTable* coverage, Glyph glyph, uint32_t& coverageIndex) const + { + switch (coverage->coverageFormat) { + case 1: { // Coverage Format 1 + const Coverage1Table* coverage1 = validatePtr<Coverage1Table>(buffer, coverage); + if (!coverage1) + return false; + uint16_t glyphCount = coverage1->glyphCount; + if (!isValidEnd(buffer, &coverage1->glyphArray[glyphCount])) + return false; + + // We do a binary search on the glyph indexes. + uint32_t imin = 0, imax = glyphCount; + while (imin < imax) { + uint32_t imid = (imin + imax) >> 1; + uint16_t glyphMid = coverage1->glyphArray[imid]; + if (glyphMid == glyph) { + coverageIndex = imid; + return true; + } + if (glyphMid < glyph) + imin = imid + 1; + else + imax = imid; + } + break; + } + case 2: { // Coverage Format 2 + const Coverage2Table* coverage2 = validatePtr<Coverage2Table>(buffer, coverage); + if (!coverage2) + return false; + uint16_t rangeCount = coverage2->rangeCount; + if (!isValidEnd(buffer, &coverage2->ranges[rangeCount])) + return false; + + // We do a binary search on the ranges. + uint32_t imin = 0, imax = rangeCount; + while (imin < imax) { + uint32_t imid = (imin + imax) >> 1; + uint16_t rStart = coverage2->ranges[imid].start; + uint16_t rEnd = coverage2->ranges[imid].end; + if (rEnd < glyph) + imin = imid + 1; + else if (glyph < rStart) + imax = imid; + else { + coverageIndex = coverage2->ranges[imid].startCoverageIndex + glyph - rStart; + return true; + } + } + break; + } + } + return false; + } +}; +#endif + } // namespace OpenType } // namespace WebCore #endif // OpenTypeTypes_h diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp new file mode 100644 index 000000000..f7d0ac8c3 --- /dev/null +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OpenTypeUtilities.h" + +#include "SharedBuffer.h" + +namespace WebCore { + +struct BigEndianUShort { + operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; } + BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { } + unsigned short v; +}; + +struct BigEndianULong { + operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; } + BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { } + unsigned v; +}; + +#pragma pack(1) + +struct EOTPrefix { + unsigned eotSize; + unsigned fontDataSize; + unsigned version; + unsigned flags; + uint8_t fontPANOSE[10]; + uint8_t charset; + uint8_t italic; + unsigned weight; + unsigned short fsType; + unsigned short magicNumber; + unsigned unicodeRange[4]; + unsigned codePageRange[2]; + unsigned checkSumAdjustment; + unsigned reserved[4]; + unsigned short padding1; +}; + +struct TableDirectoryEntry { + BigEndianULong tag; + BigEndianULong checkSum; + BigEndianULong offset; + BigEndianULong length; +}; + +#if !USE(CG) || !defined(COREGRAPHICS_INCLUDES_CORESERVICES_HEADER) +// Fixed type is not defined on non-CG and Windows platforms. |version| in sfntHeader +// and headTable and |fontRevision| in headTable are of Fixed, but they're +// not actually refered to anywhere. Therefore, we just have to match +// the size (4 bytes). For the definition of Fixed type, see +// http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6. +typedef int32_t Fixed; +#endif + +struct sfntHeader { + Fixed version; + BigEndianUShort numTables; + BigEndianUShort searchRange; + BigEndianUShort entrySelector; + BigEndianUShort rangeShift; + TableDirectoryEntry tables[1]; +}; + +struct OS2Table { + BigEndianUShort version; + BigEndianUShort avgCharWidth; + BigEndianUShort weightClass; + BigEndianUShort widthClass; + BigEndianUShort fsType; + BigEndianUShort subscriptXSize; + BigEndianUShort subscriptYSize; + BigEndianUShort subscriptXOffset; + BigEndianUShort subscriptYOffset; + BigEndianUShort superscriptXSize; + BigEndianUShort superscriptYSize; + BigEndianUShort superscriptXOffset; + BigEndianUShort superscriptYOffset; + BigEndianUShort strikeoutSize; + BigEndianUShort strikeoutPosition; + BigEndianUShort familyClass; + uint8_t panose[10]; + BigEndianULong unicodeRange[4]; + uint8_t vendID[4]; + BigEndianUShort fsSelection; + BigEndianUShort firstCharIndex; + BigEndianUShort lastCharIndex; + BigEndianUShort typoAscender; + BigEndianUShort typoDescender; + BigEndianUShort typoLineGap; + BigEndianUShort winAscent; + BigEndianUShort winDescent; + BigEndianULong codePageRange[2]; + BigEndianUShort xHeight; + BigEndianUShort capHeight; + BigEndianUShort defaultChar; + BigEndianUShort breakChar; + BigEndianUShort maxContext; +}; + +struct headTable { + Fixed version; + Fixed fontRevision; + BigEndianULong checkSumAdjustment; + BigEndianULong magicNumber; + BigEndianUShort flags; + BigEndianUShort unitsPerEm; + long long created; + long long modified; + BigEndianUShort xMin; + BigEndianUShort xMax; + BigEndianUShort yMin; + BigEndianUShort yMax; + BigEndianUShort macStyle; + BigEndianUShort lowestRectPPEM; + BigEndianUShort fontDirectionHint; + BigEndianUShort indexToLocFormat; + BigEndianUShort glyphDataFormat; +}; + +struct nameRecord { + BigEndianUShort platformID; + BigEndianUShort encodingID; + BigEndianUShort languageID; + BigEndianUShort nameID; + BigEndianUShort length; + BigEndianUShort offset; +}; + +struct nameTable { + BigEndianUShort format; + BigEndianUShort count; + BigEndianUShort stringOffset; + nameRecord nameRecords[1]; +}; + +#pragma pack() + +EOTHeader::EOTHeader() +{ + m_buffer.resize(sizeof(EOTPrefix)); +} + +void EOTHeader::updateEOTSize(size_t fontDataSize) +{ + prefix()->eotSize = m_buffer.size() + fontDataSize; +} + +void EOTHeader::appendBigEndianString(const BigEndianUShort* string, unsigned short length) +{ + size_t oldSize = m_buffer.size(); + m_buffer.resize(oldSize + length + 2 * sizeof(unsigned short)); + UChar* dst = reinterpret_cast<UChar*>(m_buffer.data() + oldSize); + unsigned i = 0; + dst[i++] = length; + unsigned numCharacters = length / 2; + for (unsigned j = 0; j < numCharacters; j++) + dst[i++] = string[j]; + dst[i] = 0; +} + +void EOTHeader::appendPaddingShort() +{ + unsigned short padding = 0; + m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding)); +} + +bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) +{ + overlayDst = 0; + overlaySrc = 0; + overlayLength = 0; + + size_t dataLength = fontData->size(); + const char* data = fontData->data(); + + EOTPrefix* prefix = eotHeader.prefix(); + + prefix->fontDataSize = dataLength; + prefix->version = 0x00020001; + prefix->flags = 0; + + if (dataLength < offsetof(sfntHeader, tables)) + return false; + + const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data); + + if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry)) + return false; + + bool haveOS2 = false; + bool haveHead = false; + bool haveName = false; + + const BigEndianUShort* familyName = 0; + unsigned short familyNameLength = 0; + const BigEndianUShort* subfamilyName = 0; + unsigned short subfamilyNameLength = 0; + const BigEndianUShort* fullName = 0; + unsigned short fullNameLength = 0; + const BigEndianUShort* versionString = 0; + unsigned short versionStringLength = 0; + + for (unsigned i = 0; i < sfnt->numTables; i++) { + unsigned tableOffset = sfnt->tables[i].offset; + unsigned tableLength = sfnt->tables[i].length; + + if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength) + return false; + + unsigned tableTag = sfnt->tables[i].tag; + switch (tableTag) { + case 'OS/2': + { + if (dataLength < tableOffset + sizeof(OS2Table)) + return false; + + haveOS2 = true; + const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset); + for (unsigned j = 0; j < 10; j++) + prefix->fontPANOSE[j] = OS2->panose[j]; + prefix->italic = OS2->fsSelection & 0x01; + prefix->weight = OS2->weightClass; + // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value. + // Since ATS does not enforce this on Mac OS X, we do not enforce it either. + prefix->fsType = 0; + for (unsigned j = 0; j < 4; j++) + prefix->unicodeRange[j] = OS2->unicodeRange[j]; + for (unsigned j = 0; j < 2; j++) + prefix->codePageRange[j] = OS2->codePageRange[j]; + break; + } + case 'head': + { + if (dataLength < tableOffset + sizeof(headTable)) + return false; + + haveHead = true; + const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset); + prefix->checkSumAdjustment = head->checkSumAdjustment; + break; + } + case 'name': + { + if (dataLength < tableOffset + offsetof(nameTable, nameRecords)) + return false; + + haveName = true; + const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset); + for (int j = 0; j < name->count; j++) { + if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord)) + return false; + if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) { + if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length) + return false; + + unsigned short nameLength = name->nameRecords[j].length; + const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset); + + switch (name->nameRecords[j].nameID) { + case 1: + familyNameLength = nameLength; + familyName = nameString; + break; + case 2: + subfamilyNameLength = nameLength; + subfamilyName = nameString; + break; + case 4: + fullNameLength = nameLength; + fullName = nameString; + break; + case 5: + versionStringLength = nameLength; + versionString = nameString; + break; + default: + break; + } + } + } + break; + } + default: + break; + } + if (haveOS2 && haveHead && haveName) + break; + } + + prefix->charset = DEFAULT_CHARSET; + prefix->magicNumber = 0x504c; + prefix->reserved[0] = 0; + prefix->reserved[1] = 0; + prefix->reserved[2] = 0; + prefix->reserved[3] = 0; + prefix->padding1 = 0; + + eotHeader.appendBigEndianString(familyName, familyNameLength); + eotHeader.appendBigEndianString(subfamilyName, subfamilyNameLength); + eotHeader.appendBigEndianString(versionString, versionStringLength); + + // If possible, ensure that the family name is a prefix of the full name. + if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) { + overlaySrc = reinterpret_cast<const char*>(fullName) - data; + overlayDst = reinterpret_cast<const char*>(familyName) - data; + overlayLength = familyNameLength; + } + eotHeader.appendBigEndianString(fullName, fullNameLength); + + eotHeader.appendPaddingShort(); + eotHeader.updateEOTSize(fontData->size()); + + return true; +} + +// adds fontName to the font table in fontData, and writes the new font table to rewrittenFontTable +// returns the size of the name table (which is used by renameAndActivateFont), or 0 on early abort +bool renameFont(const SharedBuffer& fontData, const String& fontName, Vector<char>& rewrittenFontData) +{ + size_t originalDataSize = fontData.size(); + const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData.data()); + + // Abort if the data is too small to be a font header with a "tables" entry. + if (originalDataSize < offsetof(sfntHeader, tables)) + return false; + + // Abort if the data is too small to hold all the tables specified in the header. + if (originalDataSize < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry)) + return false; + + unsigned t; + for (t = 0; t < sfnt->numTables; ++t) { + if (sfnt->tables[t].tag == 'name') + break; + } + if (t == sfnt->numTables) + return false; + + const int nameRecordCount = 5; + + // Rounded up to a multiple of 4 to simplify the checksum calculation. + size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4; + + rewrittenFontData.resize(fontData.size() + nameTableSize); + char* data = rewrittenFontData.data(); + memcpy(data, fontData.data(), originalDataSize); + + // Make the table directory entry point to the new 'name' table. + sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data); + rewrittenSfnt->tables[t].length = nameTableSize; + rewrittenSfnt->tables[t].offset = originalDataSize; + + // Write the new 'name' table after the original font data. + nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize); + name->format = 0; + name->count = nameRecordCount; + name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord); + for (unsigned i = 0; i < nameRecordCount; ++i) { + name->nameRecords[i].platformID = 3; + name->nameRecords[i].encodingID = 1; + name->nameRecords[i].languageID = 0x0409; + name->nameRecords[i].offset = 0; + name->nameRecords[i].length = fontName.length() * sizeof(UChar); + } + + // The required 'name' record types: Family, Style, Unique, Full and PostScript. + name->nameRecords[0].nameID = 1; + name->nameRecords[1].nameID = 2; + name->nameRecords[2].nameID = 3; + name->nameRecords[3].nameID = 4; + name->nameRecords[4].nameID = 6; + + for (unsigned i = 0; i < fontName.length(); ++i) + reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i]; + + // Update the table checksum in the directory entry. + rewrittenSfnt->tables[t].checkSum = 0; + for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i) + rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i]; + + return true; +} + +// Rename the font and install the new font data into the system +HANDLE renameAndActivateFont(const SharedBuffer& fontData, const String& fontName) +{ + Vector<char> rewrittenFontData; + if (!renameFont(fontData, fontName, rewrittenFontData)) + return 0; + + DWORD numFonts = 0; + HANDLE fontHandle = AddFontMemResourceEx(rewrittenFontData.data(), rewrittenFontData.size(), 0, &numFonts); + + if (fontHandle && numFonts < 1) { + RemoveFontMemResourceEx(fontHandle); + return 0; + } + + return fontHandle; +} + +} diff --git a/Source/WebCore/platform/graphics/win/DIBPixelData.h b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.h index fa2a6c0ba..832a4a85d 100644 --- a/Source/WebCore/platform/graphics/win/DIBPixelData.h +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeUtilities.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Brent Fulgham <bfulgham@webkit.org> + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,56 +21,41 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DIBPixelData_h -#define DIBPixelData_h +#ifndef OpenTypeUtilities_h +#define OpenTypeUtilities_h -#include "IntRect.h" -#include "IntSize.h" #include <windows.h> - -#if USE(CG) -#include <CoreFoundation/CFBase.h> -#else -typedef unsigned char UInt8; -#endif +#include <wtf/Forward.h> +#include <wtf/text/WTFString.h> namespace WebCore { -class DIBPixelData { - public: - DIBPixelData() - : m_bitmapBuffer(0) - , m_bitmapBufferLength(0) - , m_bytesPerRow(0) - , m_bitsPerPixel(0) - { - } - DIBPixelData(HBITMAP); +struct BigEndianUShort; +struct EOTPrefix; +class SharedBuffer; - void initialize(HBITMAP); +struct EOTHeader { + EOTHeader(); -#ifndef NDEBUG - void writeToFile(LPCWSTR); -#endif + size_t size() const { return m_buffer.size(); } + const uint8_t* data() const { return m_buffer.data(); } - UInt8* buffer() const { return m_bitmapBuffer; } - unsigned bufferLength() const { return m_bitmapBufferLength; } - const IntSize& size() const { return m_size; } - unsigned bytesPerRow() const { return m_bytesPerRow; } - unsigned short bitsPerPixel() const { return m_bitsPerPixel; } - static void setRGBABitmapAlpha(HDC, const IntRect&, unsigned char); + EOTPrefix* prefix() { return reinterpret_cast<EOTPrefix*>(m_buffer.data()); } + void updateEOTSize(size_t); + void appendBigEndianString(const BigEndianUShort*, unsigned short length); + void appendPaddingShort(); - private: - UInt8* m_bitmapBuffer; - unsigned m_bitmapBufferLength; - IntSize m_size; - unsigned m_bytesPerRow; - unsigned short m_bitsPerPixel; +private: + Vector<uint8_t, 512> m_buffer; }; +bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); +bool renameFont(const SharedBuffer&, const String&, Vector<char>&); +HANDLE renameAndActivateFont(const SharedBuffer&, const String&); + } // namespace WebCore -#endif // DIBPixelData_h +#endif // OpenTypeUtilities_h diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp index 35ae71dd7..f2e6812a2 100644 --- a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.cpp @@ -27,10 +27,10 @@ #include "OpenTypeVerticalData.h" #include "FloatRect.h" +#include "Font.h" #include "GlyphPage.h" #include "OpenTypeTypes.h" #include "SharedBuffer.h" -#include "SimpleFontData.h" #include <wtf/RefPtr.h> using namespace std; @@ -112,24 +112,6 @@ struct VORGTable { size_t requiredSize() const { return sizeof(*this) + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); } }; -struct CoverageTable : TableBase { - OpenType::UInt16 coverageFormat; -}; - -struct Coverage1Table : CoverageTable { - OpenType::UInt16 glyphCount; - OpenType::GlyphID glyphArray[1]; -}; - -struct Coverage2Table : CoverageTable { - OpenType::UInt16 rangeCount; - struct RangeRecord { - OpenType::GlyphID start; - OpenType::GlyphID end; - OpenType::UInt16 startCoverageIndex; - } ranges[1]; -}; - struct SubstitutionSubTable : TableBase { OpenType::UInt16 substFormat; OpenType::Offset coverageOffset; @@ -397,39 +379,52 @@ struct GSUBTable : TableBase { } // namespace OpenType -OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData) - : m_defaultVertOriginY(0) +static bool loadHmtxTable(const FontPlatformData& platformData, Vector<uint16_t>& advanceWidths) { - loadMetrics(platformData); - loadVerticalGlyphSubstitutions(platformData); -} - -void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData) -{ - // Load hhea and hmtx to get x-component of vertical origins. - // If these tables are missing, it's not an OpenType font. RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::HheaTag); const OpenType::HheaTable* hhea = OpenType::validateTable<OpenType::HheaTable>(buffer); if (!hhea) - return; + return false; uint16_t countHmtxEntries = hhea->numberOfHMetrics; if (!countHmtxEntries) { LOG_ERROR("Invalid numberOfHMetrics"); - return; + return false; } buffer = platformData.openTypeTable(OpenType::HmtxTag); const OpenType::HmtxTable* hmtx = OpenType::validateTable<OpenType::HmtxTable>(buffer, countHmtxEntries); if (!hmtx) { LOG_ERROR("hhea exists but hmtx does not (or broken)"); - return; + return false; } - m_advanceWidths.resize(countHmtxEntries); + + advanceWidths.resize(countHmtxEntries); for (uint16_t i = 0; i < countHmtxEntries; ++i) - m_advanceWidths[i] = hmtx->entries[i].advanceWidth; + advanceWidths[i] = hmtx->entries[i].advanceWidth; + return true; +} + +RefPtr<OpenTypeVerticalData> OpenTypeVerticalData::create(const FontPlatformData& platformData) +{ + // Load hhea and hmtx to get x-component of vertical origins. If these tables are missing, it's not an OpenType font. + Vector<uint16_t> advanceWidths; + if (!loadHmtxTable(platformData, advanceWidths)) + return nullptr; + + return adoptRef(new OpenTypeVerticalData(platformData, WTFMove(advanceWidths))); +} +OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData, Vector<uint16_t>&& advanceWidths) + : m_advanceWidths(WTFMove(advanceWidths)) +{ + loadMetrics(platformData); + loadVerticalGlyphSubstitutions(platformData); +} + +void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData) +{ // Load vhea first. This table is required for fonts that support vertical flow. - buffer = platformData.openTypeTable(OpenType::VheaTag); + RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::VheaTag); const OpenType::VheaTable* vhea = OpenType::validateTable<OpenType::VheaTable>(buffer); if (!vhea) return; @@ -497,7 +492,7 @@ void OpenTypeVerticalData::loadVerticalGlyphSubstitutions(const FontPlatformData gsub->getVerticalGlyphSubstitutions(&m_verticalGlyphMap, *buffer.get()); } -float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyph) const +float OpenTypeVerticalData::advanceHeight(const Font* font, Glyph glyph) const { size_t countHeights = m_advanceHeights.size(); if (countHeights) { @@ -510,7 +505,7 @@ float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyp return font->fontMetrics().height(); } -void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData* font, const Glyph* glyphs, size_t count, float* outXYArray) const +void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const Font* font, const Glyph* glyphs, size_t count, float* outXYArray) const { size_t countWidths = m_advanceWidths.size(); ASSERT(countWidths > 0); @@ -553,19 +548,19 @@ void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData } } -void OpenTypeVerticalData::substituteWithVerticalGlyphs(const SimpleFontData* font, GlyphPage* glyphPage, unsigned offset, unsigned length) const +void OpenTypeVerticalData::substituteWithVerticalGlyphs(const Font* font, GlyphPage* glyphPage) const { const HashMap<Glyph, Glyph>& map = m_verticalGlyphMap; if (map.isEmpty()) return; - for (unsigned index = offset, end = offset + length; index < end; ++index) { - Glyph glyph = glyphPage->glyphAt(index); + for (unsigned index = 0; index < GlyphPage::size; ++index) { + Glyph glyph = glyphPage->glyphForIndex(index); if (glyph) { - ASSERT(glyphPage->glyphDataForIndex(index).fontData == font); + ASSERT_UNUSED(font, &glyphPage->font() == font); Glyph to = map.get(glyph); if (to) - glyphPage->setGlyphDataForIndex(index, to, font); + glyphPage->setGlyphForIndex(index, to); } } } diff --git a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h index 26bc04d5c..e98aebb50 100644 --- a/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h +++ b/Source/WebCore/platform/graphics/opentype/OpenTypeVerticalData.h @@ -29,31 +29,26 @@ #include "Glyph.h" #include <wtf/HashMap.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> namespace WebCore { +class Font; class FontPlatformData; class GlyphPage; -class SimpleFontData; class OpenTypeVerticalData : public RefCounted<OpenTypeVerticalData> { public: - static PassRefPtr<OpenTypeVerticalData> create(const FontPlatformData& platformData) - { - return adoptRef(new OpenTypeVerticalData(platformData)); - } + static RefPtr<OpenTypeVerticalData> create(const FontPlatformData&); - bool isOpenType() const { return !m_advanceWidths.isEmpty(); } bool hasVerticalMetrics() const { return !m_advanceHeights.isEmpty(); } - float advanceHeight(const SimpleFontData*, Glyph) const; - void getVerticalTranslationsForGlyphs(const SimpleFontData*, const Glyph*, size_t, float* outXYArray) const; - void substituteWithVerticalGlyphs(const SimpleFontData*, GlyphPage*, unsigned offset, unsigned length) const; + float advanceHeight(const Font*, Glyph) const; + void getVerticalTranslationsForGlyphs(const Font*, const Glyph*, size_t, float* outXYArray) const; + void substituteWithVerticalGlyphs(const Font*, GlyphPage*) const; private: - explicit OpenTypeVerticalData(const FontPlatformData&); + explicit OpenTypeVerticalData(const FontPlatformData&, Vector<uint16_t>&& advanceWidths); void loadMetrics(const FontPlatformData&); void loadVerticalGlyphSubstitutions(const FontPlatformData&); @@ -63,11 +58,8 @@ private: Vector<uint16_t> m_advanceWidths; Vector<uint16_t> m_advanceHeights; Vector<int16_t> m_topSideBearings; - int16_t m_defaultVertOriginY; + int16_t m_defaultVertOriginY { 0 }; HashMap<Glyph, int16_t> m_vertOriginY; - - friend class FontCache; - bool m_inFontCache; // for mark & sweep in FontCache::purgeInactiveFontData() }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp new file mode 100644 index 000000000..487077be9 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/BitmapTexture.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BitmapTexture.h" + +#include "GraphicsLayer.h" +#include "ImageBuffer.h" +#include "TextureMapper.h" + +namespace WebCore { + +void BitmapTexture::updateContents(TextureMapper&, GraphicsLayer* sourceLayer, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag, float scale) +{ + // Making an unconditionally unaccelerated buffer here is OK because this code + // isn't used by any platforms that respect the accelerated bit. + std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(targetRect.size(), Unaccelerated); + + if (!imageBuffer) + return; + + GraphicsContext& context = imageBuffer->context(); + context.setImageInterpolationQuality(InterpolationDefault); + context.setTextDrawingMode(TextModeFill); + + IntRect sourceRect(targetRect); + sourceRect.setLocation(offset); + sourceRect.scale(1 / scale); + context.applyDeviceScaleFactor(scale); + context.translate(-sourceRect.x(), -sourceRect.y()); + + sourceLayer->paintGraphicsLayerContents(context, sourceRect); + + RefPtr<Image> image = imageBuffer->copyImage(DontCopyBackingStore); + if (!image) + return; + + updateContents(image.get(), targetRect, IntPoint(), updateContentsFlag); +} + +} // namespace diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexture.h b/Source/WebCore/platform/graphics/texmap/BitmapTexture.h new file mode 100644 index 000000000..3245638a8 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/BitmapTexture.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BitmapTexture_h +#define BitmapTexture_h + +#include "IntPoint.h" +#include "IntRect.h" +#include "IntSize.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class FilterOperations; +class GraphicsLayer; +class Image; +class TextureMapper; + +// A 2D texture that can be the target of software or GL rendering. +class BitmapTexture : public RefCounted<BitmapTexture> { +public: + enum Flag { + NoFlag = 0, + SupportsAlpha = 0x01, + FBOAttachment = 0x02 + }; + + enum UpdateContentsFlag { + UpdateCanModifyOriginalImageData, + UpdateCannotModifyOriginalImageData + }; + + typedef unsigned Flags; + + BitmapTexture() + : m_flags(0) + { + } + + virtual ~BitmapTexture() { } + virtual bool isBackedByOpenGL() const { return false; } + + virtual IntSize size() const = 0; + virtual void updateContents(Image*, const IntRect&, const IntPoint& offset, UpdateContentsFlag) = 0; + virtual void updateContents(TextureMapper&, GraphicsLayer*, const IntRect& target, const IntPoint& offset, UpdateContentsFlag, float scale = 1); + virtual void updateContents(const void*, const IntRect& target, const IntPoint& offset, int bytesPerLine, UpdateContentsFlag) = 0; + virtual bool isValid() const = 0; + inline Flags flags() const { return m_flags; } + + virtual int bpp() const { return 32; } + void reset(const IntSize& size, Flags flags = 0) + { + m_flags = flags; + m_contentSize = size; + didReset(); + } + virtual void didReset() { } + + inline IntSize contentSize() const { return m_contentSize; } + inline int numberOfBytes() const { return size().width() * size().height() * bpp() >> 3; } + inline bool isOpaque() const { return !(m_flags & SupportsAlpha); } + + virtual PassRefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&) { return this; } + +protected: + IntSize m_contentSize; + +private: + Flags m_flags; +}; + +} + +#endif // BitmapTexture_h diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp new file mode 100644 index 000000000..589060224 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp @@ -0,0 +1,347 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2012 Igalia S.L. + Copyright (C) 2012 Adobe Systems Incorporated + + 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 "BitmapTextureGL.h" + +#if USE(TEXTURE_MAPPER_GL) + +#include "Extensions3D.h" +#include "FilterOperations.h" +#include "GraphicsContext.h" +#include "Image.h" +#include "LengthFunctions.h" +#include "NotImplemented.h" +#include "TextureMapperShaderProgram.h" +#include "Timer.h" +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#if USE(CAIRO) +#include "CairoUtilities.h" +#include "RefPtrCairo.h" +#include <cairo.h> +#include <wtf/text/CString.h> +#endif + +#if OS(DARWIN) +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif + +namespace WebCore { + +BitmapTextureGL* toBitmapTextureGL(BitmapTexture* texture) +{ + if (!texture || !texture->isBackedByOpenGL()) + return 0; + + return static_cast<BitmapTextureGL*>(texture); +} + +BitmapTextureGL::BitmapTextureGL(RefPtr<GraphicsContext3D>&& context3D, const Flags flags) + : m_context3D(WTFMove(context3D)) +{ + if (flags & FBOAttachment) + m_internalFormat = m_format = GraphicsContext3D::RGBA; + else { + // If GL_EXT_texture_format_BGRA8888 is supported in the OpenGLES + // internal and external formats need to be BGRA + m_internalFormat = GraphicsContext3D::RGBA; + m_format = GraphicsContext3D::BGRA; + if (m_context3D->isGLES2Compliant()) { + if (m_context3D->getExtensions().supports("GL_EXT_texture_format_BGRA8888")) + m_internalFormat = GraphicsContext3D::BGRA; + else + m_format = GraphicsContext3D::RGBA; + } + } +} + +static void swizzleBGRAToRGBA(uint32_t* data, const IntRect& rect, int stride = 0) +{ + stride = stride ? stride : rect.width(); + for (int y = rect.y(); y < rect.maxY(); ++y) { + uint32_t* p = data + y * stride; + for (int x = rect.x(); x < rect.maxX(); ++x) + p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); + } +} + +static bool driverSupportsSubImage(GraphicsContext3D* context) +{ + if (context->isGLES2Compliant()) { + static bool supportsSubImage = context->getExtensions().supports("GL_EXT_unpack_subimage"); + return supportsSubImage; + } + + return true; +} + +void BitmapTextureGL::didReset() +{ + if (!m_id) + m_id = m_context3D->createTexture(); + + m_shouldClear = true; + if (m_textureSize == contentSize()) + return; + + m_textureSize = contentSize(); + m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id); + m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); + m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + + m_context3D->texImage2DDirect(GraphicsContext3D::TEXTURE_2D, 0, m_internalFormat, m_textureSize.width(), m_textureSize.height(), 0, m_format, m_type, 0); +} + +void BitmapTextureGL::updateContentsNoSwizzle(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel, Platform3DObject glFormat) +{ + m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id); + // For ES drivers that don't support sub-images. + if (driverSupportsSubImage(m_context3D.get())) { + // Use the OpenGL sub-image extension, now that we know it's available. + m_context3D->pixelStorei(GraphicsContext3D::UNPACK_ROW_LENGTH, bytesPerLine / bytesPerPixel); + m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_ROWS, sourceOffset.y()); + m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_PIXELS, sourceOffset.x()); + } + + m_context3D->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), glFormat, m_type, srcData); + + // For ES drivers that don't support sub-images. + if (driverSupportsSubImage(m_context3D.get())) { + m_context3D->pixelStorei(GraphicsContext3D::UNPACK_ROW_LENGTH, 0); + m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_ROWS, 0); + m_context3D->pixelStorei(GraphicsContext3D::UNPACK_SKIP_PIXELS, 0); + } +} + +void BitmapTextureGL::updateContents(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag updateContentsFlag) +{ + m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id); + + const unsigned bytesPerPixel = 4; + char* data = reinterpret_cast<char*>(const_cast<void*>(srcData)); + Vector<char> temporaryData; + IntPoint adjustedSourceOffset = sourceOffset; + + // Texture upload requires subimage buffer if driver doesn't support subimage and we don't have full image upload. + bool requireSubImageBuffer = !driverSupportsSubImage(m_context3D.get()) + && !(bytesPerLine == static_cast<int>(targetRect.width() * bytesPerPixel) && adjustedSourceOffset == IntPoint::zero()); + + // prepare temporaryData if necessary + if ((m_format == GraphicsContext3D::RGBA && updateContentsFlag == UpdateCannotModifyOriginalImageData) || requireSubImageBuffer) { + temporaryData.resize(targetRect.width() * targetRect.height() * bytesPerPixel); + data = temporaryData.data(); + const char* bits = static_cast<const char*>(srcData); + const char* src = bits + sourceOffset.y() * bytesPerLine + sourceOffset.x() * bytesPerPixel; + char* dst = data; + const int targetBytesPerLine = targetRect.width() * bytesPerPixel; + for (int y = 0; y < targetRect.height(); ++y) { + memcpy(dst, src, targetBytesPerLine); + src += bytesPerLine; + dst += targetBytesPerLine; + } + + bytesPerLine = targetBytesPerLine; + adjustedSourceOffset = IntPoint(0, 0); + } + + if (m_format == GraphicsContext3D::RGBA) + swizzleBGRAToRGBA(reinterpret_cast_ptr<uint32_t*>(data), IntRect(adjustedSourceOffset, targetRect.size()), bytesPerLine / bytesPerPixel); + + updateContentsNoSwizzle(data, targetRect, adjustedSourceOffset, bytesPerLine, bytesPerPixel, m_format); +} + +void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag) +{ + if (!image) + return; + NativeImagePtr frameImage = image->nativeImageForCurrentFrame(); + if (!frameImage) + return; + + int bytesPerLine; + const char* imageData; + +#if USE(CAIRO) + cairo_surface_t* surface = frameImage.get(); + imageData = reinterpret_cast<const char*>(cairo_image_surface_get_data(surface)); + bytesPerLine = cairo_image_surface_get_stride(surface); +#endif + + updateContents(imageData, targetRect, offset, bytesPerLine, updateContentsFlag); +} + +static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type) +{ + switch (type) { + case FilterOperation::GRAYSCALE: + case FilterOperation::SEPIA: + case FilterOperation::SATURATE: + case FilterOperation::HUE_ROTATE: + case FilterOperation::INVERT: + case FilterOperation::BRIGHTNESS: + case FilterOperation::CONTRAST: + case FilterOperation::OPACITY: + return 1; + case FilterOperation::BLUR: + case FilterOperation::DROP_SHADOW: + // We use two-passes (vertical+horizontal) for blur and drop-shadow. + return 2; + default: + return 0; + } +} + +PassRefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper& textureMapper, const FilterOperations& filters) +{ + if (filters.isEmpty()) + return this; + + TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper); + RefPtr<BitmapTexture> previousSurface = texmapGL.currentSurface(); + RefPtr<BitmapTexture> resultSurface = this; + RefPtr<BitmapTexture> intermediateSurface; + RefPtr<BitmapTexture> spareSurface; + + m_filterInfo = FilterInfo(); + + for (size_t i = 0; i < filters.size(); ++i) { + RefPtr<FilterOperation> filter = filters.operations()[i]; + ASSERT(filter); + + int numPasses = getPassesRequiredForFilter(filter->type()); + for (int j = 0; j < numPasses; ++j) { + bool last = (i == filters.size() - 1) && (j == numPasses - 1); + if (!last) { + if (!intermediateSurface) + intermediateSurface = texmapGL.acquireTextureFromPool(contentSize(), BitmapTexture::SupportsAlpha | BitmapTexture::FBOAttachment); + texmapGL.bindSurface(intermediateSurface.get()); + } + + if (last) { + toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter, j, spareSurface); + break; + } + + texmapGL.drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j); + if (!j && filter->type() == FilterOperation::DROP_SHADOW) { + spareSurface = resultSurface; + resultSurface = nullptr; + } + std::swap(resultSurface, intermediateSurface); + } + } + + texmapGL.bindSurface(previousSurface.get()); + return resultSurface; +} + +void BitmapTextureGL::initializeStencil() +{ + if (m_rbo) + return; + + m_rbo = m_context3D->createRenderbuffer(); + m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_rbo); + m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_textureSize.width(), m_textureSize.height()); + m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); + m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_rbo); + m_context3D->clearStencil(0); + m_context3D->clear(GraphicsContext3D::STENCIL_BUFFER_BIT); +} + +void BitmapTextureGL::initializeDepthBuffer() +{ + if (m_depthBufferObject) + return; + + m_depthBufferObject = m_context3D->createRenderbuffer(); + m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBufferObject); + m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_textureSize.width(), m_textureSize.height()); + m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); + m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBufferObject); +} + +void BitmapTextureGL::clearIfNeeded() +{ + if (!m_shouldClear) + return; + + m_clipStack.reset(IntRect(IntPoint::zero(), m_textureSize), ClipStack::YAxisMode::Default); + m_clipStack.applyIfNeeded(*m_context3D); + m_context3D->clearColor(0, 0, 0, 0); + m_context3D->clear(GraphicsContext3D::COLOR_BUFFER_BIT); + m_shouldClear = false; +} + +void BitmapTextureGL::createFboIfNeeded() +{ + if (m_fbo) + return; + + m_fbo = m_context3D->createFramebuffer(); + m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, id(), 0); + m_shouldClear = true; +} + +void BitmapTextureGL::bindAsSurface(GraphicsContext3D* context3D) +{ + context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); + createFboIfNeeded(); + context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + context3D->viewport(0, 0, m_textureSize.width(), m_textureSize.height()); + clearIfNeeded(); + m_clipStack.apply(*m_context3D); +} + +BitmapTextureGL::~BitmapTextureGL() +{ + if (m_id) + m_context3D->deleteTexture(m_id); + + if (m_fbo) + m_context3D->deleteFramebuffer(m_fbo); + + if (m_rbo) + m_context3D->deleteRenderbuffer(m_rbo); + + if (m_depthBufferObject) + m_context3D->deleteRenderbuffer(m_depthBufferObject); +} + +bool BitmapTextureGL::isValid() const +{ + return m_id; +} + +IntSize BitmapTextureGL::size() const +{ + return m_textureSize; +} + +}; // namespace WebCore + +#endif // USE(TEXTURE_MAPPER_GL) diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h new file mode 100644 index 000000000..c4a80224a --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h @@ -0,0 +1,114 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2014 Igalia S.L. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 BitmapTextureGL_h +#define BitmapTextureGL_h + +#if USE(TEXTURE_MAPPER_GL) + +#include "BitmapTexture.h" +#include "ClipStack.h" +#include "FilterOperation.h" +#include "GraphicsContext3D.h" +#include "IntSize.h" +#include "TextureMapperGL.h" + +namespace WebCore { + +class TextureMapper; +class TextureMapperGL; +class FilterOperation; + +class BitmapTextureGL : public BitmapTexture { +public: + static Ref<BitmapTexture> create(Ref<GraphicsContext3D>&& context3D, const Flags flags = NoFlag) + { + return adoptRef(*new BitmapTextureGL(WTFMove(context3D), flags)); + } + + virtual ~BitmapTextureGL(); + + IntSize size() const override; + bool isValid() const override; + void didReset() override; + void bindAsSurface(GraphicsContext3D*); + void initializeStencil(); + void initializeDepthBuffer(); + virtual uint32_t id() const { return m_id; } + uint32_t textureTarget() const { return GraphicsContext3D::TEXTURE_2D; } + IntSize textureSize() const { return m_textureSize; } + void updateContents(Image*, const IntRect&, const IntPoint&, UpdateContentsFlag) override; + void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag) override; + void updateContentsNoSwizzle(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel = 4, Platform3DObject glFormat = GraphicsContext3D::RGBA); + bool isBackedByOpenGL() const override { return true; } + + PassRefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&) override; + struct FilterInfo { + RefPtr<FilterOperation> filter; + unsigned pass; + RefPtr<BitmapTexture> contentTexture; + + FilterInfo(PassRefPtr<FilterOperation> f = 0, unsigned p = 0, PassRefPtr<BitmapTexture> t = 0) + : filter(f) + , pass(p) + , contentTexture(t) + { } + }; + const FilterInfo* filterInfo() const { return &m_filterInfo; } + ClipStack& clipStack() { return m_clipStack; } + + GC3Dint internalFormat() const { return m_internalFormat; } + +private: + BitmapTextureGL(RefPtr<GraphicsContext3D>&&, const Flags); + + Platform3DObject m_id { 0 }; + IntSize m_textureSize; + IntRect m_dirtyRect; + Platform3DObject m_fbo { 0 }; + Platform3DObject m_rbo { 0 }; + Platform3DObject m_depthBufferObject { 0 }; + bool m_shouldClear { true }; + ClipStack m_clipStack; + RefPtr<GraphicsContext3D> m_context3D; + + void clearIfNeeded(); + void createFboIfNeeded(); + + FilterInfo m_filterInfo; + + GC3Dint m_internalFormat; + GC3Denum m_format; + GC3Denum m_type { +#if OS(DARWIN) + GL_UNSIGNED_INT_8_8_8_8_REV +#else + GraphicsContext3D::UNSIGNED_BYTE +#endif + }; +}; + +BitmapTextureGL* toBitmapTextureGL(BitmapTexture*); + +} + +#endif // USE(TEXTURE_MAPPER_GL) + +#endif // BitmapTextureGL_h diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp new file mode 100644 index 000000000..00cc8ee36 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2015 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BitmapTexturePool.h" + +#if USE(TEXTURE_MAPPER_GL) +#include "BitmapTextureGL.h" +#endif + +namespace WebCore { + +static const double s_releaseUnusedSecondsTolerance = 3; +static const double s_releaseUnusedTexturesTimerInterval = 0.5; + +#if USE(TEXTURE_MAPPER_GL) +BitmapTexturePool::BitmapTexturePool(RefPtr<GraphicsContext3D>&& context3D) + : m_context3D(WTFMove(context3D)) + , m_releaseUnusedTexturesTimer(*this, &BitmapTexturePool::releaseUnusedTexturesTimerFired) +{ +} +#endif + +RefPtr<BitmapTexture> BitmapTexturePool::acquireTexture(const IntSize& size, const BitmapTexture::Flags flags) +{ + Vector<Entry>& list = flags & BitmapTexture::FBOAttachment ? m_attachmentTextures : m_textures; + + Entry* selectedEntry = std::find_if(list.begin(), list.end(), + [&size](Entry& entry) { return entry.m_texture->refCount() == 1 && entry.m_texture->size() == size; }); + + if (selectedEntry == list.end()) { + list.append(Entry(createTexture(flags))); + selectedEntry = &list.last(); + } + + scheduleReleaseUnusedTextures(); + selectedEntry->markIsInUse(); + return selectedEntry->m_texture.copyRef(); +} + +void BitmapTexturePool::scheduleReleaseUnusedTextures() +{ + if (m_releaseUnusedTexturesTimer.isActive()) + return; + + m_releaseUnusedTexturesTimer.startOneShot(s_releaseUnusedTexturesTimerInterval); +} + +void BitmapTexturePool::releaseUnusedTexturesTimerFired() +{ + // Delete entries, which have been unused in s_releaseUnusedSecondsTolerance. + double minUsedTime = monotonicallyIncreasingTime() - s_releaseUnusedSecondsTolerance; + + if (!m_textures.isEmpty()) { + std::sort(m_textures.begin(), m_textures.end(), + [](const Entry& a, const Entry& b) { return a.m_lastUsedTime > b.m_lastUsedTime; }); + + for (size_t i = 0; i < m_textures.size(); ++i) { + if (m_textures[i].m_lastUsedTime < minUsedTime) { + m_textures.remove(i, m_textures.size() - i); + break; + } + } + } + + if (!m_attachmentTextures.isEmpty()) { + std::sort(m_attachmentTextures.begin(), m_attachmentTextures.end(), + [](const Entry& a, const Entry& b) { return a.m_lastUsedTime > b.m_lastUsedTime; }); + + for (size_t i = 0; i < m_attachmentTextures.size(); ++i) { + if (m_attachmentTextures[i].m_lastUsedTime < minUsedTime) { + m_attachmentTextures.remove(i, m_attachmentTextures.size() - i); + break; + } + } + } + + if (!m_textures.isEmpty() || !m_attachmentTextures.isEmpty()) + scheduleReleaseUnusedTextures(); +} + +RefPtr<BitmapTexture> BitmapTexturePool::createTexture(const BitmapTexture::Flags flags) +{ +#if USE(TEXTURE_MAPPER_GL) + return BitmapTextureGL::create(*m_context3D, flags); +#else + return nullptr; +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h new file mode 100644 index 000000000..5c740fc10 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/BitmapTexturePool.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BitmapTexturePool_h +#define BitmapTexturePool_h + +#include "BitmapTexture.h" +#include "Timer.h" +#include <wtf/CurrentTime.h> + +#if USE(TEXTURE_MAPPER_GL) +#include "GraphicsContext3D.h" +#endif + +namespace WebCore { + +class GraphicsContext3D; +class IntSize; + +class BitmapTexturePool { + WTF_MAKE_NONCOPYABLE(BitmapTexturePool); + WTF_MAKE_FAST_ALLOCATED; +public: +#if USE(TEXTURE_MAPPER_GL) + explicit BitmapTexturePool(RefPtr<GraphicsContext3D>&&); +#endif + + RefPtr<BitmapTexture> acquireTexture(const IntSize&, const BitmapTexture::Flags); + +private: + struct Entry { + explicit Entry(RefPtr<BitmapTexture>&& texture) + : m_texture(WTFMove(texture)) + { } + + void markIsInUse() { m_lastUsedTime = monotonicallyIncreasingTime(); } + + RefPtr<BitmapTexture> m_texture; + double m_lastUsedTime { 0.0 }; + }; + + void scheduleReleaseUnusedTextures(); + void releaseUnusedTexturesTimerFired(); + RefPtr<BitmapTexture> createTexture(const BitmapTexture::Flags); + +#if USE(TEXTURE_MAPPER_GL) + RefPtr<GraphicsContext3D> m_context3D; +#endif + + Vector<Entry> m_textures; + Vector<Entry> m_attachmentTextures; + Timer m_releaseUnusedTexturesTimer; +}; + +} // namespace WebCore + +#endif // BitmapTexturePool_h diff --git a/Source/WebCore/platform/graphics/texmap/ClipStack.cpp b/Source/WebCore/platform/graphics/texmap/ClipStack.cpp new file mode 100644 index 000000000..11a00bc8a --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/ClipStack.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2012 Adobe Systems Incorporated + * Copyright (C) 2012, 2016 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 "ClipStack.h" + +#include "GraphicsContext3D.h" + +namespace WebCore { + +void ClipStack::push() +{ + clipStack.append(clipState); + clipStateDirty = true; +} + +void ClipStack::pop() +{ + if (clipStack.isEmpty()) + return; + clipState = clipStack.last(); + clipStack.removeLast(); + clipStateDirty = true; +} + +void ClipStack::reset(const IntRect& rect, ClipStack::YAxisMode mode) +{ + clipStack.clear(); + size = rect.size(); + yAxisMode = mode; + clipState = State(rect); + clipStateDirty = true; +} + +void ClipStack::intersect(const IntRect& rect) +{ + clipState.scissorBox.intersect(rect); + clipStateDirty = true; +} + +void ClipStack::setStencilIndex(int stencilIndex) +{ + clipState.stencilIndex = stencilIndex; + clipStateDirty = true; +} + +void ClipStack::apply(GraphicsContext3D& context) +{ + if (clipState.scissorBox.isEmpty()) + return; + + context.scissor(clipState.scissorBox.x(), + (yAxisMode == YAxisMode::Inverted) ? size.height() - clipState.scissorBox.maxY() : clipState.scissorBox.y(), + clipState.scissorBox.width(), clipState.scissorBox.height()); + context.stencilOp(GraphicsContext3D::KEEP, GraphicsContext3D::KEEP, GraphicsContext3D::KEEP); + context.stencilFunc(GraphicsContext3D::EQUAL, clipState.stencilIndex - 1, clipState.stencilIndex - 1); + if (clipState.stencilIndex == 1) + context.disable(GraphicsContext3D::STENCIL_TEST); + else + context.enable(GraphicsContext3D::STENCIL_TEST); +} + +void ClipStack::applyIfNeeded(GraphicsContext3D& context) +{ + if (!clipStateDirty) + return; + + clipStateDirty = false; + apply(context); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/texmap/ClipStack.h b/Source/WebCore/platform/graphics/texmap/ClipStack.h new file mode 100644 index 000000000..2fc40475b --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/ClipStack.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2015, 2016 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * 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 ClipStack_h +#define ClipStack_h + +#include "IntRect.h" +#include "IntSize.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class GraphicsContext3D; + +class ClipStack { +public: + struct State { + State(const IntRect& scissors = IntRect(), int stencil = 1) + : scissorBox(scissors) + , stencilIndex(stencil) + { } + + IntRect scissorBox; + int stencilIndex; + }; + + // Y-axis should be inverted only when painting into the window. + enum class YAxisMode { + Default, + Inverted, + }; + + void push(); + void pop(); + State& current() { return clipState; } + + void reset(const IntRect&, YAxisMode); + void intersect(const IntRect&); + void setStencilIndex(int); + int getStencilIndex() const { return clipState.stencilIndex; } + + void apply(GraphicsContext3D&); + void applyIfNeeded(GraphicsContext3D&); + + bool isCurrentScissorBoxEmpty() const { return clipState.scissorBox.isEmpty(); } + +private: + Vector<State> clipStack; + State clipState; + IntSize size; + bool clipStateDirty { false }; + YAxisMode yAxisMode { YAxisMode::Default }; +}; + +} // namespace WebCore + +#endif // ClipStack_h diff --git a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp index 1b87ee626..78a0246f7 100644 --- a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp +++ b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp @@ -21,31 +21,25 @@ #include "GraphicsLayerTextureMapper.h" #include "GraphicsContext.h" -#include "GraphicsLayerAnimation.h" #include "GraphicsLayerFactory.h" #include "ImageBuffer.h" +#include "TextureMapperAnimation.h" #include <wtf/CurrentTime.h> -#if USE(TEXTURE_MAPPER) +#if !USE(COORDINATED_GRAPHICS) namespace WebCore { -TextureMapperLayer* toTextureMapperLayer(GraphicsLayer* layer) -{ - return layer ? toGraphicsLayerTextureMapper(layer)->layer() : 0; -} - -std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient* client) +std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient& client, Type layerType) { if (!factory) - return std::make_unique<GraphicsLayerTextureMapper>(client); + return std::make_unique<GraphicsLayerTextureMapper>(layerType, client); - return factory->createGraphicsLayer(client); + return factory->createGraphicsLayer(layerType, client); } -GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(GraphicsLayerClient* client) - : GraphicsLayer(client) - , m_layer(adoptPtr(new TextureMapperLayer())) +GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(Type layerType, GraphicsLayerClient& client) + : GraphicsLayer(layerType, client) , m_compositedNativeImagePtr(0) , m_changeMask(NoChanges) , m_needsDisplay(false) @@ -59,15 +53,11 @@ GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(GraphicsLayerClient* clie void GraphicsLayerTextureMapper::notifyChange(ChangeMask changeMask) { + bool flushRequired = m_changeMask == NoChanges; m_changeMask |= changeMask; - if (!client()) - return; - client()->notifyFlushRequired(this); -} -void GraphicsLayerTextureMapper::setName(const String& name) -{ - GraphicsLayer::setName(name); + if (flushRequired) + client().notifyFlushRequired(this); } GraphicsLayerTextureMapper::~GraphicsLayerTextureMapper() @@ -78,33 +68,23 @@ GraphicsLayerTextureMapper::~GraphicsLayerTextureMapper() willBeDestroyed(); } -void GraphicsLayerTextureMapper::willBeDestroyed() -{ - GraphicsLayer::willBeDestroyed(); -} - -/* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display. -*/ void GraphicsLayerTextureMapper::setNeedsDisplay() { if (!drawsContent()) return; + // The current size might change, thus we need to update the whole display. m_needsDisplay = true; notifyChange(DisplayChange); addRepaintRect(FloatRect(FloatPoint(), m_size)); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setContentsNeedsDisplay() { notifyChange(DisplayChange); addRepaintRect(contentsRect()); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setNeedsDisplayInRect(const FloatRect& rect, ShouldClipToLayer) { if (!drawsContent()) @@ -117,8 +97,6 @@ void GraphicsLayerTextureMapper::setNeedsDisplayInRect(const FloatRect& rect, Sh addRepaintRect(rect); } -/* \reimp (GraphicsLayer.h) -*/ bool GraphicsLayerTextureMapper::setChildren(const Vector<GraphicsLayer*>& children) { if (GraphicsLayer::setChildren(children)) { @@ -128,40 +106,30 @@ bool GraphicsLayerTextureMapper::setChildren(const Vector<GraphicsLayer*>& child return false; } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::addChild(GraphicsLayer* layer) { notifyChange(ChildrenChange); GraphicsLayer::addChild(layer); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::addChildAtIndex(GraphicsLayer* layer, int index) { GraphicsLayer::addChildAtIndex(layer, index); notifyChange(ChildrenChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) { GraphicsLayer::addChildAbove(layer, sibling); notifyChange(ChildrenChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) { GraphicsLayer::addChildBelow(layer, sibling); notifyChange(ChildrenChange); } -/* \reimp (GraphicsLayer.h) -*/ bool GraphicsLayerTextureMapper::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) { if (GraphicsLayer::replaceChild(oldChild, newChild)) { @@ -171,8 +139,6 @@ bool GraphicsLayerTextureMapper::replaceChild(GraphicsLayer* oldChild, GraphicsL return false; } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setMaskLayer(GraphicsLayer* value) { if (value == maskLayer()) @@ -187,8 +153,6 @@ void GraphicsLayerTextureMapper::setMaskLayer(GraphicsLayer* value) } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setReplicatedByLayer(GraphicsLayer* value) { if (value == replicaLayer()) @@ -197,8 +161,6 @@ void GraphicsLayerTextureMapper::setReplicatedByLayer(GraphicsLayer* value) notifyChange(ReplicaLayerChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setPosition(const FloatPoint& value) { if (value == position()) @@ -207,8 +169,6 @@ void GraphicsLayerTextureMapper::setPosition(const FloatPoint& value) notifyChange(PositionChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value) { if (value == anchorPoint()) @@ -217,8 +177,6 @@ void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value) notifyChange(AnchorPointChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setSize(const FloatSize& value) { if (value == size()) @@ -230,8 +188,6 @@ void GraphicsLayerTextureMapper::setSize(const FloatSize& value) notifyChange(SizeChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setTransform(const TransformationMatrix& value) { if (value == transform()) @@ -241,8 +197,6 @@ void GraphicsLayerTextureMapper::setTransform(const TransformationMatrix& value) notifyChange(TransformChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setChildrenTransform(const TransformationMatrix& value) { if (value == childrenTransform()) @@ -251,8 +205,6 @@ void GraphicsLayerTextureMapper::setChildrenTransform(const TransformationMatrix notifyChange(ChildrenTransformChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setPreserves3D(bool value) { if (value == preserves3D()) @@ -261,8 +213,6 @@ void GraphicsLayerTextureMapper::setPreserves3D(bool value) notifyChange(Preserves3DChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setMasksToBounds(bool value) { if (value == masksToBounds()) @@ -271,8 +221,6 @@ void GraphicsLayerTextureMapper::setMasksToBounds(bool value) notifyChange(MasksToBoundsChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setDrawsContent(bool value) { if (value == drawsContent()) @@ -284,8 +232,6 @@ void GraphicsLayerTextureMapper::setDrawsContent(bool value) setNeedsDisplay(); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setContentsVisible(bool value) { if (value == contentsAreVisible()) @@ -296,8 +242,6 @@ void GraphicsLayerTextureMapper::setContentsVisible(bool value) maskLayer()->setContentsVisible(value); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setContentsOpaque(bool value) { if (value == contentsOpaque()) @@ -306,8 +250,6 @@ void GraphicsLayerTextureMapper::setContentsOpaque(bool value) GraphicsLayer::setContentsOpaque(value); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setBackfaceVisibility(bool value) { if (value == backfaceVisibility()) @@ -316,8 +258,6 @@ void GraphicsLayerTextureMapper::setBackfaceVisibility(bool value) notifyChange(BackfaceVisibilityChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setOpacity(float value) { if (value == opacity()) @@ -326,9 +266,7 @@ void GraphicsLayerTextureMapper::setOpacity(float value) notifyChange(OpacityChange); } -/* \reimp (GraphicsLayer.h) -*/ -void GraphicsLayerTextureMapper::setContentsRect(const IntRect& value) +void GraphicsLayerTextureMapper::setContentsRect(const FloatRect& value) { if (value == contentsRect()) return; @@ -345,15 +283,12 @@ void GraphicsLayerTextureMapper::setContentsToSolidColor(const Color& color) notifyChange(BackgroundColorChange); } - -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::setContentsToImage(Image* image) { if (image) { // Make the decision about whether the image has changed. // This code makes the assumption that pointer equality on a NativeImagePtr is a valid way to tell if the image is changed. - // This assumption is true in Qt, GTK and EFL. + // This assumption is true for the GTK+ port. NativeImagePtr newNativeImagePtr = image->nativeImageForCurrentFrame(); if (!newNativeImagePtr) return; @@ -365,28 +300,29 @@ void GraphicsLayerTextureMapper::setContentsToImage(Image* image) if (!m_compositedImage) m_compositedImage = TextureMapperTiledBackingStore::create(); m_compositedImage->setContentsToImage(image); + m_compositedImage->updateContentsScale(pageScaleFactor() * deviceScaleFactor()); } else { - m_compositedNativeImagePtr = 0; - m_compositedImage = 0; + m_compositedNativeImagePtr = nullptr; + m_compositedImage = nullptr; } - setContentsToMedia(m_compositedImage.get()); + setContentsToPlatformLayer(m_compositedImage.get(), ContentsLayerForImage); notifyChange(ContentChange); GraphicsLayer::setContentsToImage(image); } -void GraphicsLayerTextureMapper::setContentsToMedia(TextureMapperPlatformLayer* media) +void GraphicsLayerTextureMapper::setContentsToPlatformLayer(TextureMapperPlatformLayer* platformLayer, ContentsLayerPurpose purpose) { - if (media == m_contentsLayer) + if (platformLayer == m_contentsLayer) return; - GraphicsLayer::setContentsToMedia(media); + GraphicsLayer::setContentsToPlatformLayer(platformLayer, purpose); notifyChange(ContentChange); if (m_contentsLayer) m_contentsLayer->setClient(0); - m_contentsLayer = media; + m_contentsLayer = platformLayer; if (m_contentsLayer) m_contentsLayer->setClient(this); @@ -428,26 +364,25 @@ void GraphicsLayerTextureMapper::setIsScrollable(bool isScrollable) notifyChange(IsScrollableChange); } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::flushCompositingStateForThisLayerOnly() { prepareBackingStoreIfNeeded(); commitLayerChanges(); - m_layer->syncAnimations(); - updateBackingStoreIfNeeded(); + m_layer.syncAnimations(); } void GraphicsLayerTextureMapper::prepareBackingStoreIfNeeded() { - if (!shouldHaveBackingStore()) { - m_backingStore.clear(); - m_changeMask |= BackingStoreChange; - } else { + if (shouldHaveBackingStore()) { if (!m_backingStore) { m_backingStore = TextureMapperTiledBackingStore::create(); m_changeMask |= BackingStoreChange; } + } else { + if (m_backingStore) { + m_backingStore = nullptr; + m_changeMask |= BackingStoreChange; + } } updateDebugBorderAndRepaintCount(); @@ -473,112 +408,98 @@ void GraphicsLayerTextureMapper::setDebugBorder(const Color& color, float width) m_changeMask |= DebugVisualsChange; } -static void toTextureMapperLayerVector(const Vector<GraphicsLayer*>& layers, Vector<TextureMapperLayer*>& texmapLayers) -{ - texmapLayers.reserveCapacity(layers.size()); - for (size_t i = 0; i < layers.size(); ++i) - texmapLayers.append(toTextureMapperLayer(layers[i])); -} - void GraphicsLayerTextureMapper::commitLayerChanges() { if (m_changeMask == NoChanges) return; - if (m_changeMask & ChildrenChange) { - Vector<TextureMapperLayer*> textureMapperLayerChildren; - toTextureMapperLayerVector(children(), textureMapperLayerChildren); - m_layer->setChildren(textureMapperLayerChildren); - } + if (m_changeMask & ChildrenChange) + m_layer.setChildren(children()); if (m_changeMask & MaskLayerChange) - m_layer->setMaskLayer(toTextureMapperLayer(maskLayer())); + m_layer.setMaskLayer(&downcast<GraphicsLayerTextureMapper>(maskLayer())->layer()); if (m_changeMask & ReplicaLayerChange) - m_layer->setReplicaLayer(toTextureMapperLayer(replicaLayer())); + m_layer.setReplicaLayer(&downcast<GraphicsLayerTextureMapper>(replicaLayer())->layer()); if (m_changeMask & PositionChange) - m_layer->setPosition(position()); + m_layer.setPosition(position()); if (m_changeMask & AnchorPointChange) - m_layer->setAnchorPoint(anchorPoint()); + m_layer.setAnchorPoint(anchorPoint()); if (m_changeMask & SizeChange) - m_layer->setSize(size()); + m_layer.setSize(size()); if (m_changeMask & TransformChange) - m_layer->setTransform(transform()); + m_layer.setTransform(transform()); if (m_changeMask & ChildrenTransformChange) - m_layer->setChildrenTransform(childrenTransform()); + m_layer.setChildrenTransform(childrenTransform()); if (m_changeMask & Preserves3DChange) - m_layer->setPreserves3D(preserves3D()); + m_layer.setPreserves3D(preserves3D()); if (m_changeMask & ContentsRectChange) - m_layer->setContentsRect(contentsRect()); + m_layer.setContentsRect(contentsRect()); if (m_changeMask & MasksToBoundsChange) - m_layer->setMasksToBounds(masksToBounds()); + m_layer.setMasksToBounds(masksToBounds()); if (m_changeMask & DrawsContentChange) - m_layer->setDrawsContent(drawsContent()); + m_layer.setDrawsContent(drawsContent()); if (m_changeMask & ContentsVisibleChange) - m_layer->setContentsVisible(contentsAreVisible()); + m_layer.setContentsVisible(contentsAreVisible()); if (m_changeMask & ContentsOpaqueChange) - m_layer->setContentsOpaque(contentsOpaque()); + m_layer.setContentsOpaque(contentsOpaque()); if (m_changeMask & BackfaceVisibilityChange) - m_layer->setBackfaceVisibility(backfaceVisibility()); + m_layer.setBackfaceVisibility(backfaceVisibility()); if (m_changeMask & OpacityChange) - m_layer->setOpacity(opacity()); + m_layer.setOpacity(opacity()); if (m_changeMask & BackgroundColorChange) - m_layer->setSolidColor(solidColor()); + m_layer.setSolidColor(m_solidColor); -#if ENABLE(CSS_FILTERS) if (m_changeMask & FilterChange) - m_layer->setFilters(filters()); -#endif + m_layer.setFilters(filters()); if (m_changeMask & BackingStoreChange) - m_layer->setBackingStore(m_backingStore); + m_layer.setBackingStore(m_backingStore); if (m_changeMask & DebugVisualsChange) - m_layer->setDebugVisuals(isShowingDebugBorder(), debugBorderColor(), debugBorderWidth(), isShowingRepaintCounter()); + m_layer.setDebugVisuals(isShowingDebugBorder(), debugBorderColor(), debugBorderWidth(), isShowingRepaintCounter()); if (m_changeMask & RepaintCountChange) - m_layer->setRepaintCount(repaintCount()); + m_layer.setRepaintCount(repaintCount()); if (m_changeMask & ContentChange) - m_layer->setContentsLayer(platformLayer()); + m_layer.setContentsLayer(platformLayer()); if (m_changeMask & AnimationChange) - m_layer->setAnimations(m_animations); + m_layer.setAnimations(m_animations); if (m_changeMask & AnimationStarted) - client()->notifyAnimationStarted(this, m_animationStartTime); + client().notifyAnimationStarted(this, "", m_animationStartTime); if (m_changeMask & FixedToViewporChange) - m_layer->setFixedToViewport(fixedToViewport()); + m_layer.setFixedToViewport(fixedToViewport()); if (m_changeMask & IsScrollableChange) - m_layer->setIsScrollable(isScrollable()); + m_layer.setIsScrollable(isScrollable()); if (m_changeMask & CommittedScrollOffsetChange) - m_layer->didCommitScrollOffset(m_committedScrollOffset); + m_layer.didCommitScrollOffset(m_committedScrollOffset); m_changeMask = NoChanges; } -/* \reimp (GraphicsLayer.h) -*/ void GraphicsLayerTextureMapper::flushCompositingState(const FloatRect& rect) { - if (!m_layer->textureMapper()) + if (!m_layer.textureMapper()) return; flushCompositingStateForThisLayerOnly(); @@ -587,13 +508,28 @@ void GraphicsLayerTextureMapper::flushCompositingState(const FloatRect& rect) maskLayer()->flushCompositingState(rect); if (replicaLayer()) replicaLayer()->flushCompositingState(rect); - for (size_t i = 0; i < children().size(); ++i) - children()[i]->flushCompositingState(rect); + for (auto* child : children()) + child->flushCompositingState(rect); +} + +void GraphicsLayerTextureMapper::updateBackingStoreIncludingSubLayers() +{ + if (!m_layer.textureMapper()) + return; + + updateBackingStoreIfNeeded(); + + if (maskLayer()) + downcast<GraphicsLayerTextureMapper>(*maskLayer()).updateBackingStoreIfNeeded(); + if (replicaLayer()) + downcast<GraphicsLayerTextureMapper>(*replicaLayer()).updateBackingStoreIfNeeded(); + for (auto* child : children()) + downcast<GraphicsLayerTextureMapper>(*child).updateBackingStoreIncludingSubLayers(); } void GraphicsLayerTextureMapper::updateBackingStoreIfNeeded() { - TextureMapper* textureMapper = m_layer->textureMapper(); + TextureMapper* textureMapper = m_layer.textureMapper(); if (!textureMapper) return; @@ -610,8 +546,10 @@ void GraphicsLayerTextureMapper::updateBackingStoreIfNeeded() return; TextureMapperTiledBackingStore* backingStore = static_cast<TextureMapperTiledBackingStore*>(m_backingStore.get()); + backingStore->updateContentsScale(pageScaleFactor() * deviceScaleFactor()); - backingStore->updateContents(textureMapper, this, m_size, dirtyRect, BitmapTexture::UpdateCanModifyOriginalImageData); + dirtyRect.scale(pageScaleFactor() * deviceScaleFactor()); + backingStore->updateContents(*textureMapper, this, m_size, dirtyRect, BitmapTexture::UpdateCanModifyOriginalImageData); m_needsDisplay = false; m_needsDisplayRect = IntRect(); @@ -622,21 +560,44 @@ bool GraphicsLayerTextureMapper::shouldHaveBackingStore() const return drawsContent() && contentsAreVisible() && !m_size.isEmpty(); } -bool GraphicsLayerTextureMapper::addAnimation(const KeyframeValueList& valueList, const IntSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset) +bool GraphicsLayerTextureMapper::filtersCanBeComposited(const FilterOperations& filters) const +{ + if (!filters.size()) + return false; + + for (const auto& filterOperation : filters.operations()) { + if (filterOperation->type() == FilterOperation::REFERENCE) + return false; + } + + return true; +} + +bool GraphicsLayerTextureMapper::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& keyframesName, double timeOffset) { ASSERT(!keyframesName.isEmpty()); - if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyWebkitTransform && valueList.property() != AnimatedPropertyOpacity)) + if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyTransform && valueList.property() != AnimatedPropertyOpacity)) return false; + if (valueList.property() == AnimatedPropertyFilter) { + int listIndex = validateFilterOperations(valueList); + if (listIndex < 0) + return false; + + const auto& filters = static_cast<const FilterAnimationValue&>(valueList.at(listIndex)).value(); + if (!filtersCanBeComposited(filters)) + return false; + } + bool listsMatch = false; bool hasBigRotation; - if (valueList.property() == AnimatedPropertyWebkitTransform) + if (valueList.property() == AnimatedPropertyTransform) listsMatch = validateTransformOperations(valueList, hasBigRotation) >= 0; const double currentTime = monotonicallyIncreasingTime(); - m_animations.add(GraphicsLayerAnimation(keyframesName, valueList, boxSize, anim, currentTime - timeOffset, listsMatch)); + m_animations.add(TextureMapperAnimation(keyframesName, valueList, boxSize, *anim, listsMatch, currentTime - timeOffset, 0, TextureMapperAnimation::AnimationState::Playing)); // m_animationStartTime is the time of the first real frame of animation, now or delayed by a negative offset. if (timeOffset > 0) m_animationStartTime = currentTime; @@ -647,7 +608,7 @@ bool GraphicsLayerTextureMapper::addAnimation(const KeyframeValueList& valueList return true; } -void GraphicsLayerTextureMapper::setAnimations(const GraphicsLayerAnimations& animations) +void GraphicsLayerTextureMapper::setAnimations(const TextureMapperAnimations& animations) { m_animations = animations; notifyChange(AnimationChange); @@ -664,17 +625,26 @@ void GraphicsLayerTextureMapper::removeAnimation(const String& animationName) m_animations.remove(animationName); } -#if ENABLE(CSS_FILTERS) bool GraphicsLayerTextureMapper::setFilters(const FilterOperations& filters) { - TextureMapper* textureMapper = m_layer->textureMapper(); - // TextureMapperImageBuffer does not support CSS filters. - if (!textureMapper || textureMapper->accelerationMode() == TextureMapper::SoftwareMode) + if (!m_layer.textureMapper()) return false; - notifyChange(FilterChange); - return GraphicsLayer::setFilters(filters); + + bool canCompositeFilters = filtersCanBeComposited(filters); + if (GraphicsLayer::filters() == filters) + return canCompositeFilters; + + if (canCompositeFilters) { + if (!GraphicsLayer::setFilters(filters)) + return false; + notifyChange(FilterChange); + } else if (GraphicsLayer::filters().size()) { + clearFilters(); + notifyChange(FilterChange); + } + + return canCompositeFilters; } -#endif void GraphicsLayerTextureMapper::setFixedToViewport(bool fixed) { diff --git a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h index e3340d346..42ed713d3 100644 --- a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h +++ b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h @@ -20,7 +20,7 @@ #ifndef GraphicsLayerTextureMapper_h #define GraphicsLayerTextureMapper_h -#if USE(TEXTURE_MAPPER) +#if !USE(COORDINATED_GRAPHICS) #include "GraphicsLayer.h" #include "GraphicsLayerClient.h" @@ -32,70 +32,68 @@ namespace WebCore { -class GraphicsLayerTextureMapper : public GraphicsLayer, public TextureMapperPlatformLayer::Client { +class GraphicsLayerTextureMapper final : public GraphicsLayer, TextureMapperPlatformLayer::Client { public: - explicit GraphicsLayerTextureMapper(GraphicsLayerClient*); + explicit GraphicsLayerTextureMapper(Type, GraphicsLayerClient&); virtual ~GraphicsLayerTextureMapper(); - void setScrollClient(TextureMapperLayer::ScrollingClient* client) { m_layer->setScrollClient(client); } - void setID(uint32_t id) { m_layer->setID(id); } - - // reimps from GraphicsLayer.h - virtual void setNeedsDisplay(); - virtual void setContentsNeedsDisplay(); - virtual void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer); - virtual bool setChildren(const Vector<GraphicsLayer*>&); - virtual void addChild(GraphicsLayer*); - virtual void addChildAtIndex(GraphicsLayer*, int index); - virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); - virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); - virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); - virtual void setMaskLayer(GraphicsLayer* layer); - virtual void setPosition(const FloatPoint& p); - virtual void setAnchorPoint(const FloatPoint3D& p); - virtual void setSize(const FloatSize& size); - virtual void setTransform(const TransformationMatrix& t); - virtual void setChildrenTransform(const TransformationMatrix& t); - virtual void setPreserves3D(bool b); - virtual void setMasksToBounds(bool b); - virtual void setDrawsContent(bool b); - virtual void setContentsVisible(bool); - virtual void setContentsOpaque(bool b); - virtual void setBackfaceVisibility(bool b); - virtual void setOpacity(float opacity); - virtual void setContentsRect(const IntRect& r); - virtual void setReplicatedByLayer(GraphicsLayer*); - virtual void setContentsToImage(Image*); - virtual void setContentsToSolidColor(const Color&); - Color solidColor() const { return m_solidColor; } - virtual void setContentsToMedia(PlatformLayer*); - virtual void setContentsToCanvas(PlatformLayer* canvas) { setContentsToMedia(canvas); } - virtual void setShowDebugBorder(bool) override; - virtual void setDebugBorder(const Color&, float width) override; - virtual void setShowRepaintCounter(bool) override; - virtual void flushCompositingState(const FloatRect&); - virtual void flushCompositingStateForThisLayerOnly(); - virtual void setName(const String& name); - virtual bool hasContentsLayer() const { return m_contentsLayer; } - virtual PlatformLayer* platformLayer() const { return m_contentsLayer; } - - inline int changeMask() const { return m_changeMask; } - - virtual bool addAnimation(const KeyframeValueList&, const IntSize&, const Animation*, const String&, double); - virtual void pauseAnimation(const String&, double); - virtual void removeAnimation(const String&); - void setAnimations(const GraphicsLayerAnimations&); - - TextureMapperLayer* layer() const { return m_layer.get(); } + void setScrollClient(TextureMapperLayer::ScrollingClient* client) { m_layer.setScrollClient(client); } + void setID(uint32_t id) { m_layer.setID(id); } + + // GraphicsLayer + bool setChildren(const Vector<GraphicsLayer*>&) override; + void addChild(GraphicsLayer*) override; + void addChildAtIndex(GraphicsLayer*, int index) override; + void addChildAbove(GraphicsLayer*, GraphicsLayer* sibling) override; + void addChildBelow(GraphicsLayer*, GraphicsLayer* sibling) override; + bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) override; + + void setMaskLayer(GraphicsLayer*) override; + void setReplicatedByLayer(GraphicsLayer*) override; + void setPosition(const FloatPoint&) override; + void setAnchorPoint(const FloatPoint3D&) override; + void setSize(const FloatSize&) override; + void setTransform(const TransformationMatrix&) override; + void setChildrenTransform(const TransformationMatrix&) override; + void setPreserves3D(bool) override; + void setMasksToBounds(bool) override; + void setDrawsContent(bool) override; + void setContentsVisible(bool) override; + void setContentsOpaque(bool) override; + void setBackfaceVisibility(bool) override; + void setOpacity(float) override; + bool setFilters(const FilterOperations&) override; + + void setNeedsDisplay() override; + void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer) override; + void setContentsNeedsDisplay() override; + void setContentsRect(const FloatRect&) override; + + bool addAnimation(const KeyframeValueList&, const FloatSize&, const Animation*, const String&, double) override; + void pauseAnimation(const String&, double) override; + void removeAnimation(const String&) override; + + void setContentsToImage(Image*) override; + void setContentsToSolidColor(const Color&) override; + void setContentsToPlatformLayer(PlatformLayer*, ContentsLayerPurpose) override; + bool usesContentsLayer() const override { return m_contentsLayer; } + PlatformLayer* platformLayer() const override { return m_contentsLayer; } + + void setShowDebugBorder(bool) override; + void setDebugBorder(const Color&, float width) override; + void setShowRepaintCounter(bool) override; + + void flushCompositingState(const FloatRect&) override; + void flushCompositingStateForThisLayerOnly() override; + + void updateBackingStoreIncludingSubLayers(); + + TextureMapperLayer& layer() { return m_layer; } void didCommitScrollOffset(const IntSize&); void setIsScrollable(bool); bool isScrollable() const { return m_isScrollable; } -#if ENABLE(CSS_FILTERS) - virtual bool setFilters(const FilterOperations&); -#endif - void setFixedToViewport(bool); bool fixedToViewport() const { return m_fixedToViewport; } @@ -103,8 +101,15 @@ public: float debugBorderWidth() const { return m_debugBorderWidth; } void setRepaintCount(int); + void setAnimations(const TextureMapperAnimations&); + private: - virtual void willBeDestroyed(); + // GraphicsLayer + bool isGraphicsLayerTextureMapper() const override { return true; } + + // TextureMapperPlatformLayer::Client + void platformLayerWillBeDestroyed() override { setContentsToPlatformLayer(0, NoContentsLayer); } + void setPlatformLayerNeedsDisplay() override { setContentsNeedsDisplay(); } void commitLayerChanges(); void updateDebugBorderAndRepaintCount(); @@ -112,8 +117,7 @@ private: void prepareBackingStoreIfNeeded(); bool shouldHaveBackingStore() const; - virtual void platformLayerWillBeDestroyed() override { setContentsToMedia(0); } - virtual void setPlatformLayerNeedsDisplay() override { setContentsNeedsDisplay(); } + bool filtersCanBeComposited(const FilterOperations&) const; // This set of flags help us defer which properties of the layer have been // modified by the compositor, so we can know what to look for in the next flush. @@ -160,7 +164,7 @@ private: }; void notifyChange(ChangeMask); - OwnPtr<TextureMapperLayer> m_layer; + TextureMapperLayer m_layer; RefPtr<TextureMapperTiledBackingStore> m_compositedImage; NativeImagePtr m_compositedNativeImagePtr; RefPtr<TextureMapperBackingStore> m_backingStore; @@ -175,21 +179,17 @@ private: TextureMapperPlatformLayer* m_contentsLayer; FloatRect m_needsDisplayRect; - GraphicsLayerAnimations m_animations; + TextureMapperAnimations m_animations; double m_animationStartTime; IntSize m_committedScrollOffset; bool m_isScrollable; }; -inline static GraphicsLayerTextureMapper* toGraphicsLayerTextureMapper(GraphicsLayer* layer) -{ - return static_cast<GraphicsLayerTextureMapper*>(layer); -} +} // namespace WebCore -TextureMapperLayer* toTextureMapperLayer(GraphicsLayer*); +SPECIALIZE_TYPE_TRAITS_GRAPHICSLAYER(WebCore::GraphicsLayerTextureMapper, isGraphicsLayerTextureMapper()) -} #endif #endif // GraphicsLayerTextureMapper_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp index b863e79e5..973047f49 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapper.cpp @@ -20,151 +20,28 @@ #include "config.h" #include "TextureMapper.h" +#include "BitmapTexturePool.h" #include "FilterOperations.h" #include "GraphicsLayer.h" -#include "TextureMapperImageBuffer.h" #include "Timer.h" #include <wtf/CurrentTime.h> -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - namespace WebCore { -struct BitmapTexturePoolEntry { - explicit BitmapTexturePoolEntry(PassRefPtr<BitmapTexture> texture) - : m_texture(texture) - { } - inline void markUsed() { m_timeLastUsed = monotonicallyIncreasingTime(); } - static bool compareTimeLastUsed(const BitmapTexturePoolEntry& a, const BitmapTexturePoolEntry& b) - { - return a.m_timeLastUsed - b.m_timeLastUsed > 0; - } - - RefPtr<BitmapTexture> m_texture; - double m_timeLastUsed; -}; - -class BitmapTexturePool { - WTF_MAKE_NONCOPYABLE(BitmapTexturePool); - WTF_MAKE_FAST_ALLOCATED; -public: - BitmapTexturePool(); - - PassRefPtr<BitmapTexture> acquireTexture(const IntSize&, TextureMapper*); - -private: - void scheduleReleaseUnusedTextures(); - void releaseUnusedTexturesTimerFired(Timer<BitmapTexturePool>*); - - Vector<BitmapTexturePoolEntry> m_textures; - Timer<BitmapTexturePool> m_releaseUnusedTexturesTimer; - - static const double s_releaseUnusedSecondsTolerance; - static const double s_releaseUnusedTexturesTimerInterval; -}; - -const double BitmapTexturePool::s_releaseUnusedSecondsTolerance = 3; -const double BitmapTexturePool::s_releaseUnusedTexturesTimerInterval = 0.5; - -BitmapTexturePool::BitmapTexturePool() - : m_releaseUnusedTexturesTimer(this, &BitmapTexturePool::releaseUnusedTexturesTimerFired) -{ } +TextureMapper::TextureMapper() = default; -void BitmapTexturePool::scheduleReleaseUnusedTextures() -{ - if (m_releaseUnusedTexturesTimer.isActive()) - m_releaseUnusedTexturesTimer.stop(); - - m_releaseUnusedTexturesTimer.startOneShot(s_releaseUnusedTexturesTimerInterval); -} - -void BitmapTexturePool::releaseUnusedTexturesTimerFired(Timer<BitmapTexturePool>*) -{ - if (m_textures.isEmpty()) - return; - - // Delete entries, which have been unused in s_releaseUnusedSecondsTolerance. - std::sort(m_textures.begin(), m_textures.end(), BitmapTexturePoolEntry::compareTimeLastUsed); - - double minUsedTime = monotonicallyIncreasingTime() - s_releaseUnusedSecondsTolerance; - for (size_t i = 0; i < m_textures.size(); ++i) { - if (m_textures[i].m_timeLastUsed < minUsedTime) { - m_textures.remove(i, m_textures.size() - i); - break; - } - } -} - -PassRefPtr<BitmapTexture> BitmapTexturePool::acquireTexture(const IntSize& size, TextureMapper* textureMapper) -{ - BitmapTexturePoolEntry* selectedEntry = 0; - for (size_t i = 0; i < m_textures.size(); ++i) { - BitmapTexturePoolEntry* entry = &m_textures[i]; - - // If the surface has only one reference (the one in m_textures), we can safely reuse it. - if (entry->m_texture->refCount() > 1) - continue; - - if (entry->m_texture->canReuseWith(size)) { - selectedEntry = entry; - break; - } - } - - if (!selectedEntry) { - m_textures.append(BitmapTexturePoolEntry(textureMapper->createTexture())); - selectedEntry = &m_textures.last(); - } - - scheduleReleaseUnusedTextures(); - selectedEntry->markUsed(); - return selectedEntry->m_texture; -} +TextureMapper::~TextureMapper() = default; PassRefPtr<BitmapTexture> TextureMapper::acquireTextureFromPool(const IntSize& size, const BitmapTexture::Flags flags) { - RefPtr<BitmapTexture> selectedTexture = m_texturePool->acquireTexture(size, this); + RefPtr<BitmapTexture> selectedTexture = m_texturePool->acquireTexture(size, flags); selectedTexture->reset(size, flags); - return selectedTexture; + return selectedTexture.release(); } -PassOwnPtr<TextureMapper> TextureMapper::create(AccelerationMode mode) +std::unique_ptr<TextureMapper> TextureMapper::create() { - if (mode == SoftwareMode) - return TextureMapperImageBuffer::create(); return platformCreateAccelerated(); } -TextureMapper::TextureMapper(AccelerationMode accelerationMode) - : m_context(0) - , m_interpolationQuality(InterpolationDefault) - , m_textDrawingMode(TextModeFill) - , m_texturePool(adoptPtr(new BitmapTexturePool())) - , m_accelerationMode(accelerationMode) - , m_isMaskMode(false) - , m_wrapMode(StretchWrap) -{ } - -TextureMapper::~TextureMapper() -{ } - -void BitmapTexture::updateContents(TextureMapper* textureMapper, GraphicsLayer* sourceLayer, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag) -{ - std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(targetRect.size()); - GraphicsContext* context = imageBuffer->context(); - context->setImageInterpolationQuality(textureMapper->imageInterpolationQuality()); - context->setTextDrawingMode(textureMapper->textDrawingMode()); - - IntRect sourceRect(targetRect); - sourceRect.setLocation(offset); - context->translate(-offset.x(), -offset.y()); - sourceLayer->paintGraphicsLayerContents(*context, sourceRect); - - RefPtr<Image> image = imageBuffer->copyImage(DontCopyBackingStore); - - updateContents(image.get(), targetRect, IntPoint(), updateContentsFlag); -} - } // namespace - -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapper.h b/Source/WebCore/platform/graphics/texmap/TextureMapper.h index 681b24ea1..7233d0dcc 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapper.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapper.h @@ -20,13 +20,8 @@ #ifndef TextureMapper_h #define TextureMapper_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - -#if (PLATFORM(GTK) || PLATFORM(EFL)) && USE(OPENGL_ES_2) -#define TEXMAP_OPENGL_ES_2 -#endif - -#include "GraphicsContext.h" +#include "BitmapTexture.h" +#include "Color.h" #include "IntRect.h" #include "IntSize.h" #include "TransformationMatrix.h" @@ -43,68 +38,9 @@ class GraphicsLayer; class TextureMapper; class FilterOperations; -// A 2D texture that can be the target of software or GL rendering. -class BitmapTexture : public RefCounted<BitmapTexture> { -public: - enum Flag { - NoFlag = 0, - SupportsAlpha = 0x01 - }; - - enum UpdateContentsFlag { - UpdateCanModifyOriginalImageData, - UpdateCannotModifyOriginalImageData - }; - - typedef unsigned Flags; - - BitmapTexture() - : m_flags(0) - { - } - - virtual ~BitmapTexture() { } - virtual bool isBackedByOpenGL() const { return false; } - - virtual IntSize size() const = 0; - virtual void updateContents(Image*, const IntRect&, const IntPoint& offset, UpdateContentsFlag) = 0; - virtual void updateContents(TextureMapper*, GraphicsLayer*, const IntRect& target, const IntPoint& offset, UpdateContentsFlag); - virtual void updateContents(const void*, const IntRect& target, const IntPoint& offset, int bytesPerLine, UpdateContentsFlag) = 0; - virtual bool isValid() const = 0; - inline Flags flags() const { return m_flags; } - - virtual int bpp() const { return 32; } - virtual bool canReuseWith(const IntSize& /* contentsSize */, Flags = 0) { return false; } - void reset(const IntSize& size, Flags flags = 0) - { - m_flags = flags; - m_contentSize = size; - didReset(); - } - virtual void didReset() { } - - inline IntSize contentSize() const { return m_contentSize; } - inline int numberOfBytes() const { return size().width() * size().height() * bpp() >> 3; } - inline bool isOpaque() const { return !(m_flags & SupportsAlpha); } - -#if ENABLE(CSS_FILTERS) - virtual PassRefPtr<BitmapTexture> applyFilters(TextureMapper*, const FilterOperations&) { return this; } -#endif - -protected: - IntSize m_contentSize; - -private: - Flags m_flags; -}; - -// A "context" class used to encapsulate accelerated texture mapping functions: i.e. drawing a texture -// onto the screen or into another texture with a specified transform, opacity and mask. class TextureMapper { WTF_MAKE_FAST_ALLOCATED; - friend class BitmapTexture; public: - enum AccelerationMode { SoftwareMode, OpenGLMode }; enum PaintFlag { PaintingMirrored = 1 << 0, }; @@ -116,7 +52,9 @@ public: typedef unsigned PaintFlags; - static PassOwnPtr<TextureMapper> create(AccelerationMode newMode = SoftwareMode); + static std::unique_ptr<TextureMapper> create(); + + explicit TextureMapper(); virtual ~TextureMapper(); enum ExposedEdges { @@ -136,20 +74,11 @@ public: // makes a surface the target for the following drawTexture calls. virtual void bindSurface(BitmapTexture* surface) = 0; - void setGraphicsContext(GraphicsContext* context) { m_context = context; } - GraphicsContext* graphicsContext() { return m_context; } virtual void beginClip(const TransformationMatrix&, const FloatRect&) = 0; virtual void endClip() = 0; virtual IntRect clipBounds() = 0; virtual PassRefPtr<BitmapTexture> createTexture() = 0; - void setImageInterpolationQuality(InterpolationQuality quality) { m_interpolationQuality = quality; } - void setTextDrawingMode(TextDrawingModeFlags mode) { m_textDrawingMode = mode; } - - InterpolationQuality imageInterpolationQuality() const { return m_interpolationQuality; } - TextDrawingModeFlags textDrawingMode() const { return m_textDrawingMode; } - AccelerationMode accelerationMode() const { return m_accelerationMode; } - virtual void beginPainting(PaintFlags = 0) { } virtual void endPainting() { } @@ -163,9 +92,7 @@ public: void setWrapMode(WrapMode m) { m_wrapMode = m; } protected: - explicit TextureMapper(AccelerationMode); - - GraphicsContext* m_context; + std::unique_ptr<BitmapTexturePool> m_texturePool; bool isInMaskMode() const { return m_isMaskMode; } WrapMode wrapMode() const { return m_wrapMode; } @@ -173,24 +100,18 @@ protected: private: #if USE(TEXTURE_MAPPER_GL) - static PassOwnPtr<TextureMapper> platformCreateAccelerated(); + static std::unique_ptr<TextureMapper> platformCreateAccelerated(); #else - static PassOwnPtr<TextureMapper> platformCreateAccelerated() + static std::unique_ptr<TextureMapper> platformCreateAccelerated() { - return PassOwnPtr<TextureMapper>(); + return nullptr; } #endif - InterpolationQuality m_interpolationQuality; - TextDrawingModeFlags m_textDrawingMode; - OwnPtr<BitmapTexturePool> m_texturePool; - AccelerationMode m_accelerationMode; - bool m_isMaskMode; + bool m_isMaskMode { false }; TransformationMatrix m_patternTransform; - WrapMode m_wrapMode; + WrapMode m_wrapMode { StretchWrap }; }; } #endif - -#endif diff --git a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.cpp index add686f66..9cc1df6ad 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.cpp @@ -18,9 +18,7 @@ */ #include "config.h" - -#if USE(ACCELERATED_COMPOSITING) -#include "GraphicsLayerAnimation.h" +#include "TextureMapperAnimation.h" #include "LayoutSize.h" #include "UnitBezier.h" @@ -28,18 +26,14 @@ namespace WebCore { -#if ENABLE(CSS_FILTERS) -static inline PassRefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, FilterOperation* toOp, double progress, const IntSize& size, bool blendToPassthrough = false) +static RefPtr<FilterOperation> blendFunc(FilterOperation* fromOp, FilterOperation& toOp, double progress, const FloatSize& size, bool blendToPassthrough = false) { - ASSERT(toOp); - if (toOp->blendingNeedsRendererSize()) - return toOp->blend(fromOp, progress, LayoutSize(size.width(), size.height()), blendToPassthrough); - - return toOp->blend(fromOp, progress, blendToPassthrough); + if (toOp.blendingNeedsRendererSize()) + return toOp.blend(fromOp, progress, LayoutSize(size), blendToPassthrough); + return toOp.blend(fromOp, progress, blendToPassthrough); } - -static FilterOperations applyFilterAnimation(const FilterOperations& from, const FilterOperations& to, double progress, const IntSize& boxSize) +static FilterOperations applyFilterAnimation(const FilterOperations& from, const FilterOperations& to, double progress, const FloatSize& boxSize) { // First frame of an animation. if (!progress) @@ -58,9 +52,9 @@ static FilterOperations applyFilterAnimation(const FilterOperations& from, const size_t toSize = to.operations().size(); size_t size = std::max(fromSize, toSize); for (size_t i = 0; i < size; i++) { - RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0; - RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0; - RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), toOp.get(), progress, boxSize) : (fromOp ? blendFunc(0, fromOp.get(), progress, boxSize, true) : 0); + RefPtr<FilterOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : nullptr; + RefPtr<FilterOperation> toOp = (i < toSize) ? to.operations()[i].get() : nullptr; + RefPtr<FilterOperation> blendedOp = toOp ? blendFunc(fromOp.get(), *toOp, progress, boxSize) : (fromOp ? blendFunc(nullptr, *fromOp, progress, boxSize, true) : nullptr); if (blendedOp) result.operations().append(blendedOp); else { @@ -74,15 +68,12 @@ static FilterOperations applyFilterAnimation(const FilterOperations& from, const return result; } -#endif static bool shouldReverseAnimationValue(Animation::AnimationDirection direction, int loopCount) { - if (((direction == Animation::AnimationDirectionAlternate) && (loopCount & 1)) - || ((direction == Animation::AnimationDirectionAlternateReverse) && !(loopCount & 1)) - || direction == Animation::AnimationDirectionReverse) - return true; - return false; + return (direction == Animation::AnimationDirectionAlternate && loopCount & 1) + || (direction == Animation::AnimationDirectionAlternateReverse && !(loopCount & 1)) + || direction == Animation::AnimationDirectionReverse; } static double normalizedAnimationValue(double runningTime, double duration, Animation::AnimationDirection direction, double iterationCount) @@ -137,25 +128,22 @@ static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t return floor(numSteps * t) / numSteps; } -static inline float applyTimingFunction(const TimingFunction* timingFunction, float progress, double duration) +static inline float applyTimingFunction(const TimingFunction& timingFunction, float progress, double duration) { - if (!timingFunction) - return progress; - - if (timingFunction->isCubicBezierTimingFunction()) { - const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction); - return solveCubicBezierFunction(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2(), progress, duration); + if (timingFunction.isCubicBezierTimingFunction()) { + auto& ctf = static_cast<const CubicBezierTimingFunction&>(timingFunction); + return solveCubicBezierFunction(ctf.x1(), ctf.y1(), ctf.x2(), ctf.y2(), progress, duration); } - if (timingFunction->isStepsTimingFunction()) { - const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction); - return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress)); + if (timingFunction.isStepsTimingFunction()) { + auto& stf = static_cast<const StepsTimingFunction&>(timingFunction); + return solveStepsFunction(stf.numberOfSteps(), stf.stepAtStart(), double(progress)); } return progress; } -static TransformationMatrix applyTransformAnimation(const TransformOperations& from, const TransformOperations& to, double progress, const IntSize& boxSize, bool listsMatch) +static TransformationMatrix applyTransformAnimation(const TransformOperations& from, const TransformOperations& to, double progress, const FloatSize& boxSize, bool listsMatch) { TransformationMatrix matrix; @@ -183,16 +171,16 @@ static TransformationMatrix applyTransformAnimation(const TransformOperations& f // Animation to "-webkit-transform: none". if (!to.size()) { TransformOperations blended(from); - for (size_t i = 0; i < blended.operations().size(); ++i) - blended.operations()[i]->blend(0, progress, true)->apply(matrix, boxSize); + for (auto& operation : blended.operations()) + operation->blend(nullptr, progress, true)->apply(matrix, boxSize); return matrix; } // Animation from "-webkit-transform: none". if (!from.size()) { TransformOperations blended(to); - for (size_t i = 0; i < blended.operations().size(); ++i) - blended.operations()[i]->blend(0, 1. - progress, true)->apply(matrix, boxSize); + for (auto& operation : blended.operations()) + operation->blend(nullptr, 1 - progress, true)->apply(matrix, boxSize); return matrix; } @@ -203,77 +191,44 @@ static TransformationMatrix applyTransformAnimation(const TransformOperations& f return matrix; } -static const TimingFunction* timingFunctionForAnimationValue(const AnimationValue& animValue, const Animation* anim) +static const TimingFunction& timingFunctionForAnimationValue(const AnimationValue& animationValue, const Animation& animation) { - if (animValue.timingFunction()) - return animValue.timingFunction(); - if (anim->timingFunction()) - return anim->timingFunction().get(); - + if (animationValue.timingFunction()) + return *animationValue.timingFunction(); + if (animation.timingFunction()) + return *animation.timingFunction(); return CubicBezierTimingFunction::defaultTimingFunction(); } -GraphicsLayerAnimation::GraphicsLayerAnimation(const String& name, const KeyframeValueList& keyframes, const IntSize& boxSize, const Animation* animation, double startTime, bool listsMatch) - : m_keyframes(keyframes) +TextureMapperAnimation::TextureMapperAnimation(const String& name, const KeyframeValueList& keyframes, const FloatSize& boxSize, const Animation& animation, bool listsMatch, double startTime, double pauseTime, AnimationState state) + : m_name(name.isSafeToSendToAnotherThread() ? name : name.isolatedCopy()) + , m_keyframes(keyframes) , m_boxSize(boxSize) - , m_animation(Animation::create(*animation)) - , m_name(name) + , m_animation(Animation::create(animation)) , m_listsMatch(listsMatch) , m_startTime(startTime) - , m_pauseTime(0) + , m_pauseTime(pauseTime) , m_totalRunningTime(0) , m_lastRefreshedTime(m_startTime) - , m_state(PlayingState) -{ -} - -void GraphicsLayerAnimation::applyInternal(Client* client, const AnimationValue& from, const AnimationValue& to, float progress) -{ - switch (m_keyframes.property()) { - case AnimatedPropertyOpacity: - client->setAnimatedOpacity(applyOpacityAnimation((static_cast<const FloatAnimationValue&>(from).value()), (static_cast<const FloatAnimationValue&>(to).value()), progress)); - return; - case AnimatedPropertyWebkitTransform: - client->setAnimatedTransform(applyTransformAnimation(static_cast<const TransformAnimationValue&>(from).value(), static_cast<const TransformAnimationValue&>(to).value(), progress, m_boxSize, m_listsMatch)); - return; -#if ENABLE(CSS_FILTERS) - case AnimatedPropertyWebkitFilter: - client->setAnimatedFilters(applyFilterAnimation(static_cast<const FilterAnimationValue&>(from).value(), static_cast<const FilterAnimationValue&>(to).value(), progress, m_boxSize)); - return; -#endif - default: - ASSERT_NOT_REACHED(); - } -} - -bool GraphicsLayerAnimation::isActive() const -{ - if (state() != StoppedState) - return true; - - return m_animation->fillsForwards(); -} - -bool GraphicsLayerAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type) const + , m_state(state) { - for (size_t i = 0; i < m_animations.size(); ++i) { - if (m_animations[i].isActive() && m_animations[i].property() == type) - return true; - } - return false; } -bool GraphicsLayerAnimations::hasRunningAnimations() const +TextureMapperAnimation::TextureMapperAnimation(const TextureMapperAnimation& other) + : m_name(other.m_name.isSafeToSendToAnotherThread() ? other.m_name : other.m_name.isolatedCopy()) + , m_keyframes(other.m_keyframes) + , m_boxSize(other.m_boxSize) + , m_animation(Animation::create(*other.m_animation)) + , m_listsMatch(other.m_listsMatch) + , m_startTime(other.m_startTime) + , m_pauseTime(other.m_pauseTime) + , m_totalRunningTime(other.m_totalRunningTime) + , m_lastRefreshedTime(other.m_lastRefreshedTime) + , m_state(other.m_state) { - for (size_t i = 0; i < m_animations.size(); ++i) { - if (m_animations[i].state() == GraphicsLayerAnimation::PlayingState) - return true; - } - - return false; } -void GraphicsLayerAnimation::apply(Client* client) +void TextureMapperAnimation::apply(Client& client) { if (!isActive()) return; @@ -282,7 +237,8 @@ void GraphicsLayerAnimation::apply(Client* client) double normalizedValue = normalizedAnimationValue(totalRunningTime, m_animation->duration(), m_animation->direction(), m_animation->iterationCount()); if (m_animation->iterationCount() != Animation::IterationCountInfinite && totalRunningTime >= m_animation->duration() * m_animation->iterationCount()) { - setState(StoppedState); + m_state = AnimationState::Stopped; + m_pauseTime = 0; if (m_animation->fillsForwards()) normalizedValue = normalizedAnimationValueForFillsForwards(m_animation->iterationCount(), m_animation->direction()); } @@ -297,7 +253,7 @@ void GraphicsLayerAnimation::apply(Client* client) return; } if (m_keyframes.size() == 2) { - const TimingFunction* timingFunction = timingFunctionForAnimationValue(m_keyframes.at(0), m_animation.get()); + auto& timingFunction = timingFunctionForAnimationValue(m_keyframes.at(0), *m_animation); normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m_animation->duration()); applyInternal(client, m_keyframes.at(0), m_keyframes.at(1), normalizedValue); return; @@ -310,16 +266,30 @@ void GraphicsLayerAnimation::apply(Client* client) continue; normalizedValue = (normalizedValue - from.keyTime()) / (to.keyTime() - from.keyTime()); - const TimingFunction* timingFunction = timingFunctionForAnimationValue(from, m_animation.get()); + auto& timingFunction = timingFunctionForAnimationValue(from, *m_animation); normalizedValue = applyTimingFunction(timingFunction, normalizedValue, m_animation->duration()); applyInternal(client, from, to, normalizedValue); break; } } -double GraphicsLayerAnimation::computeTotalRunningTime() +void TextureMapperAnimation::pause(double time) { - if (state() == PausedState) + m_state = AnimationState::Paused; + m_pauseTime = time; +} + +void TextureMapperAnimation::resume() +{ + m_state = AnimationState::Playing; + m_pauseTime = 0; + m_totalRunningTime = m_pauseTime; + m_lastRefreshedTime = monotonicallyIncreasingTime(); +} + +double TextureMapperAnimation::computeTotalRunningTime() +{ + if (m_state == AnimationState::Paused) return m_pauseTime; double oldLastRefreshedTime = m_lastRefreshedTime; @@ -328,78 +298,96 @@ double GraphicsLayerAnimation::computeTotalRunningTime() return m_totalRunningTime; } -void GraphicsLayerAnimation::pause(double time) +bool TextureMapperAnimation::isActive() const { - setState(PausedState); - m_pauseTime = time; + return m_state != AnimationState::Stopped || m_animation->fillsForwards(); } -void GraphicsLayerAnimation::resume() +void TextureMapperAnimation::applyInternal(Client& client, const AnimationValue& from, const AnimationValue& to, float progress) { - setState(PlayingState); - m_totalRunningTime = m_pauseTime; - m_lastRefreshedTime = monotonicallyIncreasingTime(); + switch (m_keyframes.property()) { + case AnimatedPropertyOpacity: + client.setAnimatedOpacity(applyOpacityAnimation((static_cast<const FloatAnimationValue&>(from).value()), (static_cast<const FloatAnimationValue&>(to).value()), progress)); + return; + case AnimatedPropertyTransform: + client.setAnimatedTransform(applyTransformAnimation(static_cast<const TransformAnimationValue&>(from).value(), static_cast<const TransformAnimationValue&>(to).value(), progress, m_boxSize, m_listsMatch)); + return; + case AnimatedPropertyFilter: + client.setAnimatedFilters(applyFilterAnimation(static_cast<const FilterAnimationValue&>(from).value(), static_cast<const FilterAnimationValue&>(to).value(), progress, m_boxSize)); + return; + default: + ASSERT_NOT_REACHED(); + } } -void GraphicsLayerAnimations::add(const GraphicsLayerAnimation& animation) +void TextureMapperAnimations::add(const TextureMapperAnimation& animation) { // Remove the old state if we are resuming a paused animation. - remove(animation.name(), animation.property()); + remove(animation.name(), animation.keyframes().property()); m_animations.append(animation); } -void GraphicsLayerAnimations::pause(const String& name, double offset) +void TextureMapperAnimations::remove(const String& name) +{ + m_animations.removeAllMatching([&name] (const TextureMapperAnimation& animation) { + return animation.name() == name; + }); +} + +void TextureMapperAnimations::remove(const String& name, AnimatedPropertyID property) { - for (size_t i = 0; i < m_animations.size(); ++i) { - if (m_animations[i].name() == name) - m_animations[i].pause(offset); + m_animations.removeAllMatching([&name, property] (const TextureMapperAnimation& animation) { + return animation.name() == name && animation.keyframes().property() == property; + }); +} + +void TextureMapperAnimations::pause(const String& name, double offset) +{ + for (auto& animation : m_animations) { + if (animation.name() == name) + animation.pause(offset); } } -void GraphicsLayerAnimations::suspend(double offset) +void TextureMapperAnimations::suspend(double offset) { - for (size_t i = 0; i < m_animations.size(); ++i) - m_animations[i].pause(offset); + for (auto& animation : m_animations) + animation.pause(offset); } -void GraphicsLayerAnimations::resume() +void TextureMapperAnimations::resume() { - for (size_t i = 0; i < m_animations.size(); ++i) - m_animations[i].resume(); + for (auto& animation : m_animations) + animation.resume(); } -void GraphicsLayerAnimations::remove(const String& name) +void TextureMapperAnimations::apply(TextureMapperAnimation::Client& client) { - for (int i = m_animations.size() - 1; i >= 0; --i) { - if (m_animations[i].name() == name) - m_animations.remove(i); - } + for (auto& animation : m_animations) + animation.apply(client); } -void GraphicsLayerAnimations::remove(const String& name, AnimatedPropertyID property) +bool TextureMapperAnimations::hasActiveAnimationsOfType(AnimatedPropertyID type) const { - for (int i = m_animations.size() - 1; i >= 0; --i) { - if (m_animations[i].name() == name && m_animations[i].property() == property) - m_animations.remove(i); - } + return std::any_of(m_animations.begin(), m_animations.end(), + [&type](const TextureMapperAnimation& animation) { return animation.isActive() && animation.keyframes().property() == type; }); } -void GraphicsLayerAnimations::apply(GraphicsLayerAnimation::Client* client) +bool TextureMapperAnimations::hasRunningAnimations() const { - for (size_t i = 0; i < m_animations.size(); ++i) - m_animations[i].apply(client); + return std::any_of(m_animations.begin(), m_animations.end(), + [](const TextureMapperAnimation& animation) { return animation.state() == TextureMapperAnimation::AnimationState::Playing; }); } -GraphicsLayerAnimations GraphicsLayerAnimations::getActiveAnimations() const +TextureMapperAnimations TextureMapperAnimations::getActiveAnimations() const { - GraphicsLayerAnimations active; - for (size_t i = 0; i < m_animations.size(); ++i) { - if (m_animations[i].isActive()) - active.add(m_animations[i]); + TextureMapperAnimations active; + for (auto& animation : m_animations) { + if (animation.isActive()) + active.add(animation); } return active; } -} -#endif +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.h b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.h index 2d2f68573..6a447d76e 100644 --- a/Source/WebCore/platform/graphics/GraphicsLayerAnimation.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperAnimation.h @@ -17,60 +17,54 @@ Boston, MA 02110-1301, USA. */ -#ifndef GraphicsLayerAnimation_h -#define GraphicsLayerAnimation_h - -#if USE(ACCELERATED_COMPOSITING) +#ifndef TextureMapperAnimation_h +#define TextureMapperAnimation_h #include "GraphicsLayer.h" -#include "TransformationMatrix.h" -#include <wtf/HashMap.h> -#include <wtf/text/StringHash.h> namespace WebCore { -class GraphicsLayerAnimation { +class TransformationMatrix; + +class TextureMapperAnimation { public: - enum AnimationState { PlayingState, PausedState, StoppedState }; + enum class AnimationState { Playing, Paused, Stopped }; + class Client { public: virtual void setAnimatedTransform(const TransformationMatrix&) = 0; virtual void setAnimatedOpacity(float) = 0; -#if ENABLE(CSS_FILTERS) virtual void setAnimatedFilters(const FilterOperations&) = 0; -#endif }; - GraphicsLayerAnimation() + TextureMapperAnimation() : m_keyframes(AnimatedPropertyInvalid) { } - GraphicsLayerAnimation(const String&, const KeyframeValueList&, const IntSize&, const Animation*, double, bool); - void apply(Client*); + TextureMapperAnimation(const String&, const KeyframeValueList&, const FloatSize&, const Animation&, bool, double, double, AnimationState); + TextureMapperAnimation(const TextureMapperAnimation&); + + void apply(Client&); void pause(double); void resume(); - double computeTotalRunningTime(); - AnimationState state() const { return m_state; } - void setState(AnimationState s, double pauseTime = 0) - { - m_state = s; - m_pauseTime = pauseTime; - } - AnimatedPropertyID property() const { return m_keyframes.property(); } bool isActive() const; - String name() const { return m_name; } - IntSize boxSize() const { return m_boxSize; } - double startTime() const { return m_startTime; } - double pauseTime() const { return m_pauseTime; } - PassRefPtr<Animation> animation() const { return m_animation.get(); } + + const String& name() const { return m_name; } const KeyframeValueList& keyframes() const { return m_keyframes; } + const FloatSize& boxSize() const { return m_boxSize; } + const RefPtr<Animation> animation() const { return m_animation; } bool listsMatch() const { return m_listsMatch; } + double startTime() const { return m_startTime; } + double pauseTime() const { return m_pauseTime; } + AnimationState state() const { return m_state; } private: - void applyInternal(Client*, const AnimationValue& from, const AnimationValue& to, float progress); + void applyInternal(Client&, const AnimationValue& from, const AnimationValue& to, float progress); + double computeTotalRunningTime(); + + String m_name; KeyframeValueList m_keyframes; - IntSize m_boxSize; + FloatSize m_boxSize; RefPtr<Animation> m_animation; - String m_name; bool m_listsMatch; double m_startTime; double m_pauseTime; @@ -79,32 +73,32 @@ private: AnimationState m_state; }; -class GraphicsLayerAnimations { +class TextureMapperAnimations { public: - GraphicsLayerAnimations() { } + TextureMapperAnimations() = default; - void add(const GraphicsLayerAnimation&); + void add(const TextureMapperAnimation&); void remove(const String& name); void remove(const String& name, AnimatedPropertyID); void pause(const String&, double); void suspend(double); void resume(); - void apply(GraphicsLayerAnimation::Client*); + + void apply(TextureMapperAnimation::Client&); + bool isEmpty() const { return m_animations.isEmpty(); } size_t size() const { return m_animations.size(); } - const Vector<GraphicsLayerAnimation>& animations() const { return m_animations; } - Vector<GraphicsLayerAnimation>& animations() { return m_animations; } + const Vector<TextureMapperAnimation>& animations() const { return m_animations; } + Vector<TextureMapperAnimation>& animations() { return m_animations; } bool hasRunningAnimations() const; bool hasActiveAnimationsOfType(AnimatedPropertyID type) const; - - GraphicsLayerAnimations getActiveAnimations() const; + TextureMapperAnimations getActiveAnimations() const; private: - Vector<GraphicsLayerAnimation> m_animations; + Vector<TextureMapperAnimation> m_animations; }; } -#endif -#endif // GraphicsLayerAnimation_h +#endif // TextureMapperAnimation_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp index e68bc0914..ebed19a89 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.cpp @@ -19,18 +19,12 @@ #include "config.h" -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) #include "TextureMapperBackingStore.h" #include "GraphicsLayer.h" #include "ImageBuffer.h" #include "TextureMapper.h" -#if USE(GRAPHICS_SURFACE) -#include "GraphicsSurface.h" -#include "TextureMapperGL.h" -#endif - namespace WebCore { unsigned TextureMapperBackingStore::calculateExposedTileEdges(const FloatRect& totalRect, const FloatRect& tileRect) @@ -48,4 +42,3 @@ unsigned TextureMapperBackingStore::calculateExposedTileEdges(const FloatRect& t } } -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h index 4bae45b4f..914604171 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperBackingStore.h @@ -20,27 +20,21 @@ #ifndef TextureMapperBackingStore_h #define TextureMapperBackingStore_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - #include "FloatRect.h" #include "Image.h" #include "TextureMapper.h" #include "TextureMapperPlatformLayer.h" #include <wtf/RefPtr.h> -#if USE(GRAPHICS_SURFACE) -#include "GraphicsSurface.h" -#endif - namespace WebCore { class GraphicsLayer; class TextureMapperBackingStore : public TextureMapperPlatformLayer, public RefCounted<TextureMapperBackingStore> { public: - virtual PassRefPtr<BitmapTexture> texture() const = 0; - virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float) = 0; - virtual void drawRepaintCounter(TextureMapper*, int /* repaintCount */, const Color&, const FloatRect&, const TransformationMatrix&) { } + virtual RefPtr<BitmapTexture> texture() const = 0; + void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix&, float) override = 0; + virtual void drawRepaintCounter(TextureMapper&, int /* repaintCount */, const Color&, const FloatRect&, const TransformationMatrix&) { } virtual ~TextureMapperBackingStore() { } protected: @@ -48,6 +42,5 @@ protected: }; } -#endif #endif // TextureMapperBackingStore_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp index d74477010..db6a7a0d3 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.cpp @@ -21,8 +21,6 @@ #include "config.h" -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - #include "TextureMapperFPSCounter.h" #include "TextureMapper.h" @@ -46,7 +44,7 @@ TextureMapperFPSCounter::TextureMapperFPSCounter() } } -void TextureMapperFPSCounter::updateFPSAndDisplay(TextureMapper* textureMapper, const FloatPoint& location, const TransformationMatrix& matrix) +void TextureMapperFPSCounter::updateFPSAndDisplay(TextureMapper& textureMapper, const FloatPoint& location, const TransformationMatrix& matrix) { if (!m_isShowingFPS) return; @@ -59,9 +57,7 @@ void TextureMapperFPSCounter::updateFPSAndDisplay(TextureMapper* textureMapper, m_fpsTimestamp += delta; } - textureMapper->drawNumber(m_lastFPS, Color::black, location, matrix); + textureMapper.drawNumber(m_lastFPS, Color::black, location, matrix); } } // namespace WebCore - -#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h index 006237383..8aa4a9425 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperFPSCounter.h @@ -22,7 +22,6 @@ #ifndef TextureMapperFPSCounter_h #define TextureMapperFPSCounter_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) #include "FloatPoint.h" #include "TransformationMatrix.h" #include <wtf/Noncopyable.h> @@ -35,7 +34,7 @@ class TextureMapperFPSCounter { WTF_MAKE_FAST_ALLOCATED; public: TextureMapperFPSCounter(); - void updateFPSAndDisplay(TextureMapper*, const FloatPoint& = FloatPoint::zero(), const TransformationMatrix& = TransformationMatrix()); + void updateFPSAndDisplay(TextureMapper&, const FloatPoint& = FloatPoint::zero(), const TransformationMatrix& = TransformationMatrix()); private: bool m_isShowingFPS; @@ -47,8 +46,4 @@ private: } // namespace WebCore -#endif // USE(ACCELERATED_COMPOSITING) - #endif // TextureMapperFPSCounter_h - - diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp new file mode 100644 index 000000000..9a9f4fb06 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011, 2012, 2017 Igalia S.L. + * + * 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 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 "TextureMapperGC3DPlatformLayer.h" + +#if ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER) + +#if USE(OPENGL_ES_2) +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#endif + +#include "BitmapTextureGL.h" +#include "GLContext.h" +#include "TextureMapperPlatformLayerBuffer.h" + +namespace WebCore { + +TextureMapperGC3DPlatformLayer::TextureMapperGC3DPlatformLayer(GraphicsContext3D& context, GraphicsContext3D::RenderStyle renderStyle) + : m_context(context) + , m_renderStyle(renderStyle) +{ + switch (renderStyle) { + case GraphicsContext3D::RenderOffscreen: + m_glContext = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing()); + break; + case GraphicsContext3D::RenderToCurrentGLContext: + break; + case GraphicsContext3D::RenderDirectlyToHostWindow: + ASSERT_NOT_REACHED(); + break; + } + +#if USE(COORDINATED_GRAPHICS_THREADED) + if (m_renderStyle == GraphicsContext3D::RenderOffscreen) + m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy()); +#endif +} + +TextureMapperGC3DPlatformLayer::~TextureMapperGC3DPlatformLayer() +{ +#if !USE(COORDINATED_GRAPHICS_THREADED) + if (client()) + client()->platformLayerWillBeDestroyed(); +#endif +} + +bool TextureMapperGC3DPlatformLayer::makeContextCurrent() +{ + return m_glContext ? m_glContext->makeContextCurrent() : false; +} + +PlatformGraphicsContext3D TextureMapperGC3DPlatformLayer::platformContext() +{ + return m_glContext ? m_glContext->platformContext() : GLContext::current()->platformContext(); +} + +#if USE(COORDINATED_GRAPHICS_THREADED) +RefPtr<TextureMapperPlatformLayerProxy> TextureMapperGC3DPlatformLayer::proxy() const +{ + return m_platformLayerProxy.copyRef(); +} + +void TextureMapperGC3DPlatformLayer::swapBuffersIfNeeded() +{ + ASSERT(m_renderStyle == GraphicsContext3D::RenderOffscreen); + if (m_context.layerComposited()) + return; + + m_context.prepareTexture(); + IntSize textureSize(m_context.m_currentWidth, m_context.m_currentHeight); + TextureMapperGL::Flags flags = TextureMapperGL::ShouldFlipTexture | (m_context.m_attrs.alpha ? TextureMapperGL::ShouldBlend : 0); + + { + LockHolder holder(m_platformLayerProxy->lock()); + m_platformLayerProxy->pushNextBuffer(std::make_unique<TextureMapperPlatformLayerBuffer>(m_context.m_compositorTexture, textureSize, flags)); + } + + m_context.markLayerComposited(); +} +#else +void TextureMapperGC3DPlatformLayer::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) +{ + if (!m_glContext) + return; + + ASSERT(m_renderStyle == GraphicsContext3D::RenderOffscreen); + + m_context.markLayerComposited(); + +#if USE(TEXTURE_MAPPER_GL) + if (m_context.m_attrs.antialias && m_context.m_state.boundFBO == m_context.m_multisampleFBO) { + GLContext* previousActiveContext = GLContext::current(); + if (previousActiveContext != m_glContext.get()) + m_context.makeContextCurrent(); + + m_context.resolveMultisamplingIfNecessary(); + ::glBindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_context.m_state.boundFBO); + + if (previousActiveContext && previousActiveContext != m_glContext.get()) + previousActiveContext->makeContextCurrent(); + } + + TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper); + TextureMapperGL::Flags flags = TextureMapperGL::ShouldFlipTexture | (m_context.m_attrs.alpha ? TextureMapperGL::ShouldBlend : 0); + IntSize textureSize(m_context.m_currentWidth, m_context.m_currentHeight); + texmapGL.drawTexture(m_context.m_texture, flags, textureSize, targetRect, matrix, opacity); +#endif // USE(TEXTURE_MAPPER_GL) +} +#endif // USE(COORDINATED_GRAPHICS_THREADED) + +} // namespace WebCore + +#endif // ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER) diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h new file mode 100644 index 000000000..dd3f74fce --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGC3DPlatformLayer.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011, 2012, 2017 Igalia S.L. + * + * 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 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 + */ + +#pragma once + +#if ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER) + +#include "GraphicsContext3D.h" +#include "PlatformLayer.h" +#include "TextureMapperPlatformLayer.h" +#include "TextureMapperPlatformLayerProxy.h" + +namespace WebCore { + +class BitmapTextureGL; +class GLContext; +class TextureMapperPlatformLayerProxy; + +class TextureMapperGC3DPlatformLayer : public PlatformLayer { +public: + TextureMapperGC3DPlatformLayer(GraphicsContext3D&, GraphicsContext3D::RenderStyle); + virtual ~TextureMapperGC3DPlatformLayer(); + + bool makeContextCurrent(); + PlatformGraphicsContext3D platformContext(); + GraphicsContext3D::RenderStyle renderStyle() { return m_renderStyle; } + +#if USE(COORDINATED_GRAPHICS_THREADED) + RefPtr<TextureMapperPlatformLayerProxy> proxy() const override; + void swapBuffersIfNeeded() override; +#else + virtual void paintToTextureMapper(TextureMapper&, const FloatRect& target, const TransformationMatrix&, float opacity); +#endif + +private: + GraphicsContext3D& m_context; + std::unique_ptr<GLContext> m_glContext; + GraphicsContext3D::RenderStyle m_renderStyle; + +#if USE(COORDINATED_GRAPHICS_THREADED) + RefPtr<TextureMapperPlatformLayerProxy> m_platformLayerProxy; + RefPtr<BitmapTextureGL> m_compositorTexture; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(GRAPHICS_CONTEXT_3D) && USE(TEXTURE_MAPPER) diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp index 05b884bd5..7464d047e 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp @@ -22,6 +22,10 @@ #include "config.h" #include "TextureMapperGL.h" +#if USE(TEXTURE_MAPPER_GL) + +#include "BitmapTextureGL.h" +#include "BitmapTexturePool.h" #include "Extensions3D.h" #include "FilterOperations.h" #include "GraphicsContext.h" @@ -31,9 +35,10 @@ #include "TextureMapperShaderProgram.h" #include "Timer.h" #include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> -#include <wtf/TemporaryChange.h> +#include <wtf/SetForScope.h> #if USE(CAIRO) #include "CairoUtilities.h" @@ -42,191 +47,84 @@ #include <wtf/text/CString.h> #endif -#if !USE(TEXMAP_OPENGL_ES_2) -// FIXME: Move to Extensions3D.h. -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 -#define GL_UNPACK_ROW_LENGTH 0x0CF2 -#define GL_UNPACK_SKIP_PIXELS 0x0CF4 -#define GL_UNPACK_SKIP_ROWS 0x0CF3 -#endif - -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - namespace WebCore { -struct TextureMapperGLData { + +class TextureMapperGLData { WTF_MAKE_FAST_ALLOCATED; public: - struct SharedGLData : public RefCounted<SharedGLData> { + explicit TextureMapperGLData(GraphicsContext3D&); + ~TextureMapperGLData(); - typedef HashMap<PlatformGraphicsContext3D, SharedGLData*> GLContextDataMap; - static GLContextDataMap& glContextDataMap() - { - static GLContextDataMap map; - return map; - } + void initializeStencil(); + Platform3DObject getStaticVBO(GC3Denum target, GC3Dsizeiptr, const void* data); + Ref<TextureMapperShaderProgram> getShaderProgram(TextureMapperShaderProgram::Options); + + TransformationMatrix projectionMatrix; + TextureMapper::PaintFlags PaintFlags { 0 }; + GC3Dint previousProgram { 0 }; + GC3Dint targetFrameBuffer { 0 }; + bool didModifyStencil { false }; + GC3Dint previousScissorState { 0 }; + GC3Dint previousDepthState { 0 }; + GC3Dint viewport[4] { 0, }; + GC3Dint previousScissor[4] { 0, }; + RefPtr<BitmapTexture> currentSurface; + const BitmapTextureGL::FilterInfo* filterInfo { nullptr }; - static PassRefPtr<SharedGLData> currentSharedGLData(GraphicsContext3D* context) +private: + class SharedGLData : public RefCounted<SharedGLData> { + public: + static Ref<SharedGLData> currentSharedGLData(GraphicsContext3D& context) { - GLContextDataMap::iterator it = glContextDataMap().find(context->platformGraphicsContext3D()); - if (it != glContextDataMap().end()) - return it->value; + auto it = contextDataMap().find(context.platformGraphicsContext3D()); + if (it != contextDataMap().end()) + return *it->value; - return adoptRef(new SharedGLData(context)); + Ref<SharedGLData> data = adoptRef(*new SharedGLData(context)); + contextDataMap().add(context.platformGraphicsContext3D(), data.ptr()); + return data; } - PassRefPtr<TextureMapperShaderProgram> getShaderProgram(TextureMapperShaderProgram::Options options) + ~SharedGLData() { - HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram> >::AddResult result = m_programs.add(options, nullptr); - if (result.isNewEntry) - result.iterator->value = TextureMapperShaderProgram::create(m_context, options); - - return result.iterator->value; + ASSERT(std::any_of(contextDataMap().begin(), contextDataMap().end(), + [this](auto& entry) { return entry.value == this; })); + contextDataMap().removeIf([this](auto& entry) { return entry.value == this; }); } - HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram> > m_programs; - RefPtr<GraphicsContext3D> m_context; + private: + friend class TextureMapperGLData; - explicit SharedGLData(GraphicsContext3D* context) - : m_context(context) + using GLContextDataMap = HashMap<PlatformGraphicsContext3D, SharedGLData*>; + static GLContextDataMap& contextDataMap() { - glContextDataMap().add(context->platformGraphicsContext3D(), this); + static NeverDestroyed<GLContextDataMap> map; + return map; } - ~SharedGLData() + explicit SharedGLData(GraphicsContext3D& context) { - GLContextDataMap::const_iterator end = glContextDataMap().end(); - GLContextDataMap::iterator it; - for (it = glContextDataMap().begin(); it != end; ++it) { - if (it->value == this) - break; - } - - ASSERT(it != end); - glContextDataMap().remove(it); + contextDataMap().add(context.platformGraphicsContext3D(), this); } - }; - - SharedGLData& sharedGLData() const - { - return *sharedData; - } - void initializeStencil(); - - explicit TextureMapperGLData(GraphicsContext3D* context) - : context(context) - , PaintFlags(0) - , previousProgram(0) - , targetFrameBuffer(0) - , didModifyStencil(false) - , previousScissorState(0) - , previousDepthState(0) - , sharedData(TextureMapperGLData::SharedGLData::currentSharedGLData(this->context)) -#if ENABLE(CSS_FILTERS) - , filterInfo(0) -#endif - { } - - ~TextureMapperGLData(); - Platform3DObject getStaticVBO(GC3Denum target, GC3Dsizeiptr, const void* data); + HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram>> m_programs; + }; - GraphicsContext3D* context; - TransformationMatrix projectionMatrix; - TextureMapper::PaintFlags PaintFlags; - GC3Dint previousProgram; - GC3Dint targetFrameBuffer; - bool didModifyStencil; - GC3Dint previousScissorState; - GC3Dint previousDepthState; - GC3Dint viewport[4]; - GC3Dint previousScissor[4]; - RefPtr<SharedGLData> sharedData; - RefPtr<BitmapTexture> currentSurface; - HashMap<const void*, Platform3DObject> vbos; -#if ENABLE(CSS_FILTERS) - const BitmapTextureGL::FilterInfo* filterInfo; -#endif + GraphicsContext3D& m_context; + Ref<SharedGLData> m_sharedGLData; + HashMap<const void*, Platform3DObject> m_vbos; }; -Platform3DObject TextureMapperGLData::getStaticVBO(GC3Denum target, GC3Dsizeiptr size, const void* data) +TextureMapperGLData::TextureMapperGLData(GraphicsContext3D& context) + : m_context(context) + , m_sharedGLData(SharedGLData::currentSharedGLData(m_context)) { - HashMap<const void*, Platform3DObject>::AddResult result = vbos.add(data, 0); - if (result.isNewEntry) { - Platform3DObject vbo = context->createBuffer(); - context->bindBuffer(target, vbo); - context->bufferData(target, size, data, GraphicsContext3D::STATIC_DRAW); - result.iterator->value = vbo; - } - - return result.iterator->value; } TextureMapperGLData::~TextureMapperGLData() { - HashMap<const void*, Platform3DObject>::iterator end = vbos.end(); - for (HashMap<const void*, Platform3DObject>::iterator it = vbos.begin(); it != end; ++it) - context->deleteBuffer(it->value); -} - -void TextureMapperGL::ClipStack::reset(const IntRect& rect, TextureMapperGL::ClipStack::YAxisMode mode) -{ - clipStack.clear(); - size = rect.size(); - yAxisMode = mode; - clipState = TextureMapperGL::ClipState(rect); - clipStateDirty = true; -} - -void TextureMapperGL::ClipStack::intersect(const IntRect& rect) -{ - clipState.scissorBox.intersect(rect); - clipStateDirty = true; -} - -void TextureMapperGL::ClipStack::setStencilIndex(int stencilIndex) -{ - clipState.stencilIndex = stencilIndex; - clipStateDirty = true; -} - -void TextureMapperGL::ClipStack::push() -{ - clipStack.append(clipState); - clipStateDirty = true; -} - -void TextureMapperGL::ClipStack::pop() -{ - if (clipStack.isEmpty()) - return; - clipState = clipStack.last(); - clipStack.removeLast(); - clipStateDirty = true; -} - -void TextureMapperGL::ClipStack::apply(GraphicsContext3D* context) -{ - if (clipState.scissorBox.isEmpty()) - return; - - context->scissor(clipState.scissorBox.x(), - (yAxisMode == InvertedYAxis) ? size.height() - clipState.scissorBox.maxY() : clipState.scissorBox.y(), - clipState.scissorBox.width(), clipState.scissorBox.height()); - context->stencilOp(GraphicsContext3D::KEEP, GraphicsContext3D::KEEP, GraphicsContext3D::KEEP); - context->stencilFunc(GraphicsContext3D::EQUAL, clipState.stencilIndex - 1, clipState.stencilIndex - 1); - if (clipState.stencilIndex == 1) - context->disable(GraphicsContext3D::STENCIL_TEST); - else - context->enable(GraphicsContext3D::STENCIL_TEST); -} - -void TextureMapperGL::ClipStack::applyIfNeeded(GraphicsContext3D* context) -{ - if (!clipStateDirty) - return; - - clipStateDirty = false; - apply(context); + for (auto& entry : m_vbos) + m_context.deleteBuffer(entry.value); } void TextureMapperGLData::initializeStencil() @@ -239,30 +137,45 @@ void TextureMapperGLData::initializeStencil() if (didModifyStencil) return; - context->clearStencil(0); - context->clear(GraphicsContext3D::STENCIL_BUFFER_BIT); + m_context.clearStencil(0); + m_context.clear(GraphicsContext3D::STENCIL_BUFFER_BIT); didModifyStencil = true; } -BitmapTextureGL* toBitmapTextureGL(BitmapTexture* texture) +Platform3DObject TextureMapperGLData::getStaticVBO(GC3Denum target, GC3Dsizeiptr size, const void* data) { - if (!texture || !texture->isBackedByOpenGL()) - return 0; + auto addResult = m_vbos.ensure(data, + [this, target, size, data] { + Platform3DObject vbo = m_context.createBuffer(); + m_context.bindBuffer(target, vbo); + m_context.bufferData(target, size, data, GraphicsContext3D::STATIC_DRAW); + return vbo; + }); + return addResult.iterator->value; +} - return static_cast<BitmapTextureGL*>(texture); +Ref<TextureMapperShaderProgram> TextureMapperGLData::getShaderProgram(TextureMapperShaderProgram::Options options) +{ + auto addResult = m_sharedGLData->m_programs.ensure(options, + [this, options] { return TextureMapperShaderProgram::create(Ref<GraphicsContext3D>(m_context), options); }); + return *addResult.iterator->value; } TextureMapperGL::TextureMapperGL() - : TextureMapper(OpenGLMode) - , m_enableEdgeDistanceAntialiasing(false) + : m_enableEdgeDistanceAntialiasing(false) { m_context3D = GraphicsContext3D::createForCurrentGLContext(); - m_data = new TextureMapperGLData(m_context3D.get()); + ASSERT(m_context3D); + + m_data = new TextureMapperGLData(*m_context3D); +#if USE(TEXTURE_MAPPER_GL) + m_texturePool = std::make_unique<BitmapTexturePool>(m_context3D.copyRef()); +#endif } -TextureMapperGL::ClipStack& TextureMapperGL::clipStack() +ClipStack& TextureMapperGL::clipStack() { - return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->m_clipStack : m_clipStack; + return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->clipStack() : m_clipStack; } void TextureMapperGL::beginPainting(PaintFlags flags) @@ -276,7 +189,7 @@ void TextureMapperGL::beginPainting(PaintFlags flags) m_context3D->depthMask(0); m_context3D->getIntegerv(GraphicsContext3D::VIEWPORT, data().viewport); m_context3D->getIntegerv(GraphicsContext3D::SCISSOR_BOX, data().previousScissor); - m_clipStack.reset(IntRect(0, 0, data().viewport[2], data().viewport[3]), ClipStack::InvertedYAxis); + m_clipStack.reset(IntRect(0, 0, data().viewport[2], data().viewport[3]), flags & PaintingMirrored ? ClipStack::YAxisMode::Default : ClipStack::YAxisMode::Inverted); m_context3D->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &data().targetFrameBuffer); data().PaintFlags = flags; bindSurface(0); @@ -308,7 +221,7 @@ void TextureMapperGL::drawBorder(const Color& color, float width, const FloatRec if (clipStack().isCurrentScissorBoxEmpty()) return; - RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(TextureMapperShaderProgram::SolidColor); + Ref<TextureMapperShaderProgram> program = data().getShaderProgram(TextureMapperShaderProgram::SolidColor); m_context3D->useProgram(program->programID()); float r, g, b, a; @@ -316,7 +229,7 @@ void TextureMapperGL::drawBorder(const Color& color, float width, const FloatRec m_context3D->uniform4f(program->colorLocation(), r, g, b, a); m_context3D->lineWidth(width); - draw(targetRect, modelViewMatrix, program.get(), GraphicsContext3D::LINE_LOOP, color.hasAlpha() ? ShouldBlend : 0); + draw(targetRect, modelViewMatrix, program.get(), GraphicsContext3D::LINE_LOOP, !color.isOpaque() ? ShouldBlend : 0); } // FIXME: drawNumber() should save a number texture-atlas and re-use whenever possible. @@ -333,9 +246,15 @@ void TextureMapperGL::drawNumber(int number, const Color& color, const FloatPoin cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t* cr = cairo_create(surface); - float r, g, b, a; - color.getRGBA(r, g, b, a); - cairo_set_source_rgba(cr, b, g, r, a); // Since we won't swap R+B when uploading a texture, paint with the swapped R+B color. + // Since we won't swap R+B when uploading a texture, paint with the swapped R+B color. + if (color.isExtended()) + cairo_set_source_rgba(cr, color.asExtended().blue(), color.asExtended().green(), color.asExtended().red(), color.asExtended().alpha()); + else { + float r, g, b, a; + color.getRGBA(r, g, b, a); + cairo_set_source_rgba(cr, b, g, r, a); + } + cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); @@ -367,8 +286,6 @@ void TextureMapperGL::drawNumber(int number, const Color& color, const FloatPoin #endif } -#if ENABLE(CSS_FILTERS) - static TextureMapperShaderProgram::Options optionsForFilterType(FilterOperation::OperationType type, unsigned pass) { switch (type) { @@ -399,27 +316,6 @@ static TextureMapperShaderProgram::Options optionsForFilterType(FilterOperation: } } -static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type) -{ - switch (type) { - case FilterOperation::GRAYSCALE: - case FilterOperation::SEPIA: - case FilterOperation::SATURATE: - case FilterOperation::HUE_ROTATE: - case FilterOperation::INVERT: - case FilterOperation::BRIGHTNESS: - case FilterOperation::CONTRAST: - case FilterOperation::OPACITY: - return 1; - case FilterOperation::BLUR: - case FilterOperation::DROP_SHADOW: - // We use two-passes (vertical+horizontal) for blur and drop-shadow. - return 2; - default: - return 0; - } -} - // Create a normal distribution of 21 values between -2 and 2. static const unsigned GaussianKernelHalfWidth = 11; static const float GaussianKernelStep = 0.2; @@ -453,23 +349,23 @@ static float* gaussianKernel() return kernel; } -static void prepareFilterProgram(TextureMapperShaderProgram* program, const FilterOperation& operation, unsigned pass, const IntSize& size, GC3Duint contentTexture) +static void prepareFilterProgram(TextureMapperShaderProgram& program, const FilterOperation& operation, unsigned pass, const IntSize& size, GC3Duint contentTexture) { - RefPtr<GraphicsContext3D> context = program->context(); - context->useProgram(program->programID()); + Ref<GraphicsContext3D> context = program.context(); + context->useProgram(program.programID()); switch (operation.type()) { case FilterOperation::GRAYSCALE: case FilterOperation::SEPIA: case FilterOperation::SATURATE: case FilterOperation::HUE_ROTATE: - context->uniform1f(program->filterAmountLocation(), static_cast<const BasicColorMatrixFilterOperation&>(operation).amount()); + context->uniform1f(program.filterAmountLocation(), static_cast<const BasicColorMatrixFilterOperation&>(operation).amount()); break; case FilterOperation::INVERT: case FilterOperation::BRIGHTNESS: case FilterOperation::CONTRAST: case FilterOperation::OPACITY: - context->uniform1f(program->filterAmountLocation(), static_cast<const BasicComponentTransferFilterOperation&>(operation).amount()); + context->uniform1f(program.filterAmountLocation(), static_cast<const BasicComponentTransferFilterOperation&>(operation).amount()); break; case FilterOperation::BLUR: { const BlurFilterOperation& blur = static_cast<const BlurFilterOperation&>(operation); @@ -481,29 +377,29 @@ static void prepareFilterProgram(TextureMapperShaderProgram* program, const Filt else radius.setWidth(floatValueForLength(blur.stdDeviation(), size.width()) / size.width()); - context->uniform2f(program->blurRadiusLocation(), radius.width(), radius.height()); - context->uniform1fv(program->gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel()); + context->uniform2f(program.blurRadiusLocation(), radius.width(), radius.height()); + context->uniform1fv(program.gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel()); break; } case FilterOperation::DROP_SHADOW: { const DropShadowFilterOperation& shadow = static_cast<const DropShadowFilterOperation&>(operation); - context->uniform1fv(program->gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel()); + context->uniform1fv(program.gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel()); switch (pass) { case 0: // First pass: horizontal alpha blur. - context->uniform2f(program->blurRadiusLocation(), shadow.stdDeviation() / float(size.width()), 0); - context->uniform2f(program->shadowOffsetLocation(), float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height())); + context->uniform2f(program.blurRadiusLocation(), shadow.stdDeviation() / float(size.width()), 0); + context->uniform2f(program.shadowOffsetLocation(), float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height())); break; case 1: // Second pass: we need the shadow color and the content texture for compositing. float r, g, b, a; Color(premultipliedARGBFromColor(shadow.color())).getRGBA(r, g, b, a); - context->uniform4f(program->colorLocation(), r, g, b, a); - context->uniform2f(program->blurRadiusLocation(), 0, shadow.stdDeviation() / float(size.height())); - context->uniform2f(program->shadowOffsetLocation(), 0, 0); + context->uniform4f(program.colorLocation(), r, g, b, a); + context->uniform2f(program.blurRadiusLocation(), 0, shadow.stdDeviation() / float(size.height())); + context->uniform2f(program.shadowOffsetLocation(), 0, 0); context->activeTexture(GraphicsContext3D::TEXTURE1); context->bindTexture(GraphicsContext3D::TEXTURE_2D, contentTexture); - context->uniform1i(program->contentTextureLocation(), 1); + context->uniform1i(program.contentTextureLocation(), 1); break; } break; @@ -512,7 +408,6 @@ static void prepareFilterProgram(TextureMapperShaderProgram* program, const Filt break; } } -#endif void TextureMapperGL::drawTexture(const BitmapTexture& texture, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, unsigned exposedEdges) { @@ -523,13 +418,21 @@ void TextureMapperGL::drawTexture(const BitmapTexture& texture, const FloatRect& return; const BitmapTextureGL& textureGL = static_cast<const BitmapTextureGL&>(texture); -#if ENABLE(CSS_FILTERS) - TemporaryChange<const BitmapTextureGL::FilterInfo*> filterInfo(data().filterInfo, textureGL.filterInfo()); -#endif + SetForScope<const BitmapTextureGL::FilterInfo*> filterInfo(data().filterInfo, textureGL.filterInfo()); drawTexture(textureGL.id(), textureGL.isOpaque() ? 0 : ShouldBlend, textureGL.size(), targetRect, matrix, opacity, exposedEdges); } +static bool driverSupportsNPOTTextures(GraphicsContext3D& context) +{ + if (context.isGLES2Compliant()) { + static bool supportsNPOTTextures = context.getExtensions().supports("GL_OES_texture_npot"); + return supportsNPOTTextures; + } + + return true; +} + void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const IntSize& textureSize, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity, unsigned exposedEdges) { bool useRect = flags & ShouldUseARBTextureRect; @@ -546,8 +449,9 @@ void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const I options |= TextureMapperShaderProgram::Antialiasing; flags |= ShouldAntialias; } + if (wrapMode() == RepeatWrap && !driverSupportsNPOTTextures(*m_context3D)) + options |= TextureMapperShaderProgram::ManualRepeat; -#if ENABLE(CSS_FILTERS) RefPtr<FilterOperation> filter = data().filterInfo ? data().filterInfo->filter: 0; GC3Duint filterContentTextureID = 0; @@ -558,18 +462,14 @@ void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const I if (filter->affectsOpacity()) flags |= ShouldBlend; } -#endif if (useAntialiasing || opacity < 1) flags |= ShouldBlend; - RefPtr<TextureMapperShaderProgram> program; - program = data().sharedGLData().getShaderProgram(options); + Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options); -#if ENABLE(CSS_FILTERS) if (filter) prepareFilterProgram(program.get(), *filter.get(), data().filterInfo->pass, textureSize, filterContentTextureID); -#endif drawTexturedQuadWithProgram(program.get(), texture, flags, textureSize, targetRect, modelViewMatrix, opacity); } @@ -583,7 +483,7 @@ void TextureMapperGL::drawSolidColor(const FloatRect& rect, const Transformation flags |= ShouldBlend | ShouldAntialias; } - RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(options); + Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options); m_context3D->useProgram(program->programID()); float r, g, b, a; @@ -595,7 +495,7 @@ void TextureMapperGL::drawSolidColor(const FloatRect& rect, const Transformation draw(rect, matrix, program.get(), GraphicsContext3D::TRIANGLE_FAN, flags); } -void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram* program) +void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram& program) { const GC3Dfloat left = 0; const GC3Dfloat top = 0; @@ -620,29 +520,29 @@ void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram* program) Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 48, unitRectSideTriangles); m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo); - m_context3D->vertexAttribPointer(program->vertexLocation(), 4, GraphicsContext3D::FLOAT, false, 0, 0); + m_context3D->vertexAttribPointer(program.vertexLocation(), 4, GraphicsContext3D::FLOAT, false, 0, 0); m_context3D->drawArrays(GraphicsContext3D::TRIANGLES, 0, 12); m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0); } -void TextureMapperGL::drawUnitRect(TextureMapperShaderProgram* program, GC3Denum drawingMode) +void TextureMapperGL::drawUnitRect(TextureMapperShaderProgram& program, GC3Denum drawingMode) { static const GC3Dfloat unitRect[] = { 0, 0, 1, 0, 1, 1, 0, 1 }; Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, unitRect); m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo); - m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0); + m_context3D->vertexAttribPointer(program.vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0); m_context3D->drawArrays(drawingMode, 0, 4); m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0); } -void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram* shaderProgram, GC3Denum drawingMode, Flags flags) +void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram& program, GC3Denum drawingMode, Flags flags) { - TransformationMatrix matrix = - TransformationMatrix(modelViewMatrix).multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), rect)); + TransformationMatrix matrix(modelViewMatrix); + matrix.multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), rect)); - m_context3D->enableVertexAttribArray(shaderProgram->vertexLocation()); - shaderProgram->setMatrix(shaderProgram->modelViewMatrixLocation(), matrix); - shaderProgram->setMatrix(shaderProgram->projectionMatrixLocation(), data().projectionMatrix); + m_context3D->enableVertexAttribArray(program.vertexLocation()); + program.setMatrix(program.modelViewMatrixLocation(), matrix); + program.setMatrix(program.projectionMatrixLocation(), data().projectionMatrix); if (isInMaskMode()) { m_context3D->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::SRC_ALPHA); @@ -656,28 +556,40 @@ void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& mo } if (flags & ShouldAntialias) - drawEdgeTriangles(shaderProgram); + drawEdgeTriangles(program); else - drawUnitRect(shaderProgram, drawingMode); + drawUnitRect(program, drawingMode); - m_context3D->disableVertexAttribArray(shaderProgram->vertexLocation()); + m_context3D->disableVertexAttribArray(program.vertexLocation()); m_context3D->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA); m_context3D->enable(GraphicsContext3D::BLEND); } -void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* program, uint32_t texture, Flags flags, const IntSize& size, const FloatRect& rect, const TransformationMatrix& modelViewMatrix, float opacity) +void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram& program, uint32_t texture, Flags flags, const IntSize& size, const FloatRect& rect, const TransformationMatrix& modelViewMatrix, float opacity) { - m_context3D->useProgram(program->programID()); + m_context3D->useProgram(program.programID()); m_context3D->activeTexture(GraphicsContext3D::TEXTURE0); GC3Denum target = flags & ShouldUseARBTextureRect ? GC3Denum(Extensions3D::TEXTURE_RECTANGLE_ARB) : GC3Denum(GraphicsContext3D::TEXTURE_2D); m_context3D->bindTexture(target, texture); - m_context3D->uniform1i(program->samplerLocation(), 0); - if (wrapMode() == RepeatWrap) { + m_context3D->uniform1i(program.samplerLocation(), 0); + if (wrapMode() == RepeatWrap && driverSupportsNPOTTextures(*m_context3D)) { m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::REPEAT); m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::REPEAT); } TransformationMatrix patternTransform = this->patternTransform(); + if (flags & ShouldRotateTexture90) { + patternTransform.rotate(-90); + patternTransform.translate(-1, 0); + } + if (flags & ShouldRotateTexture180) { + patternTransform.rotate(180); + patternTransform.translate(-1, -1); + } + if (flags & ShouldRotateTexture270) { + patternTransform.rotate(-270); + patternTransform.translate(0, -1); + } if (flags & ShouldFlipTexture) patternTransform.flipY(); if (flags & ShouldUseARBTextureRect) @@ -685,8 +597,8 @@ void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* pr if (flags & ShouldFlipTexture) patternTransform.translate(0, -1); - program->setMatrix(program->textureSpaceMatrixLocation(), patternTransform); - m_context3D->uniform1f(program->opacityLocation(), opacity); + program.setMatrix(program.textureSpaceMatrixLocation(), patternTransform); + m_context3D->uniform1f(program.opacityLocation(), opacity); if (opacity < 1) flags |= ShouldBlend; @@ -696,225 +608,17 @@ void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* pr m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); } -BitmapTextureGL::BitmapTextureGL(TextureMapperGL* textureMapper) - : m_id(0) - , m_fbo(0) - , m_rbo(0) - , m_depthBufferObject(0) - , m_shouldClear(true) - , m_context3D(textureMapper->graphicsContext3D()) -{ -} - -bool BitmapTextureGL::canReuseWith(const IntSize& contentsSize, Flags) -{ - return contentsSize == m_textureSize; -} - -#if OS(DARWIN) -#define DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE GL_UNSIGNED_INT_8_8_8_8_REV -#else -#define DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE GraphicsContext3D::UNSIGNED_BYTE -#endif - -static void swizzleBGRAToRGBA(uint32_t* data, const IntRect& rect, int stride = 0) -{ - stride = stride ? stride : rect.width(); - for (int y = rect.y(); y < rect.maxY(); ++y) { - uint32_t* p = data + y * stride; - for (int x = rect.x(); x < rect.maxX(); ++x) - p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); - } -} - -// If GL_EXT_texture_format_BGRA8888 is supported in the OpenGLES -// internal and external formats need to be BGRA -static bool driverSupportsExternalTextureBGRA(GraphicsContext3D* context) -{ - if (context->isGLES2Compliant()) { - static bool supportsExternalTextureBGRA = context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888"); - return supportsExternalTextureBGRA; - } - - return true; -} - -static bool driverSupportsSubImage(GraphicsContext3D* context) -{ - if (context->isGLES2Compliant()) { - static bool supportsSubImage = context->getExtensions()->supports("GL_EXT_unpack_subimage"); - return supportsSubImage; - } - - return true; -} - -void BitmapTextureGL::didReset() -{ - if (!m_id) - m_id = m_context3D->createTexture(); - - m_shouldClear = true; - if (m_textureSize == contentSize()) - return; - - - m_textureSize = contentSize(); - m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id); - m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); - m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); - m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); - m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); - - Platform3DObject internalFormat = GraphicsContext3D::RGBA; - Platform3DObject externalFormat = GraphicsContext3D::BGRA; - if (m_context3D->isGLES2Compliant()) { - if (driverSupportsExternalTextureBGRA(m_context3D.get())) - internalFormat = GraphicsContext3D::BGRA; - else - externalFormat = GraphicsContext3D::RGBA; - } - - m_context3D->texImage2DDirect(GraphicsContext3D::TEXTURE_2D, 0, internalFormat, m_textureSize.width(), m_textureSize.height(), 0, externalFormat, DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE, 0); -} - -void BitmapTextureGL::updateContentsNoSwizzle(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel, Platform3DObject glFormat) -{ - m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id); - if (driverSupportsSubImage(m_context3D.get())) { // For ES drivers that don't support sub-images. - // Use the OpenGL sub-image extension, now that we know it's available. - m_context3D->pixelStorei(GL_UNPACK_ROW_LENGTH, bytesPerLine / bytesPerPixel); - m_context3D->pixelStorei(GL_UNPACK_SKIP_ROWS, sourceOffset.y()); - m_context3D->pixelStorei(GL_UNPACK_SKIP_PIXELS, sourceOffset.x()); - } - - m_context3D->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), glFormat, DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE, srcData); - - if (driverSupportsSubImage(m_context3D.get())) { // For ES drivers that don't support sub-images. - m_context3D->pixelStorei(GL_UNPACK_ROW_LENGTH, 0); - m_context3D->pixelStorei(GL_UNPACK_SKIP_ROWS, 0); - m_context3D->pixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - } -} - -void BitmapTextureGL::updateContents(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag updateContentsFlag) -{ - Platform3DObject glFormat = GraphicsContext3D::RGBA; - m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id); - - const unsigned bytesPerPixel = 4; - char* data = reinterpret_cast<char*>(const_cast<void*>(srcData)); - Vector<char> temporaryData; - IntPoint adjustedSourceOffset = sourceOffset; - - // Texture upload requires subimage buffer if driver doesn't support subimage and we don't have full image upload. - bool requireSubImageBuffer = !driverSupportsSubImage(m_context3D.get()) - && !(bytesPerLine == static_cast<int>(targetRect.width() * bytesPerPixel) && adjustedSourceOffset == IntPoint::zero()); - - // prepare temporaryData if necessary - if ((!driverSupportsExternalTextureBGRA(m_context3D.get()) && updateContentsFlag == UpdateCannotModifyOriginalImageData) || requireSubImageBuffer) { - temporaryData.resize(targetRect.width() * targetRect.height() * bytesPerPixel); - data = temporaryData.data(); - const char* bits = static_cast<const char*>(srcData); - const char* src = bits + sourceOffset.y() * bytesPerLine + sourceOffset.x() * bytesPerPixel; - char* dst = data; - const int targetBytesPerLine = targetRect.width() * bytesPerPixel; - for (int y = 0; y < targetRect.height(); ++y) { - memcpy(dst, src, targetBytesPerLine); - src += bytesPerLine; - dst += targetBytesPerLine; - } - - bytesPerLine = targetBytesPerLine; - adjustedSourceOffset = IntPoint(0, 0); - } - - if (driverSupportsExternalTextureBGRA(m_context3D.get())) - glFormat = GraphicsContext3D::BGRA; - else - swizzleBGRAToRGBA(reinterpret_cast_ptr<uint32_t*>(data), IntRect(adjustedSourceOffset, targetRect.size()), bytesPerLine / bytesPerPixel); - - updateContentsNoSwizzle(data, targetRect, adjustedSourceOffset, bytesPerLine, bytesPerPixel, glFormat); -} - -void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag) -{ - if (!image) - return; - NativeImagePtr frameImage = image->nativeImageForCurrentFrame(); - if (!frameImage) - return; - - int bytesPerLine; - const char* imageData; - -#if USE(CAIRO) - cairo_surface_t* surface = frameImage.get(); - imageData = reinterpret_cast<const char*>(cairo_image_surface_get_data(surface)); - bytesPerLine = cairo_image_surface_get_stride(surface); -#endif - - updateContents(imageData, targetRect, offset, bytesPerLine, updateContentsFlag); -} - -#if ENABLE(CSS_FILTERS) void TextureMapperGL::drawFiltered(const BitmapTexture& sampler, const BitmapTexture* contentTexture, const FilterOperation& filter, int pass) { // For standard filters, we always draw the whole texture without transformations. TextureMapperShaderProgram::Options options = optionsForFilterType(filter.type(), pass); - RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(options); - ASSERT(program); + Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options); prepareFilterProgram(program.get(), filter, pass, sampler.contentSize(), contentTexture ? static_cast<const BitmapTextureGL*>(contentTexture)->id() : 0); FloatRect targetRect(IntPoint::zero(), sampler.contentSize()); drawTexturedQuadWithProgram(program.get(), static_cast<const BitmapTextureGL&>(sampler).id(), 0, IntSize(1, 1), targetRect, TransformationMatrix(), 1); } -PassRefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper* textureMapper, const FilterOperations& filters) -{ - if (filters.isEmpty()) - return this; - - TextureMapperGL* texmapGL = static_cast<TextureMapperGL*>(textureMapper); - RefPtr<BitmapTexture> previousSurface = texmapGL->data().currentSurface; - RefPtr<BitmapTexture> resultSurface = this; - RefPtr<BitmapTexture> intermediateSurface; - RefPtr<BitmapTexture> spareSurface; - - m_filterInfo = FilterInfo(); - - for (size_t i = 0; i < filters.size(); ++i) { - RefPtr<FilterOperation> filter = filters.operations()[i]; - ASSERT(filter); - - int numPasses = getPassesRequiredForFilter(filter->type()); - for (int j = 0; j < numPasses; ++j) { - bool last = (i == filters.size() - 1) && (j == numPasses - 1); - if (!last) { - if (!intermediateSurface) - intermediateSurface = texmapGL->acquireTextureFromPool(contentSize()); - texmapGL->bindSurface(intermediateSurface.get()); - } - - if (last) { - toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter, j, spareSurface); - break; - } - - texmapGL->drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j); - if (!j && filter->type() == FilterOperation::DROP_SHADOW) { - spareSurface = resultSurface; - resultSurface.clear(); - } - std::swap(resultSurface, intermediateSurface); - } - } - - texmapGL->bindSurface(previousSurface.get()); - return resultSurface; -} -#endif - static inline TransformationMatrix createProjectionMatrix(const IntSize& size, bool mirrored) { const float nearValue = 9999999; @@ -926,95 +630,6 @@ static inline TransformationMatrix createProjectionMatrix(const IntSize& size, b -1, mirrored ? -1 : 1, -(farValue + nearValue) / (farValue - nearValue), 1); } -void BitmapTextureGL::initializeStencil() -{ - if (m_rbo) - return; - - m_rbo = m_context3D->createRenderbuffer(); - m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_rbo); -#ifdef TEXMAP_OPENGL_ES_2 - m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_textureSize.width(), m_textureSize.height()); -#else - m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_STENCIL, m_textureSize.width(), m_textureSize.height()); -#endif - m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); - m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_rbo); - m_context3D->clearStencil(0); - m_context3D->clear(GraphicsContext3D::STENCIL_BUFFER_BIT); -} - -void BitmapTextureGL::initializeDepthBuffer() -{ - if (m_depthBufferObject) - return; - - m_depthBufferObject = m_context3D->createRenderbuffer(); - m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBufferObject); - m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_textureSize.width(), m_textureSize.height()); - m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0); - m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBufferObject); -} - -void BitmapTextureGL::clearIfNeeded() -{ - if (!m_shouldClear) - return; - - m_clipStack.reset(IntRect(IntPoint::zero(), m_textureSize), TextureMapperGL::ClipStack::DefaultYAxis); - m_clipStack.applyIfNeeded(m_context3D.get()); - m_context3D->clearColor(0, 0, 0, 0); - m_context3D->clear(GraphicsContext3D::COLOR_BUFFER_BIT); - m_shouldClear = false; -} - -void BitmapTextureGL::createFboIfNeeded() -{ - if (m_fbo) - return; - - m_fbo = m_context3D->createFramebuffer(); - m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); - m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, id(), 0); - m_shouldClear = true; -} - -void BitmapTextureGL::bind(TextureMapperGL* textureMapper) -{ - m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); - createFboIfNeeded(); - m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); - m_context3D->viewport(0, 0, m_textureSize.width(), m_textureSize.height()); - clearIfNeeded(); - textureMapper->data().projectionMatrix = createProjectionMatrix(m_textureSize, true /* mirrored */); - m_clipStack.apply(m_context3D.get()); -} - -BitmapTextureGL::~BitmapTextureGL() -{ - if (m_id) - m_context3D->deleteTexture(m_id); - - if (m_fbo) - m_context3D->deleteFramebuffer(m_fbo); - - if (m_rbo) - m_context3D->deleteRenderbuffer(m_rbo); - - if (m_depthBufferObject) - m_context3D->deleteRenderbuffer(m_depthBufferObject); -} - -bool BitmapTextureGL::isValid() const -{ - return m_id; -} - -IntSize BitmapTextureGL::size() const -{ - return m_textureSize; -} - TextureMapperGL::~TextureMapperGL() { delete m_data; @@ -1023,11 +638,11 @@ TextureMapperGL::~TextureMapperGL() void TextureMapperGL::bindDefaultSurface() { m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, data().targetFrameBuffer); - IntSize viewportSize(data().viewport[2], data().viewport[3]); - data().projectionMatrix = createProjectionMatrix(viewportSize, data().PaintFlags & PaintingMirrored); - m_context3D->viewport(data().viewport[0], data().viewport[1], viewportSize.width(), viewportSize.height()); - m_clipStack.apply(m_context3D.get()); - data().currentSurface.clear(); + auto& viewport = data().viewport; + data().projectionMatrix = createProjectionMatrix(IntSize(viewport[2], viewport[3]), data().PaintFlags & PaintingMirrored); + m_context3D->viewport(viewport[0], viewport[1], viewport[2], viewport[3]); + m_clipStack.apply(*m_context3D); + data().currentSurface = nullptr; } void TextureMapperGL::bindSurface(BitmapTexture *surface) @@ -1037,10 +652,16 @@ void TextureMapperGL::bindSurface(BitmapTexture *surface) return; } - static_cast<BitmapTextureGL*>(surface)->bind(this); + static_cast<BitmapTextureGL*>(surface)->bindAsSurface(m_context3D.get()); + data().projectionMatrix = createProjectionMatrix(surface->size(), true /* mirrored */); data().currentSurface = surface; } +BitmapTexture* TextureMapperGL::currentSurface() +{ + return data().currentSurface.get(); +} + bool TextureMapperGL::beginScissorClip(const TransformationMatrix& modelViewMatrix, const FloatRect& targetRect) { // 3D transforms are currently not supported in scissor clipping @@ -1056,7 +677,7 @@ bool TextureMapperGL::beginScissorClip(const TransformationMatrix& modelViewMatr return false; clipStack().intersect(rect); - clipStack().applyIfNeeded(m_context3D.get()); + clipStack().applyIfNeeded(*m_context3D); return true; } @@ -1068,15 +689,17 @@ void TextureMapperGL::beginClip(const TransformationMatrix& modelViewMatrix, con data().initializeStencil(); - RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(TextureMapperShaderProgram::SolidColor); + Ref<TextureMapperShaderProgram> program = data().getShaderProgram(TextureMapperShaderProgram::SolidColor); m_context3D->useProgram(program->programID()); m_context3D->enableVertexAttribArray(program->vertexLocation()); const GC3Dfloat unitRect[] = {0, 0, 1, 0, 1, 1, 0, 1}; - m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, GC3Dintptr(unitRect)); + Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, unitRect); + m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo); + m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0); - TransformationMatrix matrix = TransformationMatrix(modelViewMatrix) - .multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect)); + TransformationMatrix matrix(modelViewMatrix); + matrix.multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect)); static const TransformationMatrix fullProjectionMatrix = TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), FloatRect(-1, -1, 2, 2)); @@ -1103,18 +726,19 @@ void TextureMapperGL::beginClip(const TransformationMatrix& modelViewMatrix, con m_context3D->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4); // Clear the state. + m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0); m_context3D->disableVertexAttribArray(program->vertexLocation()); m_context3D->stencilMask(0); // Increase stencilIndex and apply stencil testing. clipStack().setStencilIndex(stencilIndex * 2); - clipStack().applyIfNeeded(m_context3D.get()); + clipStack().applyIfNeeded(*m_context3D); } void TextureMapperGL::endClip() { clipStack().pop(); - clipStack().applyIfNeeded(m_context3D.get()); + clipStack().applyIfNeeded(*m_context3D); } IntRect TextureMapperGL::clipBounds() @@ -1124,14 +748,14 @@ IntRect TextureMapperGL::clipBounds() PassRefPtr<BitmapTexture> TextureMapperGL::createTexture() { - BitmapTextureGL* texture = new BitmapTextureGL(this); - return adoptRef(texture); + return BitmapTextureGL::create(*m_context3D); } -PassOwnPtr<TextureMapper> TextureMapper::platformCreateAccelerated() +std::unique_ptr<TextureMapper> TextureMapper::platformCreateAccelerated() { - return TextureMapperGL::create(); + return std::make_unique<TextureMapperGL>(); } }; -#endif + +#endif // USE(TEXTURE_MAPPER_GL) diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h index 081403f00..acc78e8c8 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h @@ -1,5 +1,6 @@ /* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2015 Igalia S.L. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -20,8 +21,9 @@ #ifndef TextureMapperGL_h #define TextureMapperGL_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) +#if USE(TEXTURE_MAPPER_GL) +#include "ClipStack.h" #include "FilterOperation.h" #include "FloatQuad.h" #include "GraphicsContext3D.h" @@ -38,95 +40,49 @@ class FilterOperation; // An OpenGL-ES2 implementation of TextureMapper. class TextureMapperGL : public TextureMapper { public: - static PassOwnPtr<TextureMapperGL> create() { return adoptPtr(new TextureMapperGL); } + TextureMapperGL(); virtual ~TextureMapperGL(); enum Flag { ShouldBlend = 0x01, ShouldFlipTexture = 0x02, ShouldUseARBTextureRect = 0x04, - ShouldAntialias = 0x08 + ShouldAntialias = 0x08, + ShouldRotateTexture90 = 0x10, + ShouldRotateTexture180 = 0x20, + ShouldRotateTexture270 = 0x40 }; typedef int Flags; // TextureMapper implementation - virtual void drawBorder(const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override; - virtual void drawNumber(int number, const Color&, const FloatPoint&, const TransformationMatrix&) override; - virtual void drawTexture(const BitmapTexture&, const FloatRect&, const TransformationMatrix&, float opacity, unsigned exposedEdges) override; + void drawBorder(const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override; + void drawNumber(int number, const Color&, const FloatPoint&, const TransformationMatrix&) override; + void drawTexture(const BitmapTexture&, const FloatRect&, const TransformationMatrix&, float opacity, unsigned exposedEdges) override; virtual void drawTexture(Platform3DObject texture, Flags, const IntSize& textureSize, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity, unsigned exposedEdges = AllEdges); - virtual void drawSolidColor(const FloatRect&, const TransformationMatrix&, const Color&) override; - - virtual void bindSurface(BitmapTexture* surface) override; - virtual void beginClip(const TransformationMatrix&, const FloatRect&) override; - virtual void beginPainting(PaintFlags = 0) override; - virtual void endPainting() override; - virtual void endClip() override; - virtual IntRect clipBounds() override; - virtual IntSize maxTextureSize() const override { return IntSize(2000, 2000); } - virtual PassRefPtr<BitmapTexture> createTexture() override; + void drawSolidColor(const FloatRect&, const TransformationMatrix&, const Color&) override; + + void bindSurface(BitmapTexture* surface) override; + BitmapTexture* currentSurface(); + void beginClip(const TransformationMatrix&, const FloatRect&) override; + void beginPainting(PaintFlags = 0) override; + void endPainting() override; + void endClip() override; + IntRect clipBounds() override; + IntSize maxTextureSize() const override { return IntSize(2000, 2000); } + PassRefPtr<BitmapTexture> createTexture() override; inline GraphicsContext3D* graphicsContext3D() const { return m_context3D.get(); } -#if ENABLE(CSS_FILTERS) void drawFiltered(const BitmapTexture& sourceTexture, const BitmapTexture* contentTexture, const FilterOperation&, int pass); -#endif void setEnableEdgeDistanceAntialiasing(bool enabled) { m_enableEdgeDistanceAntialiasing = enabled; } private: - struct ClipState { - IntRect scissorBox; - int stencilIndex; - ClipState(const IntRect& scissors = IntRect(), int stencil = 1) - : scissorBox(scissors) - , stencilIndex(stencil) - { } - }; - - class ClipStack { - public: - ClipStack() - : clipStateDirty(false) - { } - - // Y-axis should be inverted only when painting into the window. - enum YAxisMode { - DefaultYAxis, - InvertedYAxis - }; - - void push(); - void pop(); - void apply(GraphicsContext3D*); - void applyIfNeeded(GraphicsContext3D*); - inline ClipState& current() { return clipState; } - void reset(const IntRect&, YAxisMode); - void intersect(const IntRect&); - void setStencilIndex(int); - inline int getStencilIndex() const - { - return clipState.stencilIndex; - } - inline bool isCurrentScissorBoxEmpty() const - { - return clipState.scissorBox.isEmpty(); - } - - private: - ClipState clipState; - Vector<ClipState> clipStack; - bool clipStateDirty; - IntSize size; - YAxisMode yAxisMode; - }; - - TextureMapperGL(); - - void drawTexturedQuadWithProgram(TextureMapperShaderProgram*, uint32_t texture, Flags, const IntSize&, const FloatRect&, const TransformationMatrix& modelViewMatrix, float opacity); - void draw(const FloatRect&, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram*, GC3Denum drawingMode, Flags); + void drawTexturedQuadWithProgram(TextureMapperShaderProgram&, uint32_t texture, Flags, const IntSize&, const FloatRect&, const TransformationMatrix& modelViewMatrix, float opacity); + void draw(const FloatRect&, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram&, GC3Denum drawingMode, Flags); - void drawUnitRect(TextureMapperShaderProgram*, GC3Denum drawingMode); - void drawEdgeTriangles(TextureMapperShaderProgram*); + void drawUnitRect(TextureMapperShaderProgram&, GC3Denum drawingMode); + void drawEdgeTriangles(TextureMapperShaderProgram&); bool beginScissorClip(const TransformationMatrix&, const FloatRect&); void bindDefaultSurface(); @@ -136,72 +92,10 @@ private: TextureMapperGLData* m_data; ClipStack m_clipStack; bool m_enableEdgeDistanceAntialiasing; - - friend class BitmapTextureGL; -}; - -class BitmapTextureGL : public BitmapTexture { -public: - virtual IntSize size() const; - virtual bool isValid() const; - virtual bool canReuseWith(const IntSize& contentsSize, Flags = 0); - virtual void didReset(); - void bind(TextureMapperGL*); - void initializeStencil(); - void initializeDepthBuffer(); - ~BitmapTextureGL(); - virtual uint32_t id() const { return m_id; } - uint32_t textureTarget() const { return GraphicsContext3D::TEXTURE_2D; } - IntSize textureSize() const { return m_textureSize; } - void updateContents(Image*, const IntRect&, const IntPoint&, UpdateContentsFlag); - virtual void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag); - virtual bool isBackedByOpenGL() const { return true; } - -#if ENABLE(CSS_FILTERS) - virtual PassRefPtr<BitmapTexture> applyFilters(TextureMapper*, const FilterOperations&) override; - struct FilterInfo { - RefPtr<FilterOperation> filter; - unsigned pass; - RefPtr<BitmapTexture> contentTexture; - - FilterInfo(PassRefPtr<FilterOperation> f = 0, unsigned p = 0, PassRefPtr<BitmapTexture> t = 0) - : filter(f) - , pass(p) - , contentTexture(t) - { } - }; - const FilterInfo* filterInfo() const { return &m_filterInfo; } -#endif - -private: - void updateContentsNoSwizzle(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel = 4, Platform3DObject glFormat = GraphicsContext3D::RGBA); - - Platform3DObject m_id; - IntSize m_textureSize; - IntRect m_dirtyRect; - Platform3DObject m_fbo; - Platform3DObject m_rbo; - Platform3DObject m_depthBufferObject; - bool m_shouldClear; - TextureMapperGL::ClipStack m_clipStack; - RefPtr<GraphicsContext3D> m_context3D; - - explicit BitmapTextureGL(TextureMapperGL*); - BitmapTextureGL(); - - void clearIfNeeded(); - void createFboIfNeeded(); - -#if ENABLE(CSS_FILTERS) - FilterInfo m_filterInfo; -#endif - - friend class TextureMapperGL; }; -BitmapTextureGL* toBitmapTextureGL(BitmapTexture*); +} // namespace WebCore -} -#endif +#endif // USE(TEXTURE_MAPPER_GL) -#endif +#endif // TextureMapperGL_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp deleted file mode 100644 index 41041f91a..000000000 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - Copyright (C) 2012 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 "TextureMapperImageBuffer.h" - -#include "GraphicsLayer.h" -#include "NotImplemented.h" - -#if USE(TEXTURE_MAPPER) -namespace WebCore { - -static const int s_maximumAllowedImageBufferDimension = 4096; - -void BitmapTextureImageBuffer::updateContents(const void* data, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag) -{ -#if PLATFORM(CAIRO) - RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create_for_data(static_cast<unsigned char*>(data()), - CAIRO_FORMAT_ARGB32, - targetRect.width(), targetRect.height(), - bytesPerLine)); - m_image->context()->platformContext()->drawSurfaceToContext(surface.get(), targetRect, - IntRect(sourceOffset, targetRect.size()), m_image->context()); -#else - UNUSED_PARAM(data); - UNUSED_PARAM(targetRect); - UNUSED_PARAM(sourceOffset); - UNUSED_PARAM(bytesPerLine); -#endif -} - -void BitmapTextureImageBuffer::updateContents(TextureMapper*, GraphicsLayer* sourceLayer, const IntRect& targetRect, const IntPoint& sourceOffset, UpdateContentsFlag) -{ - GraphicsContext* context = m_image->context(); - - context->clearRect(targetRect); - - IntRect sourceRect(targetRect); - sourceRect.setLocation(sourceOffset); - context->save(); - context->clip(targetRect); - context->translate(targetRect.x() - sourceOffset.x(), targetRect.y() - sourceOffset.y()); - sourceLayer->paintGraphicsLayerContents(*context, sourceRect); - context->restore(); -} - -void BitmapTextureImageBuffer::didReset() -{ - m_image = ImageBuffer::create(contentSize()); -} - -void BitmapTextureImageBuffer::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag) -{ - m_image->context()->drawImage(image, ColorSpaceDeviceRGB, targetRect, IntRect(offset, targetRect.size()), CompositeCopy, ImageOrientationDescription()); -} - -IntSize TextureMapperImageBuffer::maxTextureSize() const -{ - return IntSize(s_maximumAllowedImageBufferDimension, s_maximumAllowedImageBufferDimension); -} - -void TextureMapperImageBuffer::beginClip(const TransformationMatrix& matrix, const FloatRect& rect) -{ - GraphicsContext* context = currentContext(); - if (!context) - return; -#if ENABLE(3D_RENDERING) - TransformationMatrix previousTransform = context->get3DTransform(); -#else - AffineTransform previousTransform = context->getCTM(); -#endif - context->save(); - -#if ENABLE(3D_RENDERING) - context->concat3DTransform(matrix); -#else - context->concatCTM(matrix.toAffineTransform()); -#endif - - context->clip(rect); - -#if ENABLE(3D_RENDERING) - context->set3DTransform(previousTransform); -#else - context->setCTM(previousTransform); -#endif -} - -void TextureMapperImageBuffer::drawTexture(const BitmapTexture& texture, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, unsigned /* exposedEdges */) -{ - GraphicsContext* context = currentContext(); - if (!context) - return; - - const BitmapTextureImageBuffer& textureImageBuffer = static_cast<const BitmapTextureImageBuffer&>(texture); - ImageBuffer* image = textureImageBuffer.m_image.get(); - context->save(); - context->setCompositeOperation(isInMaskMode() ? CompositeDestinationIn : CompositeSourceOver); - context->setAlpha(opacity); -#if ENABLE(3D_RENDERING) - context->concat3DTransform(matrix); -#else - context->concatCTM(matrix.toAffineTransform()); -#endif - context->drawImageBuffer(image, ColorSpaceDeviceRGB, targetRect); - context->restore(); -} - -void TextureMapperImageBuffer::drawSolidColor(const FloatRect& rect, const TransformationMatrix& matrix, const Color& color) -{ - GraphicsContext* context = currentContext(); - if (!context) - return; - - context->save(); - context->setCompositeOperation(isInMaskMode() ? CompositeDestinationIn : CompositeSourceOver); -#if ENABLE(3D_RENDERING) - context->concat3DTransform(matrix); -#else - context->concatCTM(matrix.toAffineTransform()); -#endif - - context->fillRect(rect, color, ColorSpaceDeviceRGB); - context->restore(); -} - -void TextureMapperImageBuffer::drawBorder(const Color&, float /* borderWidth */, const FloatRect&, const TransformationMatrix&) -{ - notImplemented(); -} - -void TextureMapperImageBuffer::drawNumber(int /* number */, const Color&, const FloatPoint&, const TransformationMatrix&) -{ - notImplemented(); -} - -#if ENABLE(CSS_FILTERS) -PassRefPtr<BitmapTexture> BitmapTextureImageBuffer::applyFilters(TextureMapper*, const FilterOperations&) -{ - ASSERT_NOT_REACHED(); - return this; -} -#endif - -} -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h deleted file mode 100644 index f5e38b358..000000000 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperImageBuffer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (C) 2012 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 TextureMapperImageBuffer_h -#define TextureMapperImageBuffer_h - -#include "ImageBuffer.h" -#include "TextureMapper.h" - -#if USE(TEXTURE_MAPPER) -namespace WebCore { - -class BitmapTextureImageBuffer : public BitmapTexture { - friend class TextureMapperImageBuffer; -public: - static PassRefPtr<BitmapTexture> create() { return adoptRef(new BitmapTextureImageBuffer); } - virtual IntSize size() const { return m_image->internalSize(); } - virtual void didReset(); - virtual bool isValid() const { return m_image.get(); } - inline GraphicsContext* graphicsContext() { return m_image ? m_image->context() : 0; } - virtual void updateContents(Image*, const IntRect&, const IntPoint&, UpdateContentsFlag); - virtual void updateContents(TextureMapper*, GraphicsLayer*, const IntRect& target, const IntPoint& offset, UpdateContentsFlag); - virtual void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag); -#if ENABLE(CSS_FILTERS) - PassRefPtr<BitmapTexture> applyFilters(TextureMapper*, const FilterOperations&); -#endif - -private: - BitmapTextureImageBuffer() { } - std::unique_ptr<ImageBuffer> m_image; -}; - - -class TextureMapperImageBuffer : public TextureMapper { - WTF_MAKE_FAST_ALLOCATED; -public: - static PassOwnPtr<TextureMapper> create() { return adoptPtr(new TextureMapperImageBuffer); } - - // TextureMapper implementation - virtual void drawBorder(const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override; - virtual void drawNumber(int number, const Color&, const FloatPoint&, const TransformationMatrix&) override; - virtual void drawTexture(const BitmapTexture&, const FloatRect& targetRect, const TransformationMatrix&, float opacity, unsigned exposedEdges) override; - virtual void drawSolidColor(const FloatRect&, const TransformationMatrix&, const Color&) override; - virtual void beginClip(const TransformationMatrix&, const FloatRect&) override; - virtual void bindSurface(BitmapTexture* surface) override { m_currentSurface = surface;} - virtual void endClip() override { graphicsContext()->restore(); } - virtual IntRect clipBounds() override { return currentContext()->clipBounds(); } - virtual IntSize maxTextureSize() const; - virtual PassRefPtr<BitmapTexture> createTexture() override { return BitmapTextureImageBuffer::create(); } - - inline GraphicsContext* currentContext() - { - return m_currentSurface ? static_cast<BitmapTextureImageBuffer*>(m_currentSurface.get())->graphicsContext() : graphicsContext(); - } - -private: - TextureMapperImageBuffer() - : TextureMapper(SoftwareMode) - { } - RefPtr<BitmapTexture> m_currentSurface; -}; - -} -#endif // USE(TEXTURE_MAPPER) - -#endif // TextureMapperImageBuffer_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp index 7e7babbcd..d39cffcbb 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp @@ -21,35 +21,25 @@ #include "TextureMapperLayer.h" #include "FloatQuad.h" +#include "GraphicsLayerTextureMapper.h" #include "Region.h" #include <wtf/MathExtras.h> -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - namespace WebCore { class TextureMapperPaintOptions { public: - RefPtr<BitmapTexture> surface; - float opacity; + TextureMapperPaintOptions(TextureMapper& textureMapper) + : textureMapper(textureMapper) + { } + + TextureMapper& textureMapper; TransformationMatrix transform; + RefPtr<BitmapTexture> surface; + float opacity { 1 }; IntSize offset; - TextureMapper* textureMapper; - TextureMapperPaintOptions() - : opacity(1) - , textureMapper(0) - { } }; -const TextureMapperLayer* TextureMapperLayer::rootLayer() const -{ - if (m_effectTarget) - return m_effectTarget->rootLayer(); - if (m_parent) - return m_parent->rootLayer(); - return this; -} - void TextureMapperLayer::computeTransformsRecursive() { if (m_state.size.isEmpty() && m_state.masksToBounds) @@ -72,9 +62,9 @@ void TextureMapperLayer::computeTransformsRecursive() m_state.maskLayer->computeTransformsRecursive(); if (m_state.replicaLayer) m_state.replicaLayer->computeTransformsRecursive(); - for (size_t i = 0; i < m_children.size(); ++i) { - RELEASE_ASSERT(m_children[i]->m_parent == this); - m_children[i]->computeTransformsRecursive(); + for (auto* child : m_children) { + ASSERT(child->m_parent == this); + child->computeTransformsRecursive(); } // Reorder children if needed on the way back up. @@ -86,19 +76,19 @@ void TextureMapperLayer::paint() { computeTransformsRecursive(); - TextureMapperPaintOptions options; - options.textureMapper = m_textureMapper; - options.textureMapper->bindSurface(0); + ASSERT(m_textureMapper); + TextureMapperPaintOptions options(*m_textureMapper); + options.textureMapper.bindSurface(0); + paintRecursive(options); } static Color blendWithOpacity(const Color& color, float opacity) { - RGBA32 rgba = color.rgb(); - // See Color::getRGBA() to know how to extract alpha from color. - float alpha = alphaChannel(rgba) / 255.; - float effectiveAlpha = alpha * opacity; - return Color(colorWithOverrideAlpha(rgba, effectiveAlpha)); + if (color.isOpaque() && opacity == 1.) + return color; + + return color.colorWithAlphaMultipliedBy(opacity); } void TextureMapperLayer::computePatternTransformIfNeeded() @@ -108,8 +98,8 @@ void TextureMapperLayer::computePatternTransformIfNeeded() m_patternTransformDirty = false; m_patternTransform = - TransformationMatrix::rectToRect(FloatRect(FloatPoint::zero(), m_state.contentsTileSize), m_state.contentsRect) - .multiply(TransformationMatrix().translate(m_state.contentsTilePhase.x() / m_state.contentsRect.width(), m_state.contentsTilePhase.y() / m_state.contentsRect.height())); + TransformationMatrix::rectToRect(FloatRect(FloatPoint::zero(), m_state.contentsTileSize), FloatRect(FloatPoint::zero(), m_state.contentsRect.size())) + .multiply(TransformationMatrix().translate(m_state.contentsTilePhase.width() / m_state.contentsRect.width(), m_state.contentsTilePhase.height() / m_state.contentsRect.height())); } void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) @@ -123,15 +113,15 @@ void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) transform.multiply(options.transform); transform.multiply(m_currentTransform.combined()); - if (m_state.solidColor.isValid() && !m_state.contentsRect.isEmpty() && m_state.solidColor.alpha()) { - options.textureMapper->drawSolidColor(m_state.contentsRect, transform, blendWithOpacity(m_state.solidColor, options.opacity)); + if (m_state.solidColor.isValid() && !m_state.contentsRect.isEmpty() && m_state.solidColor.isVisible()) { + options.textureMapper.drawSolidColor(m_state.contentsRect, transform, blendWithOpacity(m_state.solidColor, options.opacity)); if (m_state.showDebugBorders) - options.textureMapper->drawBorder(m_state.debugBorderColor, m_state.debugBorderWidth, layerRect(), transform); + options.textureMapper.drawBorder(m_state.debugBorderColor, m_state.debugBorderWidth, layerRect(), transform); return; } - options.textureMapper->setWrapMode(TextureMapper::StretchWrap); - options.textureMapper->setPatternTransform(TransformationMatrix()); + options.textureMapper.setWrapMode(TextureMapper::StretchWrap); + options.textureMapper.setPatternTransform(TransformationMatrix()); if (m_backingStore) { FloatRect targetRect = layerRect(); @@ -149,8 +139,8 @@ void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) if (!m_state.contentsTileSize.isEmpty()) { computePatternTransformIfNeeded(); - options.textureMapper->setWrapMode(TextureMapper::RepeatWrap); - options.textureMapper->setPatternTransform(m_patternTransform); + options.textureMapper.setWrapMode(TextureMapper::RepeatWrap); + options.textureMapper.setPatternTransform(m_patternTransform); } ASSERT(!layerRect().isEmpty()); @@ -159,16 +149,12 @@ void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) m_contentsLayer->drawBorder(options.textureMapper, m_state.debugBorderColor, m_state.debugBorderWidth, m_state.contentsRect, transform); } -int TextureMapperLayer::compareGraphicsLayersZValue(const void* a, const void* b) -{ - TextureMapperLayer* const* layerA = static_cast<TextureMapperLayer* const*>(a); - TextureMapperLayer* const* layerB = static_cast<TextureMapperLayer* const*>(b); - return int(((*layerA)->m_centerZ - (*layerB)->m_centerZ) * 1000); -} - void TextureMapperLayer::sortByZOrder(Vector<TextureMapperLayer* >& array) { - qsort(array.data(), array.size(), sizeof(TextureMapperLayer*), compareGraphicsLayersZValue); + std::sort(array.begin(), array.end(), + [](TextureMapperLayer* a, TextureMapperLayer* b) { + return a->m_centerZ < b->m_centerZ; + }); } void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& options) @@ -184,14 +170,14 @@ void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& o clipTransform.translate(options.offset.width(), options.offset.height()); clipTransform.multiply(options.transform); clipTransform.multiply(m_currentTransform.combined()); - options.textureMapper->beginClip(clipTransform, layerRect()); + options.textureMapper.beginClip(clipTransform, layerRect()); } - for (size_t i = 0; i < m_children.size(); ++i) - m_children[i]->paintRecursive(options); + for (auto* child : m_children) + child->paintRecursive(options); if (shouldClip) - options.textureMapper->endClip(); + options.textureMapper.endClip(); } bool TextureMapperLayer::shouldBlend() const @@ -224,7 +210,7 @@ void TextureMapperLayer::paintSelfAndChildrenWithReplica(const TextureMapperPain TextureMapperPaintOptions replicaOptions(options); replicaOptions.transform .multiply(m_state.replicaLayer->m_currentTransform.combined()) - .multiply(m_currentTransform.combined().inverse()); + .multiply(m_currentTransform.combined().inverse().value_or(TransformationMatrix())); paintSelfAndChildren(replicaOptions); } @@ -243,17 +229,15 @@ void TextureMapperLayer::setAnimatedOpacity(float opacity) TransformationMatrix TextureMapperLayer::replicaTransform() { - return TransformationMatrix(m_state.replicaLayer->m_currentTransform.combined()).multiply(m_currentTransform.combined().inverse()); + return TransformationMatrix(m_state.replicaLayer->m_currentTransform.combined()).multiply(m_currentTransform.combined().inverse().value_or(TransformationMatrix())); } -#if ENABLE(CSS_FILTERS) void TextureMapperLayer::setAnimatedFilters(const FilterOperations& filters) { m_currentFilters = filters; } -#endif -static void resolveOverlaps(Region newRegion, Region& overlapRegion, Region& nonOverlapRegion) +static void resolveOverlaps(Region& newRegion, Region& overlapRegion, Region& nonOverlapRegion) { Region newOverlapRegion(newRegion); newOverlapRegion.intersect(nonOverlapRegion); @@ -271,10 +255,9 @@ void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& no FloatRect boundingRect; if (m_backingStore || m_state.masksToBounds || m_state.maskLayer || hasFilters()) boundingRect = layerRect(); - else if (m_contentsLayer || m_state.solidColor.alpha()) + else if (m_contentsLayer || m_state.solidColor.isVisible()) boundingRect = m_state.contentsRect; -#if ENABLE(CSS_FILTERS) if (m_currentFilters.hasOutsets()) { FilterOutsets outsets = m_currentFilters.outsets(); IntRect unfilteredTargetRect(boundingRect); @@ -282,7 +265,6 @@ void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& no boundingRect.expand(outsets.left() + outsets.right(), outsets.top() + outsets.bottom()); boundingRect.unite(unfilteredTargetRect); } -#endif TransformationMatrix replicaMatrix; if (m_state.replicaLayer) { @@ -304,10 +286,8 @@ void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& no Region newNonOverlapRegion(enclosingIntRect(boundingRect)); if (!m_state.masksToBounds) { - for (size_t i = 0; i < m_children.size(); ++i) { - TextureMapperLayer* child = m_children[i]; + for (auto* child : m_children) child->computeOverlapRegions(newOverlapRegion, newNonOverlapRegion, ResolveSelfOverlapIfNeeded); - } } if (m_state.replicaLayer) { @@ -345,14 +325,13 @@ void TextureMapperLayer::paintUsingOverlapRegions(const TextureMapperPaintOption nonOverlapRegion.translate(options.offset); Vector<IntRect> rects = nonOverlapRegion.rects(); - for (size_t i = 0; i < rects.size(); ++i) { - IntRect rect = rects[i]; - if (!rect.intersects(options.textureMapper->clipBounds())) + for (auto& rect : rects) { + if (!rect.intersects(options.textureMapper.clipBounds())) continue; - options.textureMapper->beginClip(TransformationMatrix(), rects[i]); + options.textureMapper.beginClip(TransformationMatrix(), rect); paintSelfAndChildrenWithReplica(options); - options.textureMapper->endClip(); + options.textureMapper.endClip(); } rects = overlapRegion.rects(); @@ -362,11 +341,10 @@ void TextureMapperLayer::paintUsingOverlapRegions(const TextureMapperPaintOption rects.append(overlapRegion.bounds()); } - IntSize maxTextureSize = options.textureMapper->maxTextureSize(); - IntRect adjustedClipBounds(options.textureMapper->clipBounds()); + IntSize maxTextureSize = options.textureMapper.maxTextureSize(); + IntRect adjustedClipBounds(options.textureMapper.clipBounds()); adjustedClipBounds.move(-options.offset); - for (size_t i = 0; i < rects.size(); ++i) { - IntRect rect = rects[i]; + for (auto& rect : rects) { for (int x = rect.x(); x < rect.maxX(); x += maxTextureSize.width()) { for (int y = rect.y(); y < rect.maxY(); y += maxTextureSize.height()) { IntRect tileRect(IntPoint(x, y), maxTextureSize); @@ -382,34 +360,32 @@ void TextureMapperLayer::paintUsingOverlapRegions(const TextureMapperPaintOption void TextureMapperLayer::applyMask(const TextureMapperPaintOptions& options) { - options.textureMapper->setMaskMode(true); + options.textureMapper.setMaskMode(true); paintSelf(options); - options.textureMapper->setMaskMode(false); + options.textureMapper.setMaskMode(false); } PassRefPtr<BitmapTexture> TextureMapperLayer::paintIntoSurface(const TextureMapperPaintOptions& options, const IntSize& size) { - RefPtr<BitmapTexture> surface = options.textureMapper->acquireTextureFromPool(size); + RefPtr<BitmapTexture> surface = options.textureMapper.acquireTextureFromPool(size, BitmapTexture::SupportsAlpha | BitmapTexture::FBOAttachment); TextureMapperPaintOptions paintOptions(options); paintOptions.surface = surface; - options.textureMapper->bindSurface(surface.get()); + options.textureMapper.bindSurface(surface.get()); paintSelfAndChildren(paintOptions); if (m_state.maskLayer) m_state.maskLayer->applyMask(options); -#if ENABLE(CSS_FILTERS) surface = surface->applyFilters(options.textureMapper, m_currentFilters); -#endif - options.textureMapper->bindSurface(surface.get()); - return surface; + options.textureMapper.bindSurface(surface.get()); + return surface.release(); } static void commitSurface(const TextureMapperPaintOptions& options, PassRefPtr<BitmapTexture> surface, const IntRect& rect, float opacity) { - options.textureMapper->bindSurface(options.surface.get()); + options.textureMapper.bindSurface(options.surface.get()); TransformationMatrix targetTransform; targetTransform.translate(options.offset.width(), options.offset.height()); targetTransform.multiply(options.transform); - options.textureMapper->drawTexture(*surface.get(), rect, targetTransform, opacity); + options.textureMapper.drawTexture(*surface.get(), rect, targetTransform, opacity); } void TextureMapperLayer::paintWithIntermediateSurface(const TextureMapperPaintOptions& options, const IntRect& rect) @@ -430,13 +406,13 @@ void TextureMapperLayer::paintWithIntermediateSurface(const TextureMapperPaintOp if (replicaSurface && options.opacity == 1) { commitSurface(options, replicaSurface, rect, 1); - replicaSurface.clear(); + replicaSurface = nullptr; } mainSurface = paintIntoSurface(paintOptions, rect.size()); if (replicaSurface) { - options.textureMapper->bindSurface(replicaSurface.get()); - options.textureMapper->drawTexture(*mainSurface.get(), FloatRect(FloatPoint::zero(), rect.size())); + options.textureMapper.bindSurface(replicaSurface.get()); + options.textureMapper.drawTexture(*mainSurface.get(), FloatRect(FloatPoint::zero(), rect.size())); mainSurface = replicaSurface; } @@ -461,23 +437,33 @@ void TextureMapperLayer::paintRecursive(const TextureMapperPaintOptions& options TextureMapperLayer::~TextureMapperLayer() { - for (int i = m_children.size() - 1; i >= 0; --i) - m_children[i]->m_parent = 0; + for (auto* child : m_children) + child->m_parent = nullptr; - if (m_parent) - m_parent->m_children.remove(m_parent->m_children.find(this)); + removeFromParent(); + + if (m_effectTarget) { + if (m_effectTarget->m_state.maskLayer == this) + m_effectTarget->m_state.maskLayer = nullptr; + if (m_effectTarget->m_state.replicaLayer == this) + m_effectTarget->m_state.replicaLayer = nullptr; + } } -TextureMapper* TextureMapperLayer::textureMapper() const +#if !USE(COORDINATED_GRAPHICS) +void TextureMapperLayer::setChildren(const Vector<GraphicsLayer*>& newChildren) { - return rootLayer()->m_textureMapper; + removeAllChildren(); + for (auto* child : newChildren) + addChild(&downcast<GraphicsLayerTextureMapper>(child)->layer()); } +#endif void TextureMapperLayer::setChildren(const Vector<TextureMapperLayer*>& newChildren) { removeAllChildren(); - for (size_t i = 0; i < newChildren.size(); ++i) - addChild(newChildren[i]); + for (auto* child : newChildren) + addChild(child); } void TextureMapperLayer::addChild(TextureMapperLayer* childLayer) @@ -494,25 +480,19 @@ void TextureMapperLayer::addChild(TextureMapperLayer* childLayer) void TextureMapperLayer::removeFromParent() { if (m_parent) { - unsigned i; - for (i = 0; i < m_parent->m_children.size(); i++) { - if (this == m_parent->m_children[i]) { - m_parent->m_children.remove(i); - break; - } - } - - m_parent = 0; + size_t index = m_parent->m_children.find(this); + ASSERT(index != notFound); + m_parent->m_children.remove(index); } + + m_parent = nullptr; } void TextureMapperLayer::removeAllChildren() { - while (m_children.size()) { - TextureMapperLayer* curLayer = m_children[0]; - ASSERT(curLayer->m_parent); - curLayer->removeFromParent(); - } + auto oldChildren = WTFMove(m_children); + for (auto* child : oldChildren) + child->m_parent = nullptr; } void TextureMapperLayer::setMaskLayer(TextureMapperLayer* maskLayer) @@ -565,7 +545,7 @@ void TextureMapperLayer::setChildrenTransform(const TransformationMatrix& childr m_currentTransform.setChildrenTransform(childrenTransform); } -void TextureMapperLayer::setContentsRect(const IntRect& contentsRect) +void TextureMapperLayer::setContentsRect(const FloatRect& contentsRect) { if (contentsRect == m_state.contentsRect) return; @@ -573,7 +553,7 @@ void TextureMapperLayer::setContentsRect(const IntRect& contentsRect) m_patternTransformDirty = true; } -void TextureMapperLayer::setContentsTileSize(const IntSize& size) +void TextureMapperLayer::setContentsTileSize(const FloatSize& size) { if (size == m_state.contentsTileSize) return; @@ -581,7 +561,7 @@ void TextureMapperLayer::setContentsTileSize(const IntSize& size) m_patternTransformDirty = true; } -void TextureMapperLayer::setContentsTilePhase(const IntPoint& phase) +void TextureMapperLayer::setContentsTilePhase(const FloatSize& phase) { if (phase == m_state.contentsTilePhase) return; @@ -624,12 +604,10 @@ void TextureMapperLayer::setSolidColor(const Color& color) m_state.solidColor = color; } -#if ENABLE(CSS_FILTERS) void TextureMapperLayer::setFilters(const FilterOperations& filters) { m_state.filters = filters; } -#endif void TextureMapperLayer::setDebugVisuals(bool showDebugBorders, const Color& debugBorderColor, float debugBorderWidth, bool showRepaintCounter) { @@ -649,7 +627,7 @@ void TextureMapperLayer::setContentsLayer(TextureMapperPlatformLayer* platformLa m_contentsLayer = platformLayer; } -void TextureMapperLayer::setAnimations(const GraphicsLayerAnimations& animations) +void TextureMapperLayer::setAnimations(const TextureMapperAnimations& animations) { m_animations = animations; } @@ -669,33 +647,29 @@ bool TextureMapperLayer::descendantsOrSelfHaveRunningAnimations() const if (m_animations.hasRunningAnimations()) return true; - for (size_t i = 0; i < m_children.size(); ++i) { - if (m_children[i]->descendantsOrSelfHaveRunningAnimations()) - return true; - } - - return false; + return std::any_of(m_children.begin(), m_children.end(), + [](TextureMapperLayer* child) { + return child->descendantsOrSelfHaveRunningAnimations(); + }); } void TextureMapperLayer::applyAnimationsRecursively() { syncAnimations(); - for (size_t i = 0; i < m_children.size(); ++i) - m_children[i]->applyAnimationsRecursively(); + for (auto* child : m_children) + child->applyAnimationsRecursively(); } void TextureMapperLayer::syncAnimations() { - m_animations.apply(this); - if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyWebkitTransform)) + m_animations.apply(*this); + if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform)) m_currentTransform.setLocalTransform(m_state.transform); if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyOpacity)) m_currentOpacity = m_state.opacity; -#if ENABLE(CSS_FILTERS) - if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyWebkitFilter)) + if (!m_animations.hasActiveAnimationsOfType(AnimatedPropertyFilter)) m_currentFilters = m_state.filters; -#endif } bool TextureMapperLayer::isAncestorFixedToViewport() const @@ -756,7 +730,7 @@ TextureMapperLayer* TextureMapperLayer::findScrollableContentsLayerAt(const Floa FloatSize TextureMapperLayer::mapScrollOffset(const FloatSize& offset) { double zeroX, zeroY, offsetX, offsetY; - TransformationMatrix transform = m_currentTransform.combined().inverse(); + TransformationMatrix transform = m_currentTransform.combined().inverse().value_or(TransformationMatrix()); transform.map(0, 0, zeroX, zeroY); transform.map(offset.width(), offset.height(), offsetX, offsetY); return FloatSize(offsetX - zeroX, offsetY - zeroY); @@ -795,4 +769,3 @@ void TextureMapperLayer::didCommitScrollOffset(const IntSize& offset) } } -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h index 87ff3f4a7..3b1c16b0b 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h @@ -20,22 +20,21 @@ #ifndef TextureMapperLayer_h #define TextureMapperLayer_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - #include "FilterOperations.h" #include "FloatRect.h" -#include "GraphicsLayerAnimation.h" #include "GraphicsLayerTransform.h" #include "TextureMapper.h" +#include "TextureMapperAnimation.h" #include "TextureMapperBackingStore.h" namespace WebCore { +class GraphicsLayer; class Region; class TextureMapperPaintOptions; class TextureMapperPlatformLayer; -class TextureMapperLayer : public GraphicsLayerAnimation::Client { +class TextureMapperLayer : public TextureMapperAnimation::Client { WTF_MAKE_NONCOPYABLE(TextureMapperLayer); WTF_MAKE_FAST_ALLOCATED; public: @@ -74,9 +73,12 @@ public: void setIsScrollable(bool isScrollable) { m_isScrollable = isScrollable; } bool isScrollable() const { return m_isScrollable; } - TextureMapper* textureMapper() const; + TextureMapper* textureMapper() const { return rootLayer().m_textureMapper; } void setTextureMapper(TextureMapper* texmap) { m_textureMapper = texmap; } +#if !USE(COORDINATED_GRAPHICS) + void setChildren(const Vector<GraphicsLayer*>&); +#endif void setChildren(const Vector<TextureMapperLayer*>&); void setMaskLayer(TextureMapperLayer*); void setReplicaLayer(TextureMapperLayer*); @@ -86,7 +88,7 @@ public: void setPreserves3D(bool); void setTransform(const TransformationMatrix&); void setChildrenTransform(const TransformationMatrix&); - void setContentsRect(const IntRect&); + void setContentsRect(const FloatRect&); void setMasksToBounds(bool); void setDrawsContent(bool); bool drawsContent() const { return m_state.drawsContent; } @@ -99,26 +101,20 @@ public: void setBackfaceVisibility(bool); void setOpacity(float); void setSolidColor(const Color&); - void setContentsTileSize(const IntSize&); - void setContentsTilePhase(const IntPoint&); -#if ENABLE(CSS_FILTERS) + void setContentsTileSize(const FloatSize&); + void setContentsTilePhase(const FloatSize&); void setFilters(const FilterOperations&); -#endif bool hasFilters() const { -#if ENABLE(CSS_FILTERS) return !m_currentFilters.isEmpty(); -#else - return false; -#endif } void setDebugVisuals(bool showDebugBorders, const Color& debugBorderColor, float debugBorderWidth, bool showRepaintCounter); bool isShowingRepaintCounter() const { return m_state.showRepaintCounter; } void setRepaintCount(int); void setContentsLayer(TextureMapperPlatformLayer*); - void setAnimations(const GraphicsLayerAnimations&); + void setAnimations(const TextureMapperAnimations&); void setFixedToViewport(bool); bool fixedToViewport() const { return m_fixedToViewport; } void setBackingStore(PassRefPtr<TextureMapperBackingStore>); @@ -134,13 +130,19 @@ public: void addChild(TextureMapperLayer*); private: - const TextureMapperLayer* rootLayer() const; + const TextureMapperLayer& rootLayer() const + { + if (m_effectTarget) + return m_effectTarget->rootLayer(); + if (m_parent) + return m_parent->rootLayer(); + return *this; + } void computeTransformsRecursive(); - static int compareGraphicsLayersZValue(const void* a, const void* b); static void sortByZOrder(Vector<TextureMapperLayer* >& array); - PassRefPtr<BitmapTexture> texture() { return m_backingStore ? m_backingStore->texture() : 0; } + RefPtr<BitmapTexture> texture() { return m_backingStore ? m_backingStore->texture() : 0; } FloatPoint adjustedPosition() const { return m_state.pos + m_scrollPositionDelta - m_userScrollOffset; } bool isAncestorFixedToViewport() const; TransformationMatrix replicaTransform(); @@ -163,12 +165,10 @@ private: void applyMask(const TextureMapperPaintOptions&); void computePatternTransformIfNeeded(); - // GraphicsLayerAnimation::Client - virtual void setAnimatedTransform(const TransformationMatrix&) override; - virtual void setAnimatedOpacity(float) override; -#if ENABLE(CSS_FILTERS) - virtual void setAnimatedFilters(const FilterOperations&) override; -#endif + // TextureMapperAnimation::Client + void setAnimatedTransform(const TransformationMatrix&) override; + void setAnimatedOpacity(float) override; + void setAnimatedFilters(const FilterOperations&) override; bool isVisible() const; enum ContentsLayerCount { @@ -191,9 +191,7 @@ private: TextureMapperPlatformLayer* m_contentsLayer; GraphicsLayerTransform m_currentTransform; float m_currentOpacity; -#if ENABLE(CSS_FILTERS) FilterOperations m_currentFilters; -#endif float m_centerZ; template<class HitTestCondition> TextureMapperLayer* hitTest(const FloatPoint&, HitTestCondition); @@ -210,14 +208,12 @@ private: TransformationMatrix childrenTransform; float opacity; FloatRect contentsRect; - IntSize contentsTileSize; - IntPoint contentsTilePhase; + FloatSize contentsTileSize; + FloatSize contentsTilePhase; TextureMapperLayer* maskLayer; TextureMapperLayer* replicaLayer; Color solidColor; -#if ENABLE(CSS_FILTERS) FilterOperations filters; -#endif Color debugBorderColor; float debugBorderWidth; int repaintCount; @@ -253,7 +249,7 @@ private: State m_state; TextureMapper* m_textureMapper; - GraphicsLayerAnimations m_animations; + TextureMapperAnimations m_animations; FloatSize m_scrollPositionDelta; bool m_fixedToViewport; uint32_t m_id; @@ -266,6 +262,5 @@ private: }; } -#endif #endif // TextureMapperLayer_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h index 2482e0e94..ddacbfd0a 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h @@ -20,9 +20,7 @@ #ifndef TextureMapperPlatformLayer_h #define TextureMapperPlatformLayer_h -#if USE(GRAPHICS_SURFACE) -#include "GraphicsSurface.h" -#endif +#if USE(TEXTURE_MAPPER) #include "TextureMapper.h" #include "TransformationMatrix.h" @@ -39,22 +37,16 @@ public: TextureMapperPlatformLayer() : m_client(0) { } virtual ~TextureMapperPlatformLayer() { } - virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) = 0; + virtual void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) = 0; virtual void swapBuffers() { } - virtual void drawBorder(TextureMapper* textureMapper, const Color& color, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform) + virtual void drawBorder(TextureMapper& textureMapper, const Color& color, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform) { - textureMapper->drawBorder(color, borderWidth, targetRect, transform); + textureMapper.drawBorder(color, borderWidth, targetRect, transform); } void setClient(TextureMapperPlatformLayer::Client* client) { m_client = client; } -#if USE(GRAPHICS_SURFACE) - virtual IntSize platformLayerSize() const { return IntSize(); } - virtual uint32_t copyToGraphicsSurface() { return 0; } - virtual GraphicsSurfaceToken graphicsSurfaceToken() const { return GraphicsSurfaceToken(); } - virtual GraphicsSurface::Flags graphicsSurfaceFlags() const { return GraphicsSurface::SupportsTextureTarget | GraphicsSurface::SupportsSharing; } -#endif protected: TextureMapperPlatformLayer::Client* client() { return m_client; } @@ -65,4 +57,6 @@ private: }; +#endif // USE(TEXTURE_MAPPER) + #endif // TextureMapperPlatformLayer_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp new file mode 100644 index 000000000..88851fe4b --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextureMapperPlatformLayerBuffer.h" + +namespace WebCore { + +TextureMapperPlatformLayerBuffer::TextureMapperPlatformLayerBuffer(RefPtr<BitmapTexture>&& texture) + : m_texture(WTFMove(texture)) + , m_textureID(0) + , m_extraFlags(0) + , m_hasManagedTexture(true) +{ +} + +TextureMapperPlatformLayerBuffer::TextureMapperPlatformLayerBuffer(GLuint textureID, const IntSize& size, TextureMapperGL::Flags flags) + : m_textureID(textureID) + , m_size(size) + , m_extraFlags(flags) + , m_hasManagedTexture(false) +{ +} + +bool TextureMapperPlatformLayerBuffer::canReuseWithoutReset(const IntSize& size, GC3Dint internalFormat) +{ + return m_texture && (m_texture->size() == size) && (static_cast<BitmapTextureGL*>(m_texture.get())->internalFormat() == internalFormat || internalFormat == GraphicsContext3D::DONT_CARE); +} + +void TextureMapperPlatformLayerBuffer::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity) +{ + TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper); + + if (m_hasManagedTexture) { + ASSERT(m_texture); + BitmapTextureGL* textureGL = static_cast<BitmapTextureGL*>(m_texture.get()); + texmapGL.drawTexture(textureGL->id(), m_extraFlags, textureGL->size(), targetRect, modelViewMatrix, opacity); + return; + } + + ASSERT(m_textureID); + texmapGL.drawTexture(m_textureID, m_extraFlags, m_size, targetRect, modelViewMatrix, opacity); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h new file mode 100644 index 000000000..2afe5b308 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerBuffer.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "BitmapTextureGL.h" +#include "GraphicsTypes3D.h" +#include "TextureMapperPlatformLayer.h" +#include <wtf/CurrentTime.h> + +#if USE(COORDINATED_GRAPHICS_THREADED) + +namespace WebCore { + +class TextureMapperPlatformLayerBuffer : public TextureMapperPlatformLayer { + WTF_MAKE_NONCOPYABLE(TextureMapperPlatformLayerBuffer); + WTF_MAKE_FAST_ALLOCATED(); +public: + TextureMapperPlatformLayerBuffer(RefPtr<BitmapTexture>&&); + TextureMapperPlatformLayerBuffer(GLuint textureID, const IntSize&, TextureMapperGL::Flags); + + virtual ~TextureMapperPlatformLayerBuffer() = default; + + void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) final; + + bool canReuseWithoutReset(const IntSize&, GC3Dint internalFormat); + BitmapTextureGL& textureGL() { return static_cast<BitmapTextureGL&>(*m_texture); } + + inline void markUsed() { m_timeLastUsed = monotonicallyIncreasingTime(); } + double lastUsedTime() const { return m_timeLastUsed; } + + class UnmanagedBufferDataHolder { + WTF_MAKE_NONCOPYABLE(UnmanagedBufferDataHolder); + WTF_MAKE_FAST_ALLOCATED(); + public: + UnmanagedBufferDataHolder() = default; + virtual ~UnmanagedBufferDataHolder() = default; + }; + + bool hasManagedTexture() const { return m_hasManagedTexture; } + void setUnmanagedBufferDataHolder(std::unique_ptr<UnmanagedBufferDataHolder> holder) { m_unmanagedBufferDataHolder = WTFMove(holder); } + void setExtraFlags(TextureMapperGL::Flags flags) { m_extraFlags = flags; } + +private: + + RefPtr<BitmapTexture> m_texture; + double m_timeLastUsed { 0 }; + + GLuint m_textureID; + IntSize m_size; + TextureMapperGL::Flags m_extraFlags; + bool m_hasManagedTexture; + std::unique_ptr<UnmanagedBufferDataHolder> m_unmanagedBufferDataHolder; +}; + +} // namespace WebCore + +#endif // COORDINATED_GRAPHICS_THREADED diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp new file mode 100644 index 000000000..a3ea24754 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextureMapperPlatformLayerProxy.h" + +#if USE(COORDINATED_GRAPHICS_THREADED) + +#include "BitmapTextureGL.h" +#include "TextureMapperGL.h" +#include "TextureMapperLayer.h" +#include "TextureMapperPlatformLayerBuffer.h" + +const double s_releaseUnusedSecondsTolerance = 1; +const double s_releaseUnusedBuffersTimerInterval = 0.5; + +namespace WebCore { + +TextureMapperPlatformLayerProxy::TextureMapperPlatformLayerProxy() + : m_compositor(nullptr) + , m_targetLayer(nullptr) + , m_releaseUnusedBuffersTimer(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired) +{ +} + +TextureMapperPlatformLayerProxy::~TextureMapperPlatformLayerProxy() +{ + LockHolder locker(m_lock); + if (m_targetLayer) + m_targetLayer->setContentsLayer(nullptr); +} + +void TextureMapperPlatformLayerProxy::activateOnCompositingThread(Compositor* compositor, TextureMapperLayer* targetLayer) +{ +#ifndef NDEBUG + m_compositorThreadID = m_compositorThreadID ? m_compositorThreadID : WTF::currentThread(); +#endif + ASSERT(m_compositorThreadID == WTF::currentThread()); + ASSERT(compositor); + ASSERT(targetLayer); + LockHolder locker(m_lock); + m_compositor = compositor; + m_targetLayer = targetLayer; + if (m_targetLayer && m_currentBuffer) + m_targetLayer->setContentsLayer(m_currentBuffer.get()); + + m_compositorThreadUpdateTimer = std::make_unique<RunLoop::Timer<TextureMapperPlatformLayerProxy>>(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired); +} + +void TextureMapperPlatformLayerProxy::invalidate() +{ + ASSERT(m_compositorThreadID == WTF::currentThread()); + Function<void()> updateFunction; + { + LockHolder locker(m_lock); + m_compositor = nullptr; + m_targetLayer = nullptr; + + m_currentBuffer = nullptr; + m_pendingBuffer = nullptr; + m_releaseUnusedBuffersTimer.stop(); + m_usedBuffers.clear(); + + // Clear the timer and dispatch the update function manually now. + m_compositorThreadUpdateTimer = nullptr; + if (!m_compositorThreadUpdateFunction) + return; + updateFunction = WTFMove(m_compositorThreadUpdateFunction); + } + + updateFunction(); +} + +bool TextureMapperPlatformLayerProxy::isActive() +{ + ASSERT(m_lock.isHeld()); + return !!m_targetLayer && !!m_compositor; +} + +void TextureMapperPlatformLayerProxy::pushNextBuffer(std::unique_ptr<TextureMapperPlatformLayerBuffer> newBuffer) +{ + ASSERT(m_lock.isHeld()); + m_pendingBuffer = WTFMove(newBuffer); + + if (m_compositor) + m_compositor->onNewBufferAvailable(); +} + +std::unique_ptr<TextureMapperPlatformLayerBuffer> TextureMapperPlatformLayerProxy::getAvailableBuffer(const IntSize& size, GC3Dint internalFormat) +{ + ASSERT(m_lock.isHeld()); + std::unique_ptr<TextureMapperPlatformLayerBuffer> availableBuffer; + + auto buffers = WTFMove(m_usedBuffers); + for (auto& buffer : buffers) { + if (!buffer) + continue; + + if (!availableBuffer && buffer->canReuseWithoutReset(size, internalFormat)) { + availableBuffer = WTFMove(buffer); + availableBuffer->markUsed(); + continue; + } + m_usedBuffers.append(WTFMove(buffer)); + } + + if (!m_usedBuffers.isEmpty()) + scheduleReleaseUnusedBuffers(); + return availableBuffer; +} + +void TextureMapperPlatformLayerProxy::scheduleReleaseUnusedBuffers() +{ + if (!m_releaseUnusedBuffersTimer.isActive()) + m_releaseUnusedBuffersTimer.startOneShot(s_releaseUnusedBuffersTimerInterval); +} + +void TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired() +{ + LockHolder locker(m_lock); + if (m_usedBuffers.isEmpty()) + return; + + auto buffers = WTFMove(m_usedBuffers); + double minUsedTime = monotonicallyIncreasingTime() - s_releaseUnusedSecondsTolerance; + + for (auto& buffer : buffers) { + if (buffer && buffer->lastUsedTime() >= minUsedTime) + m_usedBuffers.append(WTFMove(buffer)); + } +} + +void TextureMapperPlatformLayerProxy::swapBuffer() +{ + ASSERT(m_compositorThreadID == WTF::currentThread()); + LockHolder locker(m_lock); + if (!m_targetLayer || !m_pendingBuffer) + return; + + auto prevBuffer = WTFMove(m_currentBuffer); + + m_currentBuffer = WTFMove(m_pendingBuffer); + m_targetLayer->setContentsLayer(m_currentBuffer.get()); + + if (prevBuffer && prevBuffer->hasManagedTexture()) + m_usedBuffers.append(WTFMove(prevBuffer)); +} + +bool TextureMapperPlatformLayerProxy::scheduleUpdateOnCompositorThread(Function<void()>&& updateFunction) +{ + LockHolder locker(m_lock); + if (!m_compositorThreadUpdateTimer) + return false; + + m_compositorThreadUpdateFunction = WTFMove(updateFunction); + m_compositorThreadUpdateTimer->startOneShot(0); + return true; +} + +void TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired() +{ + Function<void()> updateFunction; + { + LockHolder locker(m_lock); + if (!m_compositorThreadUpdateFunction) + return; + updateFunction = WTFMove(m_compositorThreadUpdateFunction); + } + + updateFunction(); +} + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS_THREADED) diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h new file mode 100644 index 000000000..b710ab4ce --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperPlatformLayerProxy.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextureMapperPlatformLayerProxy_h +#define TextureMapperPlatformLayerProxy_h + +#if USE(COORDINATED_GRAPHICS_THREADED) + +#include "GraphicsTypes3D.h" +#include "TextureMapper.h" +#include "TransformationMatrix.h" +#include <wtf/Condition.h> +#include <wtf/Function.h> +#include <wtf/Lock.h> +#include <wtf/RunLoop.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/Vector.h> + +#ifndef NDEBUG +#include <wtf/Threading.h> +#endif + +namespace WebCore { + +class TextureMapperLayer; +class TextureMapperPlatformLayerProxy; +class TextureMapperPlatformLayerBuffer; + +class TextureMapperPlatformLayerProxyProvider { +public: + virtual RefPtr<TextureMapperPlatformLayerProxy> proxy() const = 0; + virtual void swapBuffersIfNeeded() = 0; +}; + +class TextureMapperPlatformLayerProxy : public ThreadSafeRefCounted<TextureMapperPlatformLayerProxy> { + WTF_MAKE_FAST_ALLOCATED(); +public: + class Compositor { + public: + virtual void onNewBufferAvailable() = 0; + }; + + TextureMapperPlatformLayerProxy(); + virtual ~TextureMapperPlatformLayerProxy(); + + // To avoid multiple lock/release situation to update a single frame, + // the implementation of TextureMapperPlatformLayerProxyProvider should + // aquire / release the lock explicitly to use below methods. + Lock& lock() { return m_lock; } + std::unique_ptr<TextureMapperPlatformLayerBuffer> getAvailableBuffer(const IntSize&, GC3Dint internalFormat); + void pushNextBuffer(std::unique_ptr<TextureMapperPlatformLayerBuffer>); + bool isActive(); + + void activateOnCompositingThread(Compositor*, TextureMapperLayer*); + void invalidate(); + + void swapBuffer(); + + bool scheduleUpdateOnCompositorThread(Function<void()>&&); + +private: + void scheduleReleaseUnusedBuffers(); + void releaseUnusedBuffersTimerFired(); + + Compositor* m_compositor; + TextureMapperLayer* m_targetLayer; + + std::unique_ptr<TextureMapperPlatformLayerBuffer> m_currentBuffer; + std::unique_ptr<TextureMapperPlatformLayerBuffer> m_pendingBuffer; + + Lock m_lock; + + Vector<std::unique_ptr<TextureMapperPlatformLayerBuffer>> m_usedBuffers; + + RunLoop::Timer<TextureMapperPlatformLayerProxy> m_releaseUnusedBuffersTimer; +#ifndef NDEBUG + ThreadIdentifier m_compositorThreadID { 0 }; +#endif + + void compositorThreadUpdateTimerFired(); + std::unique_ptr<RunLoop::Timer<TextureMapperPlatformLayerProxy>> m_compositorThreadUpdateTimer; + Function<void()> m_compositorThreadUpdateFunction; +}; + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS_THREADED) + +#endif // TextureMapperPlatformLayerProxy_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp index 5c0a76db6..b0c371b99 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.cpp @@ -22,19 +22,12 @@ #include "config.h" #include "TextureMapperShaderProgram.h" -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) -#include "LengthFunctions.h" +#if USE(TEXTURE_MAPPER_GL) + #include "Logging.h" #include "TextureMapperGL.h" - #include <wtf/text/StringBuilder.h> -#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS)) -#undef NO_ERROR -#endif - -#define STRINGIFY(...) #__VA_ARGS__ - namespace WebCore { static inline bool compositingLogEnabled() @@ -46,85 +39,21 @@ static inline bool compositingLogEnabled() #endif } -TextureMapperShaderProgram::TextureMapperShaderProgram(PassRefPtr<GraphicsContext3D> context, const String& vertex, const String& fragment) - : m_context(context) -{ - m_vertexShader = m_context->createShader(GraphicsContext3D::VERTEX_SHADER); - m_fragmentShader = m_context->createShader(GraphicsContext3D::FRAGMENT_SHADER); - m_context->shaderSource(m_vertexShader, vertex); - m_context->shaderSource(m_fragmentShader, fragment); - m_id = m_context->createProgram(); - m_context->compileShader(m_vertexShader); - m_context->compileShader(m_fragmentShader); - m_context->attachShader(m_id, m_vertexShader); - m_context->attachShader(m_id, m_fragmentShader); - m_context->linkProgram(m_id); - - if (!compositingLogEnabled()) - return; - - if (m_context->getError() == GraphicsContext3D::NO_ERROR) - return; - - String log = m_context->getShaderInfoLog(m_vertexShader); - LOG(Compositing, "Vertex shader log: %s\n", log.utf8().data()); - log = m_context->getShaderInfoLog(m_fragmentShader); - LOG(Compositing, "Fragment shader log: %s\n", log.utf8().data()); - log = m_context->getProgramInfoLog(m_id); - LOG(Compositing, "Program log: %s\n", log.utf8().data()); -} - -void TextureMapperShaderProgram::setMatrix(GC3Duint location, const TransformationMatrix& matrix) -{ - GC3Dfloat matrixAsFloats[] = { - GC3Dfloat(matrix.m11()), GC3Dfloat(matrix.m12()), GC3Dfloat(matrix.m13()), GC3Dfloat(matrix.m14()), - GC3Dfloat(matrix.m21()), GC3Dfloat(matrix.m22()), GC3Dfloat(matrix.m23()), GC3Dfloat(matrix.m24()), - GC3Dfloat(matrix.m31()), GC3Dfloat(matrix.m32()), GC3Dfloat(matrix.m33()), GC3Dfloat(matrix.m34()), - GC3Dfloat(matrix.m41()), GC3Dfloat(matrix.m42()), GC3Dfloat(matrix.m43()), GC3Dfloat(matrix.m44()) - }; - - m_context->uniformMatrix4fv(location, 1, false, matrixAsFloats); -} - -GC3Duint TextureMapperShaderProgram::getLocation(const AtomicString& name, VariableType type) -{ - HashMap<AtomicString, GC3Duint>::iterator it = m_variables.find(name); - if (it != m_variables.end()) - return it->value; - - GC3Duint location = 0; - switch (type) { - case UniformVariable: - location = m_context->getUniformLocation(m_id, name); - break; - case AttribVariable: - location = m_context->getAttribLocation(m_id, name); - break; - default: - ASSERT_NOT_REACHED(); - break; - } - - m_variables.add(name, location); - return location; -} +#define STRINGIFY(...) #__VA_ARGS__ -TextureMapperShaderProgram::~TextureMapperShaderProgram() -{ - Platform3DObject programID = m_id; - if (!programID) - return; +#define GLSL_DIRECTIVE(...) "#"#__VA_ARGS__"\n" - m_context->detachShader(programID, m_vertexShader); - m_context->deleteShader(m_vertexShader); - m_context->detachShader(programID, m_fragmentShader); - m_context->deleteShader(m_fragmentShader); - m_context->deleteProgram(programID); -} +#define TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE \ + GLSL_DIRECTIVE(ifdef GL_FRAGMENT_PRECISION_HIGH) \ + GLSL_DIRECTIVE(define TextureSpaceMatrixPrecision highp) \ + GLSL_DIRECTIVE(else) \ + GLSL_DIRECTIVE(define TextureSpaceMatrixPrecision mediump) \ + GLSL_DIRECTIVE(endif) -#define GLSL_DIRECTIVE(...) "#"#__VA_ARGS__"\n" static const char* vertexTemplate = + TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE STRINGIFY( + precision TextureSpaceMatrixPrecision float; attribute vec4 a_vertex; uniform mat4 u_modelViewMatrix; uniform mat4 u_projectionMatrix; @@ -211,7 +140,10 @@ static const char* fragmentTemplate = RECT_TEXTURE_DIRECTIVE ANTIALIASING_TEX_COORD_DIRECTIVE BLUR_CONSTANTS + TEXTURE_SPACE_MATRIX_PRECISION_DIRECTIVE STRINGIFY( + precision TextureSpaceMatrixPrecision float; + uniform mat4 u_textureSpaceMatrix; precision mediump float; uniform SamplerType s_sampler; uniform sampler2D s_contentTexture; @@ -224,10 +156,10 @@ static const char* fragmentTemplate = uniform vec2 u_shadowOffset; uniform vec4 u_color; uniform float u_gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH]; - uniform mat4 u_textureSpaceMatrix; void noop(inout vec4 dummyParameter) { } void noop(inout vec4 dummyParameter, vec2 texCoord) { } + void noop(inout vec2 dummyParameter) { } float antialias() { return smoothstep(0., 1., v_antialias); } @@ -239,6 +171,8 @@ static const char* fragmentTemplate = vec2 vertexTransformTexCoord() { return v_transformedTexCoord; } + void applyManualRepeat(inout vec2 pos) { pos = fract(pos); } + void applyTexture(inout vec4 color, vec2 texCoord) { color = SamplerFunction(s_sampler, texCoord); } void applyOpacity(inout vec4 color) { color *= u_opacity; } void applyAntialiasing(inout vec4 color) { color *= antialias(); } @@ -350,6 +284,7 @@ static const char* fragmentTemplate = { vec4 color = vec4(1., 1., 1., 1.); vec2 texCoord = transformTexCoord(); + applyManualRepeatIfNeeded(texCoord); applyTextureIfNeeded(color, texCoord); applySolidColorIfNeeded(color); applyAntialiasingIfNeeded(color); @@ -369,13 +304,13 @@ static const char* fragmentTemplate = } ); -PassRefPtr<TextureMapperShaderProgram> TextureMapperShaderProgram::create(PassRefPtr<GraphicsContext3D> context, TextureMapperShaderProgram::Options options) +Ref<TextureMapperShaderProgram> TextureMapperShaderProgram::create(Ref<GraphicsContext3D>&& context, TextureMapperShaderProgram::Options options) { - StringBuilder shaderBuilder; #define SET_APPLIER_FROM_OPTIONS(Applier) \ - shaderBuilder.append(\ + optionsApplierBuilder.append(\ (options & TextureMapperShaderProgram::Applier) ? ENABLE_APPLIER(Applier) : DISABLE_APPLIER(Applier)) + StringBuilder optionsApplierBuilder; SET_APPLIER_FROM_OPTIONS(Texture); SET_APPLIER_FROM_OPTIONS(Rect); SET_APPLIER_FROM_OPTIONS(SolidColor); @@ -392,16 +327,84 @@ PassRefPtr<TextureMapperShaderProgram> TextureMapperShaderProgram::create(PassRe SET_APPLIER_FROM_OPTIONS(BlurFilter); SET_APPLIER_FROM_OPTIONS(AlphaBlur); SET_APPLIER_FROM_OPTIONS(ContentTexture); - StringBuilder vertexBuilder; - vertexBuilder.append(shaderBuilder.toString()); - vertexBuilder.append(vertexTemplate); - shaderBuilder.append(fragmentTemplate); + SET_APPLIER_FROM_OPTIONS(ManualRepeat); + + StringBuilder vertexShaderBuilder; + vertexShaderBuilder.append(optionsApplierBuilder.toString()); + vertexShaderBuilder.append(vertexTemplate); + + StringBuilder fragmentShaderBuilder; + fragmentShaderBuilder.append(optionsApplierBuilder.toString()); + fragmentShaderBuilder.append(fragmentTemplate); + + return adoptRef(*new TextureMapperShaderProgram(WTFMove(context), vertexShaderBuilder.toString(), fragmentShaderBuilder.toString())); +} + +TextureMapperShaderProgram::TextureMapperShaderProgram(Ref<GraphicsContext3D>&& context, const String& vertex, const String& fragment) + : m_context(WTFMove(context)) +{ + m_vertexShader = m_context->createShader(GraphicsContext3D::VERTEX_SHADER); + m_context->shaderSource(m_vertexShader, vertex); + + m_fragmentShader = m_context->createShader(GraphicsContext3D::FRAGMENT_SHADER); + m_context->shaderSource(m_fragmentShader, fragment); + + m_id = m_context->createProgram(); + m_context->compileShader(m_vertexShader); + m_context->compileShader(m_fragmentShader); + m_context->attachShader(m_id, m_vertexShader); + m_context->attachShader(m_id, m_fragmentShader); + m_context->linkProgram(m_id); + + if (!compositingLogEnabled()) + return; + + if (m_context->getError() == GraphicsContext3D::NO_ERROR) + return; - String vertexSource = vertexBuilder.toString(); - String fragmentSource = shaderBuilder.toString(); + String log = m_context->getShaderInfoLog(m_vertexShader); + LOG(Compositing, "Vertex shader log: %s\n", log.utf8().data()); + log = m_context->getShaderInfoLog(m_fragmentShader); + LOG(Compositing, "Fragment shader log: %s\n", log.utf8().data()); + log = m_context->getProgramInfoLog(m_id); + LOG(Compositing, "Program log: %s\n", log.utf8().data()); +} + +TextureMapperShaderProgram::~TextureMapperShaderProgram() +{ + if (!m_id) + return; - return adoptRef(new TextureMapperShaderProgram(context, vertexSource, fragmentSource)); + m_context->detachShader(m_id, m_vertexShader); + m_context->deleteShader(m_vertexShader); + m_context->detachShader(m_id, m_fragmentShader); + m_context->deleteShader(m_fragmentShader); + m_context->deleteProgram(m_id); } +void TextureMapperShaderProgram::setMatrix(GC3Duint location, const TransformationMatrix& matrix) +{ + TransformationMatrix::FloatMatrix4 floatMatrix; + matrix.toColumnMajorFloatArray(floatMatrix); + m_context->uniformMatrix4fv(location, 1, false, floatMatrix); } -#endif + +GC3Duint TextureMapperShaderProgram::getLocation(const AtomicString& name, VariableType type) +{ + auto addResult = m_variables.ensure(name, + [this, &name, type] { + switch (type) { + case UniformVariable: + return m_context->getUniformLocation(m_id, name); + case AttribVariable: + return m_context->getAttribLocation(m_id, name); + } + ASSERT_NOT_REACHED(); + return 0; + }); + return addResult.iterator->value; +} + +} // namespace WebCore + +#endif // USE(TEXTURE_MAPPER_GL) diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h index 3345fc9fe..541a42b27 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperShaderProgram.h @@ -21,16 +21,23 @@ #ifndef TextureMapperShaderProgram_h #define TextureMapperShaderProgram_h -#if USE(TEXTURE_MAPPER) +#if USE(TEXTURE_MAPPER_GL) + #include "GraphicsContext3D.h" #include "TransformationMatrix.h" #include <wtf/HashMap.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/Ref.h> #include <wtf/text/AtomicStringHash.h> namespace WebCore { -#define TEXMAP_DECLARE_VARIABLE(Accessor, Name, Type) GC3Duint Accessor##Location() { static const AtomicString name(Name); return getLocation(name, Type); } + +#define TEXMAP_DECLARE_VARIABLE(Accessor, Name, Type) \ + GC3Duint Accessor##Location() { \ + static NeverDestroyed<const AtomicString> name(Name, AtomicString::ConstructFromLiteral); \ + return getLocation(name.get(), Type); \ + } + #define TEXMAP_DECLARE_UNIFORM(Accessor) TEXMAP_DECLARE_VARIABLE(Accessor, "u_"#Accessor, UniformVariable) #define TEXMAP_DECLARE_ATTRIBUTE(Accessor) TEXMAP_DECLARE_VARIABLE(Accessor, "a_"#Accessor, AttribVariable) #define TEXMAP_DECLARE_SAMPLER(Accessor) TEXMAP_DECLARE_VARIABLE(Accessor, "s_"#Accessor, UniformVariable) @@ -53,15 +60,17 @@ public: OpacityFilter = 1L << 13, BlurFilter = 1L << 14, AlphaBlur = 1L << 15, - ContentTexture = 1L << 16 + ContentTexture = 1L << 16, + ManualRepeat = 1L << 17 }; typedef unsigned Options; - static PassRefPtr<TextureMapperShaderProgram> create(PassRefPtr<GraphicsContext3D>, Options); + static Ref<TextureMapperShaderProgram> create(Ref<GraphicsContext3D>&&, Options); virtual ~TextureMapperShaderProgram(); + Platform3DObject programID() const { return m_id; } - GraphicsContext3D* context() { return m_context.get(); } + GraphicsContext3D& context() { return m_context; } TEXMAP_DECLARE_ATTRIBUTE(vertex) @@ -74,30 +83,29 @@ public: TEXMAP_DECLARE_SAMPLER(sampler) TEXMAP_DECLARE_SAMPLER(mask) -#if ENABLE(CSS_FILTERS) TEXMAP_DECLARE_UNIFORM(filterAmount) TEXMAP_DECLARE_UNIFORM(gaussianKernel) TEXMAP_DECLARE_UNIFORM(blurRadius) TEXMAP_DECLARE_UNIFORM(shadowOffset) TEXMAP_DECLARE_SAMPLER(contentTexture) -#endif void setMatrix(GC3Duint, const TransformationMatrix&); private: - TextureMapperShaderProgram(PassRefPtr<GraphicsContext3D>, const String& vertexShaderSource, const String& fragmentShaderSource); + TextureMapperShaderProgram(Ref<GraphicsContext3D>&&, const String& vertexShaderSource, const String& fragmentShaderSource); + Platform3DObject m_vertexShader; Platform3DObject m_fragmentShader; enum VariableType { UniformVariable, AttribVariable }; GC3Duint getLocation(const AtomicString&, VariableType); - RefPtr<GraphicsContext3D> m_context; + Ref<GraphicsContext3D> m_context; Platform3DObject m_id; HashMap<AtomicString, GC3Duint> m_variables; }; } -#endif +#endif // USE(TEXTURE_MAPPER_GL) #endif // TextureMapperShaderProgram_h diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp deleted file mode 100644 index 8d38776e9..000000000 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright (C) 2013 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" - -#if USE(ACCELERATED_COMPOSITING) && USE(GRAPHICS_SURFACE) -#include "TextureMapperSurfaceBackingStore.h" - -#include "GraphicsSurface.h" - -namespace WebCore { - -void TextureMapperSurfaceBackingStore::setGraphicsSurface(PassRefPtr<GraphicsSurface> surface) -{ - m_graphicsSurface = surface; -} - -void TextureMapperSurfaceBackingStore::swapBuffersIfNeeded(uint32_t) -{ - if (m_graphicsSurface) - m_graphicsSurface->swapBuffers(); -} - -PassRefPtr<BitmapTexture> TextureMapperSurfaceBackingStore::texture() const -{ - // FIXME: Instead of just returning an empty texture, we should wrap the texture contents into a BitmapTexture. - RefPtr<BitmapTexture> emptyTexture; - return emptyTexture; -} - -void TextureMapperSurfaceBackingStore::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity) -{ - if (m_graphicsSurface) - m_graphicsSurface->paintToTextureMapper(textureMapper, targetRect, transform, opacity); -} - -} // namespace WebCore -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h b/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h deleted file mode 100644 index 793c61dc3..000000000 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperSurfaceBackingStore.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (C) 2013 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 TextureMapperSurfaceBackingStore_h -#define TextureMapperSurfaceBackingStore_h - -#if USE(ACCELERATED_COMPOSITING) && USE(GRAPHICS_SURFACE) - -#include "GraphicsSurface.h" -#include "TextureMapperBackingStore.h" -#include <wtf/RefPtr.h> - -namespace WebCore { - -class TextureMapper; -class FloatRect; - -class TextureMapperSurfaceBackingStore : public TextureMapperBackingStore { -public: - static PassRefPtr<TextureMapperSurfaceBackingStore> create() { return adoptRef(new TextureMapperSurfaceBackingStore); } - void setGraphicsSurface(PassRefPtr<GraphicsSurface>); - void swapBuffersIfNeeded(uint32_t frontBuffer); - virtual PassRefPtr<BitmapTexture> texture() const; - virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float); - virtual ~TextureMapperSurfaceBackingStore() { } - -private: - TextureMapperSurfaceBackingStore() - : TextureMapperBackingStore() - { } - - RefPtr<GraphicsSurface> m_graphicsSurface; -}; - -} - -#endif - -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp index 2ea7fb6ed..39365673e 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.cpp @@ -18,18 +18,16 @@ */ #include "config.h" -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) #include "TextureMapperTile.h" #include "Image.h" #include "TextureMapper.h" -#include <wtf/RefPtr.h> namespace WebCore { class GraphicsLayer; -void TextureMapperTile::updateContents(TextureMapper* textureMapper, Image* image, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) +void TextureMapperTile::updateContents(TextureMapper& textureMapper, Image* image, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) { IntRect targetRect = enclosingIntRect(m_rect); targetRect.intersect(dirtyRect); @@ -43,14 +41,14 @@ void TextureMapperTile::updateContents(TextureMapper* textureMapper, Image* imag // Normalize targetRect to the texture's coordinates. targetRect.move(-m_rect.x(), -m_rect.y()); if (!m_texture) { - m_texture = textureMapper->createTexture(); + m_texture = textureMapper.createTexture(); m_texture->reset(targetRect.size(), image->currentFrameKnownToBeOpaque() ? 0 : BitmapTexture::SupportsAlpha); } m_texture->updateContents(image, targetRect, sourceOffset, updateContentsFlag); } -void TextureMapperTile::updateContents(TextureMapper* textureMapper, GraphicsLayer* sourceLayer, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) +void TextureMapperTile::updateContents(TextureMapper& textureMapper, GraphicsLayer* sourceLayer, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag, float scale) { IntRect targetRect = enclosingIntRect(m_rect); targetRect.intersect(dirtyRect); @@ -62,18 +60,17 @@ void TextureMapperTile::updateContents(TextureMapper* textureMapper, GraphicsLay targetRect.move(-m_rect.x(), -m_rect.y()); if (!m_texture) { - m_texture = textureMapper->createTexture(); + m_texture = textureMapper.createTexture(); m_texture->reset(targetRect.size(), BitmapTexture::SupportsAlpha); } - m_texture->updateContents(textureMapper, sourceLayer, targetRect, sourceOffset, updateContentsFlag); + m_texture->updateContents(textureMapper, sourceLayer, targetRect, sourceOffset, updateContentsFlag, scale); } -void TextureMapperTile::paint(TextureMapper* textureMapper, const TransformationMatrix& transform, float opacity, const unsigned exposedEdges) +void TextureMapperTile::paint(TextureMapper& textureMapper, const TransformationMatrix& transform, float opacity, const unsigned exposedEdges) { if (texture().get()) - textureMapper->drawTexture(*texture().get(), rect(), transform, opacity, exposedEdges); + textureMapper.drawTexture(*texture().get(), rect(), transform, opacity, exposedEdges); } } // namespace WebCore -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h index ee6809f20..6aebd3139 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTile.h @@ -20,8 +20,6 @@ #ifndef TextureMapperTile_h #define TextureMapperTile_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - #include "FloatRect.h" #include "Image.h" #include "TextureMapper.h" @@ -34,14 +32,14 @@ class GraphicsLayer; class TextureMapperTile { public: - inline PassRefPtr<BitmapTexture> texture() const { return m_texture; } + inline RefPtr<BitmapTexture> texture() const { return m_texture; } inline FloatRect rect() const { return m_rect; } inline void setTexture(BitmapTexture* texture) { m_texture = texture; } inline void setRect(const FloatRect& rect) { m_rect = rect; } - void updateContents(TextureMapper*, Image*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData); - void updateContents(TextureMapper*, GraphicsLayer*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData); - virtual void paint(TextureMapper*, const TransformationMatrix&, float, const unsigned exposedEdges); + void updateContents(TextureMapper&, Image*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData); + void updateContents(TextureMapper&, GraphicsLayer*, const IntRect&, BitmapTexture::UpdateContentsFlag UpdateCanModifyOriginalImageData, float scale = 1); + virtual void paint(TextureMapper&, const TransformationMatrix&, float, const unsigned exposedEdges); virtual ~TextureMapperTile() { } explicit TextureMapperTile(const FloatRect& rect) @@ -55,6 +53,5 @@ private: }; } -#endif #endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp index ef564023d..6833ac6b5 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.cpp @@ -19,27 +19,26 @@ #include "config.h" -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) #include "TextureMapperTiledBackingStore.h" #include "ImageBuffer.h" +#include "ImageObserver.h" #include "TextureMapper.h" namespace WebCore { class GraphicsLayer; -TextureMapperTiledBackingStore::TextureMapperTiledBackingStore() -{ -} - -void TextureMapperTiledBackingStore::updateContentsFromImageIfNeeded(TextureMapper* textureMapper) +void TextureMapperTiledBackingStore::updateContentsFromImageIfNeeded(TextureMapper& textureMapper) { if (!m_image) return; - updateContents(textureMapper, m_image.get(), m_image->size(), m_image->rect(), BitmapTexture::UpdateCannotModifyOriginalImageData); - m_image.clear(); + updateContents(textureMapper, m_image.get(), m_image->size(), enclosingIntRect(m_image->rect()), BitmapTexture::UpdateCannotModifyOriginalImageData); + + if (m_image->imageObserver()) + m_image->imageObserver()->didDraw(m_image.get()); + m_image = nullptr; } TransformationMatrix TextureMapperTiledBackingStore::adjustedTransformForRect(const FloatRect& targetRect) @@ -47,34 +46,48 @@ TransformationMatrix TextureMapperTiledBackingStore::adjustedTransformForRect(co return TransformationMatrix::rectToRect(rect(), targetRect); } -void TextureMapperTiledBackingStore::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity) +void TextureMapperTiledBackingStore::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& transform, float opacity) { updateContentsFromImageIfNeeded(textureMapper); TransformationMatrix adjustedTransform = transform * adjustedTransformForRect(targetRect); - for (size_t i = 0; i < m_tiles.size(); ++i) - m_tiles[i].paint(textureMapper, adjustedTransform, opacity, calculateExposedTileEdges(rect(), m_tiles[i].rect())); + for (auto& tile : m_tiles) + tile.paint(textureMapper, adjustedTransform, opacity, calculateExposedTileEdges(rect(), tile.rect())); } -void TextureMapperTiledBackingStore::drawBorder(TextureMapper* textureMapper, const Color& borderColor, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform) +void TextureMapperTiledBackingStore::drawBorder(TextureMapper& textureMapper, const Color& borderColor, float borderWidth, const FloatRect& targetRect, const TransformationMatrix& transform) { TransformationMatrix adjustedTransform = transform * adjustedTransformForRect(targetRect); - for (size_t i = 0; i < m_tiles.size(); ++i) - textureMapper->drawBorder(borderColor, borderWidth, m_tiles[i].rect(), adjustedTransform); + for (auto& tile : m_tiles) + textureMapper.drawBorder(borderColor, borderWidth, tile.rect(), adjustedTransform); } -void TextureMapperTiledBackingStore::drawRepaintCounter(TextureMapper* textureMapper, int repaintCount, const Color& borderColor, const FloatRect& targetRect, const TransformationMatrix& transform) +void TextureMapperTiledBackingStore::drawRepaintCounter(TextureMapper& textureMapper, int repaintCount, const Color& borderColor, const FloatRect& targetRect, const TransformationMatrix& transform) { TransformationMatrix adjustedTransform = transform * adjustedTransformForRect(targetRect); - for (size_t i = 0; i < m_tiles.size(); ++i) - textureMapper->drawNumber(repaintCount, borderColor, m_tiles[i].rect().location(), adjustedTransform); + for (auto& tile : m_tiles) + textureMapper.drawNumber(repaintCount, borderColor, tile.rect().location(), adjustedTransform); +} + +void TextureMapperTiledBackingStore::updateContentsScale(float scale) +{ + if (m_contentsScale == scale) + return; + + m_isScaleDirty = true; + m_contentsScale = scale; } void TextureMapperTiledBackingStore::createOrDestroyTilesIfNeeded(const FloatSize& size, const IntSize& tileSize, bool hasAlpha) { - if (size == m_size) + if (size == m_size && !m_isScaleDirty) return; m_size = size; + m_isScaleDirty = false; + + FloatSize scaledSize(m_size); + if (!m_image) + scaledSize.scale(m_contentsScale); Vector<FloatRect> tileRectsToAdd; Vector<int> tileIndicesToRemove; @@ -82,8 +95,8 @@ void TextureMapperTiledBackingStore::createOrDestroyTilesIfNeeded(const FloatSiz // This method recycles tiles. We check which tiles we need to add, which to remove, and use as many // removable tiles as replacement for new tiles when possible. - for (float y = 0; y < m_size.height(); y += tileSize.height()) { - for (float x = 0; x < m_size.width(); x += tileSize.width()) { + for (float y = 0; y < scaledSize.height(); y += tileSize.height()) { + for (float x = 0; x < scaledSize.width(); x += tileSize.width()) { FloatRect tileRect(x, y, tileSize.width(), tileSize.height()); tileRect.intersect(rect()); tileRectsToAdd.append(tileRect); @@ -112,51 +125,52 @@ void TextureMapperTiledBackingStore::createOrDestroyTilesIfNeeded(const FloatSiz } // Recycle removable tiles to be used for newly requested tiles. - for (size_t i = 0; i < tileRectsToAdd.size(); ++i) { + for (auto& rect : tileRectsToAdd) { if (!tileIndicesToRemove.isEmpty()) { // We recycle an existing tile for usage with a new tile rect. TextureMapperTile& tile = m_tiles[tileIndicesToRemove.last()]; tileIndicesToRemove.removeLast(); - tile.setRect(tileRectsToAdd[i]); + tile.setRect(rect); if (tile.texture()) tile.texture()->reset(enclosingIntRect(tile.rect()).size(), hasAlpha ? BitmapTexture::SupportsAlpha : 0); continue; } - m_tiles.append(TextureMapperTile(tileRectsToAdd[i])); + m_tiles.append(TextureMapperTile(rect)); } // Remove unnecessary tiles, if they weren't recycled. // We use a threshold to make sure we don't create/destroy tiles too eagerly. - for (size_t i = 0; i < tileIndicesToRemove.size() && m_tiles.size() > TileEraseThreshold; ++i) - m_tiles.remove(tileIndicesToRemove[i]); + for (auto& index : tileIndicesToRemove) { + if (m_tiles.size() <= TileEraseThreshold) + break; + m_tiles.remove(index); + } } -void TextureMapperTiledBackingStore::updateContents(TextureMapper* textureMapper, Image* image, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) +void TextureMapperTiledBackingStore::updateContents(TextureMapper& textureMapper, Image* image, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) { - createOrDestroyTilesIfNeeded(totalSize, textureMapper->maxTextureSize(), !image->currentFrameKnownToBeOpaque()); - for (size_t i = 0; i < m_tiles.size(); ++i) - m_tiles[i].updateContents(textureMapper, image, dirtyRect, updateContentsFlag); + createOrDestroyTilesIfNeeded(totalSize, textureMapper.maxTextureSize(), !image->currentFrameKnownToBeOpaque()); + for (auto& tile : m_tiles) + tile.updateContents(textureMapper, image, dirtyRect, updateContentsFlag); } -void TextureMapperTiledBackingStore::updateContents(TextureMapper* textureMapper, GraphicsLayer* sourceLayer, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) +void TextureMapperTiledBackingStore::updateContents(TextureMapper& textureMapper, GraphicsLayer* sourceLayer, const FloatSize& totalSize, const IntRect& dirtyRect, BitmapTexture::UpdateContentsFlag updateContentsFlag) { - createOrDestroyTilesIfNeeded(totalSize, textureMapper->maxTextureSize(), true); - for (size_t i = 0; i < m_tiles.size(); ++i) - m_tiles[i].updateContents(textureMapper, sourceLayer, dirtyRect, updateContentsFlag); + createOrDestroyTilesIfNeeded(totalSize, textureMapper.maxTextureSize(), true); + for (auto& tile : m_tiles) + tile.updateContents(textureMapper, sourceLayer, dirtyRect, updateContentsFlag, m_contentsScale); } -PassRefPtr<BitmapTexture> TextureMapperTiledBackingStore::texture() const +RefPtr<BitmapTexture> TextureMapperTiledBackingStore::texture() const { - for (size_t i = 0; i < m_tiles.size(); ++i) { - RefPtr<BitmapTexture> texture = m_tiles[i].texture(); - if (texture) + for (const auto& tile : m_tiles) { + if (auto texture = tile.texture()) return texture; } - return PassRefPtr<BitmapTexture>(); + return nullptr; } } // namespace WebCore -#endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h index 38161c475..8514a4f73 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperTiledBackingStore.h @@ -20,8 +20,6 @@ #ifndef TextureMapperTiledBackingStore_h #define TextureMapperTiledBackingStore_h -#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) - #include "FloatRect.h" #include "Image.h" #include "TextureMapperBackingStore.h" @@ -37,28 +35,37 @@ public: static PassRefPtr<TextureMapperTiledBackingStore> create() { return adoptRef(new TextureMapperTiledBackingStore); } virtual ~TextureMapperTiledBackingStore() { } - virtual PassRefPtr<BitmapTexture> texture() const override; - virtual void paintToTextureMapper(TextureMapper*, const FloatRect&, const TransformationMatrix&, float) override; - virtual void drawBorder(TextureMapper*, const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override; - virtual void drawRepaintCounter(TextureMapper*, int repaintCount, const Color&, const FloatRect&, const TransformationMatrix&) override; - void updateContents(TextureMapper*, Image*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag); - void updateContents(TextureMapper*, GraphicsLayer*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag); + RefPtr<BitmapTexture> texture() const override; + void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix&, float) override; + void drawBorder(TextureMapper&, const Color&, float borderWidth, const FloatRect&, const TransformationMatrix&) override; + void drawRepaintCounter(TextureMapper&, int repaintCount, const Color&, const FloatRect&, const TransformationMatrix&) override; + + void updateContentsScale(float); + void updateContents(TextureMapper&, Image*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag); + void updateContents(TextureMapper&, GraphicsLayer*, const FloatSize&, const IntRect&, BitmapTexture::UpdateContentsFlag); void setContentsToImage(Image* image) { m_image = image; } private: - TextureMapperTiledBackingStore(); + TextureMapperTiledBackingStore() { } + void createOrDestroyTilesIfNeeded(const FloatSize& backingStoreSize, const IntSize& tileSize, bool hasAlpha); - void updateContentsFromImageIfNeeded(TextureMapper*); + void updateContentsFromImageIfNeeded(TextureMapper&); TransformationMatrix adjustedTransformForRect(const FloatRect&); - inline FloatRect rect() const { return FloatRect(FloatPoint::zero(), m_size); } + inline FloatRect rect() const + { + FloatRect rect(FloatPoint::zero(), m_size); + rect.scale(m_contentsScale); + return rect; + } Vector<TextureMapperTile> m_tiles; FloatSize m_size; RefPtr<Image> m_image; + float m_contentsScale { 1 }; + bool m_isScaleDirty { false }; }; } // namespace WebCore -#endif #endif diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp new file mode 100644 index 000000000..8da58b2ce --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp @@ -0,0 +1,1182 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2010 Apple Inc. All rights reserved. + Copyright (C) 2012 Company 100, Inc. + Copyright (C) 2012 Intel Corporation. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "CoordinatedGraphicsLayer.h" + +#if USE(COORDINATED_GRAPHICS) + +#include "FloatQuad.h" +#include "GraphicsContext.h" +#include "GraphicsLayer.h" +#include "GraphicsLayerFactory.h" +#include "ScrollableArea.h" +#include <wtf/CurrentTime.h> +#ifndef NDEBUG +#include <wtf/SetForScope.h> +#endif +#include <wtf/text/CString.h> + +namespace WebCore { + +std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient& client, Type layerType) +{ + if (!factory) + return std::make_unique<CoordinatedGraphicsLayer>(layerType, client); + + return factory->createGraphicsLayer(layerType, client); +} + +static CoordinatedLayerID toCoordinatedLayerID(GraphicsLayer* layer) +{ + return is<CoordinatedGraphicsLayer>(layer) ? downcast<CoordinatedGraphicsLayer>(*layer).id() : 0; +} + +void CoordinatedGraphicsLayer::notifyFlushRequired() +{ + if (!m_coordinator) + return; + + if (m_coordinator->isFlushingLayerChanges()) + return; + + client().notifyFlushRequired(this); +} + +void CoordinatedGraphicsLayer::didChangeLayerState() +{ + m_shouldSyncLayerState = true; + notifyFlushRequired(); +} + +void CoordinatedGraphicsLayer::didChangeAnimations() +{ + m_shouldSyncAnimations = true; + notifyFlushRequired(); +} + +void CoordinatedGraphicsLayer::didChangeChildren() +{ + m_shouldSyncChildren = true; + notifyFlushRequired(); +} + +void CoordinatedGraphicsLayer::didChangeFilters() +{ + m_shouldSyncFilters = true; + notifyFlushRequired(); +} + +void CoordinatedGraphicsLayer::didChangeImageBacking() +{ + m_shouldSyncImageBacking = true; + notifyFlushRequired(); +} + +void CoordinatedGraphicsLayer::setShouldUpdateVisibleRect() +{ + m_shouldUpdateVisibleRect = true; + for (auto& child : children()) + downcast<CoordinatedGraphicsLayer>(*child).setShouldUpdateVisibleRect(); + if (replicaLayer()) + downcast<CoordinatedGraphicsLayer>(*replicaLayer()).setShouldUpdateVisibleRect(); +} + +void CoordinatedGraphicsLayer::didChangeGeometry() +{ + didChangeLayerState(); + setShouldUpdateVisibleRect(); +} + +CoordinatedGraphicsLayer::CoordinatedGraphicsLayer(Type layerType, GraphicsLayerClient& client) + : GraphicsLayer(layerType, client) +#ifndef NDEBUG + , m_isPurging(false) +#endif + , m_shouldUpdateVisibleRect(true) + , m_shouldSyncLayerState(true) + , m_shouldSyncChildren(true) + , m_shouldSyncFilters(true) + , m_shouldSyncImageBacking(true) + , m_shouldSyncAnimations(true) + , m_fixedToViewport(false) + , m_movingVisibleRect(false) + , m_pendingContentsScaleAdjustment(false) + , m_pendingVisibleRectAdjustment(false) +#if USE(COORDINATED_GRAPHICS_THREADED) + , m_shouldSyncPlatformLayer(false) + , m_shouldUpdatePlatformLayer(false) +#endif + , m_coordinator(0) + , m_compositedNativeImagePtr(0) + , m_platformLayer(0) + , m_animationStartedTimer(*this, &CoordinatedGraphicsLayer::animationStartedTimerFired) + , m_scrollableArea(0) +{ + static CoordinatedLayerID nextLayerID = 1; + m_id = nextLayerID++; +} + +CoordinatedGraphicsLayer::~CoordinatedGraphicsLayer() +{ + if (m_coordinator) { + purgeBackingStores(); + m_coordinator->detachLayer(this); + } + ASSERT(!m_coordinatedImageBacking); + ASSERT(!m_mainBackingStore); + willBeDestroyed(); +} + +bool CoordinatedGraphicsLayer::setChildren(const Vector<GraphicsLayer*>& children) +{ + bool ok = GraphicsLayer::setChildren(children); + if (!ok) + return false; + didChangeChildren(); + return true; +} + +void CoordinatedGraphicsLayer::addChild(GraphicsLayer* layer) +{ + GraphicsLayer::addChild(layer); + didChangeChildren(); +} + +void CoordinatedGraphicsLayer::addChildAtIndex(GraphicsLayer* layer, int index) +{ + GraphicsLayer::addChildAtIndex(layer, index); + didChangeChildren(); +} + +void CoordinatedGraphicsLayer::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(layer, sibling); + didChangeChildren(); +} + +void CoordinatedGraphicsLayer::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildBelow(layer, sibling); + didChangeChildren(); +} + +bool CoordinatedGraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + bool ok = GraphicsLayer::replaceChild(oldChild, newChild); + if (!ok) + return false; + didChangeChildren(); + return true; +} + +void CoordinatedGraphicsLayer::removeFromParent() +{ + if (CoordinatedGraphicsLayer* parentLayer = downcast<CoordinatedGraphicsLayer>(parent())) + parentLayer->didChangeChildren(); + GraphicsLayer::removeFromParent(); +} + +void CoordinatedGraphicsLayer::setPosition(const FloatPoint& p) +{ + if (position() == p) + return; + + GraphicsLayer::setPosition(p); + m_layerState.positionChanged = true; + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setAnchorPoint(const FloatPoint3D& p) +{ + if (anchorPoint() == p) + return; + + GraphicsLayer::setAnchorPoint(p); + m_layerState.anchorPointChanged = true; + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setSize(const FloatSize& size) +{ + if (this->size() == size) + return; + + GraphicsLayer::setSize(size); + m_layerState.sizeChanged = true; + + if (maskLayer()) + maskLayer()->setSize(size); + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setTransform(const TransformationMatrix& t) +{ + if (transform() == t) + return; + + GraphicsLayer::setTransform(t); + m_layerState.transformChanged = true; + + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setChildrenTransform(const TransformationMatrix& t) +{ + if (childrenTransform() == t) + return; + + GraphicsLayer::setChildrenTransform(t); + m_layerState.childrenTransformChanged = true; + + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setPreserves3D(bool b) +{ + if (preserves3D() == b) + return; + + GraphicsLayer::setPreserves3D(b); + m_layerState.preserves3D = b; + m_layerState.flagsChanged = true; + + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setMasksToBounds(bool b) +{ + if (masksToBounds() == b) + return; + GraphicsLayer::setMasksToBounds(b); + m_layerState.masksToBounds = b; + m_layerState.flagsChanged = true; + + didChangeGeometry(); +} + +void CoordinatedGraphicsLayer::setDrawsContent(bool b) +{ + if (drawsContent() == b) + return; + GraphicsLayer::setDrawsContent(b); + m_layerState.drawsContent = b; + m_layerState.flagsChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setContentsVisible(bool b) +{ + if (contentsAreVisible() == b) + return; + GraphicsLayer::setContentsVisible(b); + m_layerState.contentsVisible = b; + m_layerState.flagsChanged = true; + + if (maskLayer()) + maskLayer()->setContentsVisible(b); + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setContentsOpaque(bool b) +{ + if (contentsOpaque() == b) + return; + if (m_mainBackingStore) + m_mainBackingStore->setSupportsAlpha(!b); + GraphicsLayer::setContentsOpaque(b); + m_layerState.contentsOpaque = b; + m_layerState.flagsChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setBackfaceVisibility(bool b) +{ + if (backfaceVisibility() == b) + return; + + GraphicsLayer::setBackfaceVisibility(b); + m_layerState.backfaceVisible = b; + m_layerState.flagsChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setOpacity(float opacity) +{ + if (this->opacity() == opacity) + return; + + GraphicsLayer::setOpacity(opacity); + m_layerState.opacity = opacity; + m_layerState.opacityChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setContentsRect(const FloatRect& r) +{ + if (contentsRect() == r) + return; + + GraphicsLayer::setContentsRect(r); + m_layerState.contentsRect = r; + m_layerState.contentsRectChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setContentsTileSize(const FloatSize& s) +{ + if (contentsTileSize() == s) + return; + + GraphicsLayer::setContentsTileSize(s); + m_layerState.contentsTileSize = s; + m_layerState.contentsTilingChanged = true; + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setContentsTilePhase(const FloatSize& p) +{ + if (contentsTilePhase() == p) + return; + + GraphicsLayer::setContentsTilePhase(p); + m_layerState.contentsTilePhase = p; + m_layerState.contentsTilingChanged = true; + didChangeLayerState(); +} + +bool GraphicsLayer::supportsContentsTiling() +{ + return true; +} + +void CoordinatedGraphicsLayer::setContentsNeedsDisplay() +{ +#if USE(COORDINATED_GRAPHICS_THREADED) + if (m_platformLayer) + m_shouldUpdatePlatformLayer = true; +#endif + + notifyFlushRequired(); + addRepaintRect(contentsRect()); +} + +void CoordinatedGraphicsLayer::setContentsToPlatformLayer(PlatformLayer* platformLayer, ContentsLayerPurpose) +{ +#if USE(COORDINATED_GRAPHICS_THREADED) + if (m_platformLayer != platformLayer) + m_shouldSyncPlatformLayer = true; + + m_platformLayer = platformLayer; + notifyFlushRequired(); +#else + UNUSED_PARAM(platformLayer); +#endif +} + +bool CoordinatedGraphicsLayer::filtersCanBeComposited(const FilterOperations& filters) const +{ + if (!filters.size()) + return false; + + for (const auto& filterOperation : filters.operations()) { + if (filterOperation->type() == FilterOperation::REFERENCE) + return false; + } + + return true; +} + +bool CoordinatedGraphicsLayer::setFilters(const FilterOperations& newFilters) +{ + bool canCompositeFilters = filtersCanBeComposited(newFilters); + if (filters() == newFilters) + return canCompositeFilters; + + if (canCompositeFilters) { + if (!GraphicsLayer::setFilters(newFilters)) + return false; + didChangeFilters(); + } else if (filters().size()) { + clearFilters(); + didChangeFilters(); + } + + return canCompositeFilters; +} + +void CoordinatedGraphicsLayer::setContentsToSolidColor(const Color& color) +{ + if (m_layerState.solidColor == color) + return; + + m_layerState.solidColor = color; + m_layerState.solidColorChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setShowDebugBorder(bool show) +{ + if (isShowingDebugBorder() == show) + return; + + GraphicsLayer::setShowDebugBorder(show); + m_layerState.showDebugBorders = true; + m_layerState.flagsChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setShowRepaintCounter(bool show) +{ + if (isShowingRepaintCounter() == show) + return; + + GraphicsLayer::setShowRepaintCounter(show); + m_layerState.showRepaintCounter = true; + m_layerState.flagsChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setContentsToImage(Image* image) +{ + NativeImagePtr nativeImagePtr = image ? image->nativeImageForCurrentFrame() : nullptr; + if (m_compositedImage == image && m_compositedNativeImagePtr == nativeImagePtr) + return; + + m_compositedImage = image; + m_compositedNativeImagePtr = nativeImagePtr; + + GraphicsLayer::setContentsToImage(image); + didChangeImageBacking(); +} + +void CoordinatedGraphicsLayer::setMaskLayer(GraphicsLayer* layer) +{ + if (layer == maskLayer()) + return; + + GraphicsLayer::setMaskLayer(layer); + + if (!layer) + return; + + layer->setSize(size()); + layer->setContentsVisible(contentsAreVisible()); + auto& coordinatedLayer = downcast<CoordinatedGraphicsLayer>(*layer); + coordinatedLayer.didChangeLayerState(); + + m_layerState.mask = coordinatedLayer.id(); + m_layerState.maskChanged = true; + + didChangeLayerState(); +} + +bool CoordinatedGraphicsLayer::shouldDirectlyCompositeImage(Image* image) const +{ + if (!image || !image->isBitmapImage()) + return false; + + enum { MaxDimenstionForDirectCompositing = 2000 }; + if (image->width() > MaxDimenstionForDirectCompositing || image->height() > MaxDimenstionForDirectCompositing) + return false; + + return true; +} + +void CoordinatedGraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer) +{ + if (layer == replicaLayer()) + return; + + GraphicsLayer::setReplicatedByLayer(layer); + m_layerState.replica = toCoordinatedLayerID(layer); + m_layerState.replicaChanged = true; + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setNeedsDisplay() +{ + setNeedsDisplayInRect(FloatRect(FloatPoint(), size())); +} + +void CoordinatedGraphicsLayer::setNeedsDisplayInRect(const FloatRect& rect, ShouldClipToLayer) +{ + if (m_mainBackingStore) + m_mainBackingStore->invalidate(IntRect(rect)); + + didChangeLayerState(); + + addRepaintRect(rect); +} + +void CoordinatedGraphicsLayer::setScrollableArea(ScrollableArea* scrollableArea) +{ + bool oldScrollable = isScrollable(); + m_scrollableArea = scrollableArea; + if (oldScrollable == isScrollable()) + return; + + m_layerState.isScrollable = isScrollable(); + m_layerState.flagsChanged = true; + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::commitScrollOffset(const IntSize& offset) +{ + if (!isScrollable() || offset.isZero()) + return; + + m_scrollableArea->notifyScrollPositionChanged(m_scrollableArea->scrollPosition() + offset); + m_layerState.committedScrollOffset += offset; + m_layerState.committedScrollOffsetChanged = true; + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setFixedToViewport(bool isFixed) +{ + if (m_fixedToViewport == isFixed) + return; + + m_fixedToViewport = isFixed; + m_layerState.fixedToViewport = isFixed; + m_layerState.flagsChanged = true; + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::flushCompositingState(const FloatRect& rect) +{ + if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer())) + mask->flushCompositingStateForThisLayerOnly(); + + if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer())) + replica->flushCompositingStateForThisLayerOnly(); + + flushCompositingStateForThisLayerOnly(); + + for (auto& child : children()) + child->flushCompositingState(rect); +} + +void CoordinatedGraphicsLayer::syncChildren() +{ + if (!m_shouldSyncChildren) + return; + m_shouldSyncChildren = false; + m_layerState.childrenChanged = true; + m_layerState.children.clear(); + for (auto& child : children()) + m_layerState.children.append(toCoordinatedLayerID(child)); +} + +void CoordinatedGraphicsLayer::syncFilters() +{ + if (!m_shouldSyncFilters) + return; + m_shouldSyncFilters = false; + + m_layerState.filters = GraphicsLayer::filters(); + m_layerState.filtersChanged = true; +} + +void CoordinatedGraphicsLayer::syncImageBacking() +{ + if (!m_shouldSyncImageBacking) + return; + m_shouldSyncImageBacking = false; + + if (m_compositedNativeImagePtr) { + ASSERT(!shouldHaveBackingStore()); + ASSERT(m_compositedImage); + + bool imageInstanceReplaced = m_coordinatedImageBacking && (m_coordinatedImageBacking->id() != CoordinatedImageBacking::getCoordinatedImageBackingID(m_compositedImage.get())); + if (imageInstanceReplaced) + releaseImageBackingIfNeeded(); + + if (!m_coordinatedImageBacking) { + m_coordinatedImageBacking = m_coordinator->createImageBackingIfNeeded(m_compositedImage.get()); + m_coordinatedImageBacking->addHost(this); + m_layerState.imageID = m_coordinatedImageBacking->id(); + } + + m_coordinatedImageBacking->markDirty(); + m_layerState.imageChanged = true; + } else + releaseImageBackingIfNeeded(); +} + +void CoordinatedGraphicsLayer::syncLayerState() +{ + if (!m_shouldSyncLayerState) + return; + m_shouldSyncLayerState = false; + + m_layerState.childrenTransform = childrenTransform(); + m_layerState.contentsRect = contentsRect(); + m_layerState.mask = toCoordinatedLayerID(maskLayer()); + m_layerState.opacity = opacity(); + m_layerState.replica = toCoordinatedLayerID(replicaLayer()); + m_layerState.transform = transform(); + + m_layerState.anchorPoint = m_adjustedAnchorPoint; + m_layerState.pos = m_adjustedPosition; + m_layerState.size = m_adjustedSize; + + if (m_layerState.flagsChanged) { + m_layerState.contentsOpaque = contentsOpaque(); + m_layerState.drawsContent = drawsContent(); + m_layerState.contentsVisible = contentsAreVisible(); + m_layerState.backfaceVisible = backfaceVisibility(); + m_layerState.masksToBounds = masksToBounds(); + m_layerState.preserves3D = preserves3D(); + m_layerState.fixedToViewport = fixedToViewport(); + m_layerState.showDebugBorders = isShowingDebugBorder(); + m_layerState.showRepaintCounter = isShowingRepaintCounter(); + m_layerState.isScrollable = isScrollable(); + } + + if (m_layerState.showDebugBorders) + updateDebugIndicators(); +} + +void CoordinatedGraphicsLayer::setDebugBorder(const Color& color, float width) +{ + ASSERT(m_layerState.showDebugBorders); + if (m_layerState.debugBorderColor != color) { + m_layerState.debugBorderColor = color; + m_layerState.debugBorderColorChanged = true; + } + + if (m_layerState.debugBorderWidth != width) { + m_layerState.debugBorderWidth = width; + m_layerState.debugBorderWidthChanged = true; + } +} + +void CoordinatedGraphicsLayer::syncAnimations() +{ + if (!m_shouldSyncAnimations) + return; + + m_shouldSyncAnimations = false; + m_layerState.animations = m_animations.getActiveAnimations(); + m_layerState.animationsChanged = true; +} + +void CoordinatedGraphicsLayer::syncPlatformLayer() +{ +#if USE(COORDINATED_GRAPHICS_THREADED) + if (!m_shouldSyncPlatformLayer) + return; + + m_shouldSyncPlatformLayer = false; + m_layerState.platformLayerChanged = true; + if (m_platformLayer) + m_layerState.platformLayerProxy = m_platformLayer->proxy(); +#endif +} + +void CoordinatedGraphicsLayer::updatePlatformLayer() +{ +#if USE(COORDINATED_GRAPHICS_THREADED) + if (!m_shouldUpdatePlatformLayer) + return; + + m_shouldUpdatePlatformLayer = false; + if (m_platformLayer) + m_platformLayer->swapBuffersIfNeeded(); +#endif +} + +void CoordinatedGraphicsLayer::flushCompositingStateForThisLayerOnly() +{ + // When we have a transform animation, we need to update visible rect every frame to adjust the visible rect of a backing store. + bool hasActiveTransformAnimation = selfOrAncestorHasActiveTransformAnimation(); + if (hasActiveTransformAnimation) + m_movingVisibleRect = true; + + // Sets the values. + computePixelAlignment(m_adjustedPosition, m_adjustedSize, m_adjustedAnchorPoint, m_pixelAlignmentOffset); + + syncImageBacking(); + syncLayerState(); + syncAnimations(); + computeTransformedVisibleRect(); + syncChildren(); + syncFilters(); + syncPlatformLayer(); + updatePlatformLayer(); + + // Only unset m_movingVisibleRect after we have updated the visible rect after the animation stopped. + if (!hasActiveTransformAnimation) + m_movingVisibleRect = false; +} + +void CoordinatedGraphicsLayer::syncPendingStateChangesIncludingSubLayers() +{ + if (m_layerState.hasPendingChanges()) { + m_coordinator->syncLayerState(m_id, m_layerState); + resetLayerState(); + } + + if (maskLayer()) + downcast<CoordinatedGraphicsLayer>(*maskLayer()).syncPendingStateChangesIncludingSubLayers(); + + for (auto& child : children()) + downcast<CoordinatedGraphicsLayer>(*child).syncPendingStateChangesIncludingSubLayers(); +} + +void CoordinatedGraphicsLayer::resetLayerState() +{ + m_layerState.changeMask = 0; + m_layerState.tilesToCreate.clear(); + m_layerState.tilesToRemove.clear(); + m_layerState.tilesToUpdate.clear(); + m_layerState.committedScrollOffset = IntSize(); +} + +bool CoordinatedGraphicsLayer::imageBackingVisible() +{ + ASSERT(m_coordinatedImageBacking); + return transformedVisibleRect().intersects(IntRect(contentsRect())); +} + +void CoordinatedGraphicsLayer::releaseImageBackingIfNeeded() +{ + if (!m_coordinatedImageBacking) + return; + + ASSERT(m_coordinator); + m_coordinatedImageBacking->removeHost(this); + m_coordinatedImageBacking = nullptr; + m_layerState.imageID = InvalidCoordinatedImageBackingID; + m_layerState.imageChanged = true; +} + +CoordinatedGraphicsLayer* CoordinatedGraphicsLayer::findFirstDescendantWithContentsRecursively() +{ + if (shouldHaveBackingStore()) + return this; + + for (auto& child : children()) { + if (CoordinatedGraphicsLayer* layer = downcast<CoordinatedGraphicsLayer>(*child).findFirstDescendantWithContentsRecursively()) + return layer; + } + + return nullptr; +} + +void CoordinatedGraphicsLayer::setVisibleContentRectTrajectoryVector(const FloatPoint& trajectoryVector) +{ + if (!m_mainBackingStore) + return; + + m_mainBackingStore->setTrajectoryVector(trajectoryVector); + setNeedsVisibleRectAdjustment(); +} + +void CoordinatedGraphicsLayer::deviceOrPageScaleFactorChanged() +{ + if (shouldHaveBackingStore()) + m_pendingContentsScaleAdjustment = true; +} + +float CoordinatedGraphicsLayer::effectiveContentsScale() +{ + return selfOrAncestorHaveNonAffineTransforms() ? 1 : deviceScaleFactor() * pageScaleFactor(); +} + +void CoordinatedGraphicsLayer::adjustContentsScale() +{ + ASSERT(shouldHaveBackingStore()); + if (!m_mainBackingStore || m_mainBackingStore->contentsScale() == effectiveContentsScale()) + return; + + // Between creating the new backing store and painting the content, + // we do not want to drop the previous one as that might result in + // briefly seeing flickering as the old tiles may be dropped before + // something replaces them. + m_previousBackingStore = WTFMove(m_mainBackingStore); + + // No reason to save the previous backing store for non-visible areas. + m_previousBackingStore->removeAllNonVisibleTiles(transformedVisibleRect(), IntRect(0, 0, size().width(), size().height())); +} + +void CoordinatedGraphicsLayer::createBackingStore() +{ + m_mainBackingStore = std::make_unique<TiledBackingStore>(this, effectiveContentsScale()); + m_mainBackingStore->setSupportsAlpha(!contentsOpaque()); +} + +void CoordinatedGraphicsLayer::tiledBackingStorePaint(GraphicsContext& context, const IntRect& rect) +{ + if (rect.isEmpty()) + return; + paintGraphicsLayerContents(context, rect); +} + +void CoordinatedGraphicsLayer::didUpdateTileBuffers() +{ + if (!isShowingRepaintCounter()) + return; + + m_layerState.repaintCount = incrementRepaintCount(); + m_layerState.repaintCountChanged = true; +} + +void CoordinatedGraphicsLayer::tiledBackingStoreHasPendingTileCreation() +{ + setNeedsVisibleRectAdjustment(); + notifyFlushRequired(); +} + +static void clampToContentsRectIfRectIsInfinite(FloatRect& rect, const FloatSize& contentsSize) +{ + if (rect.width() >= LayoutUnit::nearlyMax() || rect.width() <= LayoutUnit::nearlyMin()) { + rect.setX(0); + rect.setWidth(contentsSize.width()); + } + + if (rect.height() >= LayoutUnit::nearlyMax() || rect.height() <= LayoutUnit::nearlyMin()) { + rect.setY(0); + rect.setHeight(contentsSize.height()); + } +} + +IntRect CoordinatedGraphicsLayer::transformedVisibleRect() +{ + // Non-invertible layers are not visible. + if (!m_layerTransform.combined().isInvertible()) + return IntRect(); + + // Return a projection of the visible rect (surface coordinates) onto the layer's plane (layer coordinates). + // The resulting quad might be squewed and the visible rect is the bounding box of this quad, + // so it might spread further than the real visible area (and then even more amplified by the cover rect multiplier). + ASSERT(m_cachedInverseTransform == m_layerTransform.combined().inverse().value_or(TransformationMatrix())); + FloatRect rect = m_cachedInverseTransform.clampedBoundsOfProjectedQuad(FloatQuad(m_coordinator->visibleContentsRect())); + clampToContentsRectIfRectIsInfinite(rect, size()); + return enclosingIntRect(rect); +} + +bool CoordinatedGraphicsLayer::paintToSurface(const IntSize& size, uint32_t& atlas, IntPoint& offset, CoordinatedSurface::Client& client) +{ + ASSERT(m_coordinator); + ASSERT(m_coordinator->isFlushingLayerChanges()); + return m_coordinator->paintToSurface(size, contentsOpaque() ? CoordinatedSurface::NoFlags : CoordinatedSurface::SupportsAlpha, atlas, offset, client); +} + +void CoordinatedGraphicsLayer::createTile(uint32_t tileID, float scaleFactor) +{ + ASSERT(m_coordinator); + ASSERT(m_coordinator->isFlushingLayerChanges()); + + TileCreationInfo creationInfo; + creationInfo.tileID = tileID; + creationInfo.scale = scaleFactor; + m_layerState.tilesToCreate.append(creationInfo); +} + +void CoordinatedGraphicsLayer::updateTile(uint32_t tileID, const SurfaceUpdateInfo& updateInfo, const IntRect& tileRect) +{ + ASSERT(m_coordinator); + ASSERT(m_coordinator->isFlushingLayerChanges()); + + TileUpdateInfo tileUpdateInfo; + tileUpdateInfo.tileID = tileID; + tileUpdateInfo.tileRect = tileRect; + tileUpdateInfo.updateInfo = updateInfo; + m_layerState.tilesToUpdate.append(tileUpdateInfo); +} + +void CoordinatedGraphicsLayer::removeTile(uint32_t tileID) +{ + ASSERT(m_coordinator); + ASSERT(m_coordinator->isFlushingLayerChanges() || m_isPurging); + m_layerState.tilesToRemove.append(tileID); +} + +void CoordinatedGraphicsLayer::updateContentBuffersIncludingSubLayers() +{ + if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer())) + mask->updateContentBuffers(); + + if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer())) + replica->updateContentBuffers(); + + updateContentBuffers(); + + for (auto& child : children()) + downcast<CoordinatedGraphicsLayer>(*child).updateContentBuffersIncludingSubLayers(); +} + +void CoordinatedGraphicsLayer::updateContentBuffers() +{ + if (!shouldHaveBackingStore()) { + m_mainBackingStore = nullptr; + m_previousBackingStore = nullptr; + return; + } + + if (m_pendingContentsScaleAdjustment) { + adjustContentsScale(); + m_pendingContentsScaleAdjustment = false; + } + + // This is the only place we (re)create the main tiled backing store, once we + // have a remote client and we are ready to send our data to the UI process. + if (!m_mainBackingStore) { + createBackingStore(); + m_pendingVisibleRectAdjustment = true; + } + + if (m_pendingVisibleRectAdjustment) { + m_pendingVisibleRectAdjustment = false; + m_mainBackingStore->createTilesIfNeeded(transformedVisibleRect(), IntRect(0, 0, size().width(), size().height())); + } + + m_mainBackingStore->updateTileBuffers(); + + // The previous backing store is kept around to avoid flickering between + // removing the existing tiles and painting the new ones. The first time + // the visibleRect is full painted we remove the previous backing store. + if (m_mainBackingStore->visibleAreaIsCovered()) + m_previousBackingStore = nullptr; +} + +void CoordinatedGraphicsLayer::purgeBackingStores() +{ +#ifndef NDEBUG + SetForScope<bool> updateModeProtector(m_isPurging, true); +#endif + m_mainBackingStore = nullptr; + m_previousBackingStore = nullptr; + + releaseImageBackingIfNeeded(); + + didChangeLayerState(); +} + +void CoordinatedGraphicsLayer::setCoordinator(CoordinatedGraphicsLayerClient* coordinator) +{ + m_coordinator = coordinator; +} + +void CoordinatedGraphicsLayer::setNeedsVisibleRectAdjustment() +{ + if (shouldHaveBackingStore()) + m_pendingVisibleRectAdjustment = true; +} + +static inline bool isIntegral(float value) +{ + return static_cast<int>(value) == value; +} + +FloatPoint CoordinatedGraphicsLayer::computePositionRelativeToBase() +{ + FloatPoint offset; + for (const GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent()) + offset += currLayer->position(); + + return offset; +} + +void CoordinatedGraphicsLayer::computePixelAlignment(FloatPoint& position, FloatSize& size, FloatPoint3D& anchorPoint, FloatSize& alignmentOffset) +{ + if (isIntegral(effectiveContentsScale())) { + position = m_position; + size = m_size; + anchorPoint = m_anchorPoint; + alignmentOffset = FloatSize(); + return; + } + + FloatPoint positionRelativeToBase = computePositionRelativeToBase(); + + FloatRect baseRelativeBounds(positionRelativeToBase, m_size); + FloatRect scaledBounds = baseRelativeBounds; + + // Scale by the effective scale factor to compute the screen-relative bounds. + scaledBounds.scale(effectiveContentsScale()); + + // Round to integer boundaries. + // NOTE: When using enclosingIntRect (as mac) it will have different sizes depending on position. + FloatRect alignedBounds = enclosingIntRect(scaledBounds); + + // Convert back to layer coordinates. + alignedBounds.scale(1 / effectiveContentsScale()); + + // Convert back to layer coordinates. + alignmentOffset = baseRelativeBounds.location() - alignedBounds.location(); + + position = m_position - alignmentOffset; + size = alignedBounds.size(); + + // Now we have to compute a new anchor point which compensates for rounding. + float anchorPointX = m_anchorPoint.x(); + float anchorPointY = m_anchorPoint.y(); + + if (alignedBounds.width()) + anchorPointX = (baseRelativeBounds.width() * anchorPointX + alignmentOffset.width()) / alignedBounds.width(); + + if (alignedBounds.height()) + anchorPointY = (baseRelativeBounds.height() * anchorPointY + alignmentOffset.height()) / alignedBounds.height(); + + anchorPoint = FloatPoint3D(anchorPointX, anchorPointY, m_anchorPoint.z() * effectiveContentsScale()); +} + +void CoordinatedGraphicsLayer::computeTransformedVisibleRect() +{ + if (!m_shouldUpdateVisibleRect && !m_movingVisibleRect) + return; + + m_shouldUpdateVisibleRect = false; + TransformationMatrix currentTransform = transform(); + if (m_movingVisibleRect) + client().getCurrentTransform(this, currentTransform); + m_layerTransform.setLocalTransform(currentTransform); + + m_layerTransform.setAnchorPoint(m_adjustedAnchorPoint); + m_layerTransform.setPosition(m_adjustedPosition); + m_layerTransform.setSize(m_adjustedSize); + + m_layerTransform.setFlattening(!preserves3D()); + m_layerTransform.setChildrenTransform(childrenTransform()); + m_layerTransform.combineTransforms(parent() ? downcast<CoordinatedGraphicsLayer>(*parent()).m_layerTransform.combinedForChildren() : TransformationMatrix()); + + m_cachedInverseTransform = m_layerTransform.combined().inverse().value_or(TransformationMatrix()); + + // The combined transform will be used in tiledBackingStoreVisibleRect. + setNeedsVisibleRectAdjustment(); +} + +bool CoordinatedGraphicsLayer::shouldHaveBackingStore() const +{ + return drawsContent() && contentsAreVisible() && !m_size.isEmpty(); +} + +bool CoordinatedGraphicsLayer::selfOrAncestorHasActiveTransformAnimation() const +{ + if (m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform)) + return true; + + if (!parent()) + return false; + + return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHasActiveTransformAnimation(); +} + +bool CoordinatedGraphicsLayer::selfOrAncestorHaveNonAffineTransforms() +{ + if (m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform)) + return true; + + if (!m_layerTransform.combined().isAffine()) + return true; + + if (!parent()) + return false; + + return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHaveNonAffineTransforms(); +} + +bool CoordinatedGraphicsLayer::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& keyframesName, double delayAsNegativeTimeOffset) +{ + ASSERT(!keyframesName.isEmpty()); + + if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyTransform && valueList.property() != AnimatedPropertyOpacity && valueList.property() != AnimatedPropertyFilter)) + return false; + + if (valueList.property() == AnimatedPropertyFilter) { + int listIndex = validateFilterOperations(valueList); + if (listIndex < 0) + return false; + + const auto& filters = static_cast<const FilterAnimationValue&>(valueList.at(listIndex)).value(); + if (!filtersCanBeComposited(filters)) + return false; + } + + bool listsMatch = false; + bool ignoredHasBigRotation; + + if (valueList.property() == AnimatedPropertyTransform) + listsMatch = validateTransformOperations(valueList, ignoredHasBigRotation) >= 0; + + m_lastAnimationStartTime = monotonicallyIncreasingTime() - delayAsNegativeTimeOffset; + m_animations.add(TextureMapperAnimation(keyframesName, valueList, boxSize, *anim, listsMatch, m_lastAnimationStartTime, 0, TextureMapperAnimation::AnimationState::Playing)); + m_animationStartedTimer.startOneShot(0); + didChangeAnimations(); + return true; +} + +void CoordinatedGraphicsLayer::pauseAnimation(const String& animationName, double time) +{ + m_animations.pause(animationName, time); + didChangeAnimations(); +} + +void CoordinatedGraphicsLayer::removeAnimation(const String& animationName) +{ + m_animations.remove(animationName); + didChangeAnimations(); +} + +void CoordinatedGraphicsLayer::suspendAnimations(double time) +{ + m_animations.suspend(time); + didChangeAnimations(); +} + +void CoordinatedGraphicsLayer::resumeAnimations() +{ + m_animations.resume(); + didChangeAnimations(); +} + +void CoordinatedGraphicsLayer::animationStartedTimerFired() +{ + client().notifyAnimationStarted(this, "", m_lastAnimationStartTime); +} + +#if USE(COORDINATED_GRAPHICS_THREADED) +void CoordinatedGraphicsLayer::platformLayerWillBeDestroyed() +{ +} + +void CoordinatedGraphicsLayer::setPlatformLayerNeedsDisplay() +{ +} +#endif + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h new file mode 100644 index 000000000..bf25111db --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h @@ -0,0 +1,246 @@ +/* + 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 CoordinatedGraphicsLayer_h +#define CoordinatedGraphicsLayer_h + +#if USE(COORDINATED_GRAPHICS) + +#include "CoordinatedGraphicsState.h" +#include "CoordinatedImageBacking.h" +#include "FloatPoint3D.h" +#include "GraphicsLayer.h" +#include "GraphicsLayerTransform.h" +#include "Image.h" +#include "IntSize.h" +#include "TextureMapperAnimation.h" +#include "TextureMapperPlatformLayer.h" +#include "TiledBackingStore.h" +#include "TiledBackingStoreClient.h" +#include "TransformationMatrix.h" +#include <wtf/text/StringHash.h> + +namespace WebCore { +class CoordinatedGraphicsLayer; +class TextureMapperAnimations; +class ScrollableArea; + +class CoordinatedGraphicsLayerClient { +public: + virtual bool isFlushingLayerChanges() const = 0; + virtual FloatRect visibleContentsRect() const = 0; + virtual Ref<CoordinatedImageBacking> createImageBackingIfNeeded(Image*) = 0; + virtual void detachLayer(CoordinatedGraphicsLayer*) = 0; + virtual bool paintToSurface(const IntSize&, CoordinatedSurface::Flags, uint32_t& atlasID, IntPoint&, CoordinatedSurface::Client&) = 0; + + virtual void syncLayerState(CoordinatedLayerID, CoordinatedGraphicsLayerState&) = 0; +}; + +class CoordinatedGraphicsLayer : public GraphicsLayer + , public TiledBackingStoreClient +#if USE(COORDINATED_GRAPHICS_THREADED) + , public TextureMapperPlatformLayer::Client +#endif + , public CoordinatedImageBacking::Host { +public: + explicit CoordinatedGraphicsLayer(Type, GraphicsLayerClient&); + virtual ~CoordinatedGraphicsLayer(); + + PlatformLayerID primaryLayerID() const override { return id(); } + + // Reimplementations from GraphicsLayer.h. + bool setChildren(const Vector<GraphicsLayer*>&) override; + void addChild(GraphicsLayer*) override; + void addChildAtIndex(GraphicsLayer*, int) override; + void addChildAbove(GraphicsLayer*, GraphicsLayer*) override; + void addChildBelow(GraphicsLayer*, GraphicsLayer*) override; + bool replaceChild(GraphicsLayer*, GraphicsLayer*) override; + void removeFromParent() override; + void setPosition(const FloatPoint&) override; + void setAnchorPoint(const FloatPoint3D&) override; + void setSize(const FloatSize&) override; + void setTransform(const TransformationMatrix&) override; + void setChildrenTransform(const TransformationMatrix&) override; + void setPreserves3D(bool) override; + void setMasksToBounds(bool) override; + void setDrawsContent(bool) override; + void setContentsVisible(bool) override; + void setContentsOpaque(bool) override; + void setBackfaceVisibility(bool) override; + void setOpacity(float) override; + void setContentsRect(const FloatRect&) override; + void setContentsTilePhase(const FloatSize&) override; + void setContentsTileSize(const FloatSize&) override; + void setContentsToImage(Image*) override; + void setContentsToSolidColor(const Color&) override; + void setShowDebugBorder(bool) override; + void setShowRepaintCounter(bool) override; + bool shouldDirectlyCompositeImage(Image*) const override; + void setContentsToPlatformLayer(PlatformLayer*, ContentsLayerPurpose) override; + void setMaskLayer(GraphicsLayer*) override; + void setReplicatedByLayer(GraphicsLayer*) override; + void setNeedsDisplay() override; + void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer) override; + void setContentsNeedsDisplay() override; + void deviceOrPageScaleFactorChanged() override; + void flushCompositingState(const FloatRect&) override; + void flushCompositingStateForThisLayerOnly() override; + bool setFilters(const FilterOperations&) override; + bool addAnimation(const KeyframeValueList&, const FloatSize&, const Animation*, const String&, double) override; + void pauseAnimation(const String&, double) override; + void removeAnimation(const String&) override; + void suspendAnimations(double time) override; + void resumeAnimations() override; + bool usesContentsLayer() const override { return m_platformLayer || m_compositedImage; } + + void syncPendingStateChangesIncludingSubLayers(); + void updateContentBuffersIncludingSubLayers(); + + FloatPoint computePositionRelativeToBase(); + void computePixelAlignment(FloatPoint& position, FloatSize&, FloatPoint3D& anchorPoint, FloatSize& alignmentOffset); + + void setVisibleContentRectTrajectoryVector(const FloatPoint&); + + void setScrollableArea(ScrollableArea*); + bool isScrollable() const { return !!m_scrollableArea; } + void commitScrollOffset(const IntSize&); + + CoordinatedLayerID id() const { return m_id; } + + void setFixedToViewport(bool isFixed); + + IntRect coverRect() const { return m_mainBackingStore ? m_mainBackingStore->mapToContents(m_mainBackingStore->coverRect()) : IntRect(); } + IntRect transformedVisibleRect(); + + // TiledBackingStoreClient + void tiledBackingStorePaint(GraphicsContext&, const IntRect&) override; + void didUpdateTileBuffers() override; + void tiledBackingStoreHasPendingTileCreation() override; + void createTile(uint32_t tileID, float) override; + void updateTile(uint32_t tileID, const SurfaceUpdateInfo&, const IntRect&) override; + void removeTile(uint32_t tileID) override; + bool paintToSurface(const IntSize&, uint32_t& /* atlasID */, IntPoint&, CoordinatedSurface::Client&) override; + + void setCoordinator(CoordinatedGraphicsLayerClient*); + + void setNeedsVisibleRectAdjustment(); + void purgeBackingStores(); + + CoordinatedGraphicsLayer* findFirstDescendantWithContentsRecursively(); + +private: + bool isCoordinatedGraphicsLayer() const override { return true; } + + void syncPlatformLayer(); + void updatePlatformLayer(); +#if USE(COORDINATED_GRAPHICS_THREADED) + void platformLayerWillBeDestroyed() override; + void setPlatformLayerNeedsDisplay() override; +#endif + + void setDebugBorder(const Color&, float width) override; + + bool fixedToViewport() const { return m_fixedToViewport; } + + void didChangeLayerState(); + void didChangeAnimations(); + void didChangeGeometry(); + void didChangeChildren(); + void didChangeFilters(); + void didChangeImageBacking(); + + void resetLayerState(); + void syncLayerState(); + void syncAnimations(); + void syncChildren(); + void syncFilters(); + void syncImageBacking(); + void computeTransformedVisibleRect(); + void updateContentBuffers(); + + void createBackingStore(); + void releaseImageBackingIfNeeded(); + + void notifyFlushRequired(); + + // CoordinatedImageBacking::Host + bool imageBackingVisible() override; + bool shouldHaveBackingStore() const; + bool selfOrAncestorHasActiveTransformAnimation() const; + bool selfOrAncestorHaveNonAffineTransforms(); + void adjustContentsScale(); + + void setShouldUpdateVisibleRect(); + float effectiveContentsScale(); + + void animationStartedTimerFired(); + + bool filtersCanBeComposited(const FilterOperations&) const; + + CoordinatedLayerID m_id; + CoordinatedGraphicsLayerState m_layerState; + GraphicsLayerTransform m_layerTransform; + TransformationMatrix m_cachedInverseTransform; + FloatSize m_pixelAlignmentOffset; + FloatSize m_adjustedSize; + FloatPoint m_adjustedPosition; + FloatPoint3D m_adjustedAnchorPoint; + +#ifndef NDEBUG + bool m_isPurging; +#endif + bool m_shouldUpdateVisibleRect: 1; + bool m_shouldSyncLayerState: 1; + bool m_shouldSyncChildren: 1; + bool m_shouldSyncFilters: 1; + bool m_shouldSyncImageBacking: 1; + bool m_shouldSyncAnimations: 1; + bool m_fixedToViewport : 1; + bool m_movingVisibleRect : 1; + bool m_pendingContentsScaleAdjustment : 1; + bool m_pendingVisibleRectAdjustment : 1; +#if USE(COORDINATED_GRAPHICS_THREADED) + bool m_shouldSyncPlatformLayer : 1; + bool m_shouldUpdatePlatformLayer : 1; +#endif + + CoordinatedGraphicsLayerClient* m_coordinator; + std::unique_ptr<TiledBackingStore> m_mainBackingStore; + std::unique_ptr<TiledBackingStore> m_previousBackingStore; + + RefPtr<Image> m_compositedImage; + NativeImagePtr m_compositedNativeImagePtr; + RefPtr<CoordinatedImageBacking> m_coordinatedImageBacking; + + PlatformLayer* m_platformLayer; + Timer m_animationStartedTimer; + TextureMapperAnimations m_animations; + double m_lastAnimationStartTime { 0.0 }; + + ScrollableArea* m_scrollableArea; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_GRAPHICSLAYER(WebCore::CoordinatedGraphicsLayer, isCoordinatedGraphicsLayer()) + +#endif // USE(COORDINATED_GRAPHICS) + +#endif // CoordinatedGraphicsLayer_h diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h new file mode 100644 index 000000000..c810530b5 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsState.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2013 Company 100, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CoordinatedGraphicsState_h +#define CoordinatedGraphicsState_h + +#if USE(COORDINATED_GRAPHICS) + +#include "Color.h" +#include "FilterOperations.h" +#include "FloatRect.h" +#include "FloatSize.h" +#include "IntRect.h" +#include "IntSize.h" +#include "SurfaceUpdateInfo.h" +#include "TextureMapperAnimation.h" +#include "TransformationMatrix.h" + +#if USE(COORDINATED_GRAPHICS_THREADED) +#include "TextureMapperPlatformLayerProxy.h" +#endif + +namespace WebCore { + +class CoordinatedSurface; + +typedef uint32_t CoordinatedLayerID; +enum { InvalidCoordinatedLayerID = 0 }; + +typedef uint64_t CoordinatedImageBackingID; +enum { InvalidCoordinatedImageBackingID = 0 }; + +struct TileUpdateInfo { + uint32_t tileID; + IntRect tileRect; + WebCore::SurfaceUpdateInfo updateInfo; +}; + +struct TileCreationInfo { + uint32_t tileID; + float scale; +}; + +struct CoordinatedGraphicsLayerState { + union { + struct { + bool positionChanged: 1; + bool anchorPointChanged: 1; + bool sizeChanged: 1; + bool transformChanged: 1; + bool childrenTransformChanged: 1; + bool contentsRectChanged: 1; + bool opacityChanged: 1; + bool solidColorChanged: 1; + bool debugBorderColorChanged: 1; + bool debugBorderWidthChanged: 1; + bool replicaChanged: 1; + bool maskChanged: 1; + bool imageChanged: 1; + bool flagsChanged: 1; + bool animationsChanged: 1; + bool filtersChanged: 1; + bool childrenChanged: 1; + bool repaintCountChanged : 1; + bool platformLayerChanged: 1; + bool platformLayerShouldSwapBuffers: 1; + bool isScrollableChanged: 1; + bool committedScrollOffsetChanged: 1; + bool contentsTilingChanged: 1; + }; + unsigned changeMask; + }; + union { + struct { + bool contentsOpaque : 1; + bool drawsContent : 1; + bool contentsVisible : 1; + bool backfaceVisible : 1; + bool masksToBounds : 1; + bool preserves3D : 1; + bool fixedToViewport : 1; + bool showDebugBorders : 1; + bool showRepaintCounter : 1; + bool isScrollable: 1; + }; + unsigned flags; + }; + + CoordinatedGraphicsLayerState() + : changeMask(0) + , contentsOpaque(false) + , drawsContent(false) + , contentsVisible(true) + , backfaceVisible(true) + , masksToBounds(false) + , preserves3D(false) + , fixedToViewport(false) + , showDebugBorders(false) + , showRepaintCounter(false) + , isScrollable(false) + , opacity(0) + , debugBorderWidth(0) + , replica(InvalidCoordinatedLayerID) + , mask(InvalidCoordinatedLayerID) + , imageID(InvalidCoordinatedImageBackingID) + , repaintCount(0) +#if USE(COORDINATED_GRAPHICS_THREADED) + , platformLayerProxy(0) +#endif + { + } + + FloatPoint pos; + FloatPoint3D anchorPoint; + FloatSize size; + TransformationMatrix transform; + TransformationMatrix childrenTransform; + FloatRect contentsRect; + FloatSize contentsTilePhase; + FloatSize contentsTileSize; + float opacity; + Color solidColor; + Color debugBorderColor; + float debugBorderWidth; + FilterOperations filters; + TextureMapperAnimations animations; + Vector<uint32_t> children; + Vector<TileCreationInfo> tilesToCreate; + Vector<uint32_t> tilesToRemove; + CoordinatedLayerID replica; + CoordinatedLayerID mask; + CoordinatedImageBackingID imageID; + + unsigned repaintCount; + Vector<TileUpdateInfo> tilesToUpdate; + +#if USE(COORDINATED_GRAPHICS_THREADED) + RefPtr<TextureMapperPlatformLayerProxy> platformLayerProxy; +#endif + + IntSize committedScrollOffset; + + bool hasPendingChanges() const + { + return changeMask || tilesToUpdate.size() || tilesToRemove.size() || tilesToCreate.size(); + } +}; + +struct CoordinatedGraphicsState { + uint32_t rootCompositingLayer; + FloatPoint scrollPosition; + IntSize contentsSize; + IntRect coveredRect; + + Vector<CoordinatedLayerID> layersToCreate; + Vector<std::pair<CoordinatedLayerID, CoordinatedGraphicsLayerState> > layersToUpdate; + Vector<CoordinatedLayerID> layersToRemove; + + Vector<CoordinatedImageBackingID> imagesToCreate; + Vector<CoordinatedImageBackingID> imagesToRemove; + Vector<std::pair<CoordinatedImageBackingID, RefPtr<CoordinatedSurface> > > imagesToUpdate; + Vector<CoordinatedImageBackingID> imagesToClear; + + Vector<std::pair<uint32_t /* atlasID */, RefPtr<CoordinatedSurface> > > updateAtlasesToCreate; + Vector<uint32_t /* atlasID */> updateAtlasesToRemove; +}; + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) + +#endif // CoordinatedGraphicsState_h diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp new file mode 100644 index 000000000..3f5673e8f --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2012 Company 100, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(COORDINATED_GRAPHICS) +#include "CoordinatedImageBacking.h" + +#include "CoordinatedGraphicsState.h" +#include "GraphicsContext.h" + +namespace WebCore { + +class ImageBackingSurfaceClient : public CoordinatedSurface::Client { +public: + ImageBackingSurfaceClient(Image& image, const IntRect& rect) + : m_image(image) + , m_rect(rect) + { + } + + void paintToSurfaceContext(GraphicsContext& context) override + { + context.drawImage(m_image, m_rect, m_rect); + } + +private: + Image& m_image; + IntRect m_rect; +}; + +CoordinatedImageBackingID CoordinatedImageBacking::getCoordinatedImageBackingID(Image* image) +{ + // CoordinatedImageBacking keeps a RefPtr<Image> member, so the same Image pointer can not refer two different instances until CoordinatedImageBacking releases the member. + return reinterpret_cast<CoordinatedImageBackingID>(image); +} + +PassRefPtr<CoordinatedImageBacking> CoordinatedImageBacking::create(Client* client, PassRefPtr<Image> image) +{ + return adoptRef(new CoordinatedImageBacking(client, image)); +} + +CoordinatedImageBacking::CoordinatedImageBacking(Client* client, PassRefPtr<Image> image) + : m_client(client) + , m_image(image) + , m_id(getCoordinatedImageBackingID(m_image.get())) + , m_clearContentsTimer(*this, &CoordinatedImageBacking::clearContentsTimerFired) + , m_isDirty(false) + , m_isVisible(false) +{ + // FIXME: We would need to decode a small image directly into a GraphicsSurface. + // http://webkit.org/b/101426 + + m_client->createImageBacking(id()); +} + +CoordinatedImageBacking::~CoordinatedImageBacking() +{ +} + +void CoordinatedImageBacking::addHost(Host* host) +{ + ASSERT(!m_hosts.contains(host)); + m_hosts.append(host); +} + +void CoordinatedImageBacking::removeHost(Host* host) +{ + size_t position = m_hosts.find(host); + ASSERT(position != notFound); + m_hosts.remove(position); + + if (m_hosts.isEmpty()) + m_client->removeImageBacking(id()); +} + +void CoordinatedImageBacking::markDirty() +{ + m_isDirty = true; +} + +void CoordinatedImageBacking::update() +{ + releaseSurfaceIfNeeded(); + + bool changedToVisible; + updateVisibilityIfNeeded(changedToVisible); + if (!m_isVisible) + return; + + if (!changedToVisible) { + if (!m_isDirty) + return; + + if (m_nativeImagePtr == m_image->nativeImageForCurrentFrame()) { + m_isDirty = false; + return; + } + } + + m_surface = CoordinatedSurface::create(IntSize(m_image->size()), !m_image->currentFrameKnownToBeOpaque() ? CoordinatedSurface::SupportsAlpha : CoordinatedSurface::NoFlags); + if (!m_surface) { + m_isDirty = false; + return; + } + + IntRect rect(IntPoint::zero(), IntSize(m_image->size())); + + ImageBackingSurfaceClient surfaceClient(*m_image, rect); + m_surface->paintToSurface(rect, surfaceClient); + + m_nativeImagePtr = m_image->nativeImageForCurrentFrame(); + + m_client->updateImageBacking(id(), m_surface.copyRef()); + m_isDirty = false; +} + +void CoordinatedImageBacking::releaseSurfaceIfNeeded() +{ + // We must keep m_surface until UI Process reads m_surface. + // If m_surface exists, it was created in the previous update. + m_surface = nullptr; +} + +static const double clearContentsTimerInterval = 3; + +void CoordinatedImageBacking::updateVisibilityIfNeeded(bool& changedToVisible) +{ + bool previousIsVisible = m_isVisible; + + m_isVisible = false; + for (auto& host : m_hosts) { + if (host->imageBackingVisible()) { + m_isVisible = true; + break; + } + } + + bool changedToInvisible = previousIsVisible && !m_isVisible; + if (changedToInvisible) { + ASSERT(!m_clearContentsTimer.isActive()); + m_clearContentsTimer.startOneShot(clearContentsTimerInterval); + } + + changedToVisible = !previousIsVisible && m_isVisible; + + if (m_isVisible && m_clearContentsTimer.isActive()) { + m_clearContentsTimer.stop(); + // We don't want to update the texture if we didn't remove the texture. + changedToVisible = false; + } +} + +void CoordinatedImageBacking::clearContentsTimerFired() +{ + m_client->clearImageBackingContents(id()); +} + +} // namespace WebCore +#endif diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h new file mode 100644 index 000000000..b8603c68e --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedImageBacking.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 Company 100, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef CoordinatedImageBacking_h +#define CoordinatedImageBacking_h + +#if USE(COORDINATED_GRAPHICS) +#include "CoordinatedGraphicsState.h" +#include "CoordinatedSurface.h" +#include "Image.h" +#include "Timer.h" +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class CoordinatedImageBacking : public RefCounted<CoordinatedImageBacking> { +public: + class Client { + public: + virtual void createImageBacking(CoordinatedImageBackingID) = 0; + virtual void updateImageBacking(CoordinatedImageBackingID, RefPtr<CoordinatedSurface>&&) = 0; + virtual void clearImageBackingContents(CoordinatedImageBackingID) = 0; + virtual void removeImageBacking(CoordinatedImageBackingID) = 0; + }; + + class Host { + public: + virtual bool imageBackingVisible() = 0; + }; + + static PassRefPtr<CoordinatedImageBacking> create(Client*, PassRefPtr<Image>); + virtual ~CoordinatedImageBacking(); + + static CoordinatedImageBackingID getCoordinatedImageBackingID(Image*); + CoordinatedImageBackingID id() const { return m_id; } + + void addHost(Host*); + void removeHost(Host*); + + // When a new image is updated or an animated gif is progressed, CoordinatedGraphicsLayer calls markDirty(). + void markDirty(); + + // Create, remove or update its backing. + void update(); + +private: + CoordinatedImageBacking(Client*, PassRefPtr<Image>); + + void releaseSurfaceIfNeeded(); + void updateVisibilityIfNeeded(bool& changedToVisible); + void clearContentsTimerFired(); + + Client* m_client; + RefPtr<Image> m_image; + NativeImagePtr m_nativeImagePtr; + CoordinatedImageBackingID m_id; + Vector<Host*> m_hosts; + + RefPtr<CoordinatedSurface> m_surface; + + Timer m_clearContentsTimer; + + bool m_isDirty; + bool m_isVisible; + +}; + +} // namespace WebCore +#endif // USE(COORDINATED_GRAPHICS) + +#endif // CoordinatedImageBacking_h diff --git a/Source/WebCore/platform/graphics/TypesettingFeatures.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.cpp index aa46beb02..e0d815bd0 100644 --- a/Source/WebCore/platform/graphics/TypesettingFeatures.h +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2013 Company 100, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,16 +23,32 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TypesettingFeatures_h -#define TypesettingFeatures_h +#include "config.h" +#include "CoordinatedSurface.h" + +#if USE(COORDINATED_GRAPHICS) namespace WebCore { - enum TypesettingFeature { - Kerning = 1 << 0, - Ligatures = 1 << 1, - }; - typedef unsigned TypesettingFeatures; +CoordinatedSurface::Factory* CoordinatedSurface::s_factory = 0; + +void CoordinatedSurface::setFactory(CoordinatedSurface::Factory factory) +{ + s_factory = factory; +} + +RefPtr<CoordinatedSurface> CoordinatedSurface::create(const IntSize& size, Flags flags) +{ + ASSERT(s_factory); + return s_factory(size, flags); +} + +CoordinatedSurface::CoordinatedSurface(const IntSize& size, Flags flags) + : m_size(size) + , m_flags(flags) +{ +} + } // namespace WebCore -#endif // TypesettingFeatures_h +#endif // USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h new file mode 100644 index 000000000..ac9ae1712 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedSurface.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2012 Company 100, Inc. + + 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 CoordinatedSurface_h +#define CoordinatedSurface_h + +#if USE(COORDINATED_GRAPHICS) +#include "IntRect.h" +#include <wtf/RefPtr.h> +#include <wtf/ThreadSafeRefCounted.h> + +namespace WebCore { +class BitmapTexture; +class GraphicsContext; + +class CoordinatedSurface : public ThreadSafeRefCounted<CoordinatedSurface> { +public: + enum Flag { + NoFlags = 0, + SupportsAlpha = 1 << 0, + }; + typedef unsigned Flags; + + class Client { + public: + virtual ~Client() { } + virtual void paintToSurfaceContext(GraphicsContext&) = 0; + }; + + typedef RefPtr<CoordinatedSurface> Factory(const IntSize&, Flags); + static void setFactory(Factory); + static RefPtr<CoordinatedSurface> create(const IntSize&, Flags); + + virtual ~CoordinatedSurface() { } + + bool supportsAlpha() const { return flags() & SupportsAlpha; } + IntSize size() const { return m_size; } + + virtual void paintToSurface(const IntRect&, Client&) = 0; + +#if USE(TEXTURE_MAPPER) + virtual void copyToTexture(RefPtr<BitmapTexture>, const IntRect& target, const IntPoint& sourceOffset) = 0; +#endif + +protected: + CoordinatedSurface(const IntSize&, Flags); + Flags flags() const { return m_flags; } + + IntSize m_size; + Flags m_flags; + +private: + static CoordinatedSurface::Factory* s_factory; +}; + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) +#endif // CoordinatedSurface_h diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h b/Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h new file mode 100644 index 000000000..0e749be93 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/SurfaceUpdateInfo.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2012 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 SurfaceUpdateInfo_h +#define SurfaceUpdateInfo_h + +#if USE(COORDINATED_GRAPHICS) + +#include "IntRect.h" + +namespace WebCore { + +class SurfaceUpdateInfo { + +public: + SurfaceUpdateInfo() { } + + // The rect to be updated. + IntRect updateRect; + + // The id of the update atlas including the shareable bitmap containing the updates. + uint32_t atlasID { 0 }; + + // The offset in the bitmap where the rendered contents are. + IntPoint surfaceOffset; +}; + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) + +#endif // SurfaceUpdateInfo_h diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp new file mode 100644 index 000000000..f155d4433 --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Tile.h" + +#if USE(COORDINATED_GRAPHICS) +#include "GraphicsContext.h" +#include "SurfaceUpdateInfo.h" +#include "TiledBackingStore.h" +#include "TiledBackingStoreClient.h" + +namespace WebCore { + +static const uint32_t InvalidTileID = 0; + +Tile::Tile(TiledBackingStore& tiledBackingStore, const Coordinate& tileCoordinate) + : m_tiledBackingStore(tiledBackingStore) + , m_coordinate(tileCoordinate) + , m_rect(tiledBackingStore.tileRectForCoordinate(tileCoordinate)) + , m_ID(InvalidTileID) + , m_dirtyRect(m_rect) +{ +} + +Tile::~Tile() +{ + if (m_ID != InvalidTileID) + m_tiledBackingStore.client()->removeTile(m_ID); +} + +bool Tile::isDirty() const +{ + return !m_dirtyRect.isEmpty(); +} + +void Tile::invalidate(const IntRect& dirtyRect) +{ + IntRect tileDirtyRect = intersection(dirtyRect, m_rect); + if (tileDirtyRect.isEmpty()) + return; + + m_dirtyRect.unite(tileDirtyRect); +} + +bool Tile::updateBackBuffer() +{ + if (!isDirty()) + return false; + + SurfaceUpdateInfo updateInfo; + + if (!m_tiledBackingStore.client()->paintToSurface(m_dirtyRect.size(), updateInfo.atlasID, updateInfo.surfaceOffset, *this)) + return false; + + updateInfo.updateRect = m_dirtyRect; + updateInfo.updateRect.move(-m_rect.x(), -m_rect.y()); + + static uint32_t id = 1; + if (m_ID == InvalidTileID) { + m_ID = id++; + // We may get an invalid ID due to wrap-around on overflow. + if (m_ID == InvalidTileID) + m_ID = id++; + m_tiledBackingStore.client()->createTile(m_ID, m_tiledBackingStore.contentsScale()); + } + m_tiledBackingStore.client()->updateTile(m_ID, updateInfo, m_rect); + + m_dirtyRect = IntRect(); + + return true; +} + +void Tile::paintToSurfaceContext(GraphicsContext& context) +{ + context.translate(-m_dirtyRect.x(), -m_dirtyRect.y()); + context.scale(FloatSize(m_tiledBackingStore.contentsScale(), m_tiledBackingStore.contentsScale())); + m_tiledBackingStore.client()->tiledBackingStorePaint(context, m_tiledBackingStore.mapToContents(m_dirtyRect)); +} + +bool Tile::isReadyToPaint() const +{ + return m_ID != InvalidTileID; +} + +void Tile::resize(const IntSize& newSize) +{ + m_rect = IntRect(m_rect.location(), newSize); + m_dirtyRect = m_rect; +} + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/Tile.h b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.h new file mode 100644 index 000000000..0b04d75aa --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/Tile.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Tile_h +#define Tile_h + +#if USE(COORDINATED_GRAPHICS) +#include "CoordinatedSurface.h" +#include "IntPoint.h" +#include "IntPointHash.h" +#include "IntRect.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +class GraphicsContext; +class TiledBackingStore; + +class Tile : public CoordinatedSurface::Client { +public: + typedef IntPoint Coordinate; + + Tile(TiledBackingStore&, const Coordinate&); + ~Tile(); + + bool isDirty() const; + void invalidate(const IntRect&); + bool updateBackBuffer(); + bool isReadyToPaint() const; + + const Coordinate& coordinate() const { return m_coordinate; } + const IntRect& rect() const { return m_rect; } + void resize(const IntSize&); + + void paintToSurfaceContext(GraphicsContext&) override; + +private: + TiledBackingStore& m_tiledBackingStore; + Coordinate m_coordinate; + IntRect m_rect; + + uint32_t m_ID; + IntRect m_dirtyRect; +}; + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) + +#endif // Tile_h diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp new file mode 100644 index 000000000..b6389165a --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.cpp @@ -0,0 +1,425 @@ +/* + Copyright (C) 2010-2012 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 "TiledBackingStore.h" + +#if USE(COORDINATED_GRAPHICS) +#include "GraphicsContext.h" +#include "MemoryPressureHandler.h" +#include "TiledBackingStoreClient.h" +#include <wtf/CheckedArithmetic.h> + +namespace WebCore { + +static const int defaultTileDimension = 512; + +static IntPoint innerBottomRight(const IntRect& rect) +{ + // Actually, the rect does not contain rect.maxX(). Refer to IntRect::contain. + return IntPoint(rect.maxX() - 1, rect.maxY() - 1); +} + +TiledBackingStore::TiledBackingStore(TiledBackingStoreClient* client, float contentsScale) + : m_client(client) + , m_tileSize(defaultTileDimension, defaultTileDimension) + , m_coverAreaMultiplier(2.0f) + , m_contentsScale(contentsScale) + , m_supportsAlpha(false) + , m_pendingTileCreation(false) +{ +} + +TiledBackingStore::~TiledBackingStore() +{ +} + +void TiledBackingStore::setTrajectoryVector(const FloatPoint& trajectoryVector) +{ + m_pendingTrajectoryVector = trajectoryVector; + m_pendingTrajectoryVector.normalize(); +} + +void TiledBackingStore::createTilesIfNeeded(const IntRect& unscaledVisibleRect, const IntRect& contentsRect) +{ + IntRect scaledContentsRect = mapFromContents(contentsRect); + IntRect visibleRect = mapFromContents(unscaledVisibleRect); + float coverAreaMultiplier = MemoryPressureHandler::singleton().isUnderMemoryPressure() ? 1.0f : 2.0f; + + bool didChange = m_trajectoryVector != m_pendingTrajectoryVector || m_visibleRect != visibleRect || m_rect != scaledContentsRect || m_coverAreaMultiplier != coverAreaMultiplier; + if (didChange || m_pendingTileCreation) + createTiles(visibleRect, scaledContentsRect, coverAreaMultiplier); +} + +void TiledBackingStore::invalidate(const IntRect& contentsDirtyRect) +{ + IntRect dirtyRect(mapFromContents(contentsDirtyRect)); + IntRect keepRectFitToTileSize = tileRectForCoordinate(tileCoordinateForPoint(m_keepRect.location())); + keepRectFitToTileSize.unite(tileRectForCoordinate(tileCoordinateForPoint(innerBottomRight(m_keepRect)))); + + // Only iterate on the part of the rect that we know we might have tiles. + IntRect coveredDirtyRect = intersection(dirtyRect, keepRectFitToTileSize); + Tile::Coordinate topLeft = tileCoordinateForPoint(coveredDirtyRect.location()); + Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(coveredDirtyRect)); + + for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { + for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { + Tile* currentTile = m_tiles.get(Tile::Coordinate(xCoordinate, yCoordinate)); + if (!currentTile) + continue; + // Pass the full rect to each tile as coveredDirtyRect might not + // contain them completely and we don't want partial tile redraws. + currentTile->invalidate(dirtyRect); + } + } +} + +void TiledBackingStore::updateTileBuffers() +{ + // FIXME: In single threaded case, tile back buffers could be updated asynchronously + // one by one and then swapped to front in one go. This would minimize the time spent + // blocking on tile updates. + bool updated = false; + for (auto& tile : m_tiles.values()) { + if (!tile->isDirty()) + continue; + + updated |= tile->updateBackBuffer(); + } + + if (updated) + m_client->didUpdateTileBuffers(); +} + +double TiledBackingStore::tileDistance(const IntRect& viewport, const Tile::Coordinate& tileCoordinate) const +{ + if (viewport.intersects(tileRectForCoordinate(tileCoordinate))) + return 0; + + IntPoint viewCenter = viewport.location() + IntSize(viewport.width() / 2, viewport.height() / 2); + Tile::Coordinate centerCoordinate = tileCoordinateForPoint(viewCenter); + + return std::max(abs(centerCoordinate.y() - tileCoordinate.y()), abs(centerCoordinate.x() - tileCoordinate.x())); +} + +// Returns a ratio between 0.0f and 1.0f of the surface covered by rendered tiles. +float TiledBackingStore::coverageRatio(const WebCore::IntRect& dirtyRect) const +{ + float rectArea = dirtyRect.width() * dirtyRect.height(); + float coverArea = 0.0f; + + Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location()); + Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(dirtyRect)); + + for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { + for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { + Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate); + Tile* currentTile = m_tiles.get(currentCoordinate); + if (currentTile && currentTile->isReadyToPaint()) { + IntRect coverRect = intersection(dirtyRect, currentTile->rect()); + coverArea += coverRect.width() * coverRect.height(); + } + } + } + return coverArea / rectArea; +} + +bool TiledBackingStore::visibleAreaIsCovered() const +{ + return coverageRatio(intersection(m_visibleRect, m_rect)) == 1.0f; +} + +void TiledBackingStore::createTiles(const IntRect& visibleRect, const IntRect& scaledContentsRect, float coverAreaMultiplier) +{ + // Update our backing store geometry. + const IntRect previousRect = m_rect; + m_rect = scaledContentsRect; + m_trajectoryVector = m_pendingTrajectoryVector; + m_visibleRect = visibleRect; + m_coverAreaMultiplier = coverAreaMultiplier; + + if (m_rect.isEmpty()) { + setCoverRect(IntRect()); + setKeepRect(IntRect()); + return; + } + + /* We must compute cover and keep rects using the visibleRect, instead of the rect intersecting the visibleRect with m_rect, + * because TBS can be used as a backing store of GraphicsLayer and the visible rect usually does not intersect with m_rect. + * In the below case, the intersecting rect is an empty. + * + * +---------------+ + * | | + * | m_rect | + * | +-------|-----------------------+ + * | | HERE | cover or keep | + * +---------------+ rect | + * | +---------+ | + * | | visible | | + * | | rect | | + * | +---------+ | + * | | + * | | + * +-------------------------------+ + * + * We must create or keep the tiles in the HERE region. + */ + + IntRect coverRect; + IntRect keepRect; + computeCoverAndKeepRect(m_visibleRect, coverRect, keepRect); + + setCoverRect(coverRect); + setKeepRect(keepRect); + + if (coverRect.isEmpty()) + return; + + // Resize tiles at the edge in case the contents size has changed, but only do so + // after having dropped tiles outside the keep rect. + bool didResizeTiles = false; + if (previousRect != m_rect) + didResizeTiles = resizeEdgeTiles(); + + // Search for the tile position closest to the viewport center that does not yet contain a tile. + // Which position is considered the closest depends on the tileDistance function. + double shortestDistance = std::numeric_limits<double>::infinity(); + Vector<Tile::Coordinate> tilesToCreate; + unsigned requiredTileCount = 0; + + // Cover areas (in tiles) with minimum distance from the visible rect. If the visible rect is + // not covered already it will be covered first in one go, due to the distance being 0 for tiles + // inside the visible rect. + Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.location()); + Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(coverRect)); + for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { + for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { + Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate); + if (m_tiles.contains(currentCoordinate)) + continue; + ++requiredTileCount; + double distance = tileDistance(m_visibleRect, currentCoordinate); + if (distance > shortestDistance) + continue; + if (distance < shortestDistance) { + tilesToCreate.clear(); + shortestDistance = distance; + } + tilesToCreate.append(currentCoordinate); + } + } + + // Now construct the tile(s) within the shortest distance. + unsigned tilesToCreateCount = tilesToCreate.size(); + for (unsigned n = 0; n < tilesToCreateCount; ++n) { + Tile::Coordinate coordinate = tilesToCreate[n]; + m_tiles.add(coordinate, std::make_unique<Tile>(*this, coordinate)); + } + requiredTileCount -= tilesToCreateCount; + + // Paint the content of the newly created tiles or resized tiles. + if (tilesToCreateCount || didResizeTiles) + updateTileBuffers(); + + // Re-call createTiles on a timer to cover the visible area with the newest shortest distance. + m_pendingTileCreation = requiredTileCount; + if (m_pendingTileCreation) + m_client->tiledBackingStoreHasPendingTileCreation(); +} + +void TiledBackingStore::adjustForContentsRect(IntRect& rect) const +{ + IntRect bounds = m_rect; + IntSize candidateSize = rect.size(); + + rect.intersect(bounds); + + if (rect.size() == candidateSize) + return; + + /* + * In the following case, there is no intersection of the contents rect and the cover rect. + * Thus the latter should not be inflated. + * + * +---------------+ + * | m_rect | + * +---------------+ + * + * +-------------------------------+ + * | cover rect | + * | +---------+ | + * | | visible | | + * | | rect | | + * | +---------+ | + * +-------------------------------+ + */ + if (rect.isEmpty()) + return; + + // Try to create a cover rect of the same size as the candidate, but within content bounds. + int pixelsCovered = 0; + if (!WTF::safeMultiply(candidateSize.width(), candidateSize.height(), pixelsCovered)) + pixelsCovered = std::numeric_limits<int>::max(); + + if (rect.width() < candidateSize.width()) + rect.inflateY(((pixelsCovered / rect.width()) - rect.height()) / 2); + if (rect.height() < candidateSize.height()) + rect.inflateX(((pixelsCovered / rect.height()) - rect.width()) / 2); + + rect.intersect(bounds); +} + +void TiledBackingStore::computeCoverAndKeepRect(const IntRect& visibleRect, IntRect& coverRect, IntRect& keepRect) const +{ + coverRect = visibleRect; + keepRect = visibleRect; + + // If we cover more that the actual viewport we can be smart about which tiles we choose to render. + if (m_coverAreaMultiplier > 1) { + // The initial cover area covers equally in each direction, according to the coverAreaMultiplier. + coverRect.inflateX(visibleRect.width() * (m_coverAreaMultiplier - 1) / 2); + coverRect.inflateY(visibleRect.height() * (m_coverAreaMultiplier - 1) / 2); + keepRect = coverRect; + + if (m_trajectoryVector != FloatPoint::zero()) { + // A null trajectory vector (no motion) means that tiles for the coverArea will be created. + // A non-null trajectory vector will shrink the covered rect to visibleRect plus its expansion from its + // center toward the cover area edges in the direction of the given vector. + + // E.g. if visibleRect == (10,10)5x5 and coverAreaMultiplier == 3.0: + // a (0,0) trajectory vector will create tiles intersecting (5,5)15x15, + // a (1,0) trajectory vector will create tiles intersecting (10,10)10x5, + // and a (1,1) trajectory vector will create tiles intersecting (10,10)10x10. + + // Multiply the vector by the distance to the edge of the cover area. + float trajectoryVectorMultiplier = (m_coverAreaMultiplier - 1) / 2; + + // Unite the visible rect with a "ghost" of the visible rect moved in the direction of the trajectory vector. + coverRect = visibleRect; + coverRect.move(coverRect.width() * m_trajectoryVector.x() * trajectoryVectorMultiplier, coverRect.height() * m_trajectoryVector.y() * trajectoryVectorMultiplier); + + coverRect.unite(visibleRect); + } + ASSERT(keepRect.contains(coverRect)); + } + + adjustForContentsRect(coverRect); + + // The keep rect is an inflated version of the cover rect, inflated in tile dimensions. + keepRect.unite(coverRect); + keepRect.inflateX(m_tileSize.width() / 2); + keepRect.inflateY(m_tileSize.height() / 2); + keepRect.intersect(m_rect); + + ASSERT(coverRect.isEmpty() || keepRect.contains(coverRect)); +} + +bool TiledBackingStore::resizeEdgeTiles() +{ + bool wasResized = false; + Vector<Tile::Coordinate> tilesToRemove; + for (auto& tile : m_tiles.values()) { + Tile::Coordinate tileCoordinate = tile->coordinate(); + IntRect tileRect = tile->rect(); + IntRect expectedTileRect = tileRectForCoordinate(tileCoordinate); + if (expectedTileRect.isEmpty()) + tilesToRemove.append(tileCoordinate); + else if (expectedTileRect != tileRect) { + tile->resize(expectedTileRect.size()); + wasResized = true; + } + } + + for (auto& coordinateToRemove : tilesToRemove) + m_tiles.remove(coordinateToRemove); + + return wasResized; +} + +void TiledBackingStore::setKeepRect(const IntRect& keepRect) +{ + // Drop tiles outside the new keepRect. + + FloatRect keepRectF = keepRect; + + Vector<Tile::Coordinate> toRemove; + for (auto& tile : m_tiles.values()) { + Tile::Coordinate coordinate = tile->coordinate(); + FloatRect tileRect = tile->rect(); + if (!tileRect.intersects(keepRectF)) + toRemove.append(coordinate); + } + + for (auto& coordinateToRemove : toRemove) + m_tiles.remove(coordinateToRemove); + + m_keepRect = keepRect; +} + +void TiledBackingStore::removeAllNonVisibleTiles(const IntRect& unscaledVisibleRect, const IntRect& contentsRect) +{ + IntRect boundedVisibleRect = mapFromContents(intersection(unscaledVisibleRect, contentsRect)); + setKeepRect(boundedVisibleRect); +} + +IntRect TiledBackingStore::mapToContents(const IntRect& rect) const +{ + return enclosingIntRect(FloatRect(rect.x() / m_contentsScale, + rect.y() / m_contentsScale, + rect.width() / m_contentsScale, + rect.height() / m_contentsScale)); +} + +IntRect TiledBackingStore::mapFromContents(const IntRect& rect) const +{ + return enclosingIntRect(FloatRect(rect.x() * m_contentsScale, + rect.y() * m_contentsScale, + rect.width() * m_contentsScale, + rect.height() * m_contentsScale)); +} + +IntRect TiledBackingStore::tileRectForCoordinate(const Tile::Coordinate& coordinate) const +{ + IntRect rect(coordinate.x() * m_tileSize.width(), + coordinate.y() * m_tileSize.height(), + m_tileSize.width(), + m_tileSize.height()); + + rect.intersect(m_rect); + return rect; +} + +Tile::Coordinate TiledBackingStore::tileCoordinateForPoint(const IntPoint& point) const +{ + int x = point.x() / m_tileSize.width(); + int y = point.y() / m_tileSize.height(); + return Tile::Coordinate(std::max(x, 0), std::max(y, 0)); +} + +void TiledBackingStore::setSupportsAlpha(bool a) +{ + if (a == m_supportsAlpha) + return; + m_supportsAlpha = a; + invalidate(m_rect); +} + +} + +#endif diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h new file mode 100644 index 000000000..c17f7644d --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStore.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2010-2012 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 TiledBackingStore_h +#define TiledBackingStore_h + +#if USE(COORDINATED_GRAPHICS) + +#include "FloatPoint.h" +#include "IntPoint.h" +#include "IntRect.h" +#include "Tile.h" +#include "Timer.h" +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class GraphicsContext; +class TiledBackingStoreClient; + +class TiledBackingStore { + WTF_MAKE_NONCOPYABLE(TiledBackingStore); WTF_MAKE_FAST_ALLOCATED; +public: + TiledBackingStore(TiledBackingStoreClient*, float contentsScale = 1.f); + ~TiledBackingStore(); + + TiledBackingStoreClient* client() { return m_client; } + + void setTrajectoryVector(const FloatPoint&); + void createTilesIfNeeded(const IntRect& unscaledVisibleRect, const IntRect& contentsRect); + + float contentsScale() { return m_contentsScale; } + + void updateTileBuffers(); + + void invalidate(const IntRect& dirtyRect); + + IntRect mapToContents(const IntRect&) const; + IntRect mapFromContents(const IntRect&) const; + + IntRect tileRectForCoordinate(const Tile::Coordinate&) const; + Tile::Coordinate tileCoordinateForPoint(const IntPoint&) const; + double tileDistance(const IntRect& viewport, const Tile::Coordinate&) const; + + IntRect coverRect() const { return m_coverRect; } + bool visibleAreaIsCovered() const; + void removeAllNonVisibleTiles(const IntRect& unscaledVisibleRect, const IntRect& contentsRect); + + void setSupportsAlpha(bool); + +private: + void createTiles(const IntRect& visibleRect, const IntRect& scaledContentsRect, float coverAreaMultiplier); + void computeCoverAndKeepRect(const IntRect& visibleRect, IntRect& coverRect, IntRect& keepRect) const; + + bool resizeEdgeTiles(); + void setCoverRect(const IntRect& rect) { m_coverRect = rect; } + void setKeepRect(const IntRect&); + + float coverageRatio(const IntRect&) const; + void adjustForContentsRect(IntRect&) const; + + void paintCheckerPattern(GraphicsContext*, const IntRect&, const Tile::Coordinate&); + +private: + TiledBackingStoreClient* m_client; + + typedef HashMap<Tile::Coordinate, std::unique_ptr<Tile>> TileMap; + TileMap m_tiles; + + IntSize m_tileSize; + float m_coverAreaMultiplier; + + FloatPoint m_trajectoryVector; + FloatPoint m_pendingTrajectoryVector; + IntRect m_visibleRect; + + IntRect m_coverRect; + IntRect m_keepRect; + IntRect m_rect; + + float m_contentsScale; + + bool m_supportsAlpha; + bool m_pendingTileCreation; + + friend class Tile; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h new file mode 100644 index 000000000..61936ff9c --- /dev/null +++ b/Source/WebCore/platform/graphics/texmap/coordinated/TiledBackingStoreClient.h @@ -0,0 +1,49 @@ +/* + 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 TiledBackingStoreClient_h +#define TiledBackingStoreClient_h + +#include "CoordinatedSurface.h" + +namespace WebCore { + +#if USE(COORDINATED_GRAPHICS) + +class GraphicsContext; +class SurfaceUpdateInfo; + +class TiledBackingStoreClient { +public: + virtual ~TiledBackingStoreClient() { } + virtual void tiledBackingStorePaint(GraphicsContext&, const IntRect&) = 0; + virtual void didUpdateTileBuffers() = 0; + virtual void tiledBackingStoreHasPendingTileCreation() = 0; + + virtual void createTile(uint32_t tileID, float) = 0; + virtual void updateTile(uint32_t tileID, const SurfaceUpdateInfo&, const IntRect&) = 0; + virtual void removeTile(uint32_t tileID) = 0; + virtual bool paintToSurface(const IntSize&, uint32_t& atlasID, IntPoint&, CoordinatedSurface::Client&) = 0; +}; + +#endif + +} + +#endif diff --git a/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp b/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp index 62a7639ba..8ebdd98f1 100644 --- a/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp +++ b/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005, 2006 Apple Inc. All rights reserved. * 2010 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,21 +31,34 @@ #include "FloatQuad.h" #include "FloatRect.h" #include "IntRect.h" +#include "TextStream.h" #include "TransformationMatrix.h" #include <wtf/MathExtras.h> namespace WebCore { +#if COMPILER(MSVC) AffineTransform::AffineTransform() { - setMatrix(1, 0, 0, 1, 0, 0); + m_transform = { 1, 0, 0, 1, 0, 0 }; +} + +AffineTransform::AffineTransform(double a, double b, double c, double d, double e, double f) +{ + m_transform = { a, b, c, d, e, f }; +} +#else +AffineTransform::AffineTransform() + : m_transform { { 1, 0, 0, 1, 0, 0 } } +{ } AffineTransform::AffineTransform(double a, double b, double c, double d, double e, double f) + : m_transform{ { a, b, c, d, e, f } } { - setMatrix(a, b, c, d, e, f); } +#endif void AffineTransform::makeIdentity() { @@ -79,21 +92,23 @@ double AffineTransform::yScale() const return sqrt(m_transform[2] * m_transform[2] + m_transform[3] * m_transform[3]); } -double AffineTransform::det() const +static double det(const std::array<double, 6>& transform) { - return m_transform[0] * m_transform[3] - m_transform[1] * m_transform[2]; + return transform[0] * transform[3] - transform[1] * transform[2]; } bool AffineTransform::isInvertible() const { - return det() != 0.0; + double determinant = det(m_transform); + + return std::isfinite(determinant) && determinant != 0; } -AffineTransform AffineTransform::inverse() const +std::optional<AffineTransform> AffineTransform::inverse() const { - double determinant = det(); - if (determinant == 0.0) - return AffineTransform(); + double determinant = det(m_transform); + if (!std::isfinite(determinant) || determinant == 0) + return std::nullopt; AffineTransform result; if (isIdentityOrTranslation()) { @@ -128,7 +143,7 @@ AffineTransform& AffineTransform::multiply(const AffineTransform& other) trans.m_transform[4] = other.m_transform[4] * m_transform[0] + other.m_transform[5] * m_transform[2] + m_transform[4]; trans.m_transform[5] = other.m_transform[4] * m_transform[1] + other.m_transform[5] * m_transform[3] + m_transform[5]; - setMatrix(trans.m_transform); + *this = trans; return *this; } @@ -158,6 +173,16 @@ AffineTransform& AffineTransform::scale(double sx, double sy) return *this; } +AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy) +{ + return scale(sx, sy); +} + +AffineTransform& AffineTransform::scale(const FloatSize& s) +{ + return scale(s.width(), s.height()); +} + // *this = *this * translation AffineTransform& AffineTransform::translate(double tx, double ty) { @@ -172,9 +197,9 @@ AffineTransform& AffineTransform::translate(double tx, double ty) return *this; } -AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy) +AffineTransform& AffineTransform::translate(const FloatPoint& t) { - return scale(sx, sy); + return translate(t.x(), t.y()); } AffineTransform& AffineTransform::rotateFromVector(double x, double y) @@ -401,4 +426,20 @@ void AffineTransform::recompose(const DecomposedType& decomp) this->scale(decomp.scaleX, decomp.scaleY); } +TextStream& operator<<(TextStream& ts, const AffineTransform& transform) +{ + if (transform.isIdentity()) + ts << "identity"; + else + ts << "{m=((" + << transform.a() << "," << transform.b() + << ")(" + << transform.c() << "," << transform.d() + << ")) t=(" + << transform.e() << "," << transform.f() + << ")}"; + + return ts; +} + } diff --git a/Source/WebCore/platform/graphics/transforms/AffineTransform.h b/Source/WebCore/platform/graphics/transforms/AffineTransform.h index 0a5b45732..a0b1f7bd3 100644 --- a/Source/WebCore/platform/graphics/transforms/AffineTransform.h +++ b/Source/WebCore/platform/graphics/transforms/AffineTransform.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005-2016 Apple Inc. All rights reserved. * 2010 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,8 +27,10 @@ #ifndef AffineTransform_h #define AffineTransform_h -#include <string.h> // for memcpy +#include "PlatformExportMacros.h" +#include <array> #include <wtf/FastMalloc.h> +#include <wtf/Optional.h> #if USE(CG) typedef struct CGAffineTransform CGAffineTransform; @@ -36,6 +38,11 @@ typedef struct CGAffineTransform CGAffineTransform; #include <cairo.h> #endif +#if PLATFORM(WIN) +struct D2D_MATRIX_3X2_F; +typedef D2D_MATRIX_3X2_F D2D1_MATRIX_3X2_F; +#endif + namespace WebCore { class FloatPoint; @@ -45,18 +52,21 @@ class FloatSize; class IntPoint; class IntSize; class IntRect; +class TextStream; class TransformationMatrix; class AffineTransform { WTF_MAKE_FAST_ALLOCATED; public: - typedef double Transform[6]; - - AffineTransform(); - AffineTransform(double a, double b, double c, double d, double e, double f); + WEBCORE_EXPORT AffineTransform(); + WEBCORE_EXPORT AffineTransform(double a, double b, double c, double d, double e, double f); #if USE(CG) - AffineTransform(const CGAffineTransform&); + WEBCORE_EXPORT AffineTransform(const CGAffineTransform&); +#endif + +#if PLATFORM(WIN) + AffineTransform(const D2D1_MATRIX_3X2_F&); #endif void setMatrix(double a, double b, double c, double d, double e, double f); @@ -64,22 +74,22 @@ public: void map(double x, double y, double& x2, double& y2) const; // Rounds the mapped point to the nearest integer value. - IntPoint mapPoint(const IntPoint&) const; + WEBCORE_EXPORT IntPoint mapPoint(const IntPoint&) const; - FloatPoint mapPoint(const FloatPoint&) const; + WEBCORE_EXPORT FloatPoint mapPoint(const FloatPoint&) const; - IntSize mapSize(const IntSize&) const; + WEBCORE_EXPORT IntSize mapSize(const IntSize&) const; - FloatSize mapSize(const FloatSize&) const; + WEBCORE_EXPORT FloatSize mapSize(const FloatSize&) const; // Rounds the resulting mapped rectangle out. This is helpful for bounding // box computations but may not be what is wanted in other contexts. - IntRect mapRect(const IntRect&) const; + WEBCORE_EXPORT IntRect mapRect(const IntRect&) const; - FloatRect mapRect(const FloatRect&) const; - FloatQuad mapQuad(const FloatQuad&) const; + WEBCORE_EXPORT FloatRect mapRect(const FloatRect&) const; + WEBCORE_EXPORT FloatQuad mapQuad(const FloatQuad&) const; - bool isIdentity() const; + WEBCORE_EXPORT bool isIdentity() const; double a() const { return m_transform[0]; } void setA(double a) { m_transform[0] = a; } @@ -94,34 +104,35 @@ public: double f() const { return m_transform[5]; } void setF(double f) { m_transform[5] = f; } - void makeIdentity(); + WEBCORE_EXPORT void makeIdentity(); - AffineTransform& multiply(const AffineTransform& other); - AffineTransform& scale(double); + WEBCORE_EXPORT AffineTransform& multiply(const AffineTransform& other); + WEBCORE_EXPORT AffineTransform& scale(double); AffineTransform& scale(double sx, double sy); - AffineTransform& scaleNonUniform(double sx, double sy); - AffineTransform& rotate(double d); + WEBCORE_EXPORT AffineTransform& scaleNonUniform(double sx, double sy); + WEBCORE_EXPORT AffineTransform& scale(const FloatSize&); + WEBCORE_EXPORT AffineTransform& rotate(double); AffineTransform& rotateFromVector(double x, double y); - AffineTransform& translate(double tx, double ty); - AffineTransform& shear(double sx, double sy); - AffineTransform& flipX(); - AffineTransform& flipY(); - AffineTransform& skew(double angleX, double angleY); + WEBCORE_EXPORT AffineTransform& translate(double tx, double ty); + WEBCORE_EXPORT AffineTransform& translate(const FloatPoint&); + WEBCORE_EXPORT AffineTransform& shear(double sx, double sy); + WEBCORE_EXPORT AffineTransform& flipX(); + WEBCORE_EXPORT AffineTransform& flipY(); + WEBCORE_EXPORT AffineTransform& skew(double angleX, double angleY); AffineTransform& skewX(double angle); AffineTransform& skewY(double angle); // These functions get the length of an axis-aligned unit vector // once it has been mapped through the transform - double xScale() const; - double yScale() const; + WEBCORE_EXPORT double xScale() const; + WEBCORE_EXPORT double yScale() const; - double det() const; - bool isInvertible() const; - AffineTransform inverse() const; + bool isInvertible() const; // If you call this this, you're probably doing it wrong. + WEBCORE_EXPORT std::optional<AffineTransform> inverse() const; - void blend(const AffineTransform& from, double progress); + WEBCORE_EXPORT void blend(const AffineTransform& from, double progress); - TransformationMatrix toTransformationMatrix() const; + WEBCORE_EXPORT TransformationMatrix toTransformationMatrix() const; bool isIdentityOrTranslation() const { @@ -165,16 +176,20 @@ public: } #if USE(CG) - operator CGAffineTransform() const; + WEBCORE_EXPORT operator CGAffineTransform() const; #elif USE(CAIRO) operator cairo_matrix_t() const; #endif +#if PLATFORM(WIN) + operator D2D1_MATRIX_3X2_F() const; +#endif + static AffineTransform translation(double x, double y) { return AffineTransform(1, 0, 0, 1, x, y); } - + // decompose the matrix into its component parts typedef struct { double scaleX, scaleY; @@ -187,16 +202,12 @@ public: void recompose(const DecomposedType&); private: - void setMatrix(const Transform m) - { - if (m && m != m_transform) - memcpy(m_transform, m, sizeof(Transform)); - } - - Transform m_transform; + std::array<double, 6> m_transform; }; -AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); +WEBCORE_EXPORT AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); + +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const AffineTransform&); } diff --git a/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h b/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h index c1f7f4512..eed4c847b 100644 --- a/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/IdentityTransformOperation.h @@ -26,36 +26,44 @@ #define IdentityTransformOperation_h #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class IdentityTransformOperation : public TransformOperation { +class IdentityTransformOperation final : public TransformOperation { public: - static PassRefPtr<IdentityTransformOperation> create() + static Ref<IdentityTransformOperation> create() { - return adoptRef(new IdentityTransformOperation()); + return adoptRef(*new IdentityTransformOperation()); } - + + Ref<TransformOperation> clone() const override + { + return create(); + } + private: - virtual bool isIdentity() const { return true; } - virtual OperationType type() const { return IDENTITY; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == IDENTITY; } + bool isIdentity() const override { return true; } + OperationType type() const override { return IDENTITY; } + bool isSameType(const TransformOperation& o) const override { return o.type() == IDENTITY; } - virtual bool operator==(const TransformOperation& o) const + bool operator==(const TransformOperation& o) const override { return isSameType(o); } - virtual bool apply(TransformationMatrix&, const FloatSize&) const + bool apply(TransformationMatrix&, const FloatSize&) const override { return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation*, double, bool = false) + Ref<TransformOperation> blend(const TransformOperation*, double, bool = false) override { - return this; + return *this; } + void dump(TextStream&) const final; + IdentityTransformOperation() { } @@ -64,4 +72,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::IdentityTransformOperation, type() == WebCore::TransformOperation::IDENTITY) + #endif // IdentityTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp index 443816f9e..c710f3981 100644 --- a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,14 +26,26 @@ #include "config.h" #include "Matrix3DTransformOperation.h" +#include "TextStream.h" #include <algorithm> namespace WebCore { -PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool Matrix3DTransformOperation::operator==(const TransformOperation& other) const +{ + return isSameType(other) && m_matrix == downcast<Matrix3DTransformOperation>(other).m_matrix; +} + +static Ref<TransformOperation> createOperation(TransformationMatrix& to, TransformationMatrix& from, double progress) +{ + to.blend(from, progress); + return Matrix3DTransformOperation::create(to); +} + +Ref<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; // Convert the TransformOperations into matrices FloatSize size; @@ -45,10 +57,13 @@ PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const Transform apply(toT, size); if (blendToIdentity) - std::swap(fromT, toT); + return createOperation(fromT, toT, progress); + return createOperation(toT, fromT, progress); +} - toT.blend(fromT, progress); - return Matrix3DTransformOperation::create(toT); +void Matrix3DTransformOperation::dump(TextStream& ts) const +{ + ts << type() << "(" << m_matrix << ")"; } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h index 889c2b732..a30b0aa59 100644 --- a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,40 +27,43 @@ #define Matrix3DTransformOperation_h #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class Matrix3DTransformOperation : public TransformOperation { +class Matrix3DTransformOperation final : public TransformOperation { public: - static PassRefPtr<Matrix3DTransformOperation> create(const TransformationMatrix& matrix) + static Ref<Matrix3DTransformOperation> create(const TransformationMatrix& matrix) { - return adoptRef(new Matrix3DTransformOperation(matrix)); + return adoptRef(*new Matrix3DTransformOperation(matrix)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new Matrix3DTransformOperation(m_matrix)); } TransformationMatrix matrix() const {return m_matrix; } private: - virtual bool isIdentity() const { return m_matrix.isIdentity(); } + bool isIdentity() const override { return m_matrix.isIdentity(); } + bool isAffectedByTransformOrigin() const override { return !isIdentity(); } - virtual OperationType type() const { return MATRIX_3D; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == MATRIX_3D; } + OperationType type() const override { return MATRIX_3D; } + bool isSameType(const TransformOperation& o) const override { return o.type() == MATRIX_3D; } - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; - const Matrix3DTransformOperation* m = static_cast<const Matrix3DTransformOperation*>(&o); - return m_matrix == m->m_matrix; - } + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize&) const + bool apply(TransformationMatrix& transform, const FloatSize&) const override { - transform.multiply(TransformationMatrix(m_matrix)); + transform.multiply(m_matrix); return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + void dump(TextStream&) const final; + Matrix3DTransformOperation(const TransformationMatrix& mat) { m_matrix = mat; @@ -71,4 +74,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::Matrix3DTransformOperation, type() == WebCore::TransformOperation::MATRIX_3D) + #endif // Matrix3DTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp index a3658a91d..17860e4e1 100644 --- a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp @@ -22,29 +22,52 @@ #include "config.h" #include "MatrixTransformOperation.h" +#include "TextStream.h" #include <algorithm> namespace WebCore { -PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool MatrixTransformOperation::operator==(const TransformOperation& other) const +{ + if (!isSameType(other)) + return false; + const MatrixTransformOperation& m = downcast<MatrixTransformOperation>(other); + return m_a == m.m_a && m_b == m.m_b && m_c == m.m_c && m_d == m.m_d && m_e == m.m_e && m_f == m.m_f; +} + +static Ref<TransformOperation> createOperation(TransformationMatrix& to, TransformationMatrix& from, double progress) +{ + to.blend(from, progress); + return MatrixTransformOperation::create(to); +} + +Ref<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; // convert the TransformOperations into matrices - FloatSize size; TransformationMatrix fromT; TransformationMatrix toT(m_a, m_b, m_c, m_d, m_e, m_f); if (from) { - const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(from); - fromT.setMatrix(m->m_a, m->m_b, m->m_c, m->m_d, m->m_e, m->m_f); + const MatrixTransformOperation& m = downcast<MatrixTransformOperation>(*from); + fromT.setMatrix(m.m_a, m.m_b, m.m_c, m.m_d, m.m_e, m.m_f); } - + if (blendToIdentity) - std::swap(fromT, toT); + return createOperation(fromT, toT, progress); + return createOperation(toT, fromT, progress); +} - toT.blend(fromT, progress); - return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f()); +void MatrixTransformOperation::dump(TextStream& ts) const +{ + ts << "(" + << TextStream::FormatNumberRespectingIntegers(m_a) << ", " + << TextStream::FormatNumberRespectingIntegers(m_b) << ", " + << TextStream::FormatNumberRespectingIntegers(m_c) << ", " + << TextStream::FormatNumberRespectingIntegers(m_d) << ", " + << TextStream::FormatNumberRespectingIntegers(m_e) << ", " + << TextStream::FormatNumberRespectingIntegers(m_f) << ")"; } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h index 76c5dffcd..3e88a9954 100644 --- a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h @@ -27,47 +27,49 @@ #include "TransformOperation.h" #include "TransformationMatrix.h" +#include <wtf/Ref.h> namespace WebCore { -class MatrixTransformOperation : public TransformOperation { +class MatrixTransformOperation final : public TransformOperation { public: - static PassRefPtr<MatrixTransformOperation> create(double a, double b, double c, double d, double e, double f) + static Ref<MatrixTransformOperation> create(double a, double b, double c, double d, double e, double f) { - return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f)); + return adoptRef(*new MatrixTransformOperation(a, b, c, d, e, f)); } - static PassRefPtr<MatrixTransformOperation> create(const TransformationMatrix& t) + static Ref<MatrixTransformOperation> create(const TransformationMatrix& t) { - return adoptRef(new MatrixTransformOperation(t)); + return adoptRef(*new MatrixTransformOperation(t)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new MatrixTransformOperation(matrix())); } TransformationMatrix matrix() const { return TransformationMatrix(m_a, m_b, m_c, m_d, m_e, m_f); } private: - virtual bool isIdentity() const { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; } + bool isIdentity() const override { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; } + bool isAffectedByTransformOrigin() const override { return !isIdentity(); } - virtual OperationType type() const { return MATRIX; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == MATRIX; } - - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; + OperationType type() const override { return MATRIX; } + bool isSameType(const TransformOperation& o) const override { return o.type() == MATRIX; } - const MatrixTransformOperation* m = static_cast<const MatrixTransformOperation*>(&o); - return m_a == m->m_a && m_b == m->m_b && m_c == m->m_c && m_d == m->m_d && m_e == m->m_e && m_f == m->m_f; - } + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize&) const + bool apply(TransformationMatrix& transform, const FloatSize&) const override { TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f); transform.multiply(matrix); return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); - + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + + void dump(TextStream&) const final; + MatrixTransformOperation(double a, double b, double c, double d, double e, double f) : m_a(a) , m_b(b) @@ -98,4 +100,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::MatrixTransformOperation, type() == WebCore::TransformOperation::MATRIX) + #endif // MatrixTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp index a3ffb664c..a612aed6e 100644 --- a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,14 +27,22 @@ #include "PerspectiveTransformOperation.h" #include "AnimationUtilities.h" +#include "TextStream.h" #include <wtf/MathExtras.h> namespace WebCore { -PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool PerspectiveTransformOperation::operator==(const TransformOperation& other) const +{ + if (!isSameType(other)) + return false; + return m_p == downcast<PerspectiveTransformOperation>(other).m_p; +} + +Ref<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; if (blendToIdentity) { double p = floatValueForLength(m_p, 1); @@ -42,7 +50,7 @@ PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const Transf return PerspectiveTransformOperation::create(Length(clampToPositiveInteger(p), Fixed)); } - const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from); + const PerspectiveTransformOperation* fromOp = downcast<PerspectiveTransformOperation>(from); Length fromP = fromOp ? fromOp->m_p : Length(m_p.type()); Length toP = m_p; @@ -61,4 +69,9 @@ PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const Transf return PerspectiveTransformOperation::create(Length(0, Fixed)); } +void PerspectiveTransformOperation::dump(TextStream& ts) const +{ + ts << type() << "(" << m_p << ")"; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h index 73aa680ad..d9b20a561 100644 --- a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,38 +29,42 @@ #include "Length.h" #include "LengthFunctions.h" #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class PerspectiveTransformOperation : public TransformOperation { +class PerspectiveTransformOperation final : public TransformOperation { public: - static PassRefPtr<PerspectiveTransformOperation> create(const Length& p) + static Ref<PerspectiveTransformOperation> create(const Length& p) { - return adoptRef(new PerspectiveTransformOperation(p)); + return adoptRef(*new PerspectiveTransformOperation(p)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new PerspectiveTransformOperation(m_p)); } Length perspective() const { return m_p; } private: - virtual bool isIdentity() const { return !floatValueForLength(m_p, 1); } - virtual OperationType type() const { return PERSPECTIVE; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == PERSPECTIVE; } + bool isIdentity() const override { return !floatValueForLength(m_p, 1); } + bool isAffectedByTransformOrigin() const override { return !isIdentity(); } - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; - const PerspectiveTransformOperation* p = static_cast<const PerspectiveTransformOperation*>(&o); - return m_p == p->m_p; - } + OperationType type() const override { return PERSPECTIVE; } + bool isSameType(const TransformOperation& o) const override { return o.type() == PERSPECTIVE; } + + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize&) const + bool apply(TransformationMatrix& transform, const FloatSize&) const override { transform.applyPerspective(floatValueForLength(m_p, 1)); return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + + void dump(TextStream&) const final; PerspectiveTransformOperation(const Length& p) : m_p(p) @@ -73,4 +77,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::PerspectiveTransformOperation, type() == WebCore::TransformOperation::PERSPECTIVE) + #endif // PerspectiveTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp index 32b4c6e1c..52c3dbcc5 100644 --- a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp @@ -23,20 +23,29 @@ #include "RotateTransformOperation.h" #include "AnimationUtilities.h" +#include "TextStream.h" #include <algorithm> #include <wtf/MathExtras.h> namespace WebCore { -PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool RotateTransformOperation::operator==(const TransformOperation& other) const +{ + if (!isSameType(other)) + return false; + const RotateTransformOperation& r = downcast<RotateTransformOperation>(other); + return m_x == r.m_x && m_y == r.m_y && m_z == r.m_z && m_angle == r.m_angle; +} + +Ref<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; if (blendToIdentity) return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type); - const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from); + const RotateTransformOperation* fromOp = downcast<RotateTransformOperation>(from); // Optimize for single axis rotation if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) || @@ -91,4 +100,9 @@ PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOp return RotateTransformOperation::create(x, y, z, angle, ROTATE_3D); } +void RotateTransformOperation::dump(TextStream& ts) const +{ + ts << type() << "(" << TextStream::FormatNumberRespectingIntegers(m_x) << ", " << TextStream::FormatNumberRespectingIntegers(m_y) << ", " << TextStream::FormatNumberRespectingIntegers(m_z) << ", " << TextStream::FormatNumberRespectingIntegers(m_angle) << "deg)"; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h index f47a51e17..be8c6e2f3 100644 --- a/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.h @@ -26,19 +26,25 @@ #define RotateTransformOperation_h #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class RotateTransformOperation : public TransformOperation { +class RotateTransformOperation final : public TransformOperation { public: - static PassRefPtr<RotateTransformOperation> create(double angle, OperationType type) + static Ref<RotateTransformOperation> create(double angle, OperationType type) { - return adoptRef(new RotateTransformOperation(0, 0, 1, angle, type)); + return adoptRef(*new RotateTransformOperation(0, 0, 1, angle, type)); } - static PassRefPtr<RotateTransformOperation> create(double x, double y, double z, double angle, OperationType type) + static Ref<RotateTransformOperation> create(double x, double y, double z, double angle, OperationType type) { - return adoptRef(new RotateTransformOperation(x, y, z, angle, type)); + return adoptRef(*new RotateTransformOperation(x, y, z, angle, type)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new RotateTransformOperation(m_x, m_y, m_z, m_angle, m_type)); } double x() const { return m_x; } @@ -47,26 +53,23 @@ public: double angle() const { return m_angle; } private: - virtual bool isIdentity() const { return m_angle == 0; } + bool isIdentity() const override { return m_angle == 0; } + bool isAffectedByTransformOrigin() const override { return !isIdentity(); } - virtual OperationType type() const { return m_type; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; } + OperationType type() const override { return m_type; } + bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; } - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; - const RotateTransformOperation* r = static_cast<const RotateTransformOperation*>(&o); - return m_x == r->m_x && m_y == r->m_y && m_z == r->m_z && m_angle == r->m_angle; - } + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize& /*borderBoxSize*/) const + bool apply(TransformationMatrix& transform, const FloatSize& /*borderBoxSize*/) const override { transform.rotate3d(m_x, m_y, m_z, m_angle); return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + + void dump(TextStream&) const final; RotateTransformOperation(double x, double y, double z, double angle, OperationType type) : m_x(x) @@ -75,7 +78,7 @@ private: , m_angle(angle) , m_type(type) { - ASSERT(type == ROTATE_X || type == ROTATE_Y || type == ROTATE_Z || type == ROTATE_3D); + ASSERT(isRotateTransformOperationType()); } double m_x; @@ -87,4 +90,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::RotateTransformOperation, isRotateTransformOperationType()) + #endif // RotateTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp index e8806f203..862eafe15 100644 --- a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp @@ -23,20 +23,29 @@ #include "ScaleTransformOperation.h" #include "AnimationUtilities.h" +#include "TextStream.h" namespace WebCore { -PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool ScaleTransformOperation::operator==(const TransformOperation& other) const +{ + if (!isSameType(other)) + return false; + const ScaleTransformOperation& s = downcast<ScaleTransformOperation>(other); + return m_x == s.m_x && m_y == s.m_y && m_z == s.m_z; +} + +Ref<TransformOperation> ScaleTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; if (blendToIdentity) return ScaleTransformOperation::create(WebCore::blend(m_x, 1.0, progress), WebCore::blend(m_y, 1.0, progress), WebCore::blend(m_z, 1.0, progress), m_type); - const ScaleTransformOperation* fromOp = static_cast<const ScaleTransformOperation*>(from); + const ScaleTransformOperation* fromOp = downcast<ScaleTransformOperation>(from); double fromX = fromOp ? fromOp->m_x : 1.0; double fromY = fromOp ? fromOp->m_y : 1.0; double fromZ = fromOp ? fromOp->m_z : 1.0; @@ -45,4 +54,9 @@ PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOpe WebCore::blend(fromZ, m_z, progress), m_type); } +void ScaleTransformOperation::dump(TextStream& ts) const +{ + ts << type() << "(" << TextStream::FormatNumberRespectingIntegers(m_x) << ", " << TextStream::FormatNumberRespectingIntegers(m_y) << ", " << TextStream::FormatNumberRespectingIntegers(m_z) << ")"; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h index 62beb23ce..ae8a55556 100644 --- a/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/ScaleTransformOperation.h @@ -26,19 +26,25 @@ #define ScaleTransformOperation_h #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class ScaleTransformOperation : public TransformOperation { +class ScaleTransformOperation final : public TransformOperation { public: - static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, OperationType type) + static Ref<ScaleTransformOperation> create(double sx, double sy, OperationType type) { - return adoptRef(new ScaleTransformOperation(sx, sy, 1, type)); + return adoptRef(*new ScaleTransformOperation(sx, sy, 1, type)); } - static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, double sz, OperationType type) + static Ref<ScaleTransformOperation> create(double sx, double sy, double sz, OperationType type) { - return adoptRef(new ScaleTransformOperation(sx, sy, sz, type)); + return adoptRef(*new ScaleTransformOperation(sx, sy, sz, type)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new ScaleTransformOperation(m_x, m_y, m_z, m_type)); } double x() const { return m_x; } @@ -46,26 +52,23 @@ public: double z() const { return m_z; } private: - virtual bool isIdentity() const { return m_x == 1 && m_y == 1 && m_z == 1; } + bool isIdentity() const override { return m_x == 1 && m_y == 1 && m_z == 1; } + bool isAffectedByTransformOrigin() const override { return !isIdentity(); } - virtual OperationType type() const { return m_type; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; } + OperationType type() const override { return m_type; } + bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; } - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; - const ScaleTransformOperation* s = static_cast<const ScaleTransformOperation*>(&o); - return m_x == s->m_x && m_y == s->m_y && m_z == s->m_z; - } + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize&) const + bool apply(TransformationMatrix& transform, const FloatSize&) const override { transform.scale3d(m_x, m_y, m_z); return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + + void dump(TextStream&) const final; ScaleTransformOperation(double sx, double sy, double sz, OperationType type) : m_x(sx) @@ -73,7 +76,7 @@ private: , m_z(sz) , m_type(type) { - ASSERT(type == SCALE_X || type == SCALE_Y || type == SCALE_Z || type == SCALE || type == SCALE_3D); + ASSERT(isScaleTransformOperationType()); } double m_x; @@ -84,4 +87,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::ScaleTransformOperation, isScaleTransformOperationType()) + #endif // ScaleTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp index d92d895ce..ec52112ef 100644 --- a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.cpp @@ -23,21 +23,35 @@ #include "SkewTransformOperation.h" #include "AnimationUtilities.h" +#include "TextStream.h" namespace WebCore { -PassRefPtr<TransformOperation> SkewTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool SkewTransformOperation::operator==(const TransformOperation& other) const +{ + if (!isSameType(other)) + return false; + const SkewTransformOperation& s = downcast<SkewTransformOperation>(other); + return m_angleX == s.m_angleX && m_angleY == s.m_angleY; +} + +Ref<TransformOperation> SkewTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; if (blendToIdentity) return SkewTransformOperation::create(WebCore::blend(m_angleX, 0.0, progress), WebCore::blend(m_angleY, 0.0, progress), m_type); - const SkewTransformOperation* fromOp = static_cast<const SkewTransformOperation*>(from); + const SkewTransformOperation* fromOp = downcast<SkewTransformOperation>(from); double fromAngleX = fromOp ? fromOp->m_angleX : 0; double fromAngleY = fromOp ? fromOp->m_angleY : 0; return SkewTransformOperation::create(WebCore::blend(fromAngleX, m_angleX, progress), WebCore::blend(fromAngleY, m_angleY, progress), m_type); } +void SkewTransformOperation::dump(TextStream& ts) const +{ + ts << type() << "(" << TextStream::FormatNumberRespectingIntegers(m_angleX) << "deg, " << TextStream::FormatNumberRespectingIntegers(m_angleY) << "deg)"; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h index e9f0c819b..422d14b6a 100644 --- a/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/SkewTransformOperation.h @@ -26,45 +26,50 @@ #define SkewTransformOperation_h #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class SkewTransformOperation : public TransformOperation { +class SkewTransformOperation final : public TransformOperation { public: - static PassRefPtr<SkewTransformOperation> create(double angleX, double angleY, OperationType type) + static Ref<SkewTransformOperation> create(double angleX, double angleY, OperationType type) { - return adoptRef(new SkewTransformOperation(angleX, angleY, type)); + return adoptRef(*new SkewTransformOperation(angleX, angleY, type)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new SkewTransformOperation(m_angleX, m_angleY, m_type)); } double angleX() const { return m_angleX; } double angleY() const { return m_angleY; } private: - virtual bool isIdentity() const { return m_angleX == 0 && m_angleY == 0; } - virtual OperationType type() const { return m_type; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; } + bool isIdentity() const override { return m_angleX == 0 && m_angleY == 0; } + bool isAffectedByTransformOrigin() const override { return !isIdentity(); } - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; - const SkewTransformOperation* s = static_cast<const SkewTransformOperation*>(&o); - return m_angleX == s->m_angleX && m_angleY == s->m_angleY; - } + OperationType type() const override { return m_type; } + bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; } + + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize&) const + bool apply(TransformationMatrix& transform, const FloatSize&) const override { transform.skew(m_angleX, m_angleY); return false; } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + + void dump(TextStream&) const final; SkewTransformOperation(double angleX, double angleY, OperationType type) : m_angleX(angleX) , m_angleY(angleY) , m_type(type) { + ASSERT(isSkewTransformOperationType()); } double m_angleX; @@ -74,4 +79,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::SkewTransformOperation, isSkewTransformOperationType()) + #endif // SkewTransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/TransformOperation.cpp new file mode 100644 index 000000000..2ec90c12a --- /dev/null +++ b/Source/WebCore/platform/graphics/transforms/TransformOperation.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TransformOperation.h" + +#include "IdentityTransformOperation.h" +#include "TextStream.h" + +namespace WebCore { + +void IdentityTransformOperation::dump(TextStream& ts) const +{ + ts << type(); +} + +TextStream& operator<<(TextStream& ts, TransformOperation::OperationType type) +{ + switch (type) { + case TransformOperation::SCALE_X: ts << "scaleX"; break; + case TransformOperation::SCALE_Y: ts << "scaleY"; break; + case TransformOperation::SCALE: ts << "scale"; break; + case TransformOperation::TRANSLATE_X: ts << "translateX"; break; + case TransformOperation::TRANSLATE_Y: ts << "translateY"; break; + case TransformOperation::TRANSLATE: ts << "translate"; break; + case TransformOperation::ROTATE: ts << "rotate"; break; + case TransformOperation::SKEW_X: ts << "skewX"; break; + case TransformOperation::SKEW_Y: ts << "skewY"; break; + case TransformOperation::SKEW: ts << "skew"; break; + case TransformOperation::MATRIX: ts << "matrix"; break; + case TransformOperation::SCALE_Z: ts << "scaleX"; break; + case TransformOperation::SCALE_3D: ts << "scale3d"; break; + case TransformOperation::TRANSLATE_Z: ts << "translateZ"; break; + case TransformOperation::TRANSLATE_3D: ts << "translate3d"; break; + case TransformOperation::ROTATE_X: ts << "rotateX"; break; + case TransformOperation::ROTATE_Y: ts << "rotateY"; break; + case TransformOperation::ROTATE_3D: ts << "rotate3d"; break; + case TransformOperation::MATRIX_3D: ts << "matrix3d"; break; + case TransformOperation::PERSPECTIVE: ts << "perspective"; break; + case TransformOperation::IDENTITY: ts << "identity"; break; + case TransformOperation::NONE: ts << "none"; break; + } + + return ts; +} + +TextStream& operator<<(TextStream& ts, const TransformOperation& operation) +{ + operation.dump(ts); + return ts; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperation.h b/Source/WebCore/platform/graphics/transforms/TransformOperation.h index f9f975fa4..4f6aa88da 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/TransformOperation.h @@ -27,8 +27,9 @@ #include "FloatSize.h" #include "TransformationMatrix.h" -#include <wtf/PassRefPtr.h> +#include <wtf/Forward.h> #include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> namespace WebCore { @@ -53,6 +54,8 @@ public: virtual ~TransformOperation() { } + virtual Ref<TransformOperation> clone() const = 0; + virtual bool operator==(const TransformOperation&) const = 0; bool operator!=(const TransformOperation& o) const { return !(*this == o); } @@ -61,10 +64,12 @@ public: // Return true if the borderBoxSize was used in the computation, false otherwise. virtual bool apply(TransformationMatrix&, const FloatSize& borderBoxSize) const = 0; - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0; + virtual Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0; virtual OperationType type() const = 0; virtual bool isSameType(const TransformOperation&) const { return false; } + + virtual bool isAffectedByTransformOrigin() const { return false; } bool is3DOperation() const { @@ -79,8 +84,38 @@ public: opType == MATRIX_3D || opType == PERSPECTIVE; } + + bool isRotateTransformOperationType() const + { + return type() == ROTATE_X || type() == ROTATE_Y || type() == ROTATE_Z || type() == ROTATE || type() == ROTATE_3D; + } + + bool isScaleTransformOperationType() const + { + return type() == SCALE_X || type() == SCALE_Y || type() == SCALE_Z || type() == SCALE || type() == SCALE_3D; + } + + bool isSkewTransformOperationType() const + { + return type() == SKEW_X || type() == SKEW_Y || type() == SKEW; + } + + bool isTranslateTransformOperationType() const + { + return type() == TRANSLATE_X || type() == TRANSLATE_Y || type() == TRANSLATE_Z || type() == TRANSLATE || type() == TRANSLATE_3D; + } + + virtual void dump(TextStream&) const = 0; }; +TextStream& operator<<(TextStream&, TransformOperation::OperationType); +TextStream& operator<<(TextStream&, const TransformOperation&); + } // namespace WebCore +#define SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \ + static bool isType(const WebCore::TransformOperation& operation) { return operation.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() + #endif // TransformOperation_h diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp b/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp index a71de471a..9b156bd3d 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp +++ b/Source/WebCore/platform/graphics/transforms/TransformOperations.cpp @@ -24,6 +24,7 @@ #include "IdentityTransformOperation.h" #include "Matrix3DTransformOperation.h" +#include "TextStream.h" #include <algorithm> namespace WebCore { @@ -63,6 +64,15 @@ bool TransformOperations::operationsMatch(const TransformOperations& other) cons return true; } +bool TransformOperations::affectedByTransformOrigin() const +{ + for (const auto& operation : m_operations) { + if (operation->isAffectedByTransformOrigin()) + return true; + } + return false; +} + TransformOperations TransformOperations::blendByMatchingOperations(const TransformOperations& from, const double& progress) const { TransformOperations result; @@ -71,9 +81,9 @@ TransformOperations TransformOperations::blendByMatchingOperations(const Transfo unsigned toSize = operations().size(); unsigned size = std::max(fromSize, toSize); for (unsigned i = 0; i < size; i++) { - RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : 0; - RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : 0; - RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : 0); + RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : nullptr; + RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : nullptr; + RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? RefPtr<TransformOperation>(fromOperation->blend(nullptr, progress, true)) : nullptr); if (blendedOperation) result.operations().append(blendedOperation); else { @@ -117,4 +127,11 @@ TransformOperations TransformOperations::blend(const TransformOperations& from, return blendByUsingMatrixInterpolation(from, progress, size); } +TextStream& operator<<(TextStream& ts, const TransformOperations& ops) +{ + for (const auto& operation : ops.operations()) + ts << *operation; + return ts; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/TransformOperations.h b/Source/WebCore/platform/graphics/transforms/TransformOperations.h index 96045d3f9..3e1d08e0e 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformOperations.h +++ b/Source/WebCore/platform/graphics/transforms/TransformOperations.h @@ -65,6 +65,8 @@ public: { m_operations.clear(); } + + bool affectedByTransformOrigin() const; Vector<RefPtr<TransformOperation>>& operations() { return m_operations; } const Vector<RefPtr<TransformOperation>>& operations() const { return m_operations; } @@ -80,6 +82,8 @@ private: Vector<RefPtr<TransformOperation>> m_operations; }; +TextStream& operator<<(TextStream&, const TransformOperations&); + } // namespace WebCore #endif // TransformOperations_h diff --git a/Source/WebCore/platform/graphics/transforms/TransformState.cpp b/Source/WebCore/platform/graphics/transforms/TransformState.cpp index 541b1c648..5ef0e5bf4 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformState.cpp +++ b/Source/WebCore/platform/graphics/transforms/TransformState.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,8 +26,6 @@ #include "config.h" #include "TransformState.h" -#include <wtf/PassOwnPtr.h> - namespace WebCore { TransformState& TransformState::operator=(const TransformState& other) @@ -37,15 +35,20 @@ TransformState& TransformState::operator=(const TransformState& other) m_mapQuad = other.m_mapQuad; if (m_mapPoint) m_lastPlanarPoint = other.m_lastPlanarPoint; - if (m_mapQuad) + if (m_mapQuad) { m_lastPlanarQuad = other.m_lastPlanarQuad; + if (other.m_lastPlanarSecondaryQuad) + m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(*other.m_lastPlanarSecondaryQuad); + else + m_lastPlanarSecondaryQuad = nullptr; + } m_accumulatingTransform = other.m_accumulatingTransform; m_direction = other.m_direction; - m_accumulatedTransform.clear(); + m_accumulatedTransform = nullptr; if (other.m_accumulatedTransform) - m_accumulatedTransform = adoptPtr(new TransformationMatrix(*other.m_accumulatedTransform)); + m_accumulatedTransform = std::make_unique<TransformationMatrix>(*other.m_accumulatedTransform); return *this; } @@ -63,8 +66,11 @@ void TransformState::translateMappedCoordinates(const LayoutSize& offset) LayoutSize adjustedOffset = (m_direction == ApplyTransformDirection) ? offset : -offset; if (m_mapPoint) m_lastPlanarPoint.move(adjustedOffset); - if (m_mapQuad) + if (m_mapQuad) { m_lastPlanarQuad.move(adjustedOffset); + if (m_lastPlanarSecondaryQuad) + m_lastPlanarSecondaryQuad->move(adjustedOffset); + } } void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate) @@ -121,12 +127,12 @@ void TransformState::applyTransform(const TransformationMatrix& transformFromCon // If we have an accumulated transform from last time, multiply in this transform if (m_accumulatedTransform) { if (m_direction == ApplyTransformDirection) - m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform)); + m_accumulatedTransform = std::make_unique<TransformationMatrix>(transformFromContainer * *m_accumulatedTransform); else m_accumulatedTransform->multiply(transformFromContainer); } else if (accumulate == AccumulateTransform) { // Make one if we started to accumulate - m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer)); + m_accumulatedTransform = std::make_unique<TransformationMatrix>(transformFromContainer); } if (accumulate == FlattenTransform) { @@ -164,7 +170,7 @@ FloatPoint TransformState::mappedPoint(bool* wasClamped) const if (m_direction == ApplyTransformDirection) return m_accumulatedTransform->mapPoint(point); - return m_accumulatedTransform->inverse().projectPoint(point, wasClamped); + return m_accumulatedTransform->inverse().value_or(TransformationMatrix()).projectPoint(point, wasClamped); } FloatQuad TransformState::mappedQuad(bool* wasClamped) const @@ -173,14 +179,46 @@ FloatQuad TransformState::mappedQuad(bool* wasClamped) const *wasClamped = false; FloatQuad quad = m_lastPlanarQuad; - quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); + mapQuad(quad, m_direction, wasClamped); + return quad; +} + +std::optional<FloatQuad> TransformState::mappedSecondaryQuad(bool* wasClamped) const +{ + if (wasClamped) + *wasClamped = false; + + if (!m_lastPlanarSecondaryQuad) + return std::optional<FloatQuad>(); + + FloatQuad quad = *m_lastPlanarSecondaryQuad; + mapQuad(quad, m_direction, wasClamped); + return quad; +} + +void TransformState::setLastPlanarSecondaryQuad(const FloatQuad* quad) +{ + if (!quad) { + m_lastPlanarSecondaryQuad = nullptr; + return; + } + + // Map the quad back through any transform or offset back into the last flattening coordinate space. + FloatQuad backMappedQuad(*quad); + mapQuad(backMappedQuad, inverseDirection()); + m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(backMappedQuad); +} + +void TransformState::mapQuad(FloatQuad& quad, TransformDirection direction, bool* wasClamped) const +{ + quad.move((direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); if (!m_accumulatedTransform) - return quad; + return; - if (m_direction == ApplyTransformDirection) - return m_accumulatedTransform->mapQuad(quad); + if (direction == ApplyTransformDirection) + quad = m_accumulatedTransform->mapQuad(quad); - return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped); + quad = m_accumulatedTransform->inverse().value_or(TransformationMatrix()).projectQuad(quad, wasClamped); } void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped) @@ -188,14 +226,21 @@ void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* w if (m_direction == ApplyTransformDirection) { if (m_mapPoint) m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); - if (m_mapQuad) + if (m_mapQuad) { m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); + if (m_lastPlanarSecondaryQuad) + *m_lastPlanarSecondaryQuad = t.mapQuad(*m_lastPlanarSecondaryQuad); + } + } else { - TransformationMatrix inverseTransform = t.inverse(); + TransformationMatrix inverseTransform = t.inverse().value_or(TransformationMatrix()); if (m_mapPoint) m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); - if (m_mapQuad) + if (m_mapQuad) { m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped); + if (m_lastPlanarSecondaryQuad) + *m_lastPlanarSecondaryQuad = inverseTransform.projectQuad(*m_lastPlanarSecondaryQuad, wasClamped); + } } // We could throw away m_accumulatedTransform if we wanted to here, but that diff --git a/Source/WebCore/platform/graphics/transforms/TransformState.h b/Source/WebCore/platform/graphics/transforms/TransformState.h index 874177b5d..cd955abf4 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformState.h +++ b/Source/WebCore/platform/graphics/transforms/TransformState.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -32,7 +32,7 @@ #include "IntSize.h" #include "LayoutSize.h" #include "TransformationMatrix.h" -#include <wtf/OwnPtr.h> +#include <wtf/Optional.h> namespace WebCore { @@ -75,30 +75,45 @@ public: void setQuad(const FloatQuad& quad) { - // FIXME: this assumes that the quad being added is in the coordinate system of the current state. - // This breaks if we're simultaneously mapping a point. https://bugs.webkit.org/show_bug.cgi?id=106680 - ASSERT(!m_mapPoint); - m_accumulatedOffset = LayoutSize(); + // We must be in a flattened state (no accumulated offset) when setting this quad. + ASSERT(m_accumulatedOffset == LayoutSize()); m_lastPlanarQuad = quad; } + // FIXME: webkit.org/b/144226 use std::optional<FloatQuad>. + void setSecondaryQuad(const FloatQuad* quad) + { + // We must be in a flattened state (no accumulated offset) when setting this secondary quad. + ASSERT(m_accumulatedOffset == LayoutSize()); + if (quad) + m_lastPlanarSecondaryQuad = std::make_unique<FloatQuad>(*quad); + else + m_lastPlanarSecondaryQuad = nullptr; + } + + // FIXME: webkit.org/b/144226 use std::optional<FloatQuad>. + void setLastPlanarSecondaryQuad(const FloatQuad*); + void move(LayoutUnit x, LayoutUnit y, TransformAccumulation accumulate = FlattenTransform) { move(LayoutSize(x, y), accumulate); } void move(const LayoutSize&, TransformAccumulation = FlattenTransform); - void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = 0); - void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = 0); - void flatten(bool* wasClamped = 0); + void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = nullptr); + void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = nullptr); + void flatten(bool* wasClamped = nullptr); // Return the coords of the point or quad in the last flattened layer FloatPoint lastPlanarPoint() const { return m_lastPlanarPoint; } FloatQuad lastPlanarQuad() const { return m_lastPlanarQuad; } + FloatQuad* lastPlanarSecondaryQuad() const { return m_lastPlanarSecondaryQuad.get(); } + bool isMappingSecondaryQuad() const { return m_lastPlanarSecondaryQuad.get(); } // Return the point or quad mapped through the current transform - FloatPoint mappedPoint(bool* wasClamped = 0) const; - FloatQuad mappedQuad(bool* wasClamped = 0) const; + FloatPoint mappedPoint(bool* wasClamped = nullptr) const; + FloatQuad mappedQuad(bool* wasClamped = nullptr) const; + std::optional<FloatQuad> mappedSecondaryQuad(bool* wasClamped = nullptr) const; private: void translateTransform(const LayoutSize&); @@ -106,17 +121,29 @@ private: void flattenWithTransform(const TransformationMatrix&, bool* wasClamped); void applyAccumulatedOffset(); + TransformDirection direction() const { return m_direction; } + TransformDirection inverseDirection() const; + + void mapQuad(FloatQuad&, TransformDirection, bool* clamped = nullptr) const; + FloatPoint m_lastPlanarPoint; FloatQuad m_lastPlanarQuad; + std::unique_ptr<FloatQuad> m_lastPlanarSecondaryQuad; // Optional second quad to map. // We only allocate the transform if we need to - OwnPtr<TransformationMatrix> m_accumulatedTransform; + std::unique_ptr<TransformationMatrix> m_accumulatedTransform; LayoutSize m_accumulatedOffset; bool m_accumulatingTransform; - bool m_mapPoint, m_mapQuad; + bool m_mapPoint; + bool m_mapQuad; TransformDirection m_direction; }; +inline TransformState::TransformDirection TransformState::inverseDirection() const +{ + return m_direction == ApplyTransformDirection ? UnapplyInverseTransformDirection : ApplyTransformDirection; +} + } // namespace WebCore #endif // TransformState_h diff --git a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp index c5066668d..2427b01c1 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp +++ b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006, 2013 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2013 Apple Inc. All rights reserved. * Copyright (C) 2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -32,7 +32,7 @@ #include "FloatQuad.h" #include "IntRect.h" #include "LayoutRect.h" - +#include "TextStream.h" #include <wtf/Assertions.h> #include <wtf/MathExtras.h> @@ -1445,7 +1445,7 @@ bool TransformationMatrix::isInvertible() const return true; } -TransformationMatrix TransformationMatrix::inverse() const +std::optional<TransformationMatrix> TransformationMatrix::inverse() const { if (isIdentityOrTranslation()) { // identity matrix @@ -1460,9 +1460,10 @@ TransformationMatrix TransformationMatrix::inverse() const } TransformationMatrix invMat; - bool inverted = WebCore::inverse(m_matrix, invMat.m_matrix); - if (!inverted) - return TransformationMatrix(); + // FIXME: Use LU decomposition to apply the inverse instead of calculating the inverse explicitly. + // Calculating the inverse of a 4x4 matrix using cofactors is numerically unstable and unnecessary to apply the inverse transformation to a point. + if (!WebCore::inverse(m_matrix, invMat.m_matrix)) + return std::nullopt; return invMat; } @@ -1500,8 +1501,11 @@ void TransformationMatrix::blend2(const TransformationMatrix& from, double progr { Decomposed2Type fromDecomp; Decomposed2Type toDecomp; - from.decompose2(fromDecomp); - decompose2(toDecomp); + if (!from.decompose2(fromDecomp) || !decompose2(toDecomp)) { + if (progress < 0.5) + *this = from; + return; + } // If x-axis of one is flipped, and y-axis of the other, convert to an unflipped rotation. if ((fromDecomp.scaleX < 0 && toDecomp.scaleY < 0) || (fromDecomp.scaleY < 0 && toDecomp.scaleX < 0)) { @@ -1540,8 +1544,11 @@ void TransformationMatrix::blend4(const TransformationMatrix& from, double progr { Decomposed4Type fromDecomp; Decomposed4Type toDecomp; - from.decompose4(fromDecomp); - decompose4(toDecomp); + if (!from.decompose4(fromDecomp) || !decompose4(toDecomp)) { + if (progress < 0.5) + *this = from; + return; + } blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress); blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress); @@ -1579,6 +1586,9 @@ bool TransformationMatrix::decompose2(Decomposed2Type& decomp) const memset(&decomp, 0, sizeof(decomp)); decomp.scaleX = 1; decomp.scaleY = 1; + decomp.m11 = 1; + decomp.m22 = 1; + return true; } return WebCore::decompose2(m_matrix, decomp); @@ -1592,6 +1602,7 @@ bool TransformationMatrix::decompose4(Decomposed4Type& decomp) const decomp.scaleX = 1; decomp.scaleY = 1; decomp.scaleZ = 1; + return true; } return WebCore::decompose4(m_matrix, decomp); @@ -1738,4 +1749,20 @@ bool TransformationMatrix::isBackFaceVisible() const return zComponentOfTransformedNormal < 0; } +TextStream& operator<<(TextStream& ts, const TransformationMatrix& transform) +{ + ts << "\n"; + ts.increaseIndent(); + ts.writeIndent(); + ts << "[" << transform.m11() << " " << transform.m12() << " " << transform.m13() << " " << transform.m14() << "]\n"; + ts.writeIndent(); + ts << "[" << transform.m21() << " " << transform.m22() << " " << transform.m23() << " " << transform.m24() << "]\n"; + ts.writeIndent(); + ts << "[" << transform.m31() << " " << transform.m32() << " " << transform.m33() << " " << transform.m34() << "]\n"; + ts.writeIndent(); + ts << "[" << transform.m41() << " " << transform.m42() << " " << transform.m43() << " " << transform.m44() << "]"; + ts.decreaseIndent(); + return ts; +} + } diff --git a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h index 011cd87b5..5db0c002a 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h +++ b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,6 +31,7 @@ #include "IntPoint.h" #include <string.h> //for memcpy #include <wtf/FastMalloc.h> +#include <wtf/Optional.h> #if USE(CA) typedef struct CATransform3D CATransform3D; @@ -49,6 +50,11 @@ typedef struct tagXFORM XFORM; #endif #endif +#if PLATFORM(WIN) +struct D2D_MATRIX_3X2_F; +typedef D2D_MATRIX_3X2_F D2D1_MATRIX_3X2_F; +#endif + namespace WebCore { class AffineTransform; @@ -56,6 +62,7 @@ class IntRect; class LayoutRect; class FloatRect; class FloatQuad; +class TextStream; #if CPU(X86_64) #define TRANSFORMATION_MATRIX_USE_X86_64_SSE2 @@ -65,7 +72,7 @@ class TransformationMatrix { WTF_MAKE_FAST_ALLOCATED; public: -#if CPU(APPLE_ARMV7S) || defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2) +#if (PLATFORM(IOS) && CPU(ARM_THUMB2)) || defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2) #if COMPILER(MSVC) __declspec(align(16)) typedef double Matrix4[4][4]; #else @@ -76,7 +83,7 @@ public: #endif TransformationMatrix() { makeIdentity(); } - TransformationMatrix(const AffineTransform& t); + WEBCORE_EXPORT TransformationMatrix(const AffineTransform&); TransformationMatrix(const TransformationMatrix& t) { *this = t; } TransformationMatrix(double a, double b, double c, double d, double e, double f) { setMatrix(a, b, c, d, e, f); } TransformationMatrix(double m11, double m12, double m13, double m14, @@ -126,15 +133,15 @@ public: m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1; } - // This form preserves the double math from input to output + // This form preserves the double math from input to output. void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); } - // Map a 3D point through the transform, returning a 3D point. + // Maps a 3D point through the transform, returning a 3D point. FloatPoint3D mapPoint(const FloatPoint3D&) const; - // Map a 2D point through the transform, returning a 2D point. + // Maps a 2D point through the transform, returning a 2D point. // Note that this ignores the z component, effectively projecting the point into the z=0 plane. - FloatPoint mapPoint(const FloatPoint&) const; + WEBCORE_EXPORT FloatPoint mapPoint(const FloatPoint&) const; // Like the version above, except that it rounds the mapped point to the nearest integer value. IntPoint mapPoint(const IntPoint& p) const @@ -143,25 +150,23 @@ public: } // If the matrix has 3D components, the z component of the result is - // dropped, effectively projecting the rect into the z=0 plane - FloatRect mapRect(const FloatRect&) const; + // dropped, effectively projecting the rect into the z=0 plane. + WEBCORE_EXPORT FloatRect mapRect(const FloatRect&) const; // Rounds the resulting mapped rectangle out. This is helpful for bounding // box computations but may not be what is wanted in other contexts. - IntRect mapRect(const IntRect&) const; + WEBCORE_EXPORT IntRect mapRect(const IntRect&) const; LayoutRect mapRect(const LayoutRect&) const; // If the matrix has 3D components, the z component of the result is - // dropped, effectively projecting the quad into the z=0 plane - FloatQuad mapQuad(const FloatQuad&) const; - - // Map a point on the z=0 plane into a point on - // the plane with with the transform applied, by extending - // a ray perpendicular to the source plane and computing - // the local x,y position of the point where that ray intersects - // with the destination plane. + // dropped, effectively projecting the quad into the z=0 plane. + WEBCORE_EXPORT FloatQuad mapQuad(const FloatQuad&) const; + + // Maps a point on the z=0 plane into a point on the plane with with the transform applied, by + // extending a ray perpendicular to the source plane and computing the local x,y position of + // the point where that ray intersects with the destination plane. FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const; - // Projects the four corners of the quad + // Projects the four corners of the quad. FloatQuad projectQuad(const FloatQuad&, bool* clamped = 0) const; // Projects the four corners of the quad and takes a bounding box, // while sanitizing values created when the w component is negative. @@ -219,61 +224,74 @@ public: void setF(double f) { m_matrix[3][1] = f; } // this = mat * this. - TransformationMatrix& multiply(const TransformationMatrix&); + WEBCORE_EXPORT TransformationMatrix& multiply(const TransformationMatrix&); - TransformationMatrix& scale(double); - TransformationMatrix& scaleNonUniform(double sx, double sy); + WEBCORE_EXPORT TransformationMatrix& scale(double); + WEBCORE_EXPORT TransformationMatrix& scaleNonUniform(double sx, double sy); TransformationMatrix& scale3d(double sx, double sy, double sz); // Angle is in degrees. TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); } TransformationMatrix& rotateFromVector(double x, double y); - TransformationMatrix& rotate3d(double rx, double ry, double rz); + WEBCORE_EXPORT TransformationMatrix& rotate3d(double rx, double ry, double rz); - // The vector (x,y,z) is normalized if it's not already. A vector of - // (0,0,0) uses a vector of (0,0,1). + // The vector (x,y,z) is normalized if it's not already. A vector of (0,0,0) uses a vector of (0,0,1). TransformationMatrix& rotate3d(double x, double y, double z, double angle); - TransformationMatrix& translate(double tx, double ty); + WEBCORE_EXPORT TransformationMatrix& translate(double tx, double ty); TransformationMatrix& translate3d(double tx, double ty, double tz); // translation added with a post-multiply TransformationMatrix& translateRight(double tx, double ty); TransformationMatrix& translateRight3d(double tx, double ty, double tz); - TransformationMatrix& flipX(); - TransformationMatrix& flipY(); - TransformationMatrix& skew(double angleX, double angleY); + WEBCORE_EXPORT TransformationMatrix& flipX(); + WEBCORE_EXPORT TransformationMatrix& flipY(); + WEBCORE_EXPORT TransformationMatrix& skew(double angleX, double angleY); TransformationMatrix& skewX(double angle) { return skew(angle, 0); } TransformationMatrix& skewY(double angle) { return skew(0, angle); } TransformationMatrix& applyPerspective(double p); bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } - // returns a transformation that maps a rect to a rect - static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&); + // Returns a transformation that maps a rect to a rect. + WEBCORE_EXPORT static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&); - bool isInvertible() const; + bool isInvertible() const; // If you call this this, you're probably doing it wrong. + WEBCORE_EXPORT std::optional<TransformationMatrix> inverse() const; - // This method returns the identity matrix if it is not invertible. - // Use isInvertible() before calling this if you need to know. - TransformationMatrix inverse() const; - - // decompose the matrix into its component parts - typedef struct { + // Decompose the matrix into its component parts. + struct Decomposed2Type { double scaleX, scaleY; double translateX, translateY; double angle; double m11, m12, m21, m22; - } Decomposed2Type; - - typedef struct { + + bool operator==(const Decomposed2Type& other) const + { + return scaleX == other.scaleX && scaleY == other.scaleY + && translateX == other.translateX && translateY == other.translateY + && angle == other.angle + && m11 == other.m11 && m12 == other.m12 && m21 == other.m21 && m22 == other.m22; + } + }; + + struct Decomposed4Type { double scaleX, scaleY, scaleZ; double skewXY, skewXZ, skewYZ; double quaternionX, quaternionY, quaternionZ, quaternionW; double translateX, translateY, translateZ; double perspectiveX, perspectiveY, perspectiveZ, perspectiveW; - } Decomposed4Type; + + bool operator==(const Decomposed4Type& other) const + { + return scaleX == other.scaleX && scaleY == other.scaleY && scaleZ == other.scaleZ + && skewXY == other.skewXY && skewXZ == other.skewXZ && skewYZ == other.skewYZ + && quaternionX == other.quaternionX && quaternionY == other.quaternionY && quaternionZ == other.quaternionZ && quaternionW == other.quaternionW + && translateX == other.translateX && translateY == other.translateY && translateZ == other.translateZ + && perspectiveX == other.perspectiveX && perspectiveY == other.perspectiveY && perspectiveZ == other.perspectiveZ && perspectiveW == other.perspectiveW; + } + }; bool decompose2(Decomposed2Type&) const; void recompose2(const Decomposed2Type&); @@ -281,9 +299,9 @@ public: bool decompose4(Decomposed4Type&) const; void recompose4(const Decomposed4Type&); - void blend(const TransformationMatrix& from, double progress); - void blend2(const TransformationMatrix& from, double progress); - void blend4(const TransformationMatrix& from, double progress); + WEBCORE_EXPORT void blend(const TransformationMatrix& from, double progress); + WEBCORE_EXPORT void blend2(const TransformationMatrix& from, double progress); + WEBCORE_EXPORT void blend4(const TransformationMatrix& from, double progress); bool isAffine() const { @@ -291,10 +309,10 @@ public: m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1); } - // Throw away the non-affine parts of the matrix (lossy!) - void makeAffine(); + // Throw away the non-affine parts of the matrix (lossy!). + WEBCORE_EXPORT void makeAffine(); - AffineTransform toAffineTransform() const; + WEBCORE_EXPORT AffineTransform toAffineTransform() const; bool operator==(const TransformationMatrix& m2) const { @@ -333,12 +351,12 @@ public: } #if USE(CA) - TransformationMatrix(const CATransform3D&); - operator CATransform3D() const; + WEBCORE_EXPORT TransformationMatrix(const CATransform3D&); + WEBCORE_EXPORT operator CATransform3D() const; #endif #if USE(CG) - TransformationMatrix(const CGAffineTransform&); - operator CGAffineTransform() const; + WEBCORE_EXPORT TransformationMatrix(const CGAffineTransform&); + WEBCORE_EXPORT operator CGAffineTransform() const; #elif USE(CAIRO) operator cairo_matrix_t() const; #endif @@ -347,6 +365,11 @@ public: operator XFORM() const; #endif +#if PLATFORM(WIN) + TransformationMatrix(const D2D1_MATRIX_3X2_F&); + operator D2D1_MATRIX_3X2_F() const; +#endif + bool isIdentityOrTranslation() const { return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 @@ -357,7 +380,7 @@ public: bool isIntegerTranslation() const; - // This method returns the matrix without 3D components. + // Returns the matrix without 3D components. TransformationMatrix to2dTransform() const; typedef float FloatMatrix4[16]; @@ -380,7 +403,6 @@ private: return FloatPoint(static_cast<float>(resultX), static_cast<float>(resultY)); } - // multiply passed 3D point by matrix void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const; FloatPoint3D internalMapPoint(const FloatPoint3D& sourcePoint) const { @@ -400,6 +422,8 @@ private: Matrix4 m_matrix; }; +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const TransformationMatrix&); + } // namespace WebCore #endif // TransformationMatrix_h diff --git a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp index f140c5081..02880849e 100644 --- a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp @@ -21,24 +21,39 @@ #include "config.h" #include "TranslateTransformOperation.h" + #include "FloatConversion.h" +#include "TextStream.h" namespace WebCore { -PassRefPtr<TransformOperation> TranslateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +bool TranslateTransformOperation::operator==(const TransformOperation& other) const +{ + if (!isSameType(other)) + return false; + const TranslateTransformOperation& t = downcast<TranslateTransformOperation>(other); + return m_x == t.m_x && m_y == t.m_y && m_z == t.m_z; +} + +Ref<TransformOperation> TranslateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) { if (from && !from->isSameType(*this)) - return this; + return *this; Length zeroLength(0, Fixed); if (blendToIdentity) - return TranslateTransformOperation::create(zeroLength.blend(m_x, progress), zeroLength.blend(m_y, progress), zeroLength.blend(m_z, progress), m_type); + return TranslateTransformOperation::create(WebCore::blend(m_x, zeroLength, progress), WebCore::blend(m_y, zeroLength, progress), WebCore::blend(m_z, zeroLength, progress), m_type); - const TranslateTransformOperation* fromOp = static_cast<const TranslateTransformOperation*>(from); + const TranslateTransformOperation* fromOp = downcast<TranslateTransformOperation>(from); Length fromX = fromOp ? fromOp->m_x : zeroLength; Length fromY = fromOp ? fromOp->m_y : zeroLength; Length fromZ = fromOp ? fromOp->m_z : zeroLength; - return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_z.blend(fromZ, progress), m_type); + return TranslateTransformOperation::create(WebCore::blend(fromX, x(), progress), WebCore::blend(fromY, y(), progress), WebCore::blend(fromZ, z(), progress), m_type); +} + +void TranslateTransformOperation::dump(TextStream& ts) const +{ + ts << type() << "(" << m_x << ", " << m_y << ", " << m_z << ")"; } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h index 63cf587b2..e9f687541 100644 --- a/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/TranslateTransformOperation.h @@ -28,19 +28,25 @@ #include "Length.h" #include "LengthFunctions.h" #include "TransformOperation.h" +#include <wtf/Ref.h> namespace WebCore { -class TranslateTransformOperation : public TransformOperation { +class TranslateTransformOperation final : public TransformOperation { public: - static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type) + static Ref<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type) { - return adoptRef(new TranslateTransformOperation(tx, ty, Length(0, Fixed), type)); + return adoptRef(*new TranslateTransformOperation(tx, ty, Length(0, Fixed), type)); } - static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, const Length& tz, OperationType type) + static Ref<TranslateTransformOperation> create(const Length& tx, const Length& ty, const Length& tz, OperationType type) { - return adoptRef(new TranslateTransformOperation(tx, ty, tz, type)); + return adoptRef(*new TranslateTransformOperation(tx, ty, tz, type)); + } + + Ref<TransformOperation> clone() const override + { + return adoptRef(*new TranslateTransformOperation(m_x, m_y, m_z, m_type)); } double x(const FloatSize& borderBoxSize) const { return floatValueForLength(m_x, borderBoxSize.width()); } @@ -52,26 +58,22 @@ public: Length z() const { return m_z; } private: - virtual bool isIdentity() const { return !floatValueForLength(m_x, 1) && !floatValueForLength(m_y, 1) && !floatValueForLength(m_z, 1); } + bool isIdentity() const override { return !floatValueForLength(m_x, 1) && !floatValueForLength(m_y, 1) && !floatValueForLength(m_z, 1); } - virtual OperationType type() const { return m_type; } - virtual bool isSameType(const TransformOperation& o) const { return o.type() == m_type; } + OperationType type() const override { return m_type; } + bool isSameType(const TransformOperation& o) const override { return o.type() == m_type; } - virtual bool operator==(const TransformOperation& o) const - { - if (!isSameType(o)) - return false; - const TranslateTransformOperation* t = static_cast<const TranslateTransformOperation*>(&o); - return m_x == t->m_x && m_y == t->m_y && m_z == t->m_z; - } + bool operator==(const TransformOperation&) const override; - virtual bool apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const + bool apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const override { transform.translate3d(x(borderBoxSize), y(borderBoxSize), z(borderBoxSize)); - return m_x.type() == Percent || m_y.type() == Percent; + return m_x.isPercent() || m_y.isPercent(); } - virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + Ref<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + + void dump(TextStream&) const final; TranslateTransformOperation(const Length& tx, const Length& ty, const Length& tz, OperationType type) : m_x(tx) @@ -79,7 +81,7 @@ private: , m_z(tz) , m_type(type) { - ASSERT(type == TRANSLATE_X || type == TRANSLATE_Y || type == TRANSLATE_Z || type == TRANSLATE || type == TRANSLATE_3D); + ASSERT(isTranslateTransformOperationType()); } Length m_x; @@ -90,4 +92,6 @@ private: } // namespace WebCore +SPECIALIZE_TYPE_TRAITS_TRANSFORMOPERATION(WebCore::TranslateTransformOperation, isTranslateTransformOperationType()) + #endif // TranslateTransformOperation_h diff --git a/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp new file mode 100644 index 000000000..42291396f --- /dev/null +++ b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformDisplayWayland.h" + +#if PLATFORM(WAYLAND) + +#include "GLContextEGL.h" +#include <cstring> +// These includes need to be in this order because wayland-egl.h defines WL_EGL_PLATFORM +// and egl.h checks that to decide whether it's Wayland platform. +#include <wayland-egl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <wtf/Assertions.h> + +namespace WebCore { + +const struct wl_registry_listener PlatformDisplayWayland::s_registryListener = { + // globalCallback + [](void* data, struct wl_registry*, uint32_t name, const char* interface, uint32_t) { + static_cast<PlatformDisplayWayland*>(data)->registryGlobal(interface, name); + }, + // globalRemoveCallback + [](void*, struct wl_registry*, uint32_t) + { + } +}; + +std::unique_ptr<PlatformDisplay> PlatformDisplayWayland::create() +{ + struct wl_display* display = wl_display_connect(getenv("DISPLAY")); + if (!display) + return nullptr; + + return std::make_unique<PlatformDisplayWayland>(display, NativeDisplayOwned::Yes); +} + +PlatformDisplayWayland::PlatformDisplayWayland(struct wl_display* display, NativeDisplayOwned displayOwned) + : PlatformDisplay(displayOwned) +{ + initialize(display); +} + +PlatformDisplayWayland::~PlatformDisplayWayland() +{ + if (m_nativeDisplayOwned == NativeDisplayOwned::Yes) + wl_display_destroy(m_display); +} + +void PlatformDisplayWayland::initialize(wl_display* display) +{ + m_display = display; + if (!m_display) + return; + + m_registry.reset(wl_display_get_registry(m_display)); + wl_registry_add_listener(m_registry.get(), &s_registryListener, this); + wl_display_roundtrip(m_display); + +#if USE(EGL) +#if defined(EGL_KHR_platform_wayland) + const char* extensions = eglQueryString(nullptr, EGL_EXTENSIONS); + if (GLContext::isExtensionSupported(extensions, "EGL_KHR_platform_base")) { + if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplay"))) + m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, m_display, nullptr); + } else if (GLContext::isExtensionSupported(extensions, "EGL_EXT_platform_base")) { + if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT"))) + m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, m_display, nullptr); + } else +#endif + m_eglDisplay = eglGetDisplay(m_display); + + PlatformDisplay::initializeEGLDisplay(); +#endif +} + +void PlatformDisplayWayland::registryGlobal(const char* interface, uint32_t name) +{ + if (!std::strcmp(interface, "wl_compositor")) + m_compositor.reset(static_cast<struct wl_compositor*>(wl_registry_bind(m_registry.get(), name, &wl_compositor_interface, 1))); +} + +WlUniquePtr<struct wl_surface> PlatformDisplayWayland::createSurface() const +{ + if (!m_compositor) + return nullptr; + + return WlUniquePtr<struct wl_surface>(wl_compositor_create_surface(m_compositor.get())); +} + +} // namespace WebCore + +#endif // PLATFORM(WAYLAND) diff --git a/Source/WebCore/platform/graphics/FontData.h b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.h index 22154da9f..e17dce04a 100644 --- a/Source/WebCore/platform/graphics/FontData.h +++ b/Source/WebCore/platform/graphics/wayland/PlatformDisplayWayland.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2014 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,50 +20,50 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FontData_h -#define FontData_h +#ifndef PlatformDisplayWayland_h +#define PlatformDisplayWayland_h -#include <wtf/FastMalloc.h> -#include <wtf/Forward.h> -#include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/unicode/Unicode.h> +#if PLATFORM(WAYLAND) -namespace WebCore { +#include "PlatformDisplay.h" +#include "WlUniquePtr.h" +#include <wayland-client.h> -class SimpleFontData; +namespace WebCore { -class FontData : public RefCounted<FontData> { - WTF_MAKE_NONCOPYABLE(FontData); WTF_MAKE_FAST_ALLOCATED; +class PlatformDisplayWayland : public PlatformDisplay { public: - FontData() - : m_maxGlyphPageTreeLevel(0) - { - } + static std::unique_ptr<PlatformDisplay> create(); + PlatformDisplayWayland(struct wl_display*, NativeDisplayOwned = NativeDisplayOwned::No); + virtual ~PlatformDisplayWayland(); - virtual ~FontData(); + struct wl_display* native() const { return m_display; } - virtual const SimpleFontData* fontDataForCharacter(UChar32) const = 0; - virtual bool containsCharacters(const UChar*, int length) const = 0; - virtual bool isCustomFont() const = 0; - virtual bool isLoading() const = 0; - virtual bool isSegmented() const = 0; + WlUniquePtr<struct wl_surface> createSurface() const; - void setMaxGlyphPageTreeLevel(unsigned level) const { m_maxGlyphPageTreeLevel = level; } - unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; } +private: + static const struct wl_registry_listener s_registryListener; -#ifndef NDEBUG - virtual String description() const = 0; -#endif + Type type() const override { return PlatformDisplay::Type::Wayland; } -private: - mutable unsigned m_maxGlyphPageTreeLevel; +protected: + PlatformDisplayWayland() = default; + void initialize(struct wl_display*); + + virtual void registryGlobal(const char* interface, uint32_t name); + + struct wl_display* m_display; + WlUniquePtr<struct wl_registry> m_registry; + WlUniquePtr<struct wl_compositor> m_compositor; }; } // namespace WebCore -#endif // FontData_h +SPECIALIZE_TYPE_TRAITS_PLATFORM_DISPLAY(PlatformDisplayWayland, Wayland) + +#endif // PLATFORM(WAYLAND) + +#endif // PlatformDisplayWayland_h diff --git a/Source/WebCore/platform/graphics/FontRenderingMode.h b/Source/WebCore/platform/graphics/wayland/WlUniquePtr.h index c1ce49705..03d2dab59 100644 --- a/Source/WebCore/platform/graphics/FontRenderingMode.h +++ b/Source/WebCore/platform/graphics/wayland/WlUniquePtr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2016 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,18 +20,42 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FontRenderingMode_h -#define FontRenderingMode_h +#pragma once + +#if PLATFORM(WAYLAND) +#include <wayland-client-protocol.h> +#include <wayland-server.h> namespace WebCore { -// This setting is used to provide ways of switching between multiple rendering modes that may have different -// metrics. It is used to switch between CG and GDI text on Windows. -enum FontRenderingMode { NormalRenderingMode, AlternateRenderingMode }; +template<typename T> +struct WlPtrDeleter { +}; + +template<typename T> +using WlUniquePtr = std::unique_ptr<T, WlPtrDeleter<T>>; + +#define FOR_EACH_WAYLAND_DELETER(macro) \ + macro(struct wl_display, wl_display_destroy) \ + macro(struct wl_global, wl_global_destroy) \ + macro(struct wl_surface, wl_surface_destroy) \ + macro(struct wl_compositor, wl_compositor_destroy) \ + macro(struct wl_registry, wl_registry_destroy) \ + macro(struct wl_proxy, wl_proxy_destroy) + +#define DEFINE_WAYLAND_DELETER(typeName, deleterFunc) \ + template<> struct WlPtrDeleter<typeName> \ + { \ + void operator() (typeName* ptr) const { deleterFunc(ptr); } \ + }; + +FOR_EACH_WAYLAND_DELETER(DEFINE_WAYLAND_DELETER) +#undef FOR_EACH_WAYLAND_DELETER +#undef DEFINE_WAYLAND_DELETER } // namespace WebCore -#endif // FontRenderingMode_h +#endif // PLATFORM(WAYLAND) diff --git a/Source/WebCore/platform/graphics/win/DIBPixelData.cpp b/Source/WebCore/platform/graphics/win/DIBPixelData.cpp deleted file mode 100644 index e9a0f9b15..000000000 --- a/Source/WebCore/platform/graphics/win/DIBPixelData.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2011 Brent Fulgham <bfulgham@webkit.org> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "DIBPixelData.h" - -namespace WebCore { - -static const WORD bitmapType = 0x4d42; // BMP format -static const WORD bitmapPixelsPerMeter = 2834; // 72 dpi - -DIBPixelData::DIBPixelData(HBITMAP bitmap) -{ - initialize(bitmap); -} - -void DIBPixelData::initialize(HBITMAP bitmap) -{ - BITMAP bmpInfo; - GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); - - m_bitmapBuffer = reinterpret_cast<UInt8*>(bmpInfo.bmBits); - m_bitmapBufferLength = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; - m_size = IntSize(bmpInfo.bmWidth, bmpInfo.bmHeight); - m_bytesPerRow = bmpInfo.bmWidthBytes; - m_bitsPerPixel = bmpInfo.bmBitsPixel; -} - -#ifndef NDEBUG -void DIBPixelData::writeToFile(LPCWSTR filePath) -{ - HANDLE hFile = ::CreateFile(filePath, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - if (INVALID_HANDLE_VALUE == hFile) - return; - - BITMAPFILEHEADER header; - header.bfType = bitmapType; - header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); - header.bfReserved1 = 0; - header.bfReserved2 = 0; - header.bfSize = sizeof(BITMAPFILEHEADER); - - BITMAPINFOHEADER info; - info.biSize = sizeof(BITMAPINFOHEADER); - info.biWidth = m_size.width(); - info.biHeight = m_size.height(); - info.biPlanes = 1; - info.biBitCount = m_bitsPerPixel; - info.biCompression = BI_RGB; - info.biSizeImage = bufferLength(); - info.biXPelsPerMeter = bitmapPixelsPerMeter; - info.biYPelsPerMeter = bitmapPixelsPerMeter; - info.biClrUsed = 0; - info.biClrImportant = 0; - - DWORD bytesWritten = 0; - ::WriteFile(hFile, &header, sizeof(header), &bytesWritten, 0); - ::WriteFile(hFile, &info, sizeof(info), &bytesWritten, 0); - ::WriteFile(hFile, buffer(), bufferLength(), &bytesWritten, 0); - - ::CloseHandle(hFile); -} -#endif - -void DIBPixelData::setRGBABitmapAlpha(HDC hdc, const IntRect& dstRect, unsigned char level) -{ - HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); - DIBPixelData pixelData(bitmap); - ASSERT(pixelData.bitsPerPixel() == 32); - - IntRect drawRect(dstRect); -#if !OS(WINCE) - XFORM trans; - GetWorldTransform(hdc, &trans); - IntSize transformedPosition(trans.eDx, trans.eDy); - drawRect.move(transformedPosition); -#endif - - int pixelDataWidth = pixelData.size().width(); - int pixelDataHeight = pixelData.size().height(); - IntRect bitmapRect(0, 0, pixelDataWidth, pixelDataHeight); - drawRect.intersect(bitmapRect); - if (drawRect.isEmpty()) - return; - - RGBQUAD* bytes = reinterpret_cast<RGBQUAD*>(pixelData.buffer()); - bytes += drawRect.y() * pixelDataWidth; - - size_t width = drawRect.width(); - size_t height = drawRect.height(); - int x = drawRect.x(); - for (size_t i = 0; i < height; i++) { - RGBQUAD* p = bytes + x; - for (size_t j = 0; j < width; j++) { - p->rgbReserved = level; - p++; - } - bytes += pixelDataWidth; - } -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp deleted file mode 100644 index 3fb2ac033..000000000 --- a/Source/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "GraphicsContext.h" - -#include "AffineTransform.h" -#include "DIBPixelData.h" -#include "Path.h" - -#include <cairo-win32.h> -#include "GraphicsContextPlatformPrivateCairo.h" -#include <wtf/win/GDIObject.h> - -using namespace std; - -namespace WebCore { - -#if PLATFORM(WIN) -static cairo_t* createCairoContextWithHDC(HDC hdc, bool hasAlpha) -{ - // Put the HDC In advanced mode so it will honor affine transforms. - SetGraphicsMode(hdc, GM_ADVANCED); - - cairo_surface_t* surface = 0; - - HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); - - BITMAP info; - if (!GetObject(bitmap, sizeof(info), &info)) - surface = cairo_win32_surface_create(hdc); - else { - ASSERT(info.bmBitsPixel == 32); - - surface = cairo_image_surface_create_for_data((unsigned char*)info.bmBits, - CAIRO_FORMAT_ARGB32, - info.bmWidth, - info.bmHeight, - info.bmWidthBytes); - } - - cairo_t* context = cairo_create(surface); - cairo_surface_destroy(surface); - - return context; -} - -GraphicsContext::GraphicsContext(HDC dc, bool hasAlpha) - : m_updatingControlTints(false), - m_transparencyCount(0) -{ - platformInit(dc, hasAlpha); -} - -void GraphicsContext::platformInit(HDC dc, bool hasAlpha) -{ - cairo_t* cr = 0; - if (dc) - cr = createCairoContextWithHDC(dc, hasAlpha); - else - setPaintingDisabled(true); - - m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr)); - m_data->m_hdc = dc; - if (platformContext()->cr()) { - // Make sure the context starts in sync with our state. - setPlatformFillColor(fillColor(), fillColorSpace()); - setPlatformStrokeColor(strokeColor(), strokeColorSpace()); - } - if (cr) - cairo_destroy(cr); -} -#endif - -static void setRGBABitmapAlpha(unsigned char* bytes, size_t length, unsigned char level) -{ - for (size_t i = 0; i < length; i += 4) - bytes[i + 3] = level; -} - -static void drawBitmapToContext(GraphicsContextPlatformPrivate* context, cairo_t* cr, const DIBPixelData& pixelData, const IntSize& translate) -{ - // Need to make a cairo_surface_t out of the bitmap's pixel buffer and then draw - // it into our context. - cairo_surface_t* surface = cairo_image_surface_create_for_data(pixelData.buffer(), - CAIRO_FORMAT_ARGB32, - pixelData.size().width(), - pixelData.size().height(), - pixelData.bytesPerRow()); - - // Flip the target surface so that when we set the srcImage as - // the surface it will draw right-side-up. - cairo_save(cr); - cairo_translate(cr, static_cast<double>(translate.width()), static_cast<double>(translate.height())); - cairo_scale(cr, 1, -1); - cairo_set_source_surface(cr, surface, 0, 0); - - if (context->layers.size()) - cairo_paint_with_alpha(cr, context->layers.last()); - else - cairo_paint(cr); - - // Delete all our junk. - cairo_surface_destroy(surface); - cairo_restore(cr); -} - -void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) -{ - bool createdBitmap = mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer()); - if (!hdc || !createdBitmap) { - m_data->restore(); - return; - } - - if (dstRect.isEmpty()) - return; - - auto bitmap = adoptGDIObject(static_cast<HBITMAP>(::GetCurrentObject(hdc, OBJ_BITMAP))); - - DIBPixelData pixelData(bitmap.get()); - ASSERT(pixelData.bitsPerPixel() == 32); - - // If this context does not support alpha blending, then it may have - // been drawn with GDI functions which always set the alpha channel - // to zero. We need to manually set the bitmap to be fully opaque. - unsigned char* bytes = reinterpret_cast<unsigned char*>(pixelData.buffer()); - if (!supportAlphaBlend) - setRGBABitmapAlpha(bytes, pixelData.size().height() * pixelData.bytesPerRow(), 255); - - drawBitmapToContext(m_data, platformContext()->cr(), pixelData, IntSize(dstRect.x(), dstRect.height() + dstRect.y())); - - ::DeleteDC(hdc); -} - -#if PLATFORM(WIN) -void GraphicsContext::drawWindowsBitmap(WindowsBitmap* bitmap, const IntPoint& point) -{ - drawBitmapToContext(m_data, platformContext()->cr(), bitmap->windowsDIB(), IntSize(point.x(), bitmap->size().height() + point.y())); -} - -void GraphicsContextPlatformPrivate::syncContext(cairo_t* cr) -{ - if (!cr) - return; - - cairo_surface_t* surface = cairo_get_target(cr); - m_hdc = cairo_win32_surface_get_dc(surface); - - SetGraphicsMode(m_hdc, GM_ADVANCED); // We need this call for themes to honor world transforms. -} - -void GraphicsContextPlatformPrivate::flush() -{ - cairo_surface_t* surface = cairo_win32_surface_create(m_hdc); - cairo_surface_flush(surface); - cairo_surface_destroy(surface); -} -#endif - -} diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp deleted file mode 100644 index 44fa7f069..000000000 --- a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "GraphicsContext.h" - -#if USE(CG) -#include "GraphicsContextPlatformPrivateCG.h" -#elif USE(CAIRO) -#include "GraphicsContextPlatformPrivateCairo.h" -#endif - -#include "AffineTransform.h" -#include "BitmapInfo.h" -#include "TransformationMatrix.h" -#include "NotImplemented.h" -#include "Path.h" -#include <wtf/MathExtras.h> -#include <wtf/win/GDIObject.h> - -using namespace std; - -namespace WebCore { - -static void fillWithClearColor(HBITMAP bitmap) -{ - BITMAP bmpInfo; - GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); - int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; - memset(bmpInfo.bmBits, 0, bufferSize); -} - -#if PLATFORM(WIN) -void GraphicsContext::setShouldIncludeChildWindows(bool include) -{ - m_data->m_shouldIncludeChildWindows = include; -} - -bool GraphicsContext::shouldIncludeChildWindows() const -{ - return m_data->m_shouldIncludeChildWindows; -} - -GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, const IntSize& size) - : m_hdc(0) -{ - BitmapInfo bitmapInfo = BitmapInfo::create(size); - - void* storage = 0; - m_bitmap = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &storage, 0, 0); - if (!m_bitmap) - return; - - m_hdc = CreateCompatibleDC(hdc); - SelectObject(m_hdc, m_bitmap); - - m_pixelData.initialize(m_bitmap); - - ASSERT(storage == m_pixelData.buffer()); - - SetGraphicsMode(m_hdc, GM_ADVANCED); -} - -GraphicsContext::WindowsBitmap::~WindowsBitmap() -{ - if (!m_bitmap) - return; - - DeleteDC(m_hdc); - DeleteObject(m_bitmap); -} - -PassOwnPtr<GraphicsContext::WindowsBitmap> GraphicsContext::createWindowsBitmap(const IntSize& size) -{ - return adoptPtr(new WindowsBitmap(m_data->m_hdc, size)); -} -#endif - -HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) -{ - // FIXME: Should a bitmap be created also when a shadow is set? - if (mayCreateBitmap && (!m_data->m_hdc || isInTransparencyLayer())) { - if (dstRect.isEmpty()) - return 0; - - // Create a bitmap DC in which to draw. - BitmapInfo bitmapInfo = BitmapInfo::create(dstRect.size()); - - void* pixels = 0; - HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); - if (!bitmap) - return 0; - - auto bitmapDC = adoptGDIObject(::CreateCompatibleDC(m_data->m_hdc)); - ::SelectObject(bitmapDC.get(), bitmap); - - // Fill our buffer with clear if we're going to alpha blend. - if (supportAlphaBlend) - fillWithClearColor(bitmap); - - // Make sure we can do world transforms. - ::SetGraphicsMode(bitmapDC.get(), GM_ADVANCED); - - // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. - XFORM xform = TransformationMatrix().translate(-dstRect.x(), -dstRect.y()); - - ::SetWorldTransform(bitmapDC.get(), &xform); - - return bitmapDC.leak(); - } - - m_data->flush(); - m_data->save(); - return m_data->m_hdc; -} - -#if PLATFORM(WIN) -void GraphicsContextPlatformPrivate::save() -{ - if (!m_hdc) - return; - SaveDC(m_hdc); -} - -void GraphicsContextPlatformPrivate::restore() -{ - if (!m_hdc) - return; - RestoreDC(m_hdc, -1); -} - -void GraphicsContextPlatformPrivate::clip(const FloatRect& clipRect) -{ - if (!m_hdc) - return; - IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.maxX(), clipRect.maxY()); -} - -void GraphicsContextPlatformPrivate::clip(const Path&) -{ - notImplemented(); -} - -void GraphicsContextPlatformPrivate::scale(const FloatSize& size) -{ - if (!m_hdc) - return; - - XFORM xform = TransformationMatrix().scaleNonUniform(size.width(), size.height()); - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - -static const double deg2rad = 0.017453292519943295769; // pi/180 - -void GraphicsContextPlatformPrivate::rotate(float degreesAngle) -{ - XFORM xform = TransformationMatrix().rotate(degreesAngle); - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - -void GraphicsContextPlatformPrivate::translate(float x , float y) -{ - if (!m_hdc) - return; - - XFORM xform = TransformationMatrix().translate(x, y); - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - -void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) -{ - if (!m_hdc) - return; - - XFORM xform = transform.toTransformationMatrix(); - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - -void GraphicsContextPlatformPrivate::setCTM(const AffineTransform& transform) -{ - if (!m_hdc) - return; - - XFORM xform = transform.toTransformationMatrix(); - SetWorldTransform(m_hdc, &xform); -} -#endif - -} diff --git a/Source/WebCore/platform/graphics/win/LocalWindowsContext.h b/Source/WebCore/platform/graphics/win/LocalWindowsContext.h deleted file mode 100644 index 5951e4962..000000000 --- a/Source/WebCore/platform/graphics/win/LocalWindowsContext.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef LocalWindowsContext_h -#define LocalWindowsContext_h - -#include "config.h" -#include "GraphicsContext.h" - -namespace WebCore { - -class LocalWindowsContext { - WTF_MAKE_NONCOPYABLE(LocalWindowsContext); -public: - LocalWindowsContext(GraphicsContext* graphicsContext, const IntRect& rect, bool supportAlphaBlend = true, bool mayCreateBitmap = true) - : m_graphicsContext(graphicsContext) - , m_rect(rect) - , m_supportAlphaBlend(supportAlphaBlend) - , m_mayCreateBitmap(mayCreateBitmap) - { - m_hdc = m_graphicsContext->getWindowsContext(m_rect, m_supportAlphaBlend, m_mayCreateBitmap); - } - - ~LocalWindowsContext() - { - m_graphicsContext->releaseWindowsContext(m_hdc, m_rect, m_supportAlphaBlend, m_mayCreateBitmap); - } - - HDC hdc() const { return m_hdc; } - -private: - GraphicsContext* m_graphicsContext; - HDC m_hdc; - IntRect m_rect; - bool m_supportAlphaBlend; - bool m_mayCreateBitmap; -}; - -} // namespace WebCore -#endif // LocalWindowsContext_h diff --git a/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp b/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp deleted file mode 100644 index 47806a271..000000000 --- a/Source/WebCore/platform/graphics/win/TransformationMatrixWin.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "TransformationMatrix.h" - -#include <windows.h> - -namespace WebCore { - -TransformationMatrix::operator XFORM() const -{ - XFORM xform; - xform.eM11 = a(); - xform.eM12 = b(); - xform.eM21 = c(); - xform.eM22 = d(); - xform.eDx = e(); - xform.eDy = f(); - - return xform; -} - -} diff --git a/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp new file mode 100644 index 000000000..5aefe6d51 --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlatformDisplayX11.h" + +#include "GLContext.h" + +#if PLATFORM(X11) +#include <X11/Xlib.h> +#include <X11/extensions/Xcomposite.h> +#if PLATFORM(GTK) +#include <X11/extensions/Xdamage.h> +#endif + +#if USE(EGL) +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + +namespace WebCore { + +std::unique_ptr<PlatformDisplay> PlatformDisplayX11::create() +{ + Display* display = XOpenDisplay(getenv("DISPLAY")); + if (!display) + return nullptr; + + return std::make_unique<PlatformDisplayX11>(display, NativeDisplayOwned::Yes); +} + +PlatformDisplayX11::PlatformDisplayX11(Display* display, NativeDisplayOwned displayOwned) + : PlatformDisplay(displayOwned) + , m_display(display) +{ +} + +PlatformDisplayX11::~PlatformDisplayX11() +{ +#if USE(EGL) || USE(GLX) + // Clear the sharing context before releasing the display. + m_sharingGLContext = nullptr; +#endif + if (m_nativeDisplayOwned == NativeDisplayOwned::Yes) + XCloseDisplay(m_display); +} + +#if USE(EGL) +void PlatformDisplayX11::initializeEGLDisplay() +{ +#if defined(EGL_KHR_platform_x11) + const char* extensions = eglQueryString(nullptr, EGL_EXTENSIONS); + if (GLContext::isExtensionSupported(extensions, "EGL_KHR_platform_base")) { + if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplay"))) + m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_X11_KHR, m_display, nullptr); + } else if (GLContext::isExtensionSupported(extensions, "EGL_EXT_platform_base")) { + if (auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT"))) + m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_X11_KHR, m_display, nullptr); + } else +#endif + m_eglDisplay = eglGetDisplay(m_display); + + PlatformDisplay::initializeEGLDisplay(); +} +#endif // USE(EGL) + +bool PlatformDisplayX11::supportsXComposite() const +{ + if (!m_supportsXComposite) { + if (m_display) { + int eventBase, errorBase; + m_supportsXComposite = XCompositeQueryExtension(m_display, &eventBase, &errorBase); + } else + m_supportsXComposite = false; + } + return m_supportsXComposite.value(); +} + +bool PlatformDisplayX11::supportsXDamage(std::optional<int>& damageEventBase, std::optional<int>& damageErrorBase) const +{ + if (!m_supportsXDamage) { + m_supportsXDamage = false; +#if PLATFORM(GTK) + if (m_display) { + int eventBase, errorBase; + m_supportsXDamage = XDamageQueryExtension(m_display, &eventBase, &errorBase); + if (m_supportsXDamage.value()) { + m_damageEventBase = eventBase; + m_damageErrorBase = errorBase; + } + } +#endif + } + + damageEventBase = m_damageEventBase; + damageErrorBase = m_damageErrorBase; + return m_supportsXDamage.value(); +} + +} // namespace WebCore + +#endif // PLATFORM(X11) + diff --git a/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h new file mode 100644 index 000000000..311744576 --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/PlatformDisplayX11.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PlatformDisplayX11_h +#define PlatformDisplayX11_h + +#if PLATFORM(X11) + +#include "PlatformDisplay.h" +#include <wtf/Optional.h> + +typedef struct _XDisplay Display; + +namespace WebCore { + +class PlatformDisplayX11 final : public PlatformDisplay { +public: + static std::unique_ptr<PlatformDisplay> create(); + PlatformDisplayX11(Display*, NativeDisplayOwned = NativeDisplayOwned::No); + virtual ~PlatformDisplayX11(); + + Display* native() const { return m_display; } + bool supportsXComposite() const; + bool supportsXDamage(std::optional<int>& damageEventBase, std::optional<int>& damageErrorBase) const; + +private: + Type type() const override { return PlatformDisplay::Type::X11; } + +#if USE(EGL) + void initializeEGLDisplay() override; +#endif + + Display* m_display { nullptr }; + mutable std::optional<bool> m_supportsXComposite; + mutable std::optional<bool> m_supportsXDamage; + mutable std::optional<int> m_damageEventBase; + mutable std::optional<int> m_damageErrorBase; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_PLATFORM_DISPLAY(PlatformDisplayX11, X11) + +#endif // PLATFORM(X11) + +#endif // PlatformDisplayX11 diff --git a/Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp b/Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp new file mode 100644 index 000000000..6fd4d49df --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/XErrorTrapper.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XErrorTrapper.h" + +#if PLATFORM(X11) +#include <sys/types.h> +#include <unistd.h> +#include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +static HashMap<Display*, Vector<XErrorTrapper*>>& xErrorTrappersMap() +{ + static NeverDestroyed<HashMap<Display*, Vector<XErrorTrapper*>>> trappersMap; + return trappersMap; +} + +XErrorTrapper::XErrorTrapper(Display* display, Policy policy, Vector<unsigned char>&& expectedErrors) + : m_display(display) + , m_policy(policy) + , m_expectedErrors(WTFMove(expectedErrors)) +{ + xErrorTrappersMap().add(m_display, Vector<XErrorTrapper*>()).iterator->value.append(this); + m_previousErrorHandler = XSetErrorHandler([](Display* display, XErrorEvent* event) -> int { + auto iterator = xErrorTrappersMap().find(display); + if (iterator == xErrorTrappersMap().end()) + return 0; + + ASSERT(!iterator->value.isEmpty()); + iterator->value.last()->errorEvent(event); + return 0; + }); +} + +XErrorTrapper::~XErrorTrapper() +{ + XSync(m_display, False); + auto iterator = xErrorTrappersMap().find(m_display); + ASSERT(iterator != xErrorTrappersMap().end()); + auto* trapper = iterator->value.takeLast(); + ASSERT_UNUSED(trapper, trapper == this); + if (iterator->value.isEmpty()) + xErrorTrappersMap().remove(iterator); + + XSetErrorHandler(m_previousErrorHandler); +} + +unsigned char XErrorTrapper::errorCode() const +{ + XSync(m_display, False); + return m_errorCode; +} + +void XErrorTrapper::errorEvent(XErrorEvent* event) +{ + m_errorCode = event->error_code; + if (m_policy == Policy::Ignore) + return; + + if (m_expectedErrors.contains(m_errorCode)) + return; + + static const char errorFormatString[] = "The program with pid %d received an X Window System error.\n" + "The error was '%s'.\n" + " (Details: serial %ld error_code %d request_code %d minor_code %d)\n"; + char errorMessage[64]; + XGetErrorText(m_display, m_errorCode, errorMessage, 63); + WTFLogAlways(errorFormatString, getpid(), errorMessage, event->serial, event->error_code, event->request_code, event->minor_code); + + if (m_policy == Policy::Crash) + CRASH(); +} + +} // namespace WebCore + +#endif // PLATFORM(X11) diff --git a/Source/WebCore/platform/graphics/x11/XErrorTrapper.h b/Source/WebCore/platform/graphics/x11/XErrorTrapper.h new file mode 100644 index 000000000..5d53b6c33 --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/XErrorTrapper.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if PLATFORM(X11) +#include <X11/Xlib.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class XErrorTrapper { +public: + enum class Policy { Ignore, Warn, Crash }; + XErrorTrapper(Display*, Policy = Policy::Ignore, Vector<unsigned char>&& expectedErrors = { }); + ~XErrorTrapper(); + + unsigned char errorCode() const; + +private: + void errorEvent(XErrorEvent*); + + Display* m_display { nullptr }; + Policy m_policy { Policy::Ignore }; + Vector<unsigned char> m_expectedErrors; + XErrorHandler m_previousErrorHandler { nullptr }; + unsigned char m_errorCode { 0 }; +}; + +} // namespace WebCore + +#endif // PLATFORM(X11) diff --git a/Source/WebCore/platform/graphics/x11/XUniquePtr.h b/Source/WebCore/platform/graphics/x11/XUniquePtr.h new file mode 100644 index 000000000..37a18c29d --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/XUniquePtr.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XUniquePtr_h +#define XUniquePtr_h + +#if PLATFORM(X11) +#include "PlatformDisplayX11.h" +#include <X11/Xutil.h> + +#if USE(GLX) +typedef struct __GLXcontextRec* GLXContext; +extern "C" void glXDestroyContext(Display*, GLXContext); +#endif + +namespace WebCore { + +template<typename T> +struct XPtrDeleter { + void operator()(T* ptr) const + { + XFree(ptr); + } +}; + +template<typename T> +using XUniquePtr = std::unique_ptr<T, XPtrDeleter<T>>; + +template<> struct XPtrDeleter<XImage> { + void operator() (XImage* ptr) const + { + XDestroyImage(ptr); + } +}; + +template<> struct XPtrDeleter<_XGC> { + void operator() (_XGC* ptr) const + { + XFreeGC(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(), ptr); + } +}; +// Give a name to this to avoid having to use the internal struct name. +using XUniqueGC = XUniquePtr<_XGC>; + +#if USE(GLX) +template<> struct XPtrDeleter<__GLXcontextRec> { + void operator() (__GLXcontextRec* ptr) + { + glXDestroyContext(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(), ptr); + } +}; +// Give a name to this to avoid having to use the internal struct name. +using XUniqueGLXContext = XUniquePtr<__GLXcontextRec>; +#endif + +} // namespace WebCore + +#endif // PLATFORM(X11) + +#endif // XUniquePtr_h diff --git a/Source/WebCore/platform/graphics/x11/XUniqueResource.cpp b/Source/WebCore/platform/graphics/x11/XUniqueResource.cpp new file mode 100644 index 000000000..f300ee4f5 --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/XUniqueResource.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XUniqueResource.h" + +#if PLATFORM(X11) +#include "PlatformDisplayX11.h" +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#if PLATFORM(GTK) +#include <X11/extensions/Xdamage.h> +#endif + +#if USE(GLX) +#include <GL/glx.h> +#endif + +namespace WebCore { + +static inline Display* sharedDisplay() +{ + return downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native(); +} + +template<> void XUniqueResource<XResource::Colormap>::deleteXResource(unsigned long resource) +{ + if (resource) + XFreeColormap(sharedDisplay(), resource); +} + +#if PLATFORM(GTK) +template<> void XUniqueResource<XResource::Damage>::deleteXResource(unsigned long resource) +{ + if (resource) + XDamageDestroy(sharedDisplay(), resource); +} +#endif + +template<> void XUniqueResource<XResource::Pixmap>::deleteXResource(unsigned long resource) +{ + if (resource) + XFreePixmap(sharedDisplay(), resource); +} + +template<> void XUniqueResource<XResource::Window>::deleteXResource(unsigned long resource) +{ + if (resource) + XDestroyWindow(sharedDisplay(), resource); +} + +#if USE(GLX) +template<> void XUniqueResource<XResource::GLXPbuffer>::deleteXResource(unsigned long resource) +{ + if (resource) + glXDestroyPbuffer(sharedDisplay(), resource); +} + +template<> void XUniqueResource<XResource::GLXPixmap>::deleteXResource(unsigned long resource) +{ + if (resource) + glXDestroyGLXPixmap(sharedDisplay(), resource); +} +#endif // USE(GLX) + +} // namespace WebCore + +#endif // PLATFORM(X11) diff --git a/Source/WebCore/platform/graphics/x11/XUniqueResource.h b/Source/WebCore/platform/graphics/x11/XUniqueResource.h new file mode 100644 index 000000000..0da8b0c9c --- /dev/null +++ b/Source/WebCore/platform/graphics/x11/XUniqueResource.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XUniqueResource_h +#define XUniqueResource_h + +#if PLATFORM(X11) + +#if USE(GLX) +typedef unsigned long GLXPbuffer; +typedef unsigned long GLXPixmap; +#endif + +namespace WebCore { + +enum class XResource { + Colormap, +#if PLATFORM(GTK) + Damage, +#endif + Pixmap, + Window, +#if USE(GLX) + GLXPbuffer, + GLXPixmap, +#endif +}; + +template <XResource T> class XUniqueResource { +public: + XUniqueResource() + { + } + + XUniqueResource(unsigned long resource) + : m_resource(resource) + { + } + + XUniqueResource(XUniqueResource&& uniqueResource) + : m_resource(uniqueResource.release()) + { + } + + XUniqueResource& operator=(XUniqueResource&& uniqueResource) + { + reset(uniqueResource.release()); + return *this; + } + + ~XUniqueResource() + { + reset(); + } + + unsigned long get() const { return m_resource; } + unsigned long release() { return std::exchange(m_resource, 0); } + + void reset(unsigned long resource = 0) + { + std::swap(m_resource, resource); + deleteXResource(resource); + } + + explicit operator bool() const { return m_resource; } + +private: + static void deleteXResource(unsigned long resource); + + unsigned long m_resource { 0 }; +}; + +using XUniqueColormap = XUniqueResource<XResource::Colormap>; +#if PLATFORM(GTK) +using XUniqueDamage = XUniqueResource<XResource::Damage>; +#endif +using XUniquePixmap = XUniqueResource<XResource::Pixmap>; +using XUniqueWindow = XUniqueResource<XResource::Window>; +#if USE(GLX) +using XUniqueGLXPbuffer = XUniqueResource<XResource::GLXPbuffer>; +using XUniqueGLXPixmap = XUniqueResource<XResource::GLXPixmap>; +#endif + +} // namespace WebCore + +#endif // PLATFORM(X11) + +#endif // XUniqueResource_h |