diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/chromium')
72 files changed, 14589 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.cpp new file mode 100644 index 0000000..ad961aa --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.cpp @@ -0,0 +1,112 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) + +#include "Canvas2DLayerChromium.h" + +#include "DrawingBuffer.h" +#include "GraphicsContext3D.h" + +namespace WebCore { + +PassRefPtr<Canvas2DLayerChromium> Canvas2DLayerChromium::create(DrawingBuffer* drawingBuffer, GraphicsLayerChromium* owner) +{ + return adoptRef(new Canvas2DLayerChromium(drawingBuffer, owner)); +} + +Canvas2DLayerChromium::Canvas2DLayerChromium(DrawingBuffer* drawingBuffer, GraphicsLayerChromium* owner) + : CanvasLayerChromium(owner) + , m_drawingBuffer(drawingBuffer) +{ +} + +Canvas2DLayerChromium::~Canvas2DLayerChromium() +{ + if (m_textureId) + layerRendererContext()->deleteTexture(m_textureId); +} + +void Canvas2DLayerChromium::updateContentsIfDirty() +{ + if (!m_contentsDirty || !m_drawingBuffer) + return; + if (m_textureChanged) { // We have to generate a new backing texture. + GraphicsContext3D* context = layerRendererContext(); + if (m_textureId) + context->deleteTexture(m_textureId); + m_textureId = context->createTexture(); + context->activeTexture(GraphicsContext3D::TEXTURE0); + context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textureId); + IntSize size = m_drawingBuffer->size(); + context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, size.width(), size.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE); + // Set the min-mag filters to linear and wrap modes to GraphicsContext3D::CLAMP_TO_EDGE + // to get around NPOT texture limitations of GLES. + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_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); + m_textureChanged = false; + // FIXME: The finish() here is required because we have to make sure that the texture created in this + // context (the compositor context) is actually created by the service side before the child context + // attempts to use it (in publishToPlatformLayer). finish() is currently the only call with strong + // enough semantics to promise this, but is actually much stronger. Ideally we'd do something like + // inserting a fence here and waiting for it before trying to publish. + context->finish(); + } + // Update the contents of the texture used by the compositor. + if (m_contentsDirty) { + m_drawingBuffer->publishToPlatformLayer(); + m_contentsDirty = false; + } +} + +void Canvas2DLayerChromium::setTextureChanged() +{ + m_textureChanged = true; +} + +unsigned Canvas2DLayerChromium::textureId() const +{ + return m_textureId; +} + +void Canvas2DLayerChromium::setDrawingBuffer(DrawingBuffer* drawingBuffer) +{ + if (drawingBuffer != m_drawingBuffer) { + m_drawingBuffer = drawingBuffer; + m_textureChanged = true; + } +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.h b/Source/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.h new file mode 100644 index 0000000..44ef050 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/Canvas2DLayerChromium.h @@ -0,0 +1,65 @@ +/* + * 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 Canvas2DLayerChromium_h +#define Canvas2DLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CanvasLayerChromium.h" + +namespace WebCore { + +class DrawingBuffer; + +// A layer containing an accelerated 2d canvas +class Canvas2DLayerChromium : public CanvasLayerChromium { +public: + static PassRefPtr<Canvas2DLayerChromium> create(DrawingBuffer*, GraphicsLayerChromium* owner); + virtual ~Canvas2DLayerChromium(); + virtual bool drawsContent() { return true; } + virtual void updateContentsIfDirty(); + + void setTextureChanged(); + unsigned textureId() const; + void setDrawingBuffer(DrawingBuffer*); + +private: + explicit Canvas2DLayerChromium(DrawingBuffer*, GraphicsLayerChromium* owner); + DrawingBuffer* m_drawingBuffer; + + static unsigned m_shaderProgramId; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp new file mode 100644 index 0000000..4aef25b --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/CanvasLayerChromium.cpp @@ -0,0 +1,126 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) + +#include "CanvasLayerChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" + +namespace WebCore { + +unsigned CanvasLayerChromium::m_shaderProgramId = 0; + +CanvasLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_canvasShaderProgram(0) + , m_shaderSamplerLocation(-1) + , m_shaderMatrixLocation(-1) + , m_shaderAlphaLocation(-1) + , m_initialized(false) +{ + char vertexShaderString[] = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + + // Canvas layers need to be flipped vertically and their colors shouldn't be + // swizzled. + char fragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, vec2(v_texCoord.x, 1.0 - v_texCoord.y)); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" + "} \n"; + + m_canvasShaderProgram = createShaderProgram(m_context, vertexShaderString, fragmentShaderString); + if (!m_canvasShaderProgram) { + LOG_ERROR("CanvasLayerChromium: Failed to create shader program"); + return; + } + + m_shaderSamplerLocation = m_context->getUniformLocation(m_canvasShaderProgram, "s_texture"); + m_shaderMatrixLocation = m_context->getUniformLocation(m_canvasShaderProgram, "matrix"); + m_shaderAlphaLocation = m_context->getUniformLocation(m_canvasShaderProgram, "alpha"); + ASSERT(m_shaderSamplerLocation != -1); + ASSERT(m_shaderMatrixLocation != -1); + ASSERT(m_shaderAlphaLocation != -1); + + m_initialized = true; +} + +CanvasLayerChromium::SharedValues::~SharedValues() +{ + if (m_canvasShaderProgram) + GLC(m_context, m_context->deleteProgram(m_canvasShaderProgram)); +} + +CanvasLayerChromium::CanvasLayerChromium(GraphicsLayerChromium* owner) + : LayerChromium(owner) + , m_textureChanged(true) + , m_textureId(0) +{ +} + +CanvasLayerChromium::~CanvasLayerChromium() +{ +} + +void CanvasLayerChromium::draw() +{ + ASSERT(layerRenderer()); + const CanvasLayerChromium::SharedValues* sv = layerRenderer()->canvasLayerSharedValues(); + ASSERT(sv && sv->initialized()); + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textureId)); + layerRenderer()->useShader(sv->canvasShaderProgram()); + GLC(context, context->uniform1i(sv->shaderSamplerLocation(), 0)); + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), + bounds().width(), bounds().height(), drawOpacity(), + sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/CanvasLayerChromium.h b/Source/WebCore/platform/graphics/chromium/CanvasLayerChromium.h new file mode 100644 index 0000000..6520b55 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/CanvasLayerChromium.h @@ -0,0 +1,80 @@ +/* + * 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 CanvasLayerChromium_h +#define CanvasLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerChromium.h" + +namespace WebCore { + +// Base class for WebGL and accelerated 2d canvases. +class CanvasLayerChromium : public LayerChromium { +public: + virtual ~CanvasLayerChromium(); + + virtual void draw(); + + class SharedValues { + public: + explicit SharedValues(GraphicsContext3D*); + ~SharedValues(); + + unsigned canvasShaderProgram() const { return m_canvasShaderProgram; } + int shaderSamplerLocation() const { return m_shaderSamplerLocation; } + int shaderMatrixLocation() const { return m_shaderMatrixLocation; } + int shaderAlphaLocation() const { return m_shaderAlphaLocation; } + bool initialized() const { return m_initialized; } + + private: + GraphicsContext3D* m_context; + unsigned m_canvasShaderProgram; + int m_shaderSamplerLocation; + int m_shaderMatrixLocation; + int m_shaderAlphaLocation; + bool m_initialized; + }; + +protected: + explicit CanvasLayerChromium(GraphicsLayerChromium* owner); + bool m_textureChanged; + unsigned m_textureId; + +private: + static unsigned m_shaderProgramId; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // CanvasLayerChromium_h diff --git a/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp new file mode 100644 index 0000000..b5eda93 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp @@ -0,0 +1,426 @@ +/* + * 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" +#include "ComplexTextControllerLinux.h" + +#include "Font.h" + +#include <unicode/normlzr.h> + +namespace WebCore { + +// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't +// handle subpixel positioning so this function is used to truncate Harfbuzz +// values to a number of pixels. +static int truncateFixedPointToInteger(HB_Fixed value) +{ + return value >> 6; +} + +ComplexTextController::ComplexTextController(const TextRun& run, unsigned startingX, const Font* font) + : m_font(font) + , m_startingX(startingX) + , m_offsetX(m_startingX) + , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer)) + , m_iterateBackwards(m_run.rtl()) + , m_wordSpacingAdjustment(0) + , m_padding(0) + , m_padPerWordBreak(0) + , m_padError(0) + , m_letterSpacing(0) +{ + // Do not use |run| inside this constructor. Use |m_run| instead. + + memset(&m_item, 0, sizeof(m_item)); + // We cannot know, ahead of time, how many glyphs a given script run + // will produce. We take a guess that script runs will not produce more + // than twice as many glyphs as there are code points plus a bit of + // padding and fallback if we find that we are wrong. + createGlyphArrays((m_run.length() + 2) * 2); + + m_item.log_clusters = new unsigned short[m_run.length()]; + + m_item.face = 0; + m_item.font = allocHarfbuzzFont(); + + m_item.item.bidiLevel = m_run.rtl(); + + m_item.string = m_run.characters(); + m_item.stringLength = m_run.length(); + + reset(); +} + +ComplexTextController::~ComplexTextController() +{ + fastFree(m_item.font); + deleteGlyphArrays(); + delete[] m_item.log_clusters; +} + +bool ComplexTextController::isWordBreak(unsigned index) +{ + return index && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index - 1]); +} + +int ComplexTextController::determineWordBreakSpacing(unsigned logClustersIndex) +{ + int wordBreakSpacing = 0; + // The first half of the conjunction works around the case where + // output glyphs aren't associated with any codepoints by the + // clusters log. + if (logClustersIndex < m_item.item.length + && isWordBreak(m_item.item.pos + logClustersIndex)) { + wordBreakSpacing = m_wordSpacingAdjustment; + + if (m_padding > 0) { + int toPad = roundf(m_padPerWordBreak + m_padError); + m_padError += m_padPerWordBreak - toPad; + + if (m_padding < toPad) + toPad = m_padding; + m_padding -= toPad; + wordBreakSpacing += toPad; + } + } + return wordBreakSpacing; +} + +// setPadding sets a number of pixels to be distributed across the TextRun. +// WebKit uses this to justify text. +void ComplexTextController::setPadding(int padding) +{ + m_padding = padding; + if (!m_padding) + return; + + // If we have padding to distribute, then we try to give an equal + // amount to each space. The last space gets the smaller amount, if + // any. + unsigned numWordBreaks = 0; + + for (unsigned i = 0; i < m_item.stringLength; i++) { + if (isWordBreak(i)) + numWordBreaks++; + } + + if (numWordBreaks) + m_padPerWordBreak = m_padding / numWordBreaks; + else + m_padPerWordBreak = 0; +} + +void ComplexTextController::reset() +{ + if (m_iterateBackwards) + m_indexOfNextScriptRun = m_run.length() - 1; + else + m_indexOfNextScriptRun = 0; + m_offsetX = m_startingX; +} + +void ComplexTextController::setBackwardsIteration(bool isBackwards) +{ + m_iterateBackwards = isBackwards; + reset(); +} + +// Advance to the next script run, returning false when the end of the +// TextRun has been reached. +bool ComplexTextController::nextScriptRun() +{ + if (m_iterateBackwards) { + // In right-to-left mode we need to render the shaped glyph backwards and + // also render the script runs themselves backwards. So given a TextRun: + // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) + // we render: + // TTTTTTCAAAAAAA + // (and the glyphs in each A, C and T section are backwards too) + if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + m_currentFontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false).fontData; + } else { + if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + + // It is actually wrong to consider script runs at all in this code. + // Other WebKit code (e.g. Mac) segments complex text just by finding + // the longest span of text covered by a single font. + // But we currently need to call hb_utf16_script_run_next anyway to fill + // in the harfbuzz data structures to e.g. pick the correct script's shaper. + // So we allow that to run first, then do a second pass over the range it + // found and take the largest subregion that stays within a single font. + m_currentFontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false).fontData; + unsigned endOfRun; + for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) { + const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false).fontData; + if (nextFontData != m_currentFontData) + break; + } + m_item.item.length = endOfRun; + m_indexOfNextScriptRun = m_item.item.pos + endOfRun; + } + + setupFontForScriptRun(); + shapeGlyphs(); + setGlyphXPositions(rtl()); + + return true; +} + +float ComplexTextController::widthOfFullRun() +{ + float widthSum = 0; + while (nextScriptRun()) + widthSum += width(); + + return widthSum; +} + +void ComplexTextController::setupFontForScriptRun() +{ + const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false).fontData; + const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); + m_item.face = platformData.harfbuzzFace(); + void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); + m_item.font->userData = opaquePlatformData; +} + +HB_FontRec* ComplexTextController::allocHarfbuzzFont() +{ + HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); + memset(font, 0, sizeof(HB_FontRec)); + font->klass = &harfbuzzSkiaClass; + font->userData = 0; + // The values which harfbuzzSkiaClass returns are already scaled to + // pixel units, so we just set all these to one to disable further + // scaling. + font->x_ppem = 1; + font->y_ppem = 1; + font->x_scale = 1; + font->y_scale = 1; + + return font; +} + +void ComplexTextController::deleteGlyphArrays() +{ + delete[] m_item.glyphs; + delete[] m_item.attributes; + delete[] m_item.advances; + delete[] m_item.offsets; + delete[] m_glyphs16; + delete[] m_xPositions; +} + +void ComplexTextController::createGlyphArrays(int size) +{ + m_item.glyphs = new HB_Glyph[size]; + m_item.attributes = new HB_GlyphAttributes[size]; + m_item.advances = new HB_Fixed[size]; + m_item.offsets = new HB_FixedPoint[size]; + + m_glyphs16 = new uint16_t[size]; + m_xPositions = new SkScalar[size]; + + m_item.num_glyphs = size; + m_glyphsArrayCapacity = size; // Save the GlyphArrays size. + resetGlyphArrays(); +} + +void ComplexTextController::resetGlyphArrays() +{ + int size = m_item.num_glyphs; + // All the types here don't have pointers. It is safe to reset to + // zero unless Harfbuzz breaks the compatibility in the future. + memset(m_item.glyphs, 0, size * sizeof(HB_Glyph)); + memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes)); + memset(m_item.advances, 0, size * sizeof(HB_Fixed)); + memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint)); + memset(m_glyphs16, 0, size * sizeof(uint16_t)); + memset(m_xPositions, 0, size * sizeof(SkScalar)); +} + +void ComplexTextController::shapeGlyphs() +{ + // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to + // HB_ShapeItem() used less space than was available, the capacity of + // the array may be larger than the current value of m_item.num_glyphs. + // So, we need to reset the num_glyphs to the capacity of the array. + m_item.num_glyphs = m_glyphsArrayCapacity; + resetGlyphArrays(); + while (!HB_ShapeItem(&m_item)) { + // We overflowed our arrays. Resize and retry. + // HB_ShapeItem fills in m_item.num_glyphs with the needed size. + deleteGlyphArrays(); + // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer + // shaper (at least) can fail because of insufficient glyph buffers + // and request 0 additional glyphs: throwing us into an infinite + // loop. + createGlyphArrays(m_item.num_glyphs + 1); + } +} + +void ComplexTextController::setGlyphXPositions(bool isRTL) +{ + double position = 0; + // logClustersIndex indexes logClusters for the first (or last when + // RTL) codepoint of the current glyph. Each time we advance a glyph, + // we skip over all the codepoints that contributed to the current + // glyph. + int logClustersIndex = 0; + + if (isRTL) { + logClustersIndex = m_item.num_glyphs - 1; + + // Glyphs are stored in logical order, but for layout purposes we + // always go left to right. + for (int i = m_item.num_glyphs - 1; i >= 0; --i) { + if (!m_currentFontData->isZeroWidthSpaceGlyph(m_glyphs16[i])) { + // Whitespace must be laid out in logical order, so when inserting + // spaces in RTL (but iterating in LTR order) we must insert spaces + // _before_ the next glyph. + if (static_cast<unsigned>(i + 1) >= m_item.num_glyphs || m_item.attributes[i + 1].clusterStart) + position += m_letterSpacing; + + position += determineWordBreakSpacing(logClustersIndex); + } + + m_glyphs16[i] = m_item.glyphs[i]; + double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); + m_xPositions[i] = m_offsetX + position + offsetX; + + while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) + logClustersIndex--; + + if (!m_currentFontData->isZeroWidthSpaceGlyph(m_glyphs16[i])) + position += truncateFixedPointToInteger(m_item.advances[i]); + } + } else { + for (size_t i = 0; i < m_item.num_glyphs; ++i) { + m_glyphs16[i] = m_item.glyphs[i]; + double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); + m_xPositions[i] = m_offsetX + position + offsetX; + + if (m_currentFontData->isZeroWidthSpaceGlyph(m_glyphs16[i])) + continue; + + double advance = truncateFixedPointToInteger(m_item.advances[i]); + + advance += determineWordBreakSpacing(logClustersIndex); + + if (m_item.attributes[i].clusterStart) + advance += m_letterSpacing; + + while (static_cast<unsigned>(logClustersIndex) < m_item.item.length && logClusters()[logClustersIndex] == i) + logClustersIndex++; + + position += advance; + } + } + m_pixelWidth = std::max(position, 0.0); + m_offsetX += m_pixelWidth; +} + +void ComplexTextController::normalizeSpacesAndMirrorChars(const UChar* source, bool rtl, UChar* destination, int length) +{ + int position = 0; + bool error = false; + // Iterate characters in source and mirror character if needed. + while (position < length) { + UChar32 character; + int nextPosition = position; + U16_NEXT(source, nextPosition, length, character); + if (Font::treatAsSpace(character)) + character = ' '; + else if (Font::treatAsZeroWidthSpace(character)) + character = zeroWidthSpace; + else if (rtl) + character = u_charMirror(character); + U16_APPEND(destination, position, length, character, error); + ASSERT(!error); + position = nextPosition; + } +} + +const TextRun& ComplexTextController::getNormalizedTextRun(const TextRun& originalRun, OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer) +{ + // Normalize the text run in three ways: + // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks + // (U+0300..) are used in the run. This conversion is necessary since most OpenType + // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in + // their GSUB tables. + // + // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since + // the API returns FALSE (= not normalized) for complex runs that don't require NFC + // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, + // Harfbuzz will do the same thing for us using the GSUB table. + // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs + // for characters like '\n' otherwise. + // 3) Convert mirrored characters such as parenthesis for rtl text. + + // Convert to NFC form if the text has diacritical marks. + icu::UnicodeString normalizedString; + UErrorCode error = U_ZERO_ERROR; + + for (int16_t i = 0; i < originalRun.length(); ++i) { + UChar ch = originalRun[i]; + if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { + icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), + originalRun.length()), UNORM_NFC, 0 /* no options */, + normalizedString, error); + if (U_FAILURE(error)) + return originalRun; + break; + } + } + + // Normalize space and mirror parenthesis for rtl text. + int normalizedBufferLength; + const UChar* sourceText; + if (normalizedString.isEmpty()) { + normalizedBufferLength = originalRun.length(); + sourceText = originalRun.characters(); + } else { + normalizedBufferLength = normalizedString.length(); + sourceText = normalizedString.getBuffer(); + } + + normalizedBuffer.set(new UChar[normalizedBufferLength + 1]); + + normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(), normalizedBufferLength); + + normalizedRun.set(new TextRun(originalRun)); + normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength); + return *normalizedRun; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h new file mode 100644 index 0000000..4ebbd89 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h @@ -0,0 +1,164 @@ +/* + * 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 ComplexTextControllerLinux_h +#define ComplexTextControllerLinux_h + +#include "HarfbuzzSkia.h" +#include "SkScalar.h" +#include "TextRun.h" + +#include <unicode/uchar.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class Font; +class FontPlatformData; +class SimpleFontData; + +// ComplexTextController walks a TextRun and presents each script run in sequence. A +// TextRun is a sequence of code-points with the same embedding level (i.e. they +// are all left-to-right or right-to-left). A script run is a subsequence where +// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is +// only ever done with script runs since the shapers only know how to deal with +// a single script. +// +// After creating it, the script runs are either iterated backwards or forwards. +// It defaults to backwards for RTL and forwards otherwise (which matches the +// presentation order), however you can set it with |setBackwardsIteration|. +// +// Once you have setup the object, call |nextScriptRun| to get the first script +// run. This will return false when the iteration is complete. At any time you +// can call |reset| to start over again. +class ComplexTextController { +public: + ComplexTextController(const TextRun&, unsigned, const Font*); + ~ComplexTextController(); + + bool isWordBreak(unsigned); + int determineWordBreakSpacing(unsigned); + // setPadding sets a number of pixels to be distributed across the TextRun. + // WebKit uses this to justify text. + void setPadding(int); + void reset(); + void setBackwardsIteration(bool); + // Advance to the next script run, returning false when the end of the + // TextRun has been reached. + bool nextScriptRun(); + float widthOfFullRun(); + + // setWordSpacingAdjustment sets a delta (in pixels) which is applied at + // each word break in the TextRun. + void setWordSpacingAdjustment(int wordSpacingAdjustment) { m_wordSpacingAdjustment = wordSpacingAdjustment; } + + // setLetterSpacingAdjustment sets an additional number of pixels that is + // added to the advance after each output cluster. This matches the behaviour + // of WidthIterator::advance. + void setLetterSpacingAdjustment(int letterSpacingAdjustment) { m_letterSpacing = letterSpacingAdjustment; } + int letterSpacing() const { return m_letterSpacing; } + + // Set the x offset for the next script run. This affects the values in + // |xPositions| + void setXOffsetToZero() { m_offsetX = 0; } + bool rtl() const { return m_run.rtl(); } + const uint16_t* glyphs() const { return m_glyphs16; } + + // Return the length of the array returned by |glyphs| + const unsigned length() const { return m_item.num_glyphs; } + + // Return the x offset for each of the glyphs. Note that this is translated + // by the current x offset and that the x offset is updated for each script + // run. + const SkScalar* xPositions() const { return m_xPositions; } + + // Get the advances (widths) for each glyph. + const HB_Fixed* advances() const { return m_item.advances; } + + // Return the width (in px) of the current script run. + const unsigned width() const { return m_pixelWidth; } + + // Return the cluster log for the current script run. For example: + // script run: f i a n c é (fi gets ligatured) + // log clutrs: 0 0 1 2 3 4 + // So, for each input code point, the log tells you which output glyph was + // generated for it. + const unsigned short* logClusters() const { return m_item.log_clusters; } + + // return the number of code points in the current script run + const unsigned numCodePoints() const { return m_numCodePoints; } + + const FontPlatformData* fontPlatformDataForScriptRun() { return reinterpret_cast<FontPlatformData*>(m_item.font->userData); } + +private: + void setupFontForScriptRun(); + HB_FontRec* allocHarfbuzzFont(); + void deleteGlyphArrays(); + void createGlyphArrays(int); + void resetGlyphArrays(); + void shapeGlyphs(); + void setGlyphXPositions(bool); + + static void normalizeSpacesAndMirrorChars(const UChar* source, bool rtl, UChar* destination, int length); + static const TextRun& getNormalizedTextRun(const TextRun& originalRun, OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer); + + // This matches the logic in RenderBlock::findNextLineBreak + static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; } + + const Font* const m_font; + const SimpleFontData* m_currentFontData; + HB_ShaperItem m_item; + uint16_t* m_glyphs16; // A vector of 16-bit glyph ids. + SkScalar* m_xPositions; // A vector of x positions for each glyph. + ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. + const unsigned m_startingX; // Offset in pixels of the first script run. + unsigned m_offsetX; // Offset in pixels to the start of the next script run. + unsigned m_pixelWidth; // Width (in px) of the current script run. + unsigned m_numCodePoints; // Code points in current script run. + unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays. + + OwnPtr<TextRun> m_normalizedRun; + OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run. + const TextRun& m_run; + bool m_iterateBackwards; + int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break. + float m_padding; // pixels to be distributed over the line at word breaks. + float m_padPerWordBreak; // pixels to be added to each word break. + float m_padError; // |m_padPerWordBreak| might have a fractional component. + // Since we only add a whole number of padding pixels at + // each word break we accumulate error. This is the + // number of pixels that we are behind so far. + int m_letterSpacing; // pixels to be added after each glyph. +}; + +} // namespace WebCore + +#endif // ComplexTextControllerLinux_h diff --git a/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp new file mode 100644 index 0000000..d00faf8 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp @@ -0,0 +1,368 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) + +#include "ContentLayerChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#include "LayerTexture.h" +#include "RenderLayerBacking.h" + +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#include "SkColorPriv.h" +#include "skia/ext/platform_canvas.h" +#elif PLATFORM(CG) +#include <CoreGraphics/CGBitmapContext.h> +#endif + +namespace WebCore { + +ContentLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_contentShaderProgram(0) + , m_shaderSamplerLocation(-1) + , m_shaderMatrixLocation(-1) + , m_shaderAlphaLocation(-1) + , m_initialized(false) +{ + // Shaders for drawing the layer contents. + char vertexShaderString[] = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + +#if PLATFORM(SKIA) + // Color is in RGBA order. + char rgbaFragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor = texColor * alpha; \n" + "} \n"; +#endif + + // Color is in BGRA order. + char bgraFragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha; \n" + "} \n"; + +#if PLATFORM(SKIA) + // Assuming the packing is either Skia default RGBA or Chromium default BGRA. + char* fragmentShaderString = SK_B32_SHIFT ? rgbaFragmentShaderString : bgraFragmentShaderString; +#else + char* fragmentShaderString = bgraFragmentShaderString; +#endif + m_contentShaderProgram = createShaderProgram(m_context, vertexShaderString, fragmentShaderString); + if (!m_contentShaderProgram) { + LOG_ERROR("ContentLayerChromium: Failed to create shader program"); + return; + } + + m_shaderSamplerLocation = m_context->getUniformLocation(m_contentShaderProgram, "s_texture"); + m_shaderMatrixLocation = m_context->getUniformLocation(m_contentShaderProgram, "matrix"); + m_shaderAlphaLocation = m_context->getUniformLocation(m_contentShaderProgram, "alpha"); + ASSERT(m_shaderSamplerLocation != -1); + ASSERT(m_shaderMatrixLocation != -1); + ASSERT(m_shaderAlphaLocation != -1); + + m_initialized = true; +} + +ContentLayerChromium::SharedValues::~SharedValues() +{ + if (m_contentShaderProgram) + GLC(m_context, m_context->deleteProgram(m_contentShaderProgram)); +} + + +PassRefPtr<ContentLayerChromium> ContentLayerChromium::create(GraphicsLayerChromium* owner) +{ + return adoptRef(new ContentLayerChromium(owner)); +} + +ContentLayerChromium::ContentLayerChromium(GraphicsLayerChromium* owner) + : LayerChromium(owner) + , m_contentsTexture(0) + , m_skipsDraw(false) +{ +} + +ContentLayerChromium::~ContentLayerChromium() +{ + cleanupResources(); +} + +void ContentLayerChromium::cleanupResources() +{ + LayerChromium::cleanupResources(); + m_contentsTexture.clear(); +} + +bool ContentLayerChromium::requiresClippedUpdateRect() const +{ + // To avoid allocating excessively large textures, switch into "large layer mode" if + // one of the layer's dimensions is larger than 2000 pixels or the size of + // surface it's rendering into. This is a temporary measure until layer tiling is implemented. + static const int maxLayerSize = 2000; + return (m_bounds.width() > max(maxLayerSize, m_targetRenderSurface->contentRect().width()) + || m_bounds.height() > max(maxLayerSize, m_targetRenderSurface->contentRect().height()) + || !layerRenderer()->checkTextureSize(m_bounds)); +} + +void ContentLayerChromium::updateContentsIfDirty() +{ + RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client()); + if (!backing || backing->paintingGoesToWindow()) + return; + + ASSERT(drawsContent()); + + ASSERT(layerRenderer()); + + void* pixels = 0; + IntRect dirtyRect; + IntRect updateRect; + IntSize requiredTextureSize; + IntSize bitmapSize; + IntRect boundsRect(IntPoint(0, 0), m_bounds); + + // FIXME: Remove this test when tiled layers are implemented. + if (requiresClippedUpdateRect()) { + // A layer with 3D transforms could require an arbitrarily large number + // of texels to be repainted, so ignore these layers until tiling is + // implemented. + if (!drawTransform().isIdentityOrTranslation()) { + m_skipsDraw = true; + return; + } + + // Calculate the region of this layer that is currently visible. + const IntRect clipRect = m_targetRenderSurface->contentRect(); + + TransformationMatrix layerOriginTransform = drawTransform(); + layerOriginTransform.translate3d(-0.5 * m_bounds.width(), -0.5 * m_bounds.height(), 0); + + // For now we apply the large layer treatment only for layers that are either untransformed + // or are purely translated. Their matrix is expected to be invertible. + ASSERT(layerOriginTransform.isInvertible()); + + TransformationMatrix targetToLayerMatrix = layerOriginTransform.inverse(); + IntRect visibleRectInLayerCoords = targetToLayerMatrix.mapRect(clipRect); + visibleRectInLayerCoords.intersect(IntRect(0, 0, m_bounds.width(), m_bounds.height())); + + // For normal layers, the center of the texture corresponds with the center of the layer. + // In large layers the center of the texture is the center of the visible region so we have + // to keep track of the offset in order to render correctly. + IntRect visibleRectInSurfaceCoords = layerOriginTransform.mapRect(visibleRectInLayerCoords); + m_layerCenterInSurfaceCoords = FloatRect(visibleRectInSurfaceCoords).center(); + + // If this is still too large to render, then skip the layer completely. + if (!layerRenderer()->checkTextureSize(visibleRectInLayerCoords.size())) { + m_skipsDraw = true; + return; + } + + // If the visible portion of the layer is different from the last upload, or if our backing + // texture has been evicted, then the whole layer is considered dirty. + if (visibleRectInLayerCoords != m_visibleRectInLayerCoords || !m_contentsTexture || !m_contentsTexture->isValid(requiredTextureSize, GraphicsContext3D::RGBA)) + m_dirtyRect = boundsRect; + m_visibleRectInLayerCoords = visibleRectInLayerCoords; + + // Calculate the portion of the dirty rectangle that is visible. m_dirtyRect is in layer space. + IntRect visibleDirtyRectInLayerSpace = enclosingIntRect(m_dirtyRect); + visibleDirtyRectInLayerSpace.intersect(visibleRectInLayerCoords); + + // What the rectangles mean: + // dirtyRect: The region of this layer that will be updated. + // updateRect: The region of the layer's texture that will be uploaded into. + // requiredTextureSize: is the required size of this layer's texture. + dirtyRect = visibleDirtyRectInLayerSpace; + updateRect = dirtyRect; + IntSize visibleRectOffsetInLayerCoords(visibleRectInLayerCoords.x(), visibleRectInLayerCoords.y()); + updateRect.move(-visibleRectOffsetInLayerCoords); + requiredTextureSize = visibleRectInLayerCoords.size(); + } else { + dirtyRect = IntRect(m_dirtyRect); + requiredTextureSize = m_bounds; + // If the texture needs to be reallocated then we must redraw the entire + // contents of the layer. + if (!m_contentsTexture || !m_contentsTexture->isValid(requiredTextureSize, GraphicsContext3D::RGBA)) + dirtyRect = boundsRect; + else { + // Clip the dirtyRect to the size of the layer to avoid drawing + // outside the bounds of the backing texture. + dirtyRect.intersect(boundsRect); + } + updateRect = dirtyRect; + } + + if (dirtyRect.isEmpty()) + return; + +#if PLATFORM(SKIA) + const SkBitmap* skiaBitmap = 0; + OwnPtr<skia::PlatformCanvas> canvas; + OwnPtr<PlatformContextSkia> skiaContext; + OwnPtr<GraphicsContext> graphicsContext; + + canvas.set(new skia::PlatformCanvas(dirtyRect.width(), dirtyRect.height(), false)); + skiaContext.set(new PlatformContextSkia(canvas.get())); + + // This is needed to get text to show up correctly. + // FIXME: Does this take us down a very slow text rendering path? + skiaContext->setDrawingToImageBuffer(true); + + graphicsContext.set(new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(skiaContext.get()))); + + // Bring the canvas into the coordinate system of the paint rect. + canvas->translate(static_cast<SkScalar>(-dirtyRect.x()), static_cast<SkScalar>(-dirtyRect.y())); + + m_owner->paintGraphicsLayerContents(*graphicsContext, dirtyRect); + const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(false); + skiaBitmap = &bitmap; + ASSERT(skiaBitmap); + + SkAutoLockPixels lock(*skiaBitmap); + SkBitmap::Config skiaConfig = skiaBitmap->config(); + // FIXME: do we need to support more image configurations? + if (skiaConfig == SkBitmap::kARGB_8888_Config) { + pixels = skiaBitmap->getPixels(); + bitmapSize = IntSize(skiaBitmap->width(), skiaBitmap->height()); + } +#elif PLATFORM(CG) + Vector<uint8_t> tempVector; + int rowBytes = 4 * dirtyRect.width(); + tempVector.resize(rowBytes * dirtyRect.height()); + memset(tempVector.data(), 0, tempVector.size()); + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGContextRef> contextCG(AdoptCF, CGBitmapContextCreate(tempVector.data(), + dirtyRect.width(), dirtyRect.height(), 8, rowBytes, + colorSpace.get(), + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + CGContextTranslateCTM(contextCG.get(), 0, dirtyRect.height()); + CGContextScaleCTM(contextCG.get(), 1, -1); + + GraphicsContext graphicsContext(contextCG.get()); + + // Translate the graphics context into the coordinate system of the dirty rect. + graphicsContext.translate(-dirtyRect.x(), -dirtyRect.y()); + + m_owner->paintGraphicsLayerContents(graphicsContext, dirtyRect); + + pixels = tempVector.data(); + bitmapSize = dirtyRect.size(); +#else +#error "Need to implement for your platform." +#endif + + if (pixels) + updateTextureRect(pixels, requiredTextureSize, updateRect); +} + +void ContentLayerChromium::updateTextureRect(void* pixels, const IntSize& requiredTextureSize, const IntRect& updateRect) +{ + if (!pixels) + return; + + GraphicsContext3D* context = layerRendererContext(); + if (!m_contentsTexture) + m_contentsTexture = LayerTexture::create(context, layerRenderer()->textureManager()); + + if (!m_contentsTexture->reserve(requiredTextureSize, GraphicsContext3D::RGBA)) { + m_skipsDraw = true; + return; + } + + m_contentsTexture->bindTexture(); + + GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels)); + + m_dirtyRect.setSize(FloatSize()); + // Large layers always stay dirty, because they need to update when the content rect changes. + m_contentsDirty = requiresClippedUpdateRect(); +} + +void ContentLayerChromium::draw() +{ + if (m_skipsDraw) + return; + + ASSERT(layerRenderer()); + + const ContentLayerChromium::SharedValues* sv = layerRenderer()->contentLayerSharedValues(); + ASSERT(sv && sv->initialized()); + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); + m_contentsTexture->bindTexture(); + layerRenderer()->useShader(sv->contentShaderProgram()); + GLC(context, context->uniform1i(sv->shaderSamplerLocation(), 0)); + + if (requiresClippedUpdateRect()) { + float m43 = drawTransform().m43(); + TransformationMatrix transform; + transform.translate3d(m_layerCenterInSurfaceCoords.x(), m_layerCenterInSurfaceCoords.y(), m43); + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), + transform, m_visibleRectInLayerCoords.width(), + m_visibleRectInLayerCoords.height(), drawOpacity(), + sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + } else { + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), + drawTransform(), m_bounds.width(), m_bounds.height(), + drawOpacity(), sv->shaderMatrixLocation(), + sv->shaderAlphaLocation()); + } + m_contentsTexture->unreserve(); +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h new file mode 100644 index 0000000..dc1630b --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h @@ -0,0 +1,99 @@ +/* + * 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 ContentLayerChromium_h +#define ContentLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerChromium.h" +#include "TextureManager.h" + +namespace WebCore { + +class LayerTexture; + +// A Layer that requires a GraphicsContext to render its contents. +class ContentLayerChromium : public LayerChromium { + friend class LayerRendererChromium; +public: + static PassRefPtr<ContentLayerChromium> create(GraphicsLayerChromium* owner = 0); + + virtual ~ContentLayerChromium(); + + virtual void updateContentsIfDirty(); + virtual void draw(); + virtual bool drawsContent() { return m_owner && m_owner->drawsContent(); } + + // Stores values that are shared between instances of this class that are + // associated with the same LayerRendererChromium (and hence the same GL + // context). + class SharedValues { + public: + explicit SharedValues(GraphicsContext3D*); + ~SharedValues(); + + unsigned contentShaderProgram() const { return m_contentShaderProgram; } + int shaderSamplerLocation() const { return m_shaderSamplerLocation; } + int shaderMatrixLocation() const { return m_shaderMatrixLocation; } + int shaderAlphaLocation() const { return m_shaderAlphaLocation; } + int initialized() const { return m_initialized; } + + private: + GraphicsContext3D* m_context; + unsigned m_contentShaderProgram; + int m_shaderSamplerLocation; + int m_shaderMatrixLocation; + int m_shaderAlphaLocation; + int m_initialized; + }; + +protected: + explicit ContentLayerChromium(GraphicsLayerChromium* owner); + + void updateTextureRect(void* pixels, const IntSize& requiredTextureSize, const IntRect& updateRect); + + virtual void cleanupResources(); + bool requiresClippedUpdateRect() const; + + OwnPtr<LayerTexture> m_contentsTexture; + bool m_skipsDraw; + +private: + + IntRect m_visibleRectInLayerCoords; + FloatPoint m_layerCenterInSurfaceCoords; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/CrossProcessFontLoading.h b/Source/WebCore/platform/graphics/chromium/CrossProcessFontLoading.h new file mode 100644 index 0000000..e1fb740 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/CrossProcessFontLoading.h @@ -0,0 +1,100 @@ +/* + * 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 CrossProcessFontLoading_h +#define CrossProcessFontLoading_h + +#import <wtf/RefCounted.h> +#import <wtf/RetainPtr.h> + +typedef struct CGFont* CGFontRef; +typedef UInt32 ATSFontContainerRef; +typedef UInt32 ATSFontRef; + +namespace WebCore { + +// MemoryActivatedFont encapsulates a font loaded from another process and +// activated from memory. +// +// Responsibilities: +// * Holder for the CGFontRef & ATSFontRef belonging to the activated font. +// * Responsible for unloading the font container when done. +// +// Memory Management: +// The class is reference counted, with each instance of FontPlatformData that +// uses this class holding a reference to it. +// Entries are kept track of internally in a hash to allow quick lookup +// of existing instances for reuse: +// - fontCacheBySrcFontContainerRef() - key is the ATSFontContainerRef +// corresponding to the *original in-process NSFont* whose loading was blocked +// by the sandbox. +// This is needed to allow lookup of a pre-existing MemoryActivatedFont when +// creating a new FontPlatformData object. +// +// Assumptions: +// This code assumes that an ATSFontRef is a unique identifier tied to an +// activated font. i.e. After we activate a font, its ATSFontRef doesn't +// change. +// It also assumes that the ATSFoncontainerRef for two in-memory NSFonts that +// correspond to the same on-disk font file are always the same and don't change +// with time. +// +// Flushing caches: +// When the system notifies us of a system font cache flush, all FontDataCache +// objects are destroyed. This should in turn dereference all +// MemoryActivatedFonts and thus unload all in-memory fonts. +class MemoryActivatedFont : public RefCounted<MemoryActivatedFont> { +public: + // Use to create a new object, see docs on constructor below. + static PassRefPtr<MemoryActivatedFont> create(ATSFontContainerRef srcFontContainerRef, ATSFontContainerRef container); + ~MemoryActivatedFont(); + + // Get cached CGFontRef corresponding to the in-memory font. + CGFontRef cgFont() { return m_cgFont.get(); } + + // Get cached ATSFontRef corresponding to the in-memory font. + ATSFontRef atsFontRef() { return m_atsFontRef; } + +private: + // srcFontRef - ATSFontRef belonging to the NSFont object that failed to + // load in-process. + // container - a font container corresponding to an identical font that + // we loaded cross-process. + MemoryActivatedFont(ATSFontContainerRef srcFontContainerRef, ATSFontContainerRef container); + + ATSFontContainerRef m_fontContainer; + WTF::RetainPtr<CGFontRef> m_cgFont; + ATSFontRef m_atsFontRef; + ATSFontContainerRef m_srcFontContainerRef; +}; + +} // namespace WebCore + +#endif // CrossProcessFontLoading_h diff --git a/Source/WebCore/platform/graphics/chromium/CrossProcessFontLoading.mm b/Source/WebCore/platform/graphics/chromium/CrossProcessFontLoading.mm new file mode 100644 index 0000000..72e3369 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/CrossProcessFontLoading.mm @@ -0,0 +1,210 @@ +/* + * This file is part of the internal font implementation. + * + * Copyright (c) 2010 Google 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. + * + */ + +// This file provides additional functionality to the Mac FontPlatformData class +// defined in WebCore/platform/cocoa/FontPlatformDataCocoa.mm . +// Because we want to support loading fonts between processes in the face of +// font loading being blocked by the sandbox, we need a mechnasim to both +// do the loading of in-memory fonts and keep track of them. + +#import "config.h" +#import "CrossProcessFontLoading.h" + +#import "../graphics/cocoa/FontPlatformData.h" +#import "ChromiumBridge.h" +#import <AppKit/NSFont.h> +#import <wtf/HashMap.h> + +namespace WebCore { + +namespace { + +typedef HashMap<ATSFontContainerRef, MemoryActivatedFont*> FontContainerRefMemoryFontHash; + +// On 10.5, font loading is not blocked by the sandbox and thus there is no +// need for the cross-process font loading mechanim. +// On system versions >=10.6 cross-process font loading is required. +bool OutOfProcessFontLoadingEnabled() +{ + static SInt32 systemVersion = 0; + if (!systemVersion) { + if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr) + return false; + } + + return systemVersion >= 0x1060; +} + +FontContainerRefMemoryFontHash& fontCacheBySrcFontContainerRef() +{ + DEFINE_STATIC_LOCAL(FontContainerRefMemoryFontHash, srcFontRefCache, ()); + return srcFontRefCache; +} + +ATSFontContainerRef fontContainerRefFromNSFont(NSFont* srcFont) +{ + ATSFontRef fontRef = CTFontGetPlatformFont(toCTFontRef(srcFont), 0); + if (!fontRef) + return kATSFontContainerRefUnspecified; + ATSFontContainerRef fontContainer = kATSFontContainerRefUnspecified; + if (ATSFontGetContainer(fontRef, 0, &fontContainer) != noErr) + return kATSFontContainerRefUnspecified; + return fontContainer; +} + +// The only way we can tell that an in-process font has failed to load +// is if CTFontCopyGraphicsFont() returns the LastResort font. +bool isLastResortFont(CGFontRef cgFont) +{ + NSString* fontName = (NSString*)CGFontCopyPostScriptName(cgFont); + return [fontName isEqualToString:@"LastResort"]; +} + +// Given an in-process font which has failed to load, return a +// MemoryActivatedFont* corresponding to an in-memory representation of the +// same font loaded from the browser process. +// On failure this function returns a PassRefPtr pointing to 0. +PassRefPtr<MemoryActivatedFont> loadFontFromBrowserProcess(NSFont* nsFont) +{ + ATSFontContainerRef container; + // Send cross-process request to load font. + if (!ChromiumBridge::loadFont(nsFont, &container)) + return 0; + + ATSFontContainerRef srcFontContainerRef = fontContainerRefFromNSFont(nsFont); + if (!srcFontContainerRef) { + ATSFontDeactivate(container, 0, kATSOptionFlagsDefault); + return 0; + } + + PassRefPtr<MemoryActivatedFont> font = adoptRef(fontCacheBySrcFontContainerRef().get(srcFontContainerRef)); + if (font.get()) + return font; + + return MemoryActivatedFont::create(srcFontContainerRef, container); +} + +} // namespace + +PassRefPtr<MemoryActivatedFont> MemoryActivatedFont::create(ATSFontContainerRef srcFontContainerRef, ATSFontContainerRef container) +{ + MemoryActivatedFont* font = new MemoryActivatedFont(srcFontContainerRef, container); + if (!font->cgFont()) // Object construction failed. + { + delete font; + return 0; + } + return adoptRef(font); +} + +MemoryActivatedFont::MemoryActivatedFont(ATSFontContainerRef srcFontContainerRef, ATSFontContainerRef container) + : m_fontContainer(container) + , m_atsFontRef(kATSFontRefUnspecified) + , m_srcFontContainerRef(srcFontContainerRef) +{ + if (!container) + return; + + // Count the number of fonts in the container. + ItemCount fontCount = 0; + OSStatus err = ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 0, 0, &fontCount); + if (err != noErr || fontCount < 1) + return; + + // For now always assume that we want the first font in the container. + ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 1, &m_atsFontRef, 0); + + if (!m_atsFontRef) + return; + + // Cache CGFont representation of the font. + m_cgFont.adoptCF(CGFontCreateWithPlatformFont(&m_atsFontRef)); + + if (!m_cgFont.get()) + return; + + // Add ourselves to cache. + fontCacheBySrcFontContainerRef().add(m_srcFontContainerRef, this); +} + +// Destructor - Unload font container from memory and remove ourselves +// from cache. +MemoryActivatedFont::~MemoryActivatedFont() +{ + if (m_cgFont.get()) { + // First remove ourselves from the caches. + ASSERT(fontCacheBySrcFontContainerRef().contains(m_srcFontContainerRef)); + + fontCacheBySrcFontContainerRef().remove(m_srcFontContainerRef); + + // Make sure the CGFont is destroyed before its font container. + m_cgFont.releaseRef(); + } + + if (m_fontContainer != kATSFontContainerRefUnspecified) + ATSFontDeactivate(m_fontContainer, 0, kATSOptionFlagsDefault); +} + +// Given an NSFont, try to load a representation of that font into the cgFont +// parameter. If loading is blocked by the sandbox, the font may be loaded +// cross-process. +// If sandbox loading also fails, a fallback font is loaded. +// +// Considerations: +// * cgFont must be CFRelease()ed by the caller when done. +// +// Parameters: +// * nsFont - The font we wish to load. +// * fontSize - point size of the font we wish to load. +// * outNSFont - The font that was actually loaded, may be different from nsFont +// if a fallback font was used. +// * cgFont - on output this contains the CGFontRef corresponding to the NSFont +// that was picked in the end. The caller is responsible for calling +// CFRelease() on this parameter when done with it. +// * fontID - on output, the ID corresponding to nsFont. +void FontPlatformData::loadFont(NSFont* nsFont, float fontSize, NSFont*& outNSFont, CGFontRef& cgFont) +{ + outNSFont = nsFont; + cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); + if (OutOfProcessFontLoadingEnabled() && outNSFont && cgFont && isLastResortFont(cgFont)) { + // Release old CGFontRef since it points at the LastResort font which we don't want. + CFRelease(cgFont); + cgFont = 0; + + // Font loading was blocked by the Sandbox. + m_inMemoryFont = loadFontFromBrowserProcess(outNSFont); + if (m_inMemoryFont.get()) { + cgFont = m_inMemoryFont->cgFont(); + + // Need to add an extra retain so output semantics of this function + // are consistent. + CFRetain(cgFont); + } else { + // If we still can't load the font, then return Times, + // rather than the LastResort font. + outNSFont = [NSFont fontWithName:@"Times" size:fontSize]; + cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); + } + } +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp b/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp new file mode 100644 index 0000000..507c227 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp @@ -0,0 +1,148 @@ +/* + * 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" + +#include "DrawingBuffer.h" + +#include "Extensions3DChromium.h" +#include "GraphicsContext3D.h" +#include "SharedGraphicsContext3D.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "Canvas2DLayerChromium.h" +#endif + +namespace WebCore { + +struct DrawingBufferInternal { + unsigned offscreenColorTexture; +#if USE(ACCELERATED_COMPOSITING) + RefPtr<Canvas2DLayerChromium> platformLayer; +#endif +}; + +static unsigned generateColorTexture(GraphicsContext3D* context, const IntSize& size) +{ + unsigned offscreenColorTexture = context->createTexture(); + if (!offscreenColorTexture) + return 0; + + context->bindTexture(GraphicsContext3D::TEXTURE_2D, offscreenColorTexture); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::NEAREST); + context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST); + 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->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, size.width(), size.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE); + context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, offscreenColorTexture, 0); + + return offscreenColorTexture; +} + + +DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, const IntSize& size) + : m_context(context) + , m_size(size) + , m_fbo(0) + , m_colorBuffer(0) + , m_depthStencilBuffer(0) + , m_multisampleFBO(0) + , m_multisampleColorBuffer(0) + , m_multisampleDepthStencilBuffer(0) + , m_internal(new DrawingBufferInternal) +{ + if (!m_context->getExtensions()->supports("GL_CHROMIUM_copy_texture_to_parent_texture")) { + m_context.clear(); + return; + } + m_fbo = context->createFramebuffer(); + context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + m_colorBuffer = generateColorTexture(context, size); +} + +DrawingBuffer::~DrawingBuffer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_internal->platformLayer) + m_internal->platformLayer->setDrawingBuffer(0); +#endif + + if (!m_context) + return; + + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + m_context->deleteTexture(m_colorBuffer); + + clear(); +} + +#if USE(ACCELERATED_COMPOSITING) +void DrawingBuffer::publishToPlatformLayer() +{ + if (!m_context) + return; + + if (m_callback) + m_callback->willPublish(); + unsigned parentTexture = m_internal->platformLayer->textureId(); + // FIXME: We do the copy in the canvas' (child) context so that it executes in the correct order relative to + // other commands in the child context. This ensures that the parent texture always contains a complete + // frame and not some intermediate result. However, there is no synchronization to ensure that this copy + // happens before the compositor draws. This means we might draw stale frames sometimes. Ideally this + // would insert a fence into the child command stream that the compositor could wait for. + m_context->makeContextCurrent(); + static_cast<Extensions3DChromium*>(m_context->getExtensions())->copyTextureToParentTextureCHROMIUM(m_colorBuffer, parentTexture); + m_context->flush(); +} +#endif + +void DrawingBuffer::didReset() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_internal->platformLayer) + m_internal->platformLayer->setTextureChanged(); +#endif +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* DrawingBuffer::platformLayer() +{ + if (!m_internal->platformLayer) + m_internal->platformLayer = Canvas2DLayerChromium::create(this, 0); + return m_internal->platformLayer.get(); +} +#endif + +Platform3DObject DrawingBuffer::platformColorBuffer() const +{ + return m_colorBuffer; +} + +} diff --git a/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h b/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h new file mode 100644 index 0000000..d120424 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Extensions3DChromium_h +#define Extensions3DChromium_h + +#include "Extensions3D.h" + +namespace WebCore { + +class GraphicsContext3DInternal; + +class Extensions3DChromium : public Extensions3D { +public: + virtual ~Extensions3DChromium(); + + // Supported extensions: + // GL_CHROMIUM_resource_safe : indicating that textures/renderbuffers are always initialized before read/write. + // GL_CHROMIUM_strict_attribs : indicating a GL error is generated for out-of-bounds buffer accesses. + + // Extensions3D methods. + virtual bool supports(const String&); + virtual void ensureEnabled(const String&); + virtual int getGraphicsResetStatusARB(); + 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) { } + + enum { + // GL_CHROMIUM_map_sub (enums inherited from GL_ARB_vertex_buffer_object) + READ_ONLY = 0x88B8, + WRITE_ONLY = 0x88B9 + }; + + // GL_CHROMIUM_map_sub + void* mapBufferSubDataCHROMIUM(unsigned target, int offset, int size, unsigned access); + void unmapBufferSubDataCHROMIUM(const void*); + void* mapTexSubImage2DCHROMIUM(unsigned target, int level, int xoffset, int yoffset, int width, int height, unsigned format, unsigned type, unsigned access); + void unmapTexSubImage2DCHROMIUM(const void*); + + // GL_CHROMIUM_copy_texture_to_parent_texture + void copyTextureToParentTextureCHROMIUM(unsigned texture, unsigned parentTexture); + +private: + // Instances of this class are strictly owned by the GraphicsContext3D implementation and do not + // need to be instantiated by any other code. + friend class GraphicsContext3DInternal; + explicit Extensions3DChromium(GraphicsContext3DInternal*); + + // Weak pointer back to GraphicsContext3DInternal + GraphicsContext3DInternal* m_internal; +}; + +} // namespace WebCore + +#endif // Extensions3DChromium_h diff --git a/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp new file mode 100644 index 0000000..2c79815 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009 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 "FontCache.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "FontUtilsChromiumWin.h" +#include "HashMap.h" +#include "HashSet.h" +#include "SimpleFontData.h" +#include <unicode/uniset.h> +#include <wtf/text/StringHash.h> + +#include <windows.h> +#include <objidl.h> +#include <mlang.h> + +using std::min; + +namespace WebCore +{ + +// FIXME: consider adding to WebKit String class +static bool charactersAreAllASCII(const String& s) +{ + return WTF::charactersAreAllASCII(s.characters(), s.length()); +} + +// When asked for a CJK font with a native name under a non-CJK locale or +// asked for a CJK font with a Romanized name under a CJK locale, +// |GetTextFace| (after |CreateFont*|) returns a 'bogus' value (e.g. Arial). +// This is not consistent with what MSDN says !! +// Therefore, before we call |CreateFont*|, we have to map a Romanized name to +// the corresponding native name under a CJK locale and vice versa +// under a non-CJK locale. +// See the corresponding gecko bugs at +// https://bugzilla.mozilla.org/show_bug.cgi?id=373952 +// https://bugzilla.mozilla.org/show_bug.cgi?id=231426 +static bool LookupAltName(const String& name, String& altName) +{ + struct FontCodepage { + WCHAR* name; + int codePage; + }; + + struct NamePair { + WCHAR* name; + FontCodepage altNameCodepage; + }; + + const int japaneseCodepage = 932; + const int simplifiedChineseCodepage = 936; + const int koreanCodepage = 949; + const int traditionalChineseCodepage = 950; + + // FIXME(jungshik) : This list probably covers 99% of cases. + // To cover the remaining 1% and cut down the file size, + // consider accessing 'NAME' table of a truetype font + // using |GetFontData| and caching the mapping. + // In the table below, the ASCII keys are all lower-cased for + // case-insensitive matching. + static const NamePair namePairs[] = { + // MS Pゴシック, MS PGothic + {L"\xFF2D\xFF33 \xFF30\x30B4\x30B7\x30C3\x30AF", {L"MS PGothic", japaneseCodepage}}, + {L"ms pgothic", {L"\xFF2D\xFF33 \xFF30\x30B4\x30B7\x30C3\x30AF", japaneseCodepage}}, + // MS P明朝, MS PMincho + {L"\xFF2D\xFF33 \xFF30\x660E\x671D", {L"MS PMincho", japaneseCodepage}}, + {L"ms pmincho", {L"\xFF2D\xFF33 \xFF30\x660E\x671D", japaneseCodepage}}, + // MSゴシック, MS Gothic + {L"\xFF2D\xFF33 \x30B4\x30B7\x30C3\x30AF", {L"MS Gothic", japaneseCodepage}}, + {L"ms gothic", {L"\xFF2D\xFF33 \x30B4\x30B7\x30C3\x30AF", japaneseCodepage}}, + // MS 明朝, MS Mincho + {L"\xFF2D\xFF33 \x660E\x671D", {L"MS Mincho", japaneseCodepage}}, + {L"ms mincho", {L"\xFF2D\xFF33 \x660E\x671D", japaneseCodepage}}, + // メイリオ, Meiryo + {L"\x30E1\x30A4\x30EA\x30AA", {L"Meiryo", japaneseCodepage}}, + {L"meiryo", {L"\x30E1\x30A4\x30EA\x30AA", japaneseCodepage}}, + // 바탕, Batang + {L"\xBC14\xD0D5", {L"Batang", koreanCodepage}}, + {L"batang", {L"\xBC14\xD0D5", koreanCodepage}}, + // 바탕체, Batangche + {L"\xBC14\xD0D5\xCCB4", {L"Batangche", koreanCodepage}}, + {L"batangche", {L"\xBC14\xD0D5\xCCB4", koreanCodepage}}, + // 굴림, Gulim + {L"\xAD74\xB9BC", {L"Gulim", koreanCodepage}}, + {L"gulim", {L"\xAD74\xB9BC", koreanCodepage}}, + // 굴림체, Gulimche + {L"\xAD74\xB9BC\xCCB4", {L"Gulimche", koreanCodepage}}, + {L"gulimche", {L"\xAD74\xB9BC\xCCB4", koreanCodepage}}, + // 돋움, Dotum + {L"\xB3CB\xC6C0", {L"Dotum", koreanCodepage}}, + {L"dotum", {L"\xB3CB\xC6C0", koreanCodepage}}, + // 돋움체, Dotumche + {L"\xB3CB\xC6C0\xCCB4", {L"Dotumche", koreanCodepage}}, + {L"dotumche", {L"\xB3CB\xC6C0\xCCB4", koreanCodepage}}, + // 궁서, Gungsuh + {L"\xAD81\xC11C", {L"Gungsuh", koreanCodepage}}, + {L"gungsuh", {L"\xAD81\xC11C", koreanCodepage}}, + // 궁서체, Gungsuhche + {L"\xAD81\xC11C\xCCB4", {L"Gungsuhche", koreanCodepage}}, + {L"gungsuhche", {L"\xAD81\xC11C\xCCB4", koreanCodepage}}, + // 맑은 고딕, Malgun Gothic + {L"\xB9D1\xC740 \xACE0\xB515", {L"Malgun Gothic", koreanCodepage}}, + {L"malgun gothic", {L"\xB9D1\xC740 \xACE0\xB515", koreanCodepage}}, + // 宋体, SimSun + {L"\x5B8B\x4F53", {L"SimSun", simplifiedChineseCodepage}}, + {L"simsun", {L"\x5B8B\x4F53", simplifiedChineseCodepage}}, + // 宋体-ExtB, SimSun-ExtB + {L"\x5B8B\x4F53-ExtB", {L"SimSun-ExtB", simplifiedChineseCodepage}}, + {L"simsun-extb", {L"\x5B8B\x4F53-extb", simplifiedChineseCodepage}}, + // 黑体, SimHei + {L"\x9ED1\x4F53", {L"SimHei", simplifiedChineseCodepage}}, + {L"simhei", {L"\x9ED1\x4F53", simplifiedChineseCodepage}}, + // 新宋体, NSimSun + {L"\x65B0\x5B8B\x4F53", {L"NSimSun", simplifiedChineseCodepage}}, + {L"nsimsun", {L"\x65B0\x5B8B\x4F53", simplifiedChineseCodepage}}, + // 微软雅黑, Microsoft Yahei + {L"\x5FAE\x8F6F\x96C5\x9ED1", {L"Microsoft Yahei", simplifiedChineseCodepage}}, + {L"microsoft yahei", {L"\x5FAE\x8F6F\x96C5\x9ED1", simplifiedChineseCodepage}}, + // 仿宋, FangSong + {L"\x4EFF\x5B8B", {L"FangSong", simplifiedChineseCodepage}}, + {L"fangsong", {L"\x4EFF\x5B8B", simplifiedChineseCodepage}}, + // 楷体, KaiTi + {L"\x6977\x4F53", {L"KaiTi", simplifiedChineseCodepage}}, + {L"kaiti", {L"\x6977\x4F53", simplifiedChineseCodepage}}, + // 仿宋_GB2312, FangSong_GB2312 + {L"\x4EFF\x5B8B_GB2312", {L"FangSong_GB2312", simplifiedChineseCodepage}}, + {L"fangsong_gb2312", {L"\x4EFF\x5B8B_gb2312", simplifiedChineseCodepage}}, + // 楷体_GB2312, KaiTi_GB2312 + {L"\x6977\x4F53", {L"KaiTi_GB2312", simplifiedChineseCodepage}}, + {L"kaiti_gb2312", {L"\x6977\x4F53_gb2312", simplifiedChineseCodepage}}, + // 新細明體, PMingLiu + {L"\x65B0\x7D30\x660E\x9AD4", {L"PMingLiu", traditionalChineseCodepage}}, + {L"pmingliu", {L"\x65B0\x7D30\x660E\x9AD4", traditionalChineseCodepage}}, + // 新細明體-ExtB, PMingLiu-ExtB + {L"\x65B0\x7D30\x660E\x9AD4-ExtB", {L"PMingLiu-ExtB", traditionalChineseCodepage}}, + {L"pmingliu-extb", {L"\x65B0\x7D30\x660E\x9AD4-extb", traditionalChineseCodepage}}, + // 細明體, MingLiu + {L"\x7D30\x660E\x9AD4", {L"MingLiu", traditionalChineseCodepage}}, + {L"mingliu", {L"\x7D30\x660E\x9AD4", traditionalChineseCodepage}}, + // 細明體-ExtB, MingLiu-ExtB + {L"\x7D30\x660E\x9AD4-ExtB", {L"MingLiu-ExtB", traditionalChineseCodepage}}, + {L"mingliu-extb", {L"x65B0\x7D30\x660E\x9AD4-extb", traditionalChineseCodepage}}, + // 微軟正黑體, Microsoft JhengHei + {L"\x5FAE\x8EDF\x6B63\x9ED1\x9AD4", {L"Microsoft JhengHei", traditionalChineseCodepage}}, + {L"microsoft jhengHei", {L"\x5FAE\x8EDF\x6B63\x9ED1\x9AD4", traditionalChineseCodepage}}, + // 標楷體, DFKai-SB + {L"\x6A19\x6977\x9AD4", {L"DFKai-SB", traditionalChineseCodepage}}, + {L"dfkai-sb", {L"\x6A19\x6977\x9AD4", traditionalChineseCodepage}}, + // WenQuanYi Zen Hei + {L"\x6587\x6cc9\x9a5b\x6b63\x9ed1", {L"WenQuanYi Zen Hei", traditionalChineseCodepage}}, + {L"wenquanyi zen hei", {L"\x6587\x6cc9\x9a5b\x6b63\x9ed1", traditionalChineseCodepage}}, + // WenQuanYi Zen Hei + {L"\x6587\x6cc9\x9a7f\x6b63\x9ed1", {L"WenQuanYi Zen Hei", simplifiedChineseCodepage}}, + {L"wenquanyi zen hei", {L"\x6587\x6cc9\x9a7f\x6b63\x9ed1", simplifiedChineseCodepage}}, + // AR PL ShanHeiSun Uni, + {L"\x6587\x9f0e\x0050\x004c\x7d30\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", + {L"AR PL ShanHeiSun Uni", traditionalChineseCodepage}}, + {L"ar pl shanheisun uni", + {L"\x6587\x9f0e\x0050\x004c\x7d30\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", traditionalChineseCodepage}}, + // AR PL ShanHeiSun Uni, + {L"\x6587\x9f0e\x0050\x004c\x7ec6\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", + {L"AR PL ShanHeiSun Uni", simplifiedChineseCodepage}}, + {L"ar pl shanheisun uni", + {L"\x6587\x9f0e\x0050\x004c\x7ec6\x4e0a\x6d77\x5b8b\x0055\x006e\x0069", simplifiedChineseCodepage}}, + // AR PL ZenKai Uni + // Traditional Chinese and Simplified Chinese names are + // identical. + {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", {L"AR PL ZenKai Uni", traditionalChineseCodepage}}, + {L"ar pl zenkai uni", {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", traditionalChineseCodepage}}, + {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", {L"AR PL ZenKai Uni", simplifiedChineseCodepage}}, + {L"ar pl zenkai uni", {L"\x6587\x0050\x004C\x4E2D\x6977\x0055\x006E\x0069", simplifiedChineseCodepage}}, + }; + + typedef HashMap<String, const FontCodepage*> NameMap; + static NameMap* fontNameMap = 0; + + if (!fontNameMap) { + fontNameMap = new NameMap; + for (size_t i = 0; i < WTF_ARRAY_LENGTH(namePairs); ++i) + fontNameMap->set(String(namePairs[i].name), &(namePairs[i].altNameCodepage)); + } + + bool isAscii = false; + String n; + // use |lower| only for ASCII names + // For non-ASCII names, we don't want to invoke an expensive + // and unnecessary |lower|. + if (charactersAreAllASCII(name)) { + isAscii = true; + n = name.lower(); + } else + n = name; + + NameMap::iterator iter = fontNameMap->find(n); + if (iter == fontNameMap->end()) + return false; + + static int systemCp = ::GetACP(); + int fontCp = iter->second->codePage; + + if ((isAscii && systemCp == fontCp) || (!isAscii && systemCp != fontCp)) { + altName = String(iter->second->name); + return true; + } + + return false; +} + +static HFONT createFontIndirectAndGetWinName(const String& family, LOGFONT* winfont, String* winName) +{ + int len = min(static_cast<int>(family.length()), LF_FACESIZE - 1); + memcpy(winfont->lfFaceName, family.characters(), len * sizeof(WORD)); + winfont->lfFaceName[len] = '\0'; + + HFONT hfont = CreateFontIndirect(winfont); + if (!hfont) + return 0; + + HDC dc = GetDC(0); + HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont)); + WCHAR name[LF_FACESIZE]; + unsigned resultLength = GetTextFace(dc, LF_FACESIZE, name); + if (resultLength > 0) + resultLength--; // ignore the null terminator + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + *winName = String(name, resultLength); + return hfont; +} + +// This maps font family names to their repertoires of supported Unicode +// characters. Because it's family names rather than font faces we use +// as keys, there might be edge cases where one face of a font family +// has a different repertoire from another face of the same family. +typedef HashMap<const wchar_t*, icu::UnicodeSet*> FontCmapCache; + +static bool fontContainsCharacter(const FontPlatformData* fontData, + const wchar_t* family, UChar32 character) +{ + // FIXME: For non-BMP characters, GetFontUnicodeRanges is of + // no use. We have to read directly from the cmap table of a font. + // Return true for now. + if (character > 0xFFFF) + return true; + + // This cache is just leaked on shutdown. + static FontCmapCache* fontCmapCache = 0; + if (!fontCmapCache) + fontCmapCache = new FontCmapCache; + + HashMap<const wchar_t*, icu::UnicodeSet*>::iterator it = fontCmapCache->find(family); + if (it != fontCmapCache->end()) + return it->second->contains(character); + + HFONT hfont = fontData->hfont(); + HDC hdc = GetDC(0); + HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(hdc, hfont)); + int count = GetFontUnicodeRanges(hdc, 0); + if (count == 0 && ChromiumBridge::ensureFontLoaded(hfont)) + count = GetFontUnicodeRanges(hdc, 0); + if (count == 0) { + LOG_ERROR("Unable to get the font unicode range after second attempt"); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + return true; + } + + static Vector<char, 512> glyphsetBuffer; + glyphsetBuffer.resize(GetFontUnicodeRanges(hdc, 0)); + GLYPHSET* glyphset = reinterpret_cast<GLYPHSET*>(glyphsetBuffer.data()); + // In addition, refering to the OS/2 table and converting the codepage list + // to the coverage map might be faster. + count = GetFontUnicodeRanges(hdc, glyphset); + ASSERT(count > 0); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + // FIXME: consider doing either of the following two: + // 1) port back ICU 4.0's faster look-up code for UnicodeSet + // 2) port Mozilla's CompressedCharMap or gfxSparseBitset + unsigned i = 0; + icu::UnicodeSet* cmap = new icu::UnicodeSet; + while (i < glyphset->cRanges) { + WCHAR start = glyphset->ranges[i].wcLow; + cmap->add(start, start + glyphset->ranges[i].cGlyphs - 1); + i++; + } + cmap->freeze(); + // We don't lowercase |family| because all of them are under our control + // and they're already lowercased. + fontCmapCache->set(family, cmap); + return cmap->contains(character); +} + +// Tries the given font and save it |outFontFamilyName| if it succeeds. +static SimpleFontData* fontDataFromDescriptionAndLogFont(FontCache* fontCache, const FontDescription& fontDescription, const LOGFONT& font, wchar_t* outFontFamilyName) +{ + SimpleFontData* fontData = fontCache->getCachedFontData(fontDescription, font.lfFaceName); + if (fontData) + memcpy(outFontFamilyName, font.lfFaceName, sizeof(font.lfFaceName)); + return fontData; +} + +static LONG toGDIFontWeight(FontWeight fontWeight) +{ + static LONG gdiFontWeights[] = { + FW_THIN, // FontWeight100 + FW_EXTRALIGHT, // FontWeight200 + FW_LIGHT, // FontWeight300 + FW_NORMAL, // FontWeight400 + FW_MEDIUM, // FontWeight500 + FW_SEMIBOLD, // FontWeight600 + FW_BOLD, // FontWeight700 + FW_EXTRABOLD, // FontWeight800 + FW_HEAVY // FontWeight900 + }; + return gdiFontWeights[fontWeight]; +} + +static void FillLogFont(const FontDescription& fontDescription, LOGFONT* winfont) +{ + // The size here looks unusual. The negative number is intentional. + // Unlike WebKit trunk, we don't multiply the size by 32. That seems to be + // some kind of artifact of their CG backend, or something. + winfont->lfHeight = -fontDescription.computedPixelSize(); + winfont->lfWidth = 0; + winfont->lfEscapement = 0; + winfont->lfOrientation = 0; + winfont->lfUnderline = false; + winfont->lfStrikeOut = false; + winfont->lfCharSet = DEFAULT_CHARSET; + winfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; + winfont->lfQuality = ChromiumBridge::layoutTestMode() ? NONANTIALIASED_QUALITY : DEFAULT_QUALITY; // Honor user's desktop settings. + winfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + winfont->lfItalic = fontDescription.italic(); + winfont->lfWeight = toGDIFontWeight(fontDescription.weight()); +} + +struct TraitsInFamilyProcData { + TraitsInFamilyProcData(const AtomicString& familyName) + : m_familyName(familyName) + { + } + + const AtomicString& m_familyName; + HashSet<unsigned> m_traitsMasks; +}; + +static int CALLBACK traitsInFamilyEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) +{ + TraitsInFamilyProcData* procData = reinterpret_cast<TraitsInFamilyProcData*>(lParam); + + unsigned traitsMask = 0; + traitsMask |= logFont->lfItalic ? FontStyleItalicMask : FontStyleNormalMask; + traitsMask |= FontVariantNormalMask; + LONG weight = logFont->lfWeight; + traitsMask |= weight == FW_THIN ? FontWeight100Mask : + weight == FW_EXTRALIGHT ? FontWeight200Mask : + weight == FW_LIGHT ? FontWeight300Mask : + weight == FW_NORMAL ? FontWeight400Mask : + weight == FW_MEDIUM ? FontWeight500Mask : + weight == FW_SEMIBOLD ? FontWeight600Mask : + weight == FW_BOLD ? FontWeight700Mask : + weight == FW_EXTRABOLD ? FontWeight800Mask : + FontWeight900Mask; + procData->m_traitsMasks.add(traitsMask); + return 1; +} + +void FontCache::platformInit() +{ + // Not needed on Windows. +} + +// Given the desired base font, this will create a SimpleFontData for a specific +// font that can be used to render the given range of characters. +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + // FIXME: Consider passing fontDescription.dominantScript() + // to GetFallbackFamily here. + FontDescription fontDescription = font.fontDescription(); + UChar32 c; + UScriptCode script; + const wchar_t* family = getFallbackFamily(characters, length, + fontDescription.genericFamily(), &c, &script); + FontPlatformData* data = 0; + if (family) + data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family)), false); + + // Last resort font list : PanUnicode. CJK fonts have a pretty + // large repertoire. Eventually, we need to scan all the fonts + // on the system to have a Firefox-like coverage. + // Make sure that all of them are lowercased. + const static wchar_t* const cjkFonts[] = { + L"arial unicode ms", + L"ms pgothic", + L"simsun", + L"gulim", + L"pmingliu", + L"wenquanyi zen hei", // partial CJK Ext. A coverage but more + // widely known to Chinese users. + L"ar pl shanheisun uni", + L"ar pl zenkai uni", + L"han nom a", // Complete CJK Ext. A coverage + L"code2000", // Complete CJK Ext. A coverage + // CJK Ext. B fonts are not listed here because it's of no use + // with our current non-BMP character handling because we use + // Uniscribe for it and that code path does not go through here. + }; + + const static wchar_t* const commonFonts[] = { + L"tahoma", + L"arial unicode ms", + L"lucida sans unicode", + L"microsoft sans serif", + L"palatino linotype", + // Six fonts below (and code2000 at the end) are not from MS, but + // once installed, cover a very wide range of characters. + L"dejavu serif", + L"dejavu sasns", + L"freeserif", + L"freesans", + L"gentium", + L"gentiumalt", + L"ms pgothic", + L"simsun", + L"gulim", + L"pmingliu", + L"code2000", + }; + + const wchar_t* const* panUniFonts = 0; + int numFonts = 0; + if (script == USCRIPT_HAN) { + panUniFonts = cjkFonts; + numFonts = WTF_ARRAY_LENGTH(cjkFonts); + } else { + panUniFonts = commonFonts; + numFonts = WTF_ARRAY_LENGTH(commonFonts); + } + // Font returned from GetFallbackFamily may not cover |characters| + // because it's based on script to font mapping. This problem is + // critical enough for non-Latin scripts (especially Han) to + // warrant an additional (real coverage) check with fontCotainsCharacter. + int i; + for (i = 0; (!data || !fontContainsCharacter(data, family, c)) && i < numFonts; ++i) { + family = panUniFonts[i]; + data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family))); + } + // When i-th font (0-base) in |panUniFonts| contains a character and + // we get out of the loop, |i| will be |i + 1|. That is, if only the + // last font in the array covers the character, |i| will be numFonts. + // So, we have to use '<=" rather than '<' to see if we found a font + // covering the character. + if (i <= numFonts) + return getCachedFontData(data); + + return 0; + +} + +SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& description) +{ + FontDescription::GenericFamilyType generic = description.genericFamily(); + + // FIXME: Would be even better to somehow get the user's default font here. + // For now we'll pick the default that the user would get without changing + // any prefs. + static AtomicString timesStr("Times New Roman"); + static AtomicString courierStr("Courier New"); + static AtomicString arialStr("Arial"); + + AtomicString& fontStr = timesStr; + if (generic == FontDescription::SansSerifFamily) + fontStr = arialStr; + else if (generic == FontDescription::MonospaceFamily) + fontStr = courierStr; + + SimpleFontData* simpleFont = getCachedFontData(description, fontStr); + if (simpleFont) + return simpleFont; + + // Fall back to system fonts as Win Safari does because this function must + // return a valid font. Once we find a valid system font, we save its name + // to a static variable and use it to prevent trying system fonts again. + static wchar_t fallbackFontName[LF_FACESIZE] = {0}; + if (fallbackFontName[0]) + return getCachedFontData(description, fallbackFontName); + + // Fall back to the DEFAULT_GUI_FONT if no known Unicode fonts are available. + if (HFONT defaultGUIFont = static_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT))) { + LOGFONT defaultGUILogFont; + GetObject(defaultGUIFont, sizeof(defaultGUILogFont), &defaultGUILogFont); + if (simpleFont = fontDataFromDescriptionAndLogFont(this, description, defaultGUILogFont, fallbackFontName)) + return simpleFont; + } + + // Fall back to Non-client metrics fonts. + NONCLIENTMETRICS nonClientMetrics = {0}; + nonClientMetrics.cbSize = sizeof(nonClientMetrics); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nonClientMetrics), &nonClientMetrics, 0)) { + if (simpleFont = fontDataFromDescriptionAndLogFont(this, description, nonClientMetrics.lfMessageFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, description, nonClientMetrics.lfMenuFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, description, nonClientMetrics.lfStatusFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, description, nonClientMetrics.lfCaptionFont, fallbackFontName)) + return simpleFont; + if (simpleFont = fontDataFromDescriptionAndLogFont(this, description, nonClientMetrics.lfSmCaptionFont, fallbackFontName)) + return simpleFont; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + HDC hdc = GetDC(0); + + LOGFONT logFont; + logFont.lfCharSet = DEFAULT_CHARSET; + unsigned familyLength = min(familyName.length(), static_cast<unsigned>(LF_FACESIZE - 1)); + memcpy(logFont.lfFaceName, familyName.characters(), familyLength * sizeof(UChar)); + logFont.lfFaceName[familyLength] = 0; + logFont.lfPitchAndFamily = 0; + + TraitsInFamilyProcData procData(familyName); + EnumFontFamiliesEx(hdc, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0); + copyToVector(procData.m_traitsMasks, traitsMasks); + + ReleaseDC(0, hdc); +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +{ + LOGFONT winfont = {0}; + FillLogFont(fontDescription, &winfont); + + // Windows will always give us a valid pointer here, even if the face name + // is non-existent. We have to double-check and see if the family name was + // really used. + String winName; + HFONT hfont = createFontIndirectAndGetWinName(family, &winfont, &winName); + if (!hfont) + return 0; + + // FIXME: Do we need to use predefined fonts "guaranteed" to exist + // when we're running in layout-test mode? + if (!equalIgnoringCase(family, winName)) { + // For CJK fonts with both English and native names, + // GetTextFace returns a native name under the font's "locale" + // and an English name under other locales regardless of + // lfFaceName field of LOGFONT. As a result, we need to check + // if a font has an alternate name. If there is, we need to + // compare it with what's requested in the first place. + String altName; + if (!LookupAltName(family, altName) || + !equalIgnoringCase(altName, winName)) { + DeleteObject(hfont); + return 0; + } + } + + return new FontPlatformData(hfont, + fontDescription.computedPixelSize()); +} + +} diff --git a/Source/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/Source/WebCore/platform/graphics/chromium/FontCacheLinux.cpp new file mode 100644 index 0000000..bd33927 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009 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 "FontCache.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "FontDescription.h" +#include "FontPlatformData.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "SimpleFontData.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +#include <wtf/Assertions.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +void FontCache::platformInit() +{ +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, + const UChar* characters, + int length) +{ + String family = ChromiumBridge::getFontFamilyForCharacters(characters, length); + if (family.isEmpty()) + return 0; + + AtomicString atomicFamily(family); + return getCachedFontData(getCachedFontPlatformData(font.fontDescription(), atomicFamily, false)); +} + +SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& description) +{ + static const AtomicString sansStr("Sans"); + static const AtomicString serifStr("Serif"); + static const AtomicString monospaceStr("Monospace"); + + FontPlatformData* fontPlatformData = 0; + switch (description.genericFamily()) { + case FontDescription::SerifFamily: + fontPlatformData = getCachedFontPlatformData(description, serifStr); + break; + case FontDescription::MonospaceFamily: + fontPlatformData = getCachedFontPlatformData(description, monospaceStr); + break; + case FontDescription::SansSerifFamily: + default: + fontPlatformData = getCachedFontPlatformData(description, sansStr); + break; + } + + ASSERT(fontPlatformData); + return getCachedFontData(fontPlatformData); +} + +void FontCache::getTraitsInFamily(const AtomicString& familyName, + Vector<unsigned>& traitsMasks) +{ + notImplemented(); +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, + const AtomicString& family) +{ + const char* name = 0; + CString s; + + // 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-")) { + static const struct { + FontDescription::GenericFamilyType mType; + const char* mName; + } fontDescriptions[] = { + { FontDescription::SerifFamily, "serif" }, + { FontDescription::SansSerifFamily, "sans-serif" }, + { FontDescription::MonospaceFamily, "monospace" }, + { FontDescription::CursiveFamily, "cursive" }, + { FontDescription::FantasyFamily, "fantasy" } + }; + + FontDescription::GenericFamilyType type = fontDescription.genericFamily(); + for (unsigned i = 0; i < SK_ARRAY_COUNT(fontDescriptions); i++) { + if (type == fontDescriptions[i].mType) { + name = fontDescriptions[i].mName; + break; + } + } + if (!name) + name = ""; + } else { + // convert the name to utf8 + s = family.string().utf8(); + name = s.data(); + } + + int style = SkTypeface::kNormal; + if (fontDescription.weight() >= FontWeightBold) + style |= SkTypeface::kBold; + if (fontDescription.italic()) + style |= SkTypeface::kItalic; + + SkTypeface* tf = SkTypeface::CreateFromName(name, static_cast<SkTypeface::Style>(style)); + if (!tf) + return 0; + + FontPlatformData* result = + new FontPlatformData(tf, + name, + fontDescription.computedSize(), + (style & SkTypeface::kBold) && !tf->isBold(), + (style & SkTypeface::kItalic) && !tf->isItalic(), + fontDescription.orientation()); + tf->unref(); + return result; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp new file mode 100644 index 0000000..1a00833 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, 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 "Font.h" + +#include "ChromiumBridge.h" +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" +#include "SkiaFontWin.h" +#include "SkiaUtils.h" +#include "TransparencyWin.h" +#include "UniscribeHelperTextRun.h" + +#include "skia/ext/platform_canvas_win.h" +#include "skia/ext/skia_utils_win.h" // FIXME: remove this dependency. + +#include <windows.h> + +namespace WebCore { + +namespace { + +bool canvasHasMultipleLayers(const SkCanvas* canvas) +{ + SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false); + iter.next(); // There is always at least one layer. + return !iter.done(); // There is > 1 layer if the the iterator can stil advance. +} + +class TransparencyAwareFontPainter { +public: + TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&); + ~TransparencyAwareFontPainter(); + +protected: + // Called by our subclass' constructor to initialize GDI if necessary. This + // is a separate function so it can be called after the subclass finishes + // construction (it calls virtual functions). + void init(); + + virtual IntRect estimateTextBounds() = 0; + + // Use the context from the transparency helper when drawing with GDI. It + // may point to a temporary one. + GraphicsContext* m_graphicsContext; + PlatformGraphicsContext* m_platformContext; + + FloatPoint m_point; + + // Set when Windows can handle the type of drawing we're doing. + bool m_useGDI; + + // These members are valid only when m_useGDI is set. + HDC m_hdc; + TransparencyWin m_transparency; + +private: + // Call when we're using GDI mode to initialize the TransparencyWin to help + // us draw GDI text. + void initializeForGDI(); + + bool m_createdTransparencyLayer; // We created a layer to give the font some alpha. +}; + +TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context, + const FloatPoint& point) + : m_graphicsContext(context) + , m_platformContext(context->platformContext()) + , m_point(point) + , m_useGDI(windowsCanHandleTextDrawing(context)) + , m_hdc(0) + , m_createdTransparencyLayer(false) +{ +} + +void TransparencyAwareFontPainter::init() +{ + if (m_useGDI) + initializeForGDI(); +} + +void TransparencyAwareFontPainter::initializeForGDI() +{ + m_graphicsContext->save(); + SkColor color = m_platformContext->effectiveFillColor(); + // Used only when m_createdTransparencyLayer is true. + float layerAlpha = 0.0f; + if (SkColorGetA(color) != 0xFF) { + // When the font has some transparency, apply it by creating a new + // transparency layer with that opacity applied. We'll actually create + // a new transparency layer after we calculate the bounding box. + m_createdTransparencyLayer = true; + layerAlpha = SkColorGetA(color) / 255.0f; + // The color should be opaque now. + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); + } + + TransparencyWin::LayerMode layerMode; + IntRect layerRect; + if (m_platformContext->isDrawingToImageBuffer()) { + // Assume if we're drawing to an image buffer that the background + // is not opaque and we have to undo ClearType. We may want to + // enhance this to actually check, since it will often be opaque + // and we could do ClearType in that case. + layerMode = TransparencyWin::TextComposite; + layerRect = estimateTextBounds(); + m_graphicsContext->clip(layerRect); + if (m_createdTransparencyLayer) + m_graphicsContext->beginTransparencyLayer(layerAlpha); + + // The transparency helper requires that we draw text in black in + // this mode and it will apply the color. + m_transparency.setTextCompositeColor(color); + color = SkColorSetRGB(0, 0, 0); + } else if (m_createdTransparencyLayer || canvasHasMultipleLayers(m_platformContext->canvas())) { + // When we're drawing a web page, we know the background is opaque, + // but if we're drawing to a layer, we still need extra work. + layerMode = TransparencyWin::OpaqueCompositeLayer; + layerRect = estimateTextBounds(); + m_graphicsContext->clip(layerRect); + if (m_createdTransparencyLayer) + m_graphicsContext->beginTransparencyLayer(layerAlpha); + } else { + // Common case of drawing onto the bottom layer of a web page: we + // know everything is opaque so don't need to do anything special. + layerMode = TransparencyWin::NoLayer; + } + + // Bug 26088 - init() might fail if layerRect is invalid. Given this, we + // need to be careful to check for null pointers everywhere after this call + m_transparency.init(m_graphicsContext, layerMode, + TransparencyWin::KeepTransform, layerRect); + + // Set up the DC, using the one from the transparency helper. + if (m_transparency.platformContext()) { + m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); + SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(m_hdc, TRANSPARENT); + } +} + +TransparencyAwareFontPainter::~TransparencyAwareFontPainter() +{ + if (!m_useGDI || !m_graphicsContext || !m_platformContext) + return; // Nothing to do. + m_transparency.composite(); + if (m_createdTransparencyLayer) + m_graphicsContext->endTransparencyLayer(); + m_graphicsContext->restore(); + m_platformContext->canvas()->endPlatformPaint(); +} + +// Specialization for simple GlyphBuffer painting. +class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareGlyphPainter(GraphicsContext*, + const SimpleFontData*, + const GlyphBuffer&, + int from, int numGlyphs, + const FloatPoint&); + ~TransparencyAwareGlyphPainter(); + + // Draws the partial string of glyphs, starting at |startAdvance| to the + // left of m_point. We express it this way so that if we're using the Skia + // drawing path we can use floating-point positioning, even though we have + // to use integer positioning in the GDI path. + bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, int startAdvance) const; + + private: + virtual IntRect estimateTextBounds(); + + const SimpleFontData* m_font; + const GlyphBuffer& m_glyphBuffer; + int m_from; + int m_numGlyphs; + + // When m_useGdi is set, this stores the previous HFONT selected into the + // m_hdc so we can restore it. + HGDIOBJ m_oldFont; // For restoring the DC to its original state. +}; + +TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( + GraphicsContext* context, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_glyphBuffer(glyphBuffer) + , m_from(from) + , m_numGlyphs(numGlyphs) + , m_oldFont(0) +{ + init(); + + if (m_hdc) + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); +} + +TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() +{ + if (m_useGDI && m_hdc) + ::SelectObject(m_hdc, m_oldFont); +} + + +// Estimates the bounding box of the given text. This is copied from +// FontCGWin.cpp, it is possible, but a lot more work, to get the precide +// bounds. +IntRect TransparencyAwareGlyphPainter::estimateTextBounds() +{ + int totalWidth = 0; + for (int i = 0; i < m_numGlyphs; i++) + totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i)); + + return IntRect(m_point.x() - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + totalWidth + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, + const WORD* glyphs, + const int* advances, + int startAdvance) const +{ + if (!m_useGDI) { + SkPoint origin = m_point; + origin.fX += startAdvance; + return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(), + numGlyphs, glyphs, advances, 0, &origin); + } + + if (!m_graphicsContext || !m_hdc) + return true; + + // Windows' origin is the top-left of the bounding box, so we have + // to subtract off the font ascent to get it. + int x = lroundf(m_point.x() + startAdvance); + int y = lroundf(m_point.y() - m_font->ascent()); + + // If there is a non-blur shadow and both the fill color and shadow color + // are opaque, handle without skia. + FloatSize shadowOffset; + float shadowBlur; + Color shadowColor; + ColorSpace shadowColorSpace; + if (m_graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace)) { + // If there is a shadow and this code is reached, windowsCanHandleDrawTextShadow() + // will have already returned true during the ctor initiatization of m_useGDI + ASSERT(shadowColor.alpha() == 255); + ASSERT(m_graphicsContext->fillColor().alpha() == 255); + ASSERT(shadowBlur == 0); + COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue())); + COLORREF savedTextColor = GetTextColor(m_hdc); + SetTextColor(m_hdc, textColor); + ExtTextOut(m_hdc, x + shadowOffset.width(), y + shadowOffset.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); + SetTextColor(m_hdc, savedTextColor); + } + + return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); +} + +class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareUniscribePainter(GraphicsContext*, + const Font*, + const TextRun&, + int from, int to, + const FloatPoint&); + ~TransparencyAwareUniscribePainter(); + + // Uniscibe will draw directly into our buffer, so we need to expose our DC. + HDC hdc() const { return m_hdc; } + + private: + virtual IntRect estimateTextBounds(); + + const Font* m_font; + const TextRun& m_run; + int m_from; + int m_to; +}; + +TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter( + GraphicsContext* context, + const Font* font, + const TextRun& run, + int from, int to, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_run(run) + , m_from(from) + , m_to(to) +{ + init(); +} + +TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter() +{ +} + +IntRect TransparencyAwareUniscribePainter::estimateTextBounds() +{ + // This case really really sucks. There is no convenient way to estimate + // the bounding box. So we run Uniscribe twice. If we find this happens a + // lot, the way to fix it is to make the extra layer after the + // UniscribeHelper has measured the text. + IntPoint intPoint(lroundf(m_point.x()), + lroundf(m_point.y())); + + UniscribeHelperTextRun state(m_run, *m_font); + int left = lroundf(m_point.x()) + state.characterToX(m_from); + int right = lroundf(m_point.x()) + state.characterToX(m_to); + + // Adjust for RTL script since we just want to know the text bounds. + if (left > right) + std::swap(left, right); + + // This algorithm for estimating how much extra space we need (the text may + // go outside the selection rect) is based roughly on + // TransparencyAwareGlyphPainter::estimateTextBounds above. + return IntRect(left - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + (right - left) + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +} // namespace + +bool Font::canReturnFallbackFontsForComplexText() +{ + return false; +} + +void Font::drawGlyphs(GraphicsContext* graphicsContext, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, + int numGlyphs, + const FloatPoint& point) const +{ + graphicsContext->platformContext()->prepareForSoftwareDraw(); + + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); + unsigned char alpha = SkColorGetA(color); + // Skip 100% transparent text; no need to draw anything. + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) + return; + + TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point); + + // We draw the glyphs in chunks to avoid having to do a heap allocation for + // the arrays of characters and advances. Since ExtTextOut is the + // lowest-level text output function on Windows, there should be little + // penalty for splitting up the text. On the other hand, the buffer cannot + // be bigger than 4094 or the function will fail. + const int kMaxBufferLength = 256; + Vector<WORD, kMaxBufferLength> glyphs; + Vector<int, kMaxBufferLength> advances; + int glyphIndex = 0; // The starting glyph of the current chunk. + int curAdvance = 0; // How far from the left the current chunk is. + while (glyphIndex < numGlyphs) { + // How many chars will be in this chunk? + int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); + glyphs.resize(curLen); + advances.resize(curLen); + + int curWidth = 0; + for (int i = 0; i < curLen; ++i, ++glyphIndex) { + glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); + advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex)); + + // Bug 26088 - very large positive or negative runs can fail to + // render so we clamp the size here. In the specs, negative + // letter-spacing is implementation-defined, so this should be + // fine, and it matches Safari's implementation. The call actually + // seems to crash if kMaxNegativeRun is set to somewhere around + // -32830, so we give ourselves a little breathing room. + const int maxNegativeRun = -32768; + const int maxPositiveRun = 32768; + if ((curWidth + advances[i] < maxNegativeRun) || (curWidth + advances[i] > maxPositiveRun)) + advances[i] = 0; + curWidth += advances[i]; + } + + // Actually draw the glyphs (with retry on failure). + bool success = false; + for (int executions = 0; executions < 2; ++executions) { + success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], curAdvance); + if (!success && executions == 0) { + // Ask the browser to load the font for us and retry. + ChromiumBridge::ensureFontLoaded(font->platformData().hfont()); + continue; + } + break; + } + + if (!success) + LOG_ERROR("Unable to draw the glyphs after second attempt"); + + curAdvance += curWidth; + } +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, + const FloatPoint& point, + int h, + int from, + int to) const +{ + UniscribeHelperTextRun state(run, *this); + float left = static_cast<float>(point.x() + state.characterToX(from)); + float right = static_cast<float>(point.x() + state.characterToX(to)); + + // If the text is RTL, left will actually be after right. + if (left < right) + return FloatRect(left, point.y(), + right - left, static_cast<float>(h)); + + return FloatRect(right, point.y(), + left - right, static_cast<float>(h)); +} + +void Font::drawComplexText(GraphicsContext* graphicsContext, + const TextRun& run, + const FloatPoint& point, + int from, + int to) const +{ + PlatformGraphicsContext* context = graphicsContext->platformContext(); + UniscribeHelperTextRun state(run, *this); + + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); + unsigned char alpha = SkColorGetA(color); + // Skip 100% transparent text; no need to draw anything. + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) + return; + + TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); + + HDC hdc = painter.hdc(); + if (windowsCanHandleTextDrawing(graphicsContext) && !hdc) + return; + + // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. + // Enforce non-transparent color. + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); + if (hdc) { + SetTextColor(hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(hdc, TRANSPARENT); + } + + // If there is a non-blur shadow and both the fill color and shadow color + // are opaque, handle without skia. + FloatSize shadowOffset; + float shadowBlur; + Color shadowColor; + ColorSpace shadowColorSpace; + if (graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace) && windowsCanHandleDrawTextShadow(graphicsContext)) { + COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue())); + COLORREF savedTextColor = GetTextColor(hdc); + SetTextColor(hdc, textColor); + state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowOffset.width(), + static_cast<int>(point.y() - ascent()) + shadowOffset.height(), from, to); + SetTextColor(hdc, savedTextColor); + } + + // Uniscribe counts the coordinates from the upper left, while WebKit uses + // the baseline, so we have to subtract off the ascent. + state.draw(graphicsContext, hdc, static_cast<int>(point.x()), + static_cast<int>(point.y() - ascent()), from, to); + + context->canvas()->endPlatformPaint(); +} + +void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const +{ + notImplemented(); +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const +{ + UniscribeHelperTextRun state(run, *this); + return static_cast<float>(state.width()); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, + bool includePartialGlyphs) const +{ + // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers + // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. + int x = static_cast<int>(xFloat); + + // Mac code ignores includePartialGlyphs, and they don't know what it's + // supposed to do, so we just ignore it as well. + UniscribeHelperTextRun state(run, *this); + int charIndex = state.xToCharacter(x); + + // XToCharacter will return -1 if the position is before the first + // character (we get called like this sometimes). + if (charIndex < 0) + charIndex = 0; + return charIndex; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/FontLinux.cpp b/Source/WebCore/platform/graphics/chromium/FontLinux.cpp new file mode 100644 index 0000000..b256e70 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2007, 2008, 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" +#include "Font.h" + +#include "ComplexTextControllerLinux.h" +#include "FloatRect.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "HarfbuzzSkia.h" +#include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkTemplates.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + +bool Font::canReturnFallbackFontsForComplexText() +{ + return false; +} + +static bool isCanvasMultiLayered(SkCanvas* canvas) +{ + SkCanvas::LayerIter layerIterator(canvas, false); + layerIterator.next(); + return !layerIterator.done(); +} + +static void adjustTextRenderMode(SkPaint* paint, PlatformContextSkia* skiaContext) +{ + // Our layers only have a single alpha channel. This means that subpixel + // rendered text cannot be compositied correctly when the layer is + // collapsed. Therefore, subpixel text is disabled when we are drawing + // onto a layer or when the compositor is being used. + if (isCanvasMultiLayered(skiaContext->canvas()) || skiaContext->isDrawingToImageBuffer()) + paint->setLCDRenderText(false); +} + +void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, int from, int numGlyphs, + const FloatPoint& point) const { + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + + const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); + SkScalar x = SkFloatToScalar(point.x()); + SkScalar y = SkFloatToScalar(point.y()); + + // FIXME: text rendering speed: + // Android has code in their WebCore fork to special case when the + // GlyphBuffer has no advances other than the defaults. In that case the + // text drawing can proceed faster. However, it's unclear when those + // patches may be upstreamed to WebKit so we always use the slower path + // here. + const GlyphBufferAdvance* adv = glyphBuffer.advances(from); + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs); + SkPoint* pos = storage.get(); + SkPoint* vPosBegin = storage2.get(); + SkPoint* vPosEnd = storage3.get(); + + bool isVertical = font->orientation() == Vertical; + for (int i = 0; i < numGlyphs; i++) { + SkScalar myWidth = SkFloatToScalar(adv[i].width()); + pos[i].set(x, y); + if (isVertical) { + vPosBegin[i].set(x + myWidth, y); + vPosEnd[i].set(x + myWidth, y - myWidth); + } + x += myWidth; + y += SkFloatToScalar(adv[i].height()); + } + + gc->platformContext()->prepareForSoftwareDraw(); + + SkCanvas* canvas = gc->platformContext()->canvas(); + TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); + + // We draw text up to two times (once for fill, once for stroke). + if (textMode & TextModeFill) { + SkPaint paint; + gc->platformContext()->setupPaintForFilling(&paint); + font->platformData().setupPaint(&paint); + adjustTextRenderMode(&paint, gc->platformContext()); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->fillColor().rgb()); + + if (isVertical) { + SkPath path; + for (int i = 0; i < numGlyphs; ++i) { + path.reset(); + path.moveTo(vPosBegin[i]); + path.lineTo(vPosEnd[i]); + canvas->drawTextOnPath(glyphs + i, 2, path, 0, paint); + } + } else + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } + + if ((textMode & TextModeStroke) + && gc->platformContext()->getStrokeStyle() != NoStroke + && gc->platformContext()->getStrokeThickness() > 0) { + + SkPaint paint; + gc->platformContext()->setupPaintForStroking(&paint, 0, 0); + font->platformData().setupPaint(&paint); + adjustTextRenderMode(&paint, gc->platformContext()); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->strokeColor().rgb()); + + if (textMode & TextModeFill) { + // If we also filled, we don't want to draw shadows twice. + // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. + SkSafeUnref(paint.setLooper(0)); + } + + if (isVertical) { + SkPath path; + for (int i = 0; i < numGlyphs; ++i) { + path.reset(); + path.moveTo(vPosBegin[i]); + path.lineTo(vPosEnd[i]); + canvas->drawTextOnPath(glyphs + i, 2, path, 0, paint); + } + } else + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } +} + +// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't +// handle subpixel positioning so this function is used to truncate Harfbuzz +// values to a number of pixels. +static int truncateFixedPointToInteger(HB_Fixed value) +{ + return value >> 6; +} + +static void setupForTextPainting(SkPaint* paint, SkColor color) +{ + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint->setColor(color); +} + +void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, + const FloatPoint& point, int from, int to) const +{ + if (!run.length()) + return; + + SkCanvas* canvas = gc->platformContext()->canvas(); + TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); + bool fill = textMode & TextModeFill; + bool stroke = (textMode & TextModeStroke) + && gc->platformContext()->getStrokeStyle() != NoStroke + && gc->platformContext()->getStrokeThickness() > 0; + + if (!fill && !stroke) + return; + + SkPaint strokePaint, fillPaint; + if (fill) { + gc->platformContext()->setupPaintForFilling(&fillPaint); + setupForTextPainting(&fillPaint, gc->fillColor().rgb()); + } + if (stroke) { + gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0); + setupForTextPainting(&strokePaint, gc->strokeColor().rgb()); + } + + ComplexTextController controller(run, point.x(), this); + controller.setWordSpacingAdjustment(wordSpacing()); + controller.setLetterSpacingAdjustment(letterSpacing()); + controller.setPadding(run.padding()); + + while (controller.nextScriptRun()) { + if (fill) { + controller.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); + adjustTextRenderMode(&fillPaint, gc->platformContext()); + canvas->drawPosTextH(controller.glyphs(), controller.length() << 1, controller.xPositions(), point.y(), fillPaint); + } + + if (stroke) { + controller.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); + adjustTextRenderMode(&strokePaint, gc->platformContext()); + canvas->drawPosTextH(controller.glyphs(), controller.length() << 1, controller.xPositions(), point.y(), strokePaint); + } + } +} + +void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const +{ + notImplemented(); +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const +{ + ComplexTextController controller(run, 0, this); + controller.setWordSpacingAdjustment(wordSpacing()); + controller.setLetterSpacingAdjustment(letterSpacing()); + return controller.widthOfFullRun(); +} + +static int glyphIndexForXPositionInScriptRun(const ComplexTextController& controller, int x) +{ + const HB_Fixed* advances = controller.advances(); + int letterSpacing = controller.letterSpacing(); + int glyphIndex; + if (controller.rtl()) { + for (glyphIndex = controller.length() - 1; glyphIndex >= 0; --glyphIndex) { + // When iterating LTR over RTL text, we must include the whitespace + // _before_ the glyph, so no + 1 here. + if (x < (static_cast<int>(controller.length()) - glyphIndex) * letterSpacing + truncateFixedPointToInteger(advances[glyphIndex])) + break; + x -= truncateFixedPointToInteger(advances[glyphIndex]); + } + } else { + for (glyphIndex = 0; static_cast<unsigned>(glyphIndex) < controller.length(); ++glyphIndex) { + if (x < (glyphIndex * letterSpacing + truncateFixedPointToInteger(advances[glyphIndex]))) + break; + x -= truncateFixedPointToInteger(advances[glyphIndex]); + } + } + + return glyphIndex; +} + +// Return the code point index for the given |x| offset into the text run. +int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, + bool includePartialGlyphs) const +{ + // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers + // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. + int x = static_cast<int>(xFloat); + + // (Mac code ignores includePartialGlyphs, and they don't know what it's + // supposed to do, so we just ignore it as well.) + ComplexTextController controller(run, 0, this); + controller.setWordSpacingAdjustment(wordSpacing()); + controller.setLetterSpacingAdjustment(letterSpacing()); + + // If this is RTL text, the first glyph from the left is actually the last + // code point. So we need to know how many code points there are total in + // order to subtract. This is different from the length of the TextRun + // because UTF-16 surrogate pairs are a single code point, but 32-bits long. + // In LTR we leave this as 0 so that we get the correct value for + // |basePosition|, below. + unsigned totalCodePoints = 0; + if (controller.rtl()) { + ssize_t offset = 0; + while (offset < run.length()) { + utf16_to_code_point(run.characters(), run.length(), &offset); + totalCodePoints++; + } + } + + unsigned basePosition = totalCodePoints; + + // For RTL: + // code-point order: abcd efg hijkl + // on screen: lkjih gfe dcba + // ^ ^ + // | | + // basePosition--| | + // totalCodePoints----| + // Since basePosition is currently the total number of code-points, the + // first thing we do is decrement it so that it's pointing to the start of + // the current script-run. + // + // For LTR, basePosition is zero so it already points to the start of the + // first script run. + while (controller.nextScriptRun()) { + if (controller.rtl()) + basePosition -= controller.numCodePoints(); + + if (x >= 0 && static_cast<unsigned>(x) < controller.width()) { + // The x value in question is within this script run. We consider + // each glyph in presentation order and stop when we find the one + // covering this position. + const int glyphIndex = glyphIndexForXPositionInScriptRun(controller, x); + + // Now that we have a glyph index, we have to turn that into a + // code-point index. Because of ligatures, several code-points may + // have gone into a single glyph. We iterate over the clusters log + // and find the first code-point which contributed to the glyph. + + // Some shapers (i.e. Khmer) will produce cluster logs which report + // that /no/ code points contributed to certain glyphs. Because of + // this, we take any code point which contributed to the glyph in + // question, or any subsequent glyph. If we run off the end, then + // we take the last code point. + const unsigned short* log = controller.logClusters(); + for (unsigned j = 0; j < controller.numCodePoints(); ++j) { + if (log[j] >= glyphIndex) + return basePosition + j; + } + + return basePosition + controller.numCodePoints() - 1; + } + + x -= controller.width(); + + if (!controller.rtl()) + basePosition += controller.numCodePoints(); + } + + return basePosition; +} + +// Return the rectangle for selecting the given range of code-points in the TextRun. +FloatRect Font::selectionRectForComplexText(const TextRun& run, + const FloatPoint& point, int height, + int from, int to) const +{ + int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1; + ComplexTextController controller(run, 0, this); + controller.setWordSpacingAdjustment(wordSpacing()); + controller.setLetterSpacingAdjustment(letterSpacing()); + + // Base will point to the x offset for the current script run. Note that, in + // the LTR case, width will be 0. + int base = controller.rtl() ? controller.widthOfFullRun() : 0; + const int leftEdge = base; + + // We want to enumerate the script runs in code point order in the following + // code. This call also resets |controller|. + controller.setBackwardsIteration(false); + + while (controller.nextScriptRun() && (fromX == -1 || toX == -1)) { + // ComplexTextController will helpfully accululate the x offsets for different + // script runs for us. For this code, however, we always want the x offsets + // to start from zero so we call this before each script run. + controller.setXOffsetToZero(); + + if (controller.rtl()) + base -= controller.width(); + + if (fromX == -1 && from >= 0 && static_cast<unsigned>(from) < controller.numCodePoints()) { + // |from| is within this script run. So we index the clusters log to + // find which glyph this code-point contributed to and find its x + // position. + int glyph = controller.logClusters()[from]; + fromX = base + controller.xPositions()[glyph]; + fromAdvance = controller.advances()[glyph]; + } else + from -= controller.numCodePoints(); + + if (toX == -1 && to >= 0 && static_cast<unsigned>(to) < controller.numCodePoints()) { + int glyph = controller.logClusters()[to]; + toX = base + controller.xPositions()[glyph]; + toAdvance = controller.advances()[glyph]; + } else + to -= controller.numCodePoints(); + + if (!controller.rtl()) + base += controller.width(); + } + + // The position in question might be just after the text. + const int rightEdge = base; + if (fromX == -1 && !from) + fromX = leftEdge; + else if (controller.rtl()) + fromX += truncateFixedPointToInteger(fromAdvance); + + if (toX == -1 && !to) + toX = rightEdge; + + ASSERT(fromX != -1 && toX != -1); + + if (fromX < toX) + return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); + + return FloatRect(point.x() + toX, point.y(), fromX - toX, height); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformData.h b/Source/WebCore/platform/graphics/chromium/FontPlatformData.h new file mode 100644 index 0000000..d8ce3e2 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformData.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2007, 2008, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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 FontPlatformData_h +#define FontPlatformData_h + +#if OS(WINDOWS) +#include "FontPlatformDataChromiumWin.h" +#elif OS(LINUX) || OS(FREEBSD) +#include "FontPlatformDataLinux.h" +#endif + +#endif // FontPlatformData_h diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp new file mode 100644 index 0000000..d6c83ec --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, 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 "FontPlatformData.h" + +#include <windows.h> +#include <objidl.h> +#include <mlang.h> + +#include "ChromiumBridge.h" +#include "SkiaFontWin.h" + +namespace WebCore { + +FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType) + : m_font(hashTableDeletedFontValue()) + , m_size(-1) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData::FontPlatformData() + : m_font(0) + , m_size(0) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData::FontPlatformData(HFONT font, float size) + : m_font(RefCountedHFONT::create(font)) + , m_size(size) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +// FIXME: this constructor is needed for SVG fonts but doesn't seem to do much +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : m_font(0) + , m_size(size) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData::FontPlatformData(const FontPlatformData& data) + : m_font(data.m_font) + , m_size(data.m_size) + , m_scriptCache(0) + , m_scriptFontProperties(0) +{ +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& data) +{ + if (this != &data) { + m_font = data.m_font; + m_size = data.m_size; + + // The following fields will get re-computed if necessary. + ScriptFreeCache(&m_scriptCache); + m_scriptCache = 0; + + delete m_scriptFontProperties; + m_scriptFontProperties = 0; + } + return *this; +} + +FontPlatformData::~FontPlatformData() +{ + ScriptFreeCache(&m_scriptCache); + m_scriptCache = 0; + + delete m_scriptFontProperties; + m_scriptFontProperties = 0; +} + +FontPlatformData::RefCountedHFONT::~RefCountedHFONT() +{ + if (m_hfont != reinterpret_cast<HFONT>(-1)) { + SkiaWinOutlineCache::removePathsForFont(m_hfont); + DeleteObject(m_hfont); + } +} + +FontPlatformData::RefCountedHFONT* FontPlatformData::hashTableDeletedFontValue() +{ + static RefPtr<RefCountedHFONT> deletedValue = + RefCountedHFONT::create(reinterpret_cast<HFONT>(-1)); + return deletedValue.get(); +} + +SCRIPT_FONTPROPERTIES* FontPlatformData::scriptFontProperties() const +{ + if (!m_scriptFontProperties) { + m_scriptFontProperties = new SCRIPT_FONTPROPERTIES; + memset(m_scriptFontProperties, 0, sizeof(SCRIPT_FONTPROPERTIES)); + m_scriptFontProperties->cBytes = sizeof(SCRIPT_FONTPROPERTIES); + HRESULT result = ScriptGetFontProperties(0, scriptCache(), + m_scriptFontProperties); + if (result == E_PENDING) { + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont()); + HRESULT hr = ScriptGetFontProperties(dc, scriptCache(), + m_scriptFontProperties); + if (S_OK != hr) { + if (ChromiumBridge::ensureFontLoaded(hfont())) { + // FIXME: Handle gracefully the error if this call also fails. + hr = ScriptGetFontProperties(dc, scriptCache(), + m_scriptFontProperties); + if (S_OK != hr) { + LOG_ERROR("Unable to get the font properties after second attempt"); + } + } + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + } + } + return m_scriptFontProperties; +} + +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + +} diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h b/Source/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h new file mode 100644 index 0000000..b6ebb2e --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformDataChromiumWin.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2006, 2007 Apple Computer, Inc. + * Copyright (c) 2006, 2007, 2008, 2009, 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 FontPlatformDataChromiumWin_h +#define FontPlatformDataChromiumWin_h + +#include "config.h" + +#include "FontOrientation.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/text/StringImpl.h> + +#include <usp10.h> + +typedef struct HFONT__ *HFONT; + +namespace WebCore { + +class FontDescription; + +class FontPlatformData { +public: + // Used for deleted values in the font cache's hash tables. The hash table + // will create us with this structure, and it will compare other values + // to this "Deleted" one. It expects the Deleted one to be differentiable + // from the NULL one (created with the empty constructor), so we can't just + // set everything to NULL. + FontPlatformData(WTF::HashTableDeletedValueType); + FontPlatformData(); + FontPlatformData(HFONT, float size); + FontPlatformData(float size, bool bold, bool oblique); + FontPlatformData(const FontPlatformData&); + + FontPlatformData& operator=(const FontPlatformData&); + + bool isHashTableDeletedValue() const { return m_font == hashTableDeletedFontValue(); } + + ~FontPlatformData(); + + HFONT hfont() const { return m_font ? m_font->hfont() : 0; } + float size() const { return m_size; } + + FontOrientation orientation() const { return Horizontal; } // FIXME: Implement. + + unsigned hash() const + { + return m_font ? m_font->hash() : NULL; + } + + bool operator==(const FontPlatformData& other) const + { + return m_font == other.m_font && m_size == other.m_size; + } + +#ifndef NDEBUG + String description() const; +#endif + + SCRIPT_FONTPROPERTIES* scriptFontProperties() const; + SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } + +private: + // We refcount the internal HFONT so that FontPlatformData can be + // efficiently copied. WebKit depends on being able to copy it, and we + // don't really want to re-create the HFONT. + class RefCountedHFONT : public RefCounted<RefCountedHFONT> { + public: + static PassRefPtr<RefCountedHFONT> create(HFONT hfont) + { + return adoptRef(new RefCountedHFONT(hfont)); + } + + ~RefCountedHFONT(); + + HFONT hfont() const { return m_hfont; } + unsigned hash() const + { + return WTF::StringHasher::createBlobHash<sizeof(HFONT)>(&m_hfont); + } + + bool operator==(const RefCountedHFONT& other) const + { + return m_hfont == other.m_hfont; + } + + private: + // The create() function assumes there is already a refcount of one + // so it can do adoptRef. + RefCountedHFONT(HFONT hfont) : m_hfont(hfont) + { + } + + HFONT m_hfont; + }; + + static RefCountedHFONT* hashTableDeletedFontValue(); + + RefPtr<RefCountedHFONT> m_font; + float m_size; // Point size of the font in pixels. + + mutable SCRIPT_CACHE m_scriptCache; + mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; +}; + +} // WebCore + +#endif // FontPlatformDataChromiumWin_h diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp new file mode 100644 index 0000000..42942cc --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2006, 2007, 2008, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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 "FontPlatformData.h" + +#include "ChromiumBridge.h" +#include "HarfbuzzSkia.h" +#include "NotImplemented.h" +#include "PlatformString.h" + +#include "SkPaint.h" +#include "SkTypeface.h" + +#include <wtf/text/StringImpl.h> + +namespace WebCore { + +static SkPaint::Hinting skiaHinting = SkPaint::kNormal_Hinting; +static bool isSkiaAntiAlias = true; +static bool isSkiaSubpixelGlyphs = false; + +void FontPlatformData::setHinting(SkPaint::Hinting hinting) +{ + skiaHinting = hinting; +} + +void FontPlatformData::setAntiAlias(bool isAntiAlias) +{ + isSkiaAntiAlias = isAntiAlias; +} + +void FontPlatformData::setSubpixelGlyphs(bool isSubpixelGlyphs) +{ + isSkiaSubpixelGlyphs = isSubpixelGlyphs; +} + +FontPlatformData::RefCountedHarfbuzzFace::~RefCountedHarfbuzzFace() +{ + HB_FreeFace(m_harfbuzzFace); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src) + : m_typeface(src.m_typeface) + , m_family(src.m_family) + , m_textSize(src.m_textSize) + , m_fakeBold(src.m_fakeBold) + , m_fakeItalic(src.m_fakeItalic) + , m_orientation(src.m_orientation) + , m_style(src.m_style) + , m_harfbuzzFace(src.m_harfbuzzFace) +{ + SkSafeRef(m_typeface); +} + +FontPlatformData::FontPlatformData(SkTypeface* tf, const char* family, float textSize, bool fakeBold, bool fakeItalic, FontOrientation orientation) + : m_typeface(tf) + , m_family(family) + , m_textSize(textSize) + , m_fakeBold(fakeBold) + , m_fakeItalic(fakeItalic) + , m_orientation(orientation) +{ + SkSafeRef(m_typeface); + querySystemForRenderStyle(); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) + : m_typeface(src.m_typeface) + , m_family(src.m_family) + , m_textSize(textSize) + , m_fakeBold(src.m_fakeBold) + , m_fakeItalic(src.m_fakeItalic) + , m_harfbuzzFace(src.m_harfbuzzFace) +{ + SkSafeRef(m_typeface); + querySystemForRenderStyle(); +} + +FontPlatformData::~FontPlatformData() +{ + SkSafeUnref(m_typeface); +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) +{ + SkRefCnt_SafeAssign(m_typeface, src.m_typeface); + + m_family = src.m_family; + m_textSize = src.m_textSize; + m_fakeBold = src.m_fakeBold; + m_fakeItalic = src.m_fakeItalic; + m_harfbuzzFace = src.m_harfbuzzFace; + m_orientation = src.m_orientation; + m_style = src.m_style; + + return *this; +} + +#ifndef NDEBUG +String FontPlatformData::description() const +{ + return String(); +} +#endif + +void FontPlatformData::setupPaint(SkPaint* paint) const +{ + const float ts = m_textSize >= 0 ? m_textSize : 12; + + paint->setAntiAlias(m_style.useAntiAlias == FontRenderStyle::NoPreference ? isSkiaAntiAlias : m_style.useAntiAlias); + switch (m_style.useHinting) { + case FontRenderStyle::NoPreference: + paint->setHinting(skiaHinting); + break; + case 0: + paint->setHinting(SkPaint::kNo_Hinting); + break; + default: + paint->setHinting(static_cast<SkPaint::Hinting>(m_style.hintStyle)); + break; + } + + paint->setEmbeddedBitmapText(m_style.useBitmaps); + paint->setTextSize(SkFloatToScalar(ts)); + paint->setTypeface(m_typeface); + paint->setFakeBoldText(m_fakeBold); + paint->setTextSkewX(m_fakeItalic ? -SK_Scalar1 / 4 : 0); + paint->setAutohinted(m_style.useAutoHint); + + if (m_style.useAntiAlias == 1 || (m_style.useAntiAlias == FontRenderStyle::NoPreference && isSkiaAntiAlias)) + paint->setLCDRenderText(m_style.useSubpixel == FontRenderStyle::NoPreference ? isSkiaSubpixelGlyphs : m_style.useSubpixel); +} + +SkFontID FontPlatformData::uniqueID() const +{ + return m_typeface->uniqueID(); +} + +bool FontPlatformData::operator==(const FontPlatformData& a) const +{ + // If either of the typeface pointers are invalid (either NULL or the + // special deleted value) then we test for pointer equality. Otherwise, we + // call SkTypeface::Equal on the valid pointers. + bool typefacesEqual; + if (m_typeface == hashTableDeletedFontValue() + || a.m_typeface == hashTableDeletedFontValue() + || !m_typeface + || !a.m_typeface) + typefacesEqual = m_typeface == a.m_typeface; + else + typefacesEqual = SkTypeface::Equal(m_typeface, a.m_typeface); + + return typefacesEqual + && m_textSize == a.m_textSize + && m_fakeBold == a.m_fakeBold + && m_fakeItalic == a.m_fakeItalic + && m_orientation == a.m_orientation + && m_style == a.m_style; +} + +unsigned FontPlatformData::hash() const +{ + unsigned h = SkTypeface::UniqueID(m_typeface); + h ^= 0x01010101 * ((static_cast<int>(m_orientation) << 2) | (static_cast<int>(m_fakeBold) << 1) | static_cast<int>(m_fakeItalic)); + + // This memcpy is to avoid a reinterpret_cast that breaks strict-aliasing + // rules. Memcpy is generally optimized enough so that performance doesn't + // matter here. + uint32_t textSizeBytes; + memcpy(&textSizeBytes, &m_textSize, sizeof(uint32_t)); + h ^= textSizeBytes; + + return h; +} + +bool FontPlatformData::isFixedPitch() const +{ + notImplemented(); + return false; +} + +HB_FaceRec_* FontPlatformData::harfbuzzFace() const +{ + if (!m_harfbuzzFace) + m_harfbuzzFace = RefCountedHarfbuzzFace::create(HB_NewFace(const_cast<FontPlatformData*>(this), harfbuzzSkiaGetTable)); + + return m_harfbuzzFace->face(); +} + +void FontPlatformData::querySystemForRenderStyle() +{ + if (!m_family.length()) { + // We don't have a family for this. Probably because it's a webfont. We + // set all the values to 'no preference' and take the defaults passed + // in from XSETTINGS. + m_style.useBitmaps = FontRenderStyle::NoPreference; + m_style.useAutoHint = FontRenderStyle::NoPreference; + m_style.useHinting = FontRenderStyle::NoPreference; + m_style.useAntiAlias = FontRenderStyle::NoPreference; + m_style.useSubpixel = FontRenderStyle::NoPreference; + return; + } + + ChromiumBridge::getRenderStyleForStrike(m_family.data(), (((int)m_textSize) << 2) | (m_typeface->style() & 3), &m_style); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h new file mode 100644 index 0000000..43771d7 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006, 2007, 2008, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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 FontPlatformDataLinux_h +#define FontPlatformDataLinux_h + +#include "FontOrientation.h" +#include "FontRenderStyle.h" +#include <wtf/Forward.h> +#include <wtf/RefPtr.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringImpl.h> +#include <SkPaint.h> + +class SkTypeface; +typedef uint32_t SkFontID; + +struct HB_FaceRec_; + +namespace WebCore { + +class FontDescription; + +// ----------------------------------------------------------------------------- +// FontPlatformData is the handle which WebKit has on a specific face. A face +// is the tuple of (font, size, ...etc). Here we are just wrapping a Skia +// SkTypeface pointer and dealing with the reference counting etc. +// ----------------------------------------------------------------------------- +class FontPlatformData { +public: + // Used for deleted values in the font cache's hash tables. The hash table + // will create us with this structure, and it will compare other values + // to this "Deleted" one. It expects the Deleted one to be differentiable + // from the NULL one (created with the empty constructor), so we can't just + // set everything to NULL. + FontPlatformData(WTF::HashTableDeletedValueType) + : m_typeface(hashTableDeletedFontValue()) + , m_textSize(0) + , m_fakeBold(false) + , m_fakeItalic(false) + { } + + FontPlatformData() + : m_typeface(0) + , m_textSize(0) + , m_fakeBold(false) + , m_fakeItalic(false) + , m_orientation(Horizontal) + { } + + FontPlatformData(float textSize, bool fakeBold, bool fakeItalic) + : m_typeface(0) + , m_textSize(textSize) + , m_fakeBold(fakeBold) + , m_fakeItalic(fakeItalic) + , m_orientation(Horizontal) + { } + + FontPlatformData(const FontPlatformData&); + FontPlatformData(SkTypeface*, const char* name, float textSize, bool fakeBold, bool fakeItalic, FontOrientation orientation = Horizontal); + FontPlatformData(const FontPlatformData& src, float textSize); + ~FontPlatformData(); + + // ------------------------------------------------------------------------- + // Return true iff this font is monospaced (i.e. every glyph has an equal x + // advance) + // ------------------------------------------------------------------------- + bool isFixedPitch() const; + + // ------------------------------------------------------------------------- + // Setup a Skia painting context to use this font. + // ------------------------------------------------------------------------- + void setupPaint(SkPaint*) const; + + // ------------------------------------------------------------------------- + // Return Skia's unique id for this font. This encodes both the style and + // the font's file name so refers to a single face. + // ------------------------------------------------------------------------- + SkFontID uniqueID() const; + + unsigned hash() const; + float size() const { return m_textSize; } + + FontOrientation orientation() const { return m_orientation; } + + bool operator==(const FontPlatformData&) const; + FontPlatformData& operator=(const FontPlatformData&); + bool isHashTableDeletedValue() const { return m_typeface == hashTableDeletedFontValue(); } + +#ifndef NDEBUG + String description() const; +#endif + + HB_FaceRec_* harfbuzzFace() const; + + // ------------------------------------------------------------------------- + // Global font preferences... + + static void setHinting(SkPaint::Hinting); + static void setAntiAlias(bool on); + static void setSubpixelGlyphs(bool on); + +private: + class RefCountedHarfbuzzFace : public RefCounted<RefCountedHarfbuzzFace> { + public: + static PassRefPtr<RefCountedHarfbuzzFace> create(HB_FaceRec_* harfbuzzFace) + { + return adoptRef(new RefCountedHarfbuzzFace(harfbuzzFace)); + } + + ~RefCountedHarfbuzzFace(); + + HB_FaceRec_* face() const { return m_harfbuzzFace; } + + private: + RefCountedHarfbuzzFace(HB_FaceRec_* harfbuzzFace) : m_harfbuzzFace(harfbuzzFace) + { + } + + HB_FaceRec_* m_harfbuzzFace; + }; + + void querySystemForRenderStyle(); + + // FIXME: Could SkAutoUnref be used here? + SkTypeface* m_typeface; + CString m_family; + float m_textSize; + bool m_fakeBold; + bool m_fakeItalic; + FontOrientation m_orientation; + FontRenderStyle m_style; + mutable RefPtr<RefCountedHarfbuzzFace> m_harfbuzzFace; + + SkTypeface* hashTableDeletedFontValue() const { return reinterpret_cast<SkTypeface*>(-1); } +}; + +} // namespace WebCore + +#endif // ifdef FontPlatformData_h diff --git a/Source/WebCore/platform/graphics/chromium/FontRenderStyle.h b/Source/WebCore/platform/graphics/chromium/FontRenderStyle.h new file mode 100644 index 0000000..6e2ae54 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontRenderStyle.h @@ -0,0 +1,74 @@ +/* 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 FontRenderStyle_h +#define FontRenderStyle_h + +namespace WebCore { + +// FontRenderStyle describes the user's preferences for rendering a font at a +// given size. +struct FontRenderStyle { + enum { + NoPreference = 2, + }; + + FontRenderStyle() + : useBitmaps(0), + useAutoHint(0), + useHinting(0), + hintStyle(0), + useAntiAlias(0), + useSubpixel(0) { } + + bool operator==(const FontRenderStyle& a) const + { + return useBitmaps == a.useBitmaps + && useAutoHint == a.useAutoHint + && useHinting == a.useHinting + && hintStyle == a.hintStyle + && useAntiAlias == a.useAntiAlias + && useSubpixel == a.useSubpixel; + } + + // Each of the use* members below can take one of three values: + // 0: off + // 1: on + // NoPreference: no preference expressed + char useBitmaps; // use embedded bitmap strike if possible + char useAutoHint; // use 'auto' hinting (FreeType specific) + char useHinting; // hint glyphs to the pixel grid + char hintStyle; // level of hinting, 0..3 + char useAntiAlias; // antialias glyph shapes + char useSubpixel; // use subpixel antialias +}; + +} + +#endif // FontRenderStyle_h diff --git a/Source/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.cpp new file mode 100644 index 0000000..bea0572 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.cpp @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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" +#include "FontUtilsChromiumWin.h" + +#include <limits> + +#include "PlatformString.h" +#include "UniscribeHelper.h" +#include <unicode/locid.h> +#include <unicode/uchar.h> +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +namespace { + +bool isFontPresent(const UChar* fontName) +{ + HFONT hfont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + fontName); + if (!hfont) + return false; + HDC dc = GetDC(0); + HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont)); + WCHAR actualFontName[LF_FACESIZE]; + GetTextFace(dc, LF_FACESIZE, actualFontName); + actualFontName[LF_FACESIZE - 1] = 0; + SelectObject(dc, oldFont); + DeleteObject(hfont); + ReleaseDC(0, dc); + // We don't have to worry about East Asian fonts with locale-dependent + // names here for now. + return !wcscmp(fontName, actualFontName); +} + +// A simple mapping from UScriptCode to family name. This is a sparse array, +// which works well since the range of UScriptCode values is small. +typedef const UChar* ScriptToFontMap[USCRIPT_CODE_LIMIT]; + +void initializeScriptFontMap(ScriptToFontMap& scriptFontMap) +{ + struct FontMap { + UScriptCode script; + const UChar* family; + }; + + static const FontMap fontMap[] = { + {USCRIPT_LATIN, L"times new roman"}, + {USCRIPT_GREEK, L"times new roman"}, + {USCRIPT_CYRILLIC, L"times new roman"}, + // FIXME: Consider trying new Vista fonts before XP fonts for CJK. + // Some Vista users do want to use Vista cleartype CJK fonts. If we + // did, the results of tests with CJK characters would have to be + // regenerated for Vista. + {USCRIPT_SIMPLIFIED_HAN, L"simsun"}, + {USCRIPT_TRADITIONAL_HAN, L"pmingliu"}, + {USCRIPT_HIRAGANA, L"ms pgothic"}, + {USCRIPT_KATAKANA, L"ms pgothic"}, + {USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"}, + {USCRIPT_HANGUL, L"gulim"}, + {USCRIPT_THAI, L"tahoma"}, + {USCRIPT_HEBREW, L"david"}, + {USCRIPT_ARABIC, L"tahoma"}, + {USCRIPT_DEVANAGARI, L"mangal"}, + {USCRIPT_BENGALI, L"vrinda"}, + {USCRIPT_GURMUKHI, L"raavi"}, + {USCRIPT_GUJARATI, L"shruti"}, + {USCRIPT_TAMIL, L"latha"}, + {USCRIPT_TELUGU, L"gautami"}, + {USCRIPT_KANNADA, L"tunga"}, + {USCRIPT_GEORGIAN, L"sylfaen"}, + {USCRIPT_ARMENIAN, L"sylfaen"}, + {USCRIPT_THAANA, L"mv boli"}, + {USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"}, + {USCRIPT_CHEROKEE, L"plantagenet cherokee"}, + {USCRIPT_MONGOLIAN, L"mongolian balti"}, + // For USCRIPT_COMMON, we map blocks to scripts when + // that makes sense. + }; + + struct ScriptToFontFamilies { + UScriptCode script; + const UChar** families; + }; + + // Kartika on Vista or earlier lacks the support for Chillu + // letters added to Unicode 5.1. + // Try AnjaliOldLipi (a very widely used Malaylalam font with the full + // Unicode 5.x support) before falling back to Kartika. + static const UChar* malayalamFonts[] = {L"AnjaliOldLipi", L"Lohit Malayalam", L"Kartika", L"Rachana", 0}; + // Try Khmer OS before Vista fonts because 'Khmer OS' goes along better + // with Latin and looks better/larger for the same size. + static const UChar* khmerFonts[] = {L"Khmer OS", L"MoolBoran", L"DaunPenh", L"Code2000", 0}; + // For the following 6 scripts, two or fonts are listed. The fonts in + // the 1st slot are not available on Windows XP. To support these + // scripts on XP, listed in the rest of slots are widely used + // fonts. + static const UChar* ethiopicFonts[] = {L"Nyala", L"Abyssinica SIL", L"Ethiopia Jiret", L"Visual Geez Unicode", L"GF Zemen Unicode", 0}; + static const UChar* oriyaFonts[] = {L"Kalinga", L"ori1Uni", L"Lohit Oriya", 0}; + static const UChar* laoFonts[] = {L"DokChampa", L"Saysettha OT", L"Phetsarath OT", L"Code2000", 0}; + static const UChar* tibetanFonts[] = {L"Microsoft Himalaya", L"Jomolhari", L"Tibetan Machine Uni", 0}; + static const UChar* sinhalaFonts[] = {L"Iskoola Pota", L"AksharUnicode", 0}; + static const UChar* yiFonts[] = {L"Microsoft Yi Balti", L"Nuosu SIL", L"Code2000", 0}; + // http://www.bethmardutho.org/support/meltho/download/index.php + static const UChar* syriacFonts[] = {L"Estrangelo Edessa", L"Estrangelo Nisibin", L"Code2000", 0}; + // No Myanmar/Burmese font is shipped with Windows, yet. Try a few + // widely available/used ones that supports Unicode 5.1 or later. + static const UChar* myanmarFonts[] = {L"Padauk", L"Parabaik", L"Myanmar3", L"Code2000", 0}; + + static const ScriptToFontFamilies scriptToFontFamilies[] = { + {USCRIPT_MALAYALAM, malayalamFonts}, + {USCRIPT_KHMER, khmerFonts}, + {USCRIPT_ETHIOPIC, ethiopicFonts}, + {USCRIPT_ORIYA, oriyaFonts}, + {USCRIPT_LAO, laoFonts}, + {USCRIPT_TIBETAN, tibetanFonts}, + {USCRIPT_SINHALA, sinhalaFonts}, + {USCRIPT_YI, yiFonts}, + {USCRIPT_SYRIAC, syriacFonts}, + {USCRIPT_MYANMAR, myanmarFonts}, + }; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontMap); ++i) + scriptFontMap[fontMap[i].script] = fontMap[i].family; + + // FIXME: Instead of scanning the hard-coded list, we have to + // use EnumFont* to 'inspect' fonts to pick up fonts covering scripts + // when it's possible (e.g. using OS/2 table). If we do that, this + // had better be pulled out of here. + for (size_t i = 0; i < WTF_ARRAY_LENGTH(scriptToFontFamilies); ++i) { + UScriptCode script = scriptToFontFamilies[i].script; + scriptFontMap[script] = 0; + const UChar** familyPtr = scriptToFontFamilies[i].families; + while (*familyPtr) { + if (isFontPresent(*familyPtr)) { + scriptFontMap[script] = *familyPtr; + break; + } + ++familyPtr; + } + } + + // Initialize the locale-dependent mapping. + // Since Chrome synchronizes the ICU default locale with its UI locale, + // this ICU locale tells the current UI locale of Chrome. + icu::Locale locale = icu::Locale::getDefault(); + const UChar* localeFamily = 0; + if (locale == icu::Locale::getJapanese()) + localeFamily = scriptFontMap[USCRIPT_HIRAGANA]; + else if (locale == icu::Locale::getKorean()) + localeFamily = scriptFontMap[USCRIPT_HANGUL]; + else if (locale == icu::Locale::getTraditionalChinese()) + localeFamily = scriptFontMap[USCRIPT_TRADITIONAL_HAN]; + else { + // For other locales, use the simplified Chinese font for Han. + localeFamily = scriptFontMap[USCRIPT_SIMPLIFIED_HAN]; + } + if (localeFamily) + scriptFontMap[USCRIPT_HAN] = localeFamily; +} + +// There are a lot of characters in USCRIPT_COMMON that can be covered +// by fonts for scripts closely related to them. See +// http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Script=Common:] +// FIXME: make this more efficient with a wider coverage +UScriptCode getScriptBasedOnUnicodeBlock(int ucs4) +{ + UBlockCode block = ublock_getCode(ucs4); + switch (block) { + case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: + return USCRIPT_HAN; + case UBLOCK_HIRAGANA: + case UBLOCK_KATAKANA: + return USCRIPT_HIRAGANA; + case UBLOCK_ARABIC: + return USCRIPT_ARABIC; + case UBLOCK_THAI: + return USCRIPT_THAI; + case UBLOCK_GREEK: + return USCRIPT_GREEK; + case UBLOCK_DEVANAGARI: + // For Danda and Double Danda (U+0964, U+0965), use a Devanagari + // font for now although they're used by other scripts as well. + // Without a context, we can't do any better. + return USCRIPT_DEVANAGARI; + case UBLOCK_ARMENIAN: + return USCRIPT_ARMENIAN; + case UBLOCK_GEORGIAN: + return USCRIPT_GEORGIAN; + case UBLOCK_KANNADA: + return USCRIPT_KANNADA; + default: + return USCRIPT_COMMON; + } +} + +UScriptCode getScript(int ucs4) +{ + UErrorCode err = U_ZERO_ERROR; + UScriptCode script = uscript_getScript(ucs4, &err); + // If script is invalid, common or inherited or there's an error, + // infer a script based on the unicode block of a character. + if (script <= USCRIPT_INHERITED || U_FAILURE(err)) + script = getScriptBasedOnUnicodeBlock(ucs4); + return script; +} + +const int kUndefinedAscent = std::numeric_limits<int>::min(); + +// Given an HFONT, return the ascent. If GetTextMetrics fails, +// kUndefinedAscent is returned, instead. +int getAscent(HFONT hfont) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + TEXTMETRIC tm; + BOOL gotMetrics = GetTextMetrics(dc, &tm); + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + return gotMetrics ? tm.tmAscent : kUndefinedAscent; +} + +struct FontData { + FontData() + : hfont(0) + , ascent(kUndefinedAscent) + , scriptCache(0) + { + } + + HFONT hfont; + int ascent; + mutable SCRIPT_CACHE scriptCache; +}; + +// Again, using hash_map does not earn us much here. page_cycler_test intl2 +// gave us a 'better' result with map than with hash_map even though they're +// well-within 1-sigma of each other so that the difference is not significant. +// On the other hand, some pages in intl2 seem to take longer to load with map +// in the 1st pass. Need to experiment further. +typedef HashMap<String, FontData> FontDataCache; + +} // namespace + +// FIXME: this is font fallback code version 0.1 +// - Cover all the scripts +// - Get the default font for each script/generic family from the +// preference instead of hardcoding in the source. +// (at least, read values from the registry for IE font settings). +// - Support generic families (from FontDescription) +// - If the default font for a script is not available, +// try some more fonts known to support it. Finally, we can +// use EnumFontFamilies or similar APIs to come up with a list of +// fonts supporting the script and cache the result. +// - Consider using UnicodeSet (or UnicodeMap) converted from +// GLYPHSET (BMP) or directly read from truetype cmap tables to +// keep track of which character is supported by which font +// - Update script_font_cache in response to WM_FONTCHANGE + +const UChar* getFontFamilyForScript(UScriptCode script, + FontDescription::GenericFamilyType generic) +{ + static ScriptToFontMap scriptFontMap; + static bool initialized = false; + if (!initialized) { + initializeScriptFontMap(scriptFontMap); + initialized = true; + } + if (script == USCRIPT_INVALID_CODE) + return 0; + ASSERT(script < USCRIPT_CODE_LIMIT); + return scriptFontMap[script]; +} + +// FIXME: +// - Handle 'Inherited', 'Common' and 'Unknown' +// (see http://www.unicode.org/reports/tr24/#Usage_Model ) +// For 'Inherited' and 'Common', perhaps we need to +// accept another parameter indicating the previous family +// and just return it. +// - All the characters (or characters up to the point a single +// font can cover) need to be taken into account +const UChar* getFallbackFamily(const UChar* characters, + int length, + FontDescription::GenericFamilyType generic, + UChar32* charChecked, + UScriptCode* scriptChecked) +{ + ASSERT(characters && characters[0] && length > 0); + UScriptCode script = USCRIPT_COMMON; + + // Sometimes characters common to script (e.g. space) is at + // the beginning of a string so that we need to skip them + // to get a font required to render the string. + int i = 0; + UChar32 ucs4 = 0; + while (i < length && script == USCRIPT_COMMON) { + U16_NEXT(characters, i, length, ucs4); + script = getScript(ucs4); + } + + // For the full-width ASCII characters (U+FF00 - U+FF5E), use the font for + // Han (determined in a locale-dependent way above). Full-width ASCII + // characters are rather widely used in Japanese and Chinese documents and + // they're fully covered by Chinese, Japanese and Korean fonts. + if (0xFF00 < ucs4 && ucs4 < 0xFF5F) + script = USCRIPT_HAN; + + if (script == USCRIPT_COMMON) + script = getScriptBasedOnUnicodeBlock(ucs4); + + const UChar* family = getFontFamilyForScript(script, generic); + // Another lame work-around to cover non-BMP characters. + // If the font family for script is not found or the character is + // not in BMP (> U+FFFF), we resort to the hard-coded list of + // fallback fonts for now. + if (!family || ucs4 > 0xFFFF) { + int plane = ucs4 >> 16; + switch (plane) { + case 1: + family = L"code2001"; + break; + case 2: + // Use a Traditional Chinese ExtB font if in Traditional Chinese locale. + // Otherwise, use a Simplified Chinese ExtB font. Windows Japanese + // fonts do support a small subset of ExtB (that are included in JIS X 0213), + // but its coverage is rather sparse. + // Eventually, this should be controlled by lang/xml:lang. + if (icu::Locale::getDefault() == icu::Locale::getTraditionalChinese()) + family = L"pmingliu-extb"; + else + family = L"simsun-extb"; + break; + default: + family = L"lucida sans unicode"; + } + } + + if (charChecked) + *charChecked = ucs4; + if (scriptChecked) + *scriptChecked = script; + return family; +} + +// Be aware that this is not thread-safe. +bool getDerivedFontData(const UChar* family, + int style, + LOGFONT* logfont, + int* ascent, + HFONT* hfont, + SCRIPT_CACHE** scriptCache) +{ + ASSERT(logfont); + ASSERT(family); + ASSERT(*family); + + // It does not matter that we leak font data when we exit. + static FontDataCache fontDataCache; + + // FIXME: This comes up pretty high in the profile so that + // we need to measure whether using SHA256 (after coercing all the + // fields to char*) is faster than String::format. + String fontKey = String::format("%1d:%d:%ls", style, logfont->lfHeight, family); + FontDataCache::iterator iter = fontDataCache.find(fontKey); + FontData* derived; + if (iter == fontDataCache.end()) { + ASSERT(wcslen(family) < LF_FACESIZE); + wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family); + // FIXME: CreateFontIndirect always comes up with + // a font even if there's no font matching the name. Need to + // check it against what we actually want (as is done in + // FontCacheWin.cpp) + pair<FontDataCache::iterator, bool> entry = fontDataCache.add(fontKey, FontData()); + derived = &entry.first->second; + derived->hfont = CreateFontIndirect(logfont); + // GetAscent may return kUndefinedAscent, but we still want to + // cache it so that we won't have to call CreateFontIndirect once + // more for HFONT next time. + derived->ascent = getAscent(derived->hfont); + } else { + derived = &iter->second; + // Last time, GetAscent failed so that only HFONT was + // cached. Try once more assuming that TryPreloadFont + // was called by a caller between calls. + if (kUndefinedAscent == derived->ascent) + derived->ascent = getAscent(derived->hfont); + } + *hfont = derived->hfont; + *ascent = derived->ascent; + *scriptCache = &(derived->scriptCache); + return *ascent != kUndefinedAscent; +} + +int getStyleFromLogfont(const LOGFONT* logfont) +{ + // FIXME: consider defining UNDEFINED or INVALID for style and + // returning it when logfont is 0 + if (!logfont) { + ASSERT_NOT_REACHED(); + return FontStyleNormal; + } + return (logfont->lfItalic ? FontStyleItalic : FontStyleNormal) | + (logfont->lfUnderline ? FontStyleUnderlined : FontStyleNormal) | + (logfont->lfWeight >= 700 ? FontStyleBold : FontStyleNormal); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.h b/Source/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.h new file mode 100644 index 0000000..b637ede --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/FontUtilsChromiumWin.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006, 2007, 2008, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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. + */ + +// A collection of utilities for font handling. + +// FIXME: Move all methods to the files that have their callsites and remove this file. +// *Utils files are not very WebKit-ty. + +#ifndef FontUtilsChromiumWin_h +#define FontUtilsChromiumWin_h + +#include <usp10.h> +#include <wchar.h> +#include <windows.h> + +#include "FontDescription.h" +#include <unicode/uscript.h> + +namespace WebCore { + +// Return a font family that supports a script and belongs to |generic| font +// family. It can return NULL and a caller has to implement its own fallback. +const UChar* getFontFamilyForScript(UScriptCode, FontDescription::GenericFamilyType); + +// Return a font family that can render |characters| based on +// what script characters belong to. When char_checked is non-NULL, +// it's filled with the character used to determine the script. +// When script_checked is non-NULL, the script used to determine +// the family is returned. +// FIXME: This function needs a total overhaul. +const UChar* getFallbackFamily(const UChar* characters, int length, + FontDescription::GenericFamilyType, + UChar32* charChecked, + UScriptCode* scriptChecked); + +// Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|, +// calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE +// in FontData. +// |style| is only used for cache key generation. |style| is +// bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and +// should match what's contained in LOGFONT. It should be calculated +// by calling GetStyleFromLogFont. +// Returns false if the font is not accessible, in which case |ascent| field +// of |fontdata| is set to kUndefinedAscent. +// Be aware that this is not thread-safe. +// FIXME: Instead of having three out params, we'd better have one +// (|*FontData|), but somehow it mysteriously messes up the layout for +// certain complex script pages (e.g. hi.wikipedia.org) and also crashes +// at the start-up if recently visited page list includes pages with complex +// scripts in their title. Moreover, somehow the very first-pass of +// intl2 page-cycler test is noticeably slower with one out param than +// the current version although the subsequent 9 passes take about the +// same time. +bool getDerivedFontData(const UChar* family, int style, LOGFONT*, int* ascent, HFONT*, SCRIPT_CACHE**); + +enum { + FontStyleNormal = 0, + FontStyleBold = 1, + FontStyleItalic = 2, + FontStyleUnderlined = 4 +}; + +// Derive style (bit-wise OR of FONT_STYLE_BOLD, FONT_STYLE_UNDERLINED, and +// FONT_STYLE_ITALIC) from LOGFONT. Returns 0 if |*logfont| is NULL. +int getStyleFromLogfont(const LOGFONT*); + +} // namespace WebCore + +#endif // FontUtilsChromiumWin_h diff --git a/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp b/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp new file mode 100644 index 0000000..697cf5e --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp @@ -0,0 +1,286 @@ +/* + * 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" + +#include "GLES2Canvas.h" + +#include "DrawingBuffer.h" +#include "FloatRect.h" +#include "GraphicsContext3D.h" +#include "IntRect.h" +#include "PlatformString.h" +#include "SharedGraphicsContext3D.h" +#include "SolidFillShader.h" +#include "TexShader.h" +#include "Texture.h" + +#define _USE_MATH_DEFINES +#include <math.h> + +#include <wtf/OwnArrayPtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +struct GLES2Canvas::State { + State() + : m_fillColor(0, 0, 0, 255) + , m_alpha(1.0f) + , m_compositeOp(CompositeSourceOver) + { + } + Color m_fillColor; + float m_alpha; + CompositeOperator m_compositeOp; + AffineTransform m_ctm; +}; + +GLES2Canvas::GLES2Canvas(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const IntSize& size) + : m_size(size) + , m_context(context) + , m_drawingBuffer(drawingBuffer) + , m_state(0) +{ + m_flipMatrix.translate(-1.0f, 1.0f); + m_flipMatrix.scale(2.0f / size.width(), -2.0f / size.height()); + + m_stateStack.append(State()); + m_state = &m_stateStack.last(); +} + +GLES2Canvas::~GLES2Canvas() +{ +} + +void GLES2Canvas::bindFramebuffer() +{ + m_drawingBuffer->bind(); +} + +void GLES2Canvas::clearRect(const FloatRect& rect) +{ + bindFramebuffer(); + if (m_state->m_ctm.isIdentity()) { + m_context->scissor(rect); + m_context->enable(GraphicsContext3D::SCISSOR_TEST); + m_context->clearColor(Color(RGBA32(0))); + m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); + m_context->disable(GraphicsContext3D::SCISSOR_TEST); + } else { + save(); + setCompositeOperation(CompositeClear); + fillRect(rect, Color(RGBA32(0)), ColorSpaceDeviceRGB); + restore(); + } +} + +void GLES2Canvas::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) +{ + m_context->applyCompositeOperator(m_state->m_compositeOp); + m_context->useQuadVertices(); + + AffineTransform matrix(m_flipMatrix); + matrix.multLeft(m_state->m_ctm); + matrix.translate(rect.x(), rect.y()); + matrix.scale(rect.width(), rect.height()); + + m_context->useFillSolidProgram(matrix, color); + + bindFramebuffer(); + m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4); +} + +void GLES2Canvas::fillRect(const FloatRect& rect) +{ + fillRect(rect, m_state->m_fillColor, ColorSpaceDeviceRGB); +} + +void GLES2Canvas::setFillColor(const Color& color, ColorSpace colorSpace) +{ + m_state->m_fillColor = color; +} + +void GLES2Canvas::setAlpha(float alpha) +{ + m_state->m_alpha = alpha; +} + +void GLES2Canvas::translate(float x, float y) +{ + m_state->m_ctm.translate(x, y); +} + +void GLES2Canvas::rotate(float angleInRadians) +{ + m_state->m_ctm.rotate(angleInRadians * (180.0f / M_PI)); +} + +void GLES2Canvas::scale(const FloatSize& size) +{ + m_state->m_ctm.scale(size.width(), size.height()); +} + +void GLES2Canvas::concatCTM(const AffineTransform& affine) +{ + m_state->m_ctm.multLeft(affine); +} + +void GLES2Canvas::save() +{ + m_stateStack.append(State(m_stateStack.last())); + m_state = &m_stateStack.last(); +} + +void GLES2Canvas::restore() +{ + ASSERT(!m_stateStack.isEmpty()); + m_stateStack.removeLast(); + m_state = &m_stateStack.last(); +} + +void GLES2Canvas::drawTexturedRect(unsigned texture, const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp) +{ + m_context->applyCompositeOperator(compositeOp); + + m_context->useQuadVertices(); + m_context->setActiveTexture(GraphicsContext3D::TEXTURE0); + + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, texture); + + drawQuad(textureSize, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha); +} + +void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp) +{ + drawTexturedRect(texture, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha, colorSpace, compositeOp); +} + + +void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha, ColorSpace colorSpace, CompositeOperator compositeOp) +{ + m_context->applyCompositeOperator(compositeOp); + const TilingData& tiles = texture->tiles(); + IntRect tileIdxRect = tiles.overlappedTileIndices(srcRect); + + m_context->useQuadVertices(); + m_context->setActiveTexture(GraphicsContext3D::TEXTURE0); + + for (int y = tileIdxRect.y(); y <= tileIdxRect.bottom(); y++) { + for (int x = tileIdxRect.x(); x <= tileIdxRect.right(); x++) + drawTexturedRectTile(texture, tiles.tileIndex(x, y), srcRect, dstRect, transform, alpha); + } +} + +void GLES2Canvas::drawTexturedRectTile(Texture* texture, int tile, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha) +{ + if (dstRect.isEmpty()) + return; + + const TilingData& tiles = texture->tiles(); + + texture->bindTile(tile); + + FloatRect srcRectClippedInTileSpace; + FloatRect dstRectIntersected; + tiles.intersectDrawQuad(srcRect, dstRect, tile, &srcRectClippedInTileSpace, &dstRectIntersected); + + IntRect tileBoundsWithBorder = tiles.tileBoundsWithBorder(tile); + + drawQuad(tileBoundsWithBorder.size(), srcRectClippedInTileSpace, dstRectIntersected, transform, alpha); +} + +void GLES2Canvas::drawQuad(const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha) +{ + AffineTransform matrix(m_flipMatrix); + matrix.multLeft(transform); + matrix.translate(dstRect.x(), dstRect.y()); + matrix.scale(dstRect.width(), dstRect.height()); + + AffineTransform texMatrix; + texMatrix.scale(1.0f / textureSize.width(), 1.0f / textureSize.height()); + texMatrix.translate(srcRect.x(), srcRect.y()); + texMatrix.scale(srcRect.width(), srcRect.height()); + + bindFramebuffer(); + + m_context->useTextureProgram(matrix, texMatrix, alpha); + m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4); + checkGLError("glDrawArrays"); +} + +void GLES2Canvas::setCompositeOperation(CompositeOperator op) +{ + m_state->m_compositeOp = op; +} + +Texture* GLES2Canvas::createTexture(NativeImagePtr ptr, Texture::Format format, int width, int height) +{ + return m_context->createTexture(ptr, format, width, height); +} + +Texture* GLES2Canvas::getTexture(NativeImagePtr ptr) +{ + return m_context->getTexture(ptr); +} + +void GLES2Canvas::checkGLError(const char* header) +{ +#ifndef NDEBUG + unsigned err; + while ((err = m_context->getError()) != GraphicsContext3D::NO_ERROR) { + const char* errorStr = "*** UNKNOWN ERROR ***"; + switch (err) { + case GraphicsContext3D::INVALID_ENUM: + errorStr = "GraphicsContext3D::INVALID_ENUM"; + break; + case GraphicsContext3D::INVALID_VALUE: + errorStr = "GraphicsContext3D::INVALID_VALUE"; + break; + case GraphicsContext3D::INVALID_OPERATION: + errorStr = "GraphicsContext3D::INVALID_OPERATION"; + break; + case GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION: + errorStr = "GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION"; + break; + case GraphicsContext3D::OUT_OF_MEMORY: + errorStr = "GraphicsContext3D::OUT_OF_MEMORY"; + break; + } + if (header) + LOG_ERROR("%s: %s", header, errorStr); + else + LOG_ERROR("%s", errorStr); + } +#endif +} + +} + diff --git a/Source/WebCore/platform/graphics/chromium/GLES2Canvas.h b/Source/WebCore/platform/graphics/chromium/GLES2Canvas.h new file mode 100644 index 0000000..6fc1a0e --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/GLES2Canvas.h @@ -0,0 +1,106 @@ +/* + * 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 GLES2Canvas_h +#define GLES2Canvas_h + +#include "AffineTransform.h" +#include "Color.h" +#include "ColorSpace.h" +#include "GraphicsTypes.h" +#include "ImageSource.h" +#include "Texture.h" + +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Color; +class DrawingBuffer; +class FloatRect; +class GraphicsContext3D; +class SharedGraphicsContext3D; + +class GLES2Canvas : public Noncopyable { +public: + GLES2Canvas(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&); + ~GLES2Canvas(); + + void fillRect(const FloatRect&, const Color&, ColorSpace); + void fillRect(const FloatRect&); + void clearRect(const FloatRect&); + void setFillColor(const Color&, ColorSpace); + void setAlpha(float alpha); + void setCompositeOperation(CompositeOperator); + void translate(float x, float y); + void rotate(float angleInRadians); + void scale(const FloatSize&); + void concatCTM(const AffineTransform&); + + void save(); + void restore(); + + // non-standard functions + // These are not standard GraphicsContext functions, and should be pushed + // down into a PlatformContextGLES2 at some point. + void drawTexturedRect(unsigned texture, const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace, CompositeOperator); + void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha, ColorSpace, CompositeOperator); + void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace, CompositeOperator); + Texture* createTexture(NativeImagePtr, Texture::Format, int width, int height); + Texture* getTexture(NativeImagePtr); + + SharedGraphicsContext3D* context() const { return m_context; } + + void bindFramebuffer(); + + DrawingBuffer* drawingBuffer() const { return m_drawingBuffer; } + +private: + void drawTexturedRectTile(Texture* texture, int tile, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha); + void drawQuad(const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha); + void applyCompositeOperator(CompositeOperator); + void checkGLError(const char* header); + + IntSize m_size; + + SharedGraphicsContext3D* m_context; + DrawingBuffer* m_drawingBuffer; + + struct State; + WTF::Vector<State> m_stateStack; + State* m_state; + AffineTransform m_flipMatrix; +}; + +} + +#endif // GLES2Canvas_h diff --git a/Source/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp new file mode 100644 index 0000000..e71f66a --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2008, 2009 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 <windows.h> +#include <vector> + +#include "ChromiumBridge.h" +#include "Font.h" +#include "GlyphPageTreeNode.h" +#include "SimpleFontData.h" +#include "UniscribeHelperTextRun.h" +#include "WindowsVersion.h" + +namespace WebCore { + +// Fills one page of font data pointers with 0 to indicate that there +// are no glyphs for the characters. +static void fillEmptyGlyphs(GlyphPage* page) +{ + for (int i = 0; i < GlyphPage::size; ++i) + page->setGlyphDataForIndex(i, 0, 0); +} + +// Lazily initializes space glyph +static Glyph initSpaceGlyph(HDC dc, Glyph* spaceGlyph) +{ + if (*spaceGlyph) + return *spaceGlyph; + + static wchar_t space = ' '; + GetGlyphIndices(dc, &space, 1, spaceGlyph, 0); + return *spaceGlyph; +} + +// Fills |length| glyphs starting at |offset| in a |page| in the Basic +// Multilingual Plane (<= U+FFFF). The input buffer size should be the +// same as |length|. We can use the standard Windows GDI functions here. +// Returns true if any glyphs were found. +static bool fillBMPGlyphs(unsigned offset, + unsigned length, + UChar* buffer, + GlyphPage* page, + const SimpleFontData* fontData, + bool recurse) +{ + HDC dc = GetDC((HWND)0); + HGDIOBJ oldFont = SelectObject(dc, fontData->platformData().hfont()); + + TEXTMETRIC tm = {0}; + if (!GetTextMetrics(dc, &tm)) { + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + if (recurse) { + if (ChromiumBridge::ensureFontLoaded(fontData->platformData().hfont())) + return fillBMPGlyphs(offset, length, buffer, page, fontData, false); + else { + fillEmptyGlyphs(page); + return false; + } + } else { + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401 + LOG_ERROR("Unable to get the text metrics after second attempt"); + fillEmptyGlyphs(page); + return false; + } + } + + // FIXME: GetGlyphIndices() sets each item of localGlyphBuffer[] + // with the one of the values listed below. + // * With the GGI_MARK_NONEXISTING_GLYPHS flag + // + If the font has a glyph available for the character, + // localGlyphBuffer[i] > 0x0. + // + If the font does not have glyphs available for the character, + // localGlyphBuffer[i] = 0x1F (TrueType Collection?) or + // 0xFFFF (OpenType?). + // * Without the GGI_MARK_NONEXISTING_GLYPHS flag + // + If the font has a glyph available for the character, + // localGlyphBuffer[i] > 0x0. + // + If the font does not have glyphs available for the character, + // localGlyphBuffer[i] = 0x80. + // (Windows automatically assigns the glyph for a box character to + // prevent ExtTextOut() from returning errors.) + // To avoid from hurting the rendering performance, this code just + // tells WebKit whether or not the all glyph indices for the given + // characters are 0x80 (i.e. a possibly-invalid glyph) and let it + // use alternative fonts for the characters. + // Although this may cause a problem, it seems to work fine as far as I + // have tested. (Obviously, I need more tests.) + WORD localGlyphBuffer[GlyphPage::size]; + + // FIXME: I find some Chinese characters can not be correctly displayed + // when call GetGlyphIndices without flag GGI_MARK_NONEXISTING_GLYPHS, + // because the corresponding glyph index is set as 0x20 when current font + // does not have glyphs available for the character. According a blog post + // http://blogs.msdn.com/michkap/archive/2006/06/28/649791.aspx + // I think we should switch to the way about calling GetGlyphIndices with + // flag GGI_MARK_NONEXISTING_GLYPHS, it should be OK according the + // description of MSDN. + // Also according to Jungshik and Hironori's suggestion and modification + // we treat turetype and raster Font as different way when windows version + // is less than Vista. + GetGlyphIndices(dc, buffer, length, localGlyphBuffer, GGI_MARK_NONEXISTING_GLYPHS); + + // Copy the output to the GlyphPage + bool haveGlyphs = false; + int invalidGlyph = 0xFFFF; + const DWORD cffTableTag = 0x20464643; // 4-byte identifier for OpenType CFF table ('CFF '). + if (!isVistaOrNewer() && !(tm.tmPitchAndFamily & TMPF_TRUETYPE) && (GetFontData(dc, cffTableTag, 0, 0, 0) == GDI_ERROR)) + invalidGlyph = 0x1F; + + Glyph spaceGlyph = 0; // Glyph for a space. Lazily filled. + + for (unsigned i = 0; i < length; i++) { + UChar c = buffer[i]; + Glyph glyph = localGlyphBuffer[i]; + const SimpleFontData* glyphFontData = fontData; + // When this character should be a space, we ignore whatever the font + // says and use a space. Otherwise, if fonts don't map one of these + // space or zero width glyphs, we will get a box. + if (Font::treatAsSpace(c)) { + // Hard code the glyph indices for characters that should be + // treated like spaces. + glyph = initSpaceGlyph(dc, &spaceGlyph); + } else if (glyph == invalidGlyph) { + // WebKit expects both the glyph index and FontData + // pointer to be 0 if the glyph is not present + glyph = 0; + glyphFontData = 0; + } else + haveGlyphs = true; + page->setGlyphDataForCharacter(offset + i, glyph, glyphFontData); + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + return haveGlyphs; +} + +// For non-BMP characters, each is two words (UTF-16) and the input buffer +// size is 2 * |length|. Since GDI doesn't know how to handle non-BMP +// characters, we must use Uniscribe to tell us the glyph indices. +// +// We don't want to call this in the case of "regular" characters since some +// fonts may not have the correct combining rules for accents. See the notes +// at the bottom of ScriptGetCMap. We can't use ScriptGetCMap, though, since +// it doesn't seem to support UTF-16, despite what this blog post says: +// http://blogs.msdn.com/michkap/archive/2006/06/29/650680.aspx +// +// So we fire up the full Uniscribe doohicky, give it our string, and it will +// correctly handle the UTF-16 for us. The hard part is taking this and getting +// the glyph indices back out that correspond to the correct input characters, +// since they may be missing. +// +// Returns true if any glyphs were found. +static bool fillNonBMPGlyphs(unsigned offset, + unsigned length, + UChar* buffer, + GlyphPage* page, + const SimpleFontData* fontData) +{ + bool haveGlyphs = false; + + UniscribeHelperTextRun state(buffer, length * 2, false, + fontData->platformData().hfont(), + fontData->platformData().scriptCache(), + fontData->platformData().scriptFontProperties()); + state.setInhibitLigate(true); + state.setDisableFontFallback(true); + state.init(); + + for (unsigned i = 0; i < length; i++) { + // Each character in this input buffer is a surrogate pair, which + // consists of two UChars. So, the offset for its i-th character is + // (i * 2). + WORD glyph = state.firstGlyphForCharacter(i * 2); + if (glyph) { + haveGlyphs = true; + page->setGlyphDataForIndex(offset + i, glyph, fontData); + } else + // Clear both glyph and fontData fields. + page->setGlyphDataForIndex(offset + i, 0, 0); + } + return haveGlyphs; +} + +// We're supposed to return true if there are any glyphs in the range +// specified by |offset| and |length| in our font, +// false if there are none. +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* characterBuffer, + unsigned bufferLength, const SimpleFontData* fontData) +{ + // We have to handle BMP and non-BMP characters differently. + // FIXME: Add assertions to make sure that buffer is entirely in BMP + // or entirely in non-BMP. + if (bufferLength == length) + return fillBMPGlyphs(offset, length, characterBuffer, this, fontData, true); + + if (bufferLength == 2 * length) { + // A non-BMP input buffer will be twice as long as output glyph buffer + // because each character in the non-BMP input buffer will be + // represented by a surrogate pair (two UChar's). + return fillNonBMPGlyphs(offset, length, characterBuffer, this, fontData); + } + + ASSERT_NOT_REACHED(); + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp new file mode 100644 index 0000000..5e8d148 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * 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: + * + * * 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. + */ + + +/** FIXME + * This file borrows code heavily from platform/graphics/win/GraphicsLayerCACF.cpp + * (and hence it includes both copyrights) + * Ideally the common code (mostly the code that keeps track of the layer hierarchy) + * should be kept separate and shared between platforms. It would be a well worthwhile + * effort once the Windows implementation (binaries and headers) of CoreAnimation is + * checked in to the WebKit repository. Until then only Apple can make this happen. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayerChromium.h" + +#include "Canvas2DLayerChromium.h" +#include "ContentLayerChromium.h" +#include "DrawingBuffer.h" +#include "FloatConversion.h" +#include "FloatRect.h" +#include "Image.h" +#include "ImageLayerChromium.h" +#include "LayerChromium.h" +#include "PlatformString.h" +#include "SystemTime.h" + +#include <wtf/CurrentTime.h> +#include <wtf/StringExtras.h> +#include <wtf/text/CString.h> + +using namespace std; + +namespace WebCore { + +static void setLayerBorderColor(LayerChromium& layer, const Color& color) +{ + layer.setBorderColor(color); +} + +static void clearBorderColor(LayerChromium& layer) +{ + layer.setBorderColor(static_cast<RGBA32>(0)); +} + +static void setLayerBackgroundColor(LayerChromium& layer, const Color& color) +{ + layer.setBackgroundColor(color); +} + +static void clearLayerBackgroundColor(LayerChromium& layer) +{ + layer.setBackgroundColor(static_cast<RGBA32>(0)); +} + +PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerChromium(client); +} + +GraphicsLayerChromium::GraphicsLayerChromium(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_contentsLayerPurpose(NoContentsLayer) + , m_contentsLayerHasBackgroundColor(false) +{ + m_layer = ContentLayerChromium::create(this); + + updateDebugIndicators(); +} + +GraphicsLayerChromium::~GraphicsLayerChromium() +{ + if (m_layer) + m_layer->setOwner(0); + if (m_contentsLayer) + m_contentsLayer->setOwner(0); + if (m_transformLayer) + m_transformLayer->setOwner(0); +} + +void GraphicsLayerChromium::setName(const String& inName) +{ + String name = String::format("GraphicsLayerChromium(%p) GraphicsLayer(%p) ", m_layer.get(), this) + inName; + GraphicsLayer::setName(name); +} + +bool GraphicsLayerChromium::setChildren(const Vector<GraphicsLayer*>& children) +{ + bool childrenChanged = GraphicsLayer::setChildren(children); + // FIXME: GraphicsLayer::setChildren calls addChild() for each sublayer, which + // will end up calling updateSublayerList() N times. + if (childrenChanged) + updateSublayerList(); + + return childrenChanged; +} + +void GraphicsLayerChromium::addChild(GraphicsLayer* childLayer) +{ + GraphicsLayer::addChild(childLayer); + updateSublayerList(); +} + +void GraphicsLayerChromium::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + GraphicsLayer::addChildAtIndex(childLayer, index); + updateSublayerList(); +} + +void GraphicsLayerChromium::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildBelow(childLayer, sibling); + updateSublayerList(); +} + +void GraphicsLayerChromium::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer *sibling) +{ + GraphicsLayer::addChildAbove(childLayer, sibling); + updateSublayerList(); +} + +bool GraphicsLayerChromium::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + updateSublayerList(); + return true; + } + return false; +} + +void GraphicsLayerChromium::removeFromParent() +{ + GraphicsLayer::removeFromParent(); + layerForSuperlayer()->removeFromSuperlayer(); +} + +void GraphicsLayerChromium::setPosition(const FloatPoint& point) +{ + GraphicsLayer::setPosition(point); + updateLayerPosition(); +} + +void GraphicsLayerChromium::setAnchorPoint(const FloatPoint3D& point) +{ + if (point == m_anchorPoint) + return; + + GraphicsLayer::setAnchorPoint(point); + updateAnchorPoint(); +} + +void GraphicsLayerChromium::setSize(const FloatSize& size) +{ + if (size == m_size) + return; + + GraphicsLayer::setSize(size); + updateLayerSize(); +} + +void GraphicsLayerChromium::setTransform(const TransformationMatrix& transform) +{ + if (transform == m_transform) + return; + + GraphicsLayer::setTransform(transform); + updateTransform(); +} + +void GraphicsLayerChromium::setChildrenTransform(const TransformationMatrix& transform) +{ + if (transform == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(transform); + updateChildrenTransform(); +} + +void GraphicsLayerChromium::setPreserves3D(bool preserves3D) +{ + if (preserves3D == m_preserves3D) + return; + + GraphicsLayer::setPreserves3D(preserves3D); + updateLayerPreserves3D(); +} + +void GraphicsLayerChromium::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + updateMasksToBounds(); +} + +void GraphicsLayerChromium::setDrawsContent(bool drawsContent) +{ + if (drawsContent == m_drawsContent) + return; + + GraphicsLayer::setDrawsContent(drawsContent); + updateLayerDrawsContent(); +} + +void GraphicsLayerChromium::setBackgroundColor(const Color& color) +{ + if (m_backgroundColorSet && m_backgroundColor == color) + return; + + GraphicsLayer::setBackgroundColor(color); + + m_contentsLayerHasBackgroundColor = true; + updateLayerBackgroundColor(); +} + +void GraphicsLayerChromium::clearBackgroundColor() +{ + if (!m_backgroundColorSet) + return; + + GraphicsLayer::clearBackgroundColor(); + clearLayerBackgroundColor(*m_contentsLayer); +} + +void GraphicsLayerChromium::setContentsOpaque(bool opaque) +{ + if (m_contentsOpaque == opaque) + return; + + GraphicsLayer::setContentsOpaque(opaque); + updateContentsOpaque(); +} + +void GraphicsLayerChromium::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + updateBackfaceVisibility(); +} + +void GraphicsLayerChromium::setOpacity(float opacity) +{ + float clampedOpacity = max(min(opacity, 1.0f), 0.0f); + + if (m_opacity == clampedOpacity) + return; + + GraphicsLayer::setOpacity(clampedOpacity); + primaryLayer()->setOpacity(opacity); +} + +void GraphicsLayerChromium::setContentsNeedsDisplay() +{ + if (m_contentsLayer) + m_contentsLayer->setNeedsDisplay(); +} + +void GraphicsLayerChromium::setNeedsDisplay() +{ + if (drawsContent()) + m_layer->setNeedsDisplay(); +} + +void GraphicsLayerChromium::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (drawsContent()) + m_layer->setNeedsDisplay(rect); +} + +void GraphicsLayerChromium::setContentsRect(const IntRect& rect) +{ + if (rect == m_contentsRect) + return; + + GraphicsLayer::setContentsRect(rect); + updateContentsRect(); +} + +void GraphicsLayerChromium::setContentsToImage(Image* image) +{ + bool childrenChanged = false; + if (image) { + if (!m_contentsLayer.get() || m_contentsLayerPurpose != ContentsLayerForImage) { + RefPtr<ImageLayerChromium> imageLayer = ImageLayerChromium::create(this); + setupContentsLayer(imageLayer.get()); + m_contentsLayer = imageLayer; + m_contentsLayerPurpose = ContentsLayerForImage; + childrenChanged = true; + } + ImageLayerChromium* imageLayer = static_cast<ImageLayerChromium*>(m_contentsLayer.get()); + imageLayer->setContents(image); + updateContentsRect(); + } else { + if (m_contentsLayer) { + childrenChanged = true; + + // The old contents layer will be removed via updateSublayerList. + m_contentsLayer = 0; + } + } + + if (childrenChanged) + updateSublayerList(); +} + +void GraphicsLayerChromium::setContentsToCanvas(PlatformLayer* platformLayer) +{ + bool childrenChanged = false; + if (platformLayer) { + platformLayer->setOwner(this); + if (m_contentsLayer.get() != platformLayer) { + setupContentsLayer(platformLayer); + m_contentsLayer = platformLayer; + m_contentsLayerPurpose = ContentsLayerForCanvas; + childrenChanged = true; + } + m_contentsLayer->setNeedsDisplay(); + updateContentsRect(); + } else { + if (m_contentsLayer) { + childrenChanged = true; + + // The old contents layer will be removed via updateSublayerList. + m_contentsLayer = 0; + } + } + + if (childrenChanged) + updateSublayerList(); +} + +void GraphicsLayerChromium::setContentsToMedia(PlatformLayer* layer) +{ + bool childrenChanged = false; + if (layer) { + if (!m_contentsLayer.get() || m_contentsLayerPurpose != ContentsLayerForVideo) { + setupContentsLayer(layer); + m_contentsLayer = layer; + m_contentsLayerPurpose = ContentsLayerForVideo; + childrenChanged = true; + } + layer->setOwner(this); + layer->setNeedsDisplay(); + updateContentsRect(); + } else { + if (m_contentsLayer) { + childrenChanged = true; + + // The old contents layer will be removed via updateSublayerList. + m_contentsLayer = 0; + } + } + + if (childrenChanged) + updateSublayerList(); +} + +PlatformLayer* GraphicsLayerChromium::hostLayerForSublayers() const +{ + return m_transformLayer ? m_transformLayer.get() : m_layer.get(); +} + +PlatformLayer* GraphicsLayerChromium::layerForSuperlayer() const +{ + return m_transformLayer ? m_transformLayer.get() : m_layer.get(); +} + +PlatformLayer* GraphicsLayerChromium::platformLayer() const +{ + return primaryLayer(); +} + +void GraphicsLayerChromium::setDebugBackgroundColor(const Color& color) +{ + if (color.isValid()) + setLayerBackgroundColor(*m_layer, color); + else + clearLayerBackgroundColor(*m_layer); +} + +void GraphicsLayerChromium::setDebugBorder(const Color& color, float borderWidth) +{ + if (color.isValid()) { + setLayerBorderColor(*m_layer, color); + m_layer->setBorderWidth(borderWidth); + } else { + clearBorderColor(*m_layer); + m_layer->setBorderWidth(0); + } +} + +void GraphicsLayerChromium::updateSublayerList() +{ + Vector<RefPtr<LayerChromium> > newSublayers; + + if (m_transformLayer) { + // Add the primary layer first. Even if we have negative z-order children, the primary layer always comes behind. + newSublayers.append(m_layer.get()); + } else if (m_contentsLayer) { + // FIXME: add the contents layer in the correct order with negative z-order children. + // This does not cause visible rendering issues because currently contents layers are only used + // for replaced elements that don't have children. + newSublayers.append(m_contentsLayer.get()); + } + + const Vector<GraphicsLayer*>& childLayers = children(); + size_t numChildren = childLayers.size(); + for (size_t i = 0; i < numChildren; ++i) { + GraphicsLayerChromium* curChild = static_cast<GraphicsLayerChromium*>(childLayers[i]); + + LayerChromium* childLayer = curChild->layerForSuperlayer(); + newSublayers.append(childLayer); + } + + for (size_t i = 0; i < newSublayers.size(); ++i) + newSublayers[i]->removeFromSuperlayer(); + + if (m_transformLayer) { + m_transformLayer->setSublayers(newSublayers); + + if (m_contentsLayer) { + // If we have a transform layer, then the contents layer is parented in the + // primary layer (which is itself a child of the transform layer). + m_layer->removeAllSublayers(); + m_layer->addSublayer(m_contentsLayer); + } + } else + m_layer->setSublayers(newSublayers); +} + +void GraphicsLayerChromium::updateLayerPosition() +{ + // Position is offset on the layer by the layer anchor point. + FloatPoint layerPosition(m_position.x() + m_anchorPoint.x() * m_size.width(), + m_position.y() + m_anchorPoint.y() * m_size.height()); + + primaryLayer()->setPosition(layerPosition); +} + +void GraphicsLayerChromium::updateLayerSize() +{ + IntSize layerSize(m_size.width(), m_size.height()); + if (m_transformLayer) { + m_transformLayer->setBounds(layerSize); + // The anchor of the contents layer is always at 0.5, 0.5, so the position is center-relative. + FloatPoint centerPoint(m_size.width() / 2, m_size.height() / 2); + m_layer->setPosition(centerPoint); + } + + m_layer->setBounds(layerSize); + + // Note that we don't resize m_contentsLayer. It's up the caller to do that. + + // If we've changed the bounds, we need to recalculate the position + // of the layer, taking anchor point into account. + updateLayerPosition(); +} + +void GraphicsLayerChromium::updateAnchorPoint() +{ + primaryLayer()->setAnchorPoint(FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())); + primaryLayer()->setAnchorPointZ(m_anchorPoint.z()); + updateLayerPosition(); +} + +void GraphicsLayerChromium::updateTransform() +{ + primaryLayer()->setTransform(m_transform); +} + +void GraphicsLayerChromium::updateChildrenTransform() +{ + primaryLayer()->setSublayerTransform(m_childrenTransform); +} + +void GraphicsLayerChromium::updateMasksToBounds() +{ + m_layer->setMasksToBounds(m_masksToBounds); + updateDebugIndicators(); +} + +void GraphicsLayerChromium::updateContentsOpaque() +{ + m_layer->setOpaque(m_contentsOpaque); +} + +void GraphicsLayerChromium::updateBackfaceVisibility() +{ + m_layer->setDoubleSided(m_backfaceVisibility); +} + +void GraphicsLayerChromium::updateLayerPreserves3D() +{ + if (m_preserves3D && !m_transformLayer) { + // Create the transform layer. + m_transformLayer = LayerChromium::create(this); + + // Copy the position from this layer. + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + m_layer->setPosition(FloatPoint(m_size.width() / 2.0f, m_size.height() / 2.0f)); + + m_layer->setAnchorPoint(FloatPoint(0.5f, 0.5f)); + TransformationMatrix identity; + m_layer->setTransform(identity); + + // Set the old layer to opacity of 1. Further down we will set the opacity on the transform layer. + m_layer->setOpacity(1); + + // Move this layer to be a child of the transform layer. + if (m_layer->superlayer()) + m_layer->superlayer()->replaceSublayer(m_layer.get(), m_transformLayer.get()); + m_transformLayer->addSublayer(m_layer.get()); + + updateSublayerList(); + } else if (!m_preserves3D && m_transformLayer) { + // Relace the transformLayer in the parent with this layer. + m_layer->removeFromSuperlayer(); + m_transformLayer->superlayer()->replaceSublayer(m_transformLayer.get(), m_layer.get()); + + // Release the transform layer. + m_transformLayer = 0; + + updateLayerPosition(); + updateLayerSize(); + updateAnchorPoint(); + updateTransform(); + updateChildrenTransform(); + + updateSublayerList(); + } + + updateOpacityOnLayer(); +} + +void GraphicsLayerChromium::updateLayerDrawsContent() +{ + if (m_drawsContent) + m_layer->setNeedsDisplay(); + + updateDebugIndicators(); +} + +void GraphicsLayerChromium::updateLayerBackgroundColor() +{ + if (!m_contentsLayer) + return; + + // We never create the contents layer just for background color yet. + if (m_backgroundColorSet) + setLayerBackgroundColor(*m_contentsLayer, m_backgroundColor); + else + clearLayerBackgroundColor(*m_contentsLayer); +} + +void GraphicsLayerChromium::updateContentsVideo() +{ + // FIXME: Implement +} + +void GraphicsLayerChromium::updateContentsRect() +{ + if (!m_contentsLayer) + return; + + m_contentsLayer->setPosition(FloatPoint(m_contentsRect.x(), m_contentsRect.y())); + m_contentsLayer->setBounds(IntSize(m_contentsRect.width(), m_contentsRect.height())); +} + +void GraphicsLayerChromium::setupContentsLayer(LayerChromium* contentsLayer) +{ + if (contentsLayer == m_contentsLayer) + return; + + if (m_contentsLayer) { + m_contentsLayer->removeFromSuperlayer(); + m_contentsLayer = 0; + } + + if (contentsLayer) { + m_contentsLayer = contentsLayer; + + m_contentsLayer->setAnchorPoint(FloatPoint(0, 0)); + + // Insert the content layer first. Video elements require this, because they have + // shadow content that must display in front of the video. + m_layer->insertSublayer(m_contentsLayer.get(), 0); + + updateContentsRect(); + + if (showDebugBorders()) { + setLayerBorderColor(*m_contentsLayer, Color(0, 0, 128, 180)); + m_contentsLayer->setBorderWidth(1); + } + } + updateDebugIndicators(); +} + +// This function simply mimics the operation of GraphicsLayerCA +void GraphicsLayerChromium::updateOpacityOnLayer() +{ + primaryLayer()->setOpacity(m_opacity); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h new file mode 100644 index 0000000..130c25c --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h @@ -0,0 +1,146 @@ +/* + * 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 GraphicsLayerChromium_h +#define GraphicsLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsContext.h" +#include "GraphicsLayer.h" + +namespace WebCore { + +class LayerChromium; + +class GraphicsLayerChromium : public GraphicsLayer { +public: + GraphicsLayerChromium(GraphicsLayerClient*); + virtual ~GraphicsLayerChromium(); + + virtual void setName(const String&); + + virtual bool setChildren(const Vector<GraphicsLayer*>&); + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer*, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer*, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + + virtual void removeFromParent(); + + virtual void setPosition(const FloatPoint&); + virtual void setAnchorPoint(const FloatPoint3D&); + virtual void setSize(const FloatSize&); + + virtual void setTransform(const TransformationMatrix&); + + virtual void setChildrenTransform(const TransformationMatrix&); + + virtual void setPreserves3D(bool); + virtual void setMasksToBounds(bool); + virtual void setDrawsContent(bool); + + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + + virtual void setContentsOpaque(bool); + virtual void setBackfaceVisibility(bool); + + virtual void setOpacity(float); + + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + virtual void setContentsNeedsDisplay(); + + virtual void setContentsRect(const IntRect&); + + virtual void setContentsToImage(Image*); + virtual void setContentsToMedia(PlatformLayer*); + virtual void setContentsToCanvas(PlatformLayer*); + + virtual PlatformLayer* platformLayer() const; + + virtual void setDebugBackgroundColor(const Color&); + virtual void setDebugBorder(const Color&, float borderWidth); + + void notifySyncRequired() + { + if (m_client) + m_client->notifySyncRequired(this); + } + +private: + void updateOpacityOnLayer(); + + LayerChromium* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } + LayerChromium* hostLayerForSublayers() const; + LayerChromium* layerForSuperlayer() const; + + void updateSublayerList(); + void updateLayerPosition(); + void updateLayerSize(); + void updateAnchorPoint(); + void updateTransform(); + void updateChildrenTransform(); + void updateMasksToBounds(); + void updateContentsOpaque(); + void updateBackfaceVisibility(); + void updateLayerPreserves3D(); + void updateLayerDrawsContent(); + void updateLayerBackgroundColor(); + + void updateContentsImage(); + void updateContentsVideo(); + void updateContentsRect(); + + void setupContentsLayer(LayerChromium*); + LayerChromium* contentsLayer() const { return m_contentsLayer.get(); } + + RefPtr<LayerChromium> m_layer; + RefPtr<LayerChromium> m_transformLayer; + RefPtr<LayerChromium> m_contentsLayer; + + enum ContentsLayerPurpose { + NoContentsLayer = 0, + ContentsLayerForImage, + ContentsLayerForVideo, + ContentsLayerForCanvas, + }; + + ContentsLayerPurpose m_contentsLayerPurpose; + bool m_contentsLayerHasBackgroundColor : 1; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp new file mode 100644 index 0000000..056d8eb --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2009 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 "FontPlatformData.h" +#include "wtf/OwnArrayPtr.h" + +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkUtils.h" + +extern "C" { +#include "harfbuzz-shaper.h" +} + +// This file implements the callbacks which Harfbuzz requires by using Skia +// calls. See the Harfbuzz source for references about what these callbacks do. + +namespace WebCore { + +static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value) +{ + // HB_Fixed is a 26.6 fixed point format. + return value * 64; +} + +static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + unsigned codepoints = 0; + for (hb_uint32 i = 0; i < length; i++) { + if (!SkUTF16_IsHighSurrogate(characters[i])) + codepoints++; + if (codepoints > *glyphsSize) + return 0; + } + + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs)); + + // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our + // |glyphs| array needs to be converted. + for (int i = numGlyphs - 1; i >= 0; --i) { + uint16_t value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t)); + glyphs[i] = value; + } + + *glyphsSize = numGlyphs; + return 1; +} + +static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + OwnArrayPtr<uint16_t> glyphs16(new uint16_t[numGlyphs]); + if (!glyphs16.get()) + return; + for (unsigned i = 0; i < numGlyphs; ++i) + glyphs16[i] = glyphs[i]; + paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances)); + + // The |advances| values which Skia outputs are SkScalars, which are floats + // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. + // These two formats are both 32-bits long. + for (unsigned i = 0; i < numGlyphs; ++i) { + float value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(float)); + advances[i] = SkiaScalarToHarfbuzzFixed(value); + } +} + +static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + OwnArrayPtr<uint16_t> glyphs16(new uint16_t[length]); + if (!glyphs16.get()) + return 0; + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get()); + + bool canRender = true; + for (int i = 0; i < numGlyphs; ++i) { + if (!glyphs16[i]) { + canRender = false; + break; + } + } + + return canRender; +} + +static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + if (flags & HB_ShaperFlag_UseDesignMetrics) + return HB_Err_Invalid_Argument; // This is requesting pre-hinted positions. We can't support this. + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + uint16_t glyph16 = glyph; + SkPath path; + paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); + int numPoints = path.getPoints(0, 0); + if (point >= static_cast<unsigned>(numPoints)) + return HB_Err_Invalid_SubTable; + SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1))); + if (!points) + return HB_Err_Invalid_SubTable; + // Skia does let us get a single point from the path. + path.getPoints(points, point + 1); + *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX); + *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY); + *resultingNumPoints = numPoints; + fastFree(points); + + return HB_Err_Ok; +} + +static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + uint16_t glyph16 = glyph; + SkScalar width; + SkRect bounds; + paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); + + metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft); + metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop); + metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); + metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); + + metrics->xOffset = SkiaScalarToHarfbuzzFixed(width); + // We can't actually get the |y| correct because Skia doesn't export + // the vertical advance. However, nor we do ever render vertical text at + // the moment so it's unimportant. + metrics->yOffset = 0; +} + +static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + SkPaint::FontMetrics skiaMetrics; + paint.getFontMetrics(&skiaMetrics); + + switch (metric) { + case HB_FontAscent: + return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent); + // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. + default: + return 0; + } +} + +HB_FontClass harfbuzzSkiaClass = { + stringToGlyphs, + glyphsToAdvances, + canRender, + getOutlinePoint, + getGlyphMetrics, + getFontMetric, +}; + +HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len) +{ + FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface); + + const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag); + if (!tableSize) + return HB_Err_Invalid_Argument; + // If Harfbuzz specified a NULL buffer then it's asking for the size of the table. + if (!buffer) { + *len = tableSize; + return HB_Err_Ok; + } + + if (*len < tableSize) + return HB_Err_Invalid_Argument; + SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer); + return HB_Err_Ok; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.h b/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.h new file mode 100644 index 0000000..f7e0496 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009, 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 HarfbuzzSkia_h +#define HarfbuzzSkia_h + +extern "C" { +#include "harfbuzz-shaper.h" +#include "harfbuzz-unicode.h" +} + +namespace WebCore { + HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len); + extern const HB_FontClass harfbuzzSkiaClass; +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp b/Source/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp new file mode 100644 index 0000000..16f55e2 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008, 2009 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 "Icon.h" + +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "PlatformString.h" + +namespace WebCore { + +Icon::Icon(const PlatformIcon& icon) + : m_icon(icon) +{ +} + +Icon::~Icon() +{ +} + +void Icon::paint(GraphicsContext*, const IntRect&) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/IconChromiumMac.cpp b/Source/WebCore/platform/graphics/chromium/IconChromiumMac.cpp new file mode 100644 index 0000000..a24afb2 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/IconChromiumMac.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * 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 "Icon.h" + +#include "PassRefPtr.h" + +// FIXME: These are temporary stubs, we need real implementations which +// may come in the form of IconChromium.cpp. The Windows Chromium +// implementation is currently in IconWin.cpp. + +namespace WebCore { + +Icon::~Icon() +{ +} + +void Icon::paint(GraphicsContext*, const IntRect&) +{ +} + +} diff --git a/Source/WebCore/platform/graphics/chromium/IconChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/IconChromiumWin.cpp new file mode 100644 index 0000000..e958d4a --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/IconChromiumWin.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2008, 2009, 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 "Icon.h" + +#include <windows.h> +#include <shellapi.h> + +#include "GraphicsContext.h" +#include "PlatformContextSkia.h" +#include "PlatformString.h" +#include "SkiaUtils.h" + +namespace WebCore { + +Icon::Icon(const PlatformIcon& icon) + : m_icon(icon) +{ +} + +Icon::~Icon() +{ + if (m_icon) + DestroyIcon(m_icon); +} + +void Icon::paint(GraphicsContext* context, const IntRect& rect) +{ + if (context->paintingDisabled()) + return; + + HDC hdc = context->platformContext()->canvas()->beginPlatformPaint(); + DrawIconEx(hdc, rect.x(), rect.y(), m_icon, rect.width(), rect.height(), 0, 0, DI_NORMAL); + context->platformContext()->canvas()->endPlatformPaint(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/ImageBufferData.h b/Source/WebCore/platform/graphics/chromium/ImageBufferData.h new file mode 100644 index 0000000..504b893 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ImageBufferData.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008, 2009, 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 ImageBufferData_h +#define ImageBufferData_h + +#include "PlatformContextSkia.h" + +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +class ImageBufferData { +public: + ImageBufferData(const IntSize&); + + skia::PlatformCanvas m_canvas; + PlatformContextSkia m_platformContext; +}; + +} // namespace WebCore + +#endif // ImageBufferData_h diff --git a/Source/WebCore/platform/graphics/chromium/ImageChromium.cpp b/Source/WebCore/platform/graphics/chromium/ImageChromium.cpp new file mode 100644 index 0000000..e90d566 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ImageChromium.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-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" +#include "Image.h" + +#include "ChromiumBridge.h" + +namespace WebCore { + +// Other Image methods are implemented in ImageSkia.cpp + +PassRefPtr<Image> Image::loadPlatformResource(const char *name) +{ + return ChromiumBridge::loadPlatformImageResource(name); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/ImageChromiumMac.mm b/Source/WebCore/platform/graphics/chromium/ImageChromiumMac.mm new file mode 100644 index 0000000..073a409 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ImageChromiumMac.mm @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008, 2009, 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. + */ + +// A wrapper around Uniscribe that provides a reasonable API. + +#include "config.h" +#include "BitmapImage.h" + +#include "ChromiumBridge.h" +#include "Image.h" + +namespace WebCore { + +PassRefPtr<Image> Image::loadPlatformResource(const char* name) +{ + return ChromiumBridge::loadPlatformImageResource(name); +} + +// FIXME: These are temporary stubs, we need real implementations which +// may come in the form of ImageChromium.cpp. The Windows Chromium +// implementation is currently in ImageSkia.cpp. + +void BitmapImage::initPlatformData() +{ +} + +void BitmapImage::invalidatePlatformData() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp new file mode 100644 index 0000000..cd299c1 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp @@ -0,0 +1,164 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) + +#include "ImageLayerChromium.h" + +#include "Image.h" +#include "LayerRendererChromium.h" +#include "LayerTexture.h" + +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#endif + +#if PLATFORM(CG) +#include <CoreGraphics/CGBitmapContext.h> +#include <CoreGraphics/CGContext.h> +#include <CoreGraphics/CGImage.h> +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +PassRefPtr<ImageLayerChromium> ImageLayerChromium::create(GraphicsLayerChromium* owner) +{ + return adoptRef(new ImageLayerChromium(owner)); +} + +ImageLayerChromium::ImageLayerChromium(GraphicsLayerChromium* owner) + : ContentLayerChromium(owner) + , m_contents(0) +{ +} + +void ImageLayerChromium::setContents(Image* contents) +{ + // Check if the image has changed. + if (m_contents == contents) + return; + m_contents = contents; + setNeedsDisplay(); +} + +void ImageLayerChromium::updateContentsIfDirty() +{ + ASSERT(layerRenderer()); + + // FIXME: Remove this test when tiled layers are implemented. + if (requiresClippedUpdateRect()) { + // Use the base version of updateContents which draws a subset of the + // image to a bitmap, as the pixel contents can't be uploaded directly. + ContentLayerChromium::updateContentsIfDirty(); + return; + } + + void* pixels = 0; + IntSize bitmapSize; + + NativeImagePtr nativeImage = m_contents->nativeImageForCurrentFrame(); + +#if PLATFORM(SKIA) + // The layer contains an Image. + NativeImageSkia* skiaImage = static_cast<NativeImageSkia*>(nativeImage); + const SkBitmap* skiaBitmap = skiaImage; + bitmapSize = IntSize(skiaBitmap->width(), skiaBitmap->height()); + ASSERT(skiaBitmap); +#elif PLATFORM(CG) + // NativeImagePtr is a CGImageRef on Mac OS X. + int width = CGImageGetWidth(nativeImage); + int height = CGImageGetHeight(nativeImage); + bitmapSize = IntSize(width, height); +#endif + + // Clip the dirty rect to the bitmap dimensions. + IntRect dirtyRect(m_dirtyRect); + dirtyRect.intersect(IntRect(IntPoint(0, 0), bitmapSize)); + + if (!m_contentsTexture || !m_contentsTexture->isValid(bitmapSize, GraphicsContext3D::RGBA)) + dirtyRect = IntRect(IntPoint(0, 0), bitmapSize); + else if (!m_contentsDirty) { + m_contentsTexture->reserve(bitmapSize, GraphicsContext3D::RGBA); + return; + } + +#if PLATFORM(SKIA) + SkAutoLockPixels lock(*skiaBitmap); + SkBitmap::Config skiaConfig = skiaBitmap->config(); + // FIXME: do we need to support more image configurations? + if (skiaConfig == SkBitmap::kARGB_8888_Config) + pixels = skiaBitmap->getPixels(); +#elif PLATFORM(CG) + // FIXME: we should get rid of this temporary copy where possible. + int tempRowBytes = width * 4; + Vector<uint8_t> tempVector; + tempVector.resize(height * tempRowBytes); + // Note we do not zero this vector since we are going to + // completely overwrite its contents with the image below. + // Try to reuse the color space from the image to preserve its colors. + // Some images use a color space (such as indexed) unsupported by the bitmap context. + RetainPtr<CGColorSpaceRef> colorSpaceReleaser; + CGColorSpaceRef colorSpace = CGImageGetColorSpace(nativeImage); + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); + switch (colorSpaceModel) { + case kCGColorSpaceModelMonochrome: + case kCGColorSpaceModelRGB: + case kCGColorSpaceModelCMYK: + case kCGColorSpaceModelLab: + case kCGColorSpaceModelDeviceN: + break; + default: + colorSpaceReleaser.adoptCF(CGColorSpaceCreateDeviceRGB()); + colorSpace = colorSpaceReleaser.get(); + break; + } + RetainPtr<CGContextRef> tempContext(AdoptCF, CGBitmapContextCreate(tempVector.data(), + width, height, 8, tempRowBytes, + colorSpace, + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + CGContextSetBlendMode(tempContext.get(), kCGBlendModeCopy); + CGContextDrawImage(tempContext.get(), + CGRectMake(0, 0, static_cast<CGFloat>(width), static_cast<CGFloat>(height)), + nativeImage); + pixels = tempVector.data(); +#else +#error "Need to implement for your platform." +#endif + + if (pixels) + updateTextureRect(pixels, bitmapSize, dirtyRect); +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/ImageLayerChromium.h b/Source/WebCore/platform/graphics/chromium/ImageLayerChromium.h new file mode 100644 index 0000000..a5c1450 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/ImageLayerChromium.h @@ -0,0 +1,66 @@ +/* + * 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 ImageLayerChromium_h +#define ImageLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "ContentLayerChromium.h" + +#if PLATFORM(CG) +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +class Image; + +// A Layer that contains only an Image element. +class ImageLayerChromium : public ContentLayerChromium { +public: + static PassRefPtr<ImageLayerChromium> create(GraphicsLayerChromium* owner = 0); + + virtual void updateContentsIfDirty(); + virtual bool drawsContent() { return m_contents; } + + void setContents(Image* image); + +private: + ImageLayerChromium(GraphicsLayerChromium* owner); + + RefPtr<Image> m_contents; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp new file mode 100644 index 0000000..b7ab098 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp @@ -0,0 +1,520 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) + +#include "LayerChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#endif +#include "RenderLayerBacking.h" +#include "skia/ext/platform_canvas.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +using namespace std; + +const unsigned LayerChromium::s_positionAttribLocation = 0; +const unsigned LayerChromium::s_texCoordAttribLocation = 1; + +static unsigned loadShader(GraphicsContext3D* context, unsigned type, const char* shaderSource) +{ + unsigned shader = context->createShader(type); + if (!shader) + return 0; + String sourceString(shaderSource); + GLC(context, context->shaderSource(shader, sourceString)); + GLC(context, context->compileShader(shader)); + int compiled = 0; + GLC(context, context->getShaderiv(shader, GraphicsContext3D::COMPILE_STATUS, &compiled)); + if (!compiled) { + GLC(context, context->deleteShader(shader)); + return 0; + } + return shader; +} + +LayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_quadVerticesVbo(0) + , m_quadElementsVbo(0) + , m_maxTextureSize(0) + , m_borderShaderProgram(0) + , m_borderShaderMatrixLocation(-1) + , m_borderShaderColorLocation(-1) + , m_initialized(false) +{ + // Vertex positions and texture coordinates for the 4 corners of a 1x1 quad. + float vertices[] = { -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.0f, 1.0f, 1.0f }; + uint16_t indices[] = { 0, 1, 2, 0, 2, 3, // The two triangles that make up the layer quad. + 0, 1, 2, 3}; // A line path for drawing the layer border. + + GLC(m_context, m_quadVerticesVbo = m_context->createBuffer()); + GLC(m_context, m_quadElementsVbo = m_context->createBuffer()); + GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_quadVerticesVbo)); + GLC(m_context, m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, sizeof(vertices), vertices, GraphicsContext3D::STATIC_DRAW)); + GLC(m_context, m_context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, m_quadElementsVbo)); + GLC(m_context, m_context->bufferData(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GraphicsContext3D::STATIC_DRAW)); + + // Get the max texture size supported by the system. + GLC(m_context, m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize)); + + // Shaders for drawing the debug borders around the layers. + char borderVertexShaderString[] = + "attribute vec4 a_position; \n" + "uniform mat4 matrix; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + "} \n"; + char borderFragmentShaderString[] = + "precision mediump float; \n" + "uniform vec4 color; \n" + "void main() \n" + "{ \n" + " gl_FragColor = vec4(color.xyz * color.w, color.w);\n" + "} \n"; + + m_borderShaderProgram = createShaderProgram(m_context, borderVertexShaderString, borderFragmentShaderString); + if (!m_borderShaderProgram) { + LOG_ERROR("ContentLayerChromium: Failed to create shader program"); + return; + } + + m_borderShaderMatrixLocation = m_context->getUniformLocation(m_borderShaderProgram, "matrix"); + m_borderShaderColorLocation = m_context->getUniformLocation(m_borderShaderProgram, "color"); + ASSERT(m_borderShaderMatrixLocation != -1); + ASSERT(m_borderShaderColorLocation != -1); + + m_initialized = true; +} + +LayerChromium::SharedValues::~SharedValues() +{ + GLC(m_context, m_context->deleteBuffer(m_quadVerticesVbo)); + GLC(m_context, m_context->deleteBuffer(m_quadElementsVbo)); + if (m_borderShaderProgram) + GLC(m_context, m_context->deleteProgram(m_borderShaderProgram)); +} + + +PassRefPtr<LayerChromium> LayerChromium::create(GraphicsLayerChromium* owner) +{ + return adoptRef(new LayerChromium(owner)); +} + +LayerChromium::LayerChromium(GraphicsLayerChromium* owner) + : m_owner(owner) + , m_contentsDirty(false) + , m_targetRenderSurface(0) + , m_superlayer(0) + , m_anchorPoint(0.5, 0.5) + , m_backgroundColor(0, 0, 0, 0) + , m_borderColor(0, 0, 0, 0) + , m_opacity(1.0) + , m_zPosition(0.0) + , m_anchorPointZ(0) + , m_borderWidth(0) + , m_clearsContext(false) + , m_doubleSided(true) + , m_hidden(false) + , m_masksToBounds(false) + , m_opaque(true) + , m_geometryFlipped(false) + , m_needsDisplayOnBoundsChange(false) + , m_drawDepth(0) + , m_layerRenderer(0) + , m_renderSurface(0) +{ +} + +LayerChromium::~LayerChromium() +{ + // Our superlayer should be holding a reference to us so there should be no + // way for us to be destroyed while we still have a superlayer. + ASSERT(!superlayer()); + + // Remove the superlayer reference from all sublayers. + removeAllSublayers(); +} + +void LayerChromium::cleanupResources() +{ + if (m_renderSurface) + m_renderSurface->cleanupResources(); +} + +void LayerChromium::setLayerRenderer(LayerRendererChromium* renderer) +{ + // If we're changing layer renderers then we need to free up any resources + // allocated by the old renderer. + if (layerRenderer() && layerRenderer() != renderer) { + cleanupResources(); + setNeedsDisplay(); + } + + m_layerRenderer = renderer; +} + +RenderSurfaceChromium* LayerChromium::createRenderSurface() +{ + m_renderSurface = new RenderSurfaceChromium(this); + return m_renderSurface.get(); +} + +unsigned LayerChromium::createShaderProgram(GraphicsContext3D* context, const char* vertexShaderSource, const char* fragmentShaderSource) +{ + unsigned vertexShader = loadShader(context, GraphicsContext3D::VERTEX_SHADER, vertexShaderSource); + if (!vertexShader) { + LOG_ERROR("Failed to create vertex shader"); + return 0; + } + + unsigned fragmentShader = loadShader(context, GraphicsContext3D::FRAGMENT_SHADER, fragmentShaderSource); + if (!fragmentShader) { + GLC(context, context->deleteShader(vertexShader)); + LOG_ERROR("Failed to create fragment shader"); + return 0; + } + + unsigned programObject = context->createProgram(); + if (!programObject) { + LOG_ERROR("Failed to create shader program"); + return 0; + } + + GLC(context, context->attachShader(programObject, vertexShader)); + GLC(context, context->attachShader(programObject, fragmentShader)); + + // Bind the common attrib locations. + GLC(context, context->bindAttribLocation(programObject, s_positionAttribLocation, "a_position")); + GLC(context, context->bindAttribLocation(programObject, s_texCoordAttribLocation, "a_texCoord")); + + GLC(context, context->linkProgram(programObject)); + int linked = 0; + GLC(context, context->getProgramiv(programObject, GraphicsContext3D::LINK_STATUS, &linked)); + if (!linked) { + LOG_ERROR("Failed to link shader program"); + GLC(context, context->deleteProgram(programObject)); + return 0; + } + + GLC(context, context->deleteShader(vertexShader)); + GLC(context, context->deleteShader(fragmentShader)); + return programObject; +} + +void LayerChromium::setNeedsCommit() +{ + // Call notifySyncRequired(), which for non-root layers plumbs through to + // call setRootLayerNeedsDisplay() on the WebView, which will cause LayerRendererChromium + // to render a frame. + // This function has no effect on root layers. + if (m_owner) + m_owner->notifySyncRequired(); +} + +void LayerChromium::addSublayer(PassRefPtr<LayerChromium> sublayer) +{ + insertSublayer(sublayer, numSublayers()); +} + +void LayerChromium::insertSublayer(PassRefPtr<LayerChromium> sublayer, size_t index) +{ + index = min(index, m_sublayers.size()); + sublayer->removeFromSuperlayer(); + sublayer->setSuperlayer(this); + m_sublayers.insert(index, sublayer); + setNeedsCommit(); +} + +void LayerChromium::removeFromSuperlayer() +{ + if (m_superlayer) + m_superlayer->removeSublayer(this); +} + +void LayerChromium::removeSublayer(LayerChromium* sublayer) +{ + int foundIndex = indexOfSublayer(sublayer); + if (foundIndex == -1) + return; + + sublayer->setSuperlayer(0); + m_sublayers.remove(foundIndex); + setNeedsCommit(); +} + +void LayerChromium::replaceSublayer(LayerChromium* reference, PassRefPtr<LayerChromium> newLayer) +{ + ASSERT_ARG(reference, reference); + ASSERT_ARG(reference, reference->superlayer() == this); + + if (reference == newLayer) + return; + + int referenceIndex = indexOfSublayer(reference); + if (referenceIndex == -1) { + ASSERT_NOT_REACHED(); + return; + } + + reference->removeFromSuperlayer(); + + if (newLayer) { + newLayer->removeFromSuperlayer(); + insertSublayer(newLayer, referenceIndex); + } +} + +int LayerChromium::indexOfSublayer(const LayerChromium* reference) +{ + for (size_t i = 0; i < m_sublayers.size(); i++) { + if (m_sublayers[i] == reference) + return i; + } + return -1; +} + +void LayerChromium::setBounds(const IntSize& size) +{ + if (m_bounds == size) + return; + + bool firstResize = !m_bounds.width() && !m_bounds.height() && size.width() && size.height(); + + m_bounds = size; + m_backingStoreSize = size; + + if (firstResize) + setNeedsDisplay(FloatRect(0, 0, m_bounds.width(), m_bounds.height())); + else + setNeedsCommit(); +} + +void LayerChromium::setFrame(const FloatRect& rect) +{ + if (rect == m_frame) + return; + + m_frame = rect; + setNeedsDisplay(FloatRect(0, 0, m_bounds.width(), m_bounds.height())); +} + +const LayerChromium* LayerChromium::rootLayer() const +{ + const LayerChromium* layer = this; + for (LayerChromium* superlayer = layer->superlayer(); superlayer; layer = superlayer, superlayer = superlayer->superlayer()) { } + return layer; +} + +void LayerChromium::removeAllSublayers() +{ + while (m_sublayers.size()) { + LayerChromium* layer = m_sublayers[0].get(); + ASSERT(layer->superlayer()); + layer->removeFromSuperlayer(); + } +} + +void LayerChromium::setSublayers(const Vector<RefPtr<LayerChromium> >& sublayers) +{ + if (sublayers == m_sublayers) + return; + + removeAllSublayers(); + size_t listSize = sublayers.size(); + for (size_t i = 0; i < listSize; i++) + addSublayer(sublayers[i]); +} + +LayerChromium* LayerChromium::superlayer() const +{ + return m_superlayer; +} + +void LayerChromium::setNeedsDisplay(const FloatRect& dirtyRect) +{ + // Simply mark the contents as dirty. For non-root layers, the call to + // setNeedsCommit will schedule a fresh compositing pass. + // For the root layer, setNeedsCommit has no effect. + m_contentsDirty = true; + + m_dirtyRect.unite(dirtyRect); + setNeedsCommit(); +} + +void LayerChromium::setNeedsDisplay() +{ + m_dirtyRect.setLocation(FloatPoint()); + m_dirtyRect.setSize(m_bounds); + m_contentsDirty = true; + setNeedsCommit(); +} + +void LayerChromium::resetNeedsDisplay() +{ + m_dirtyRect = FloatRect(); + m_contentsDirty = false; +} + +void LayerChromium::toGLMatrix(float* flattened, const TransformationMatrix& m) +{ + flattened[0] = m.m11(); + flattened[1] = m.m12(); + flattened[2] = m.m13(); + flattened[3] = m.m14(); + flattened[4] = m.m21(); + flattened[5] = m.m22(); + flattened[6] = m.m23(); + flattened[7] = m.m24(); + flattened[8] = m.m31(); + flattened[9] = m.m32(); + flattened[10] = m.m33(); + flattened[11] = m.m34(); + flattened[12] = m.m41(); + flattened[13] = m.m42(); + flattened[14] = m.m43(); + flattened[15] = m.m44(); +} + +GraphicsContext3D* LayerChromium::layerRendererContext() const +{ + ASSERT(layerRenderer()); + return layerRenderer()->context(); +} + +void LayerChromium::drawTexturedQuad(GraphicsContext3D* context, const TransformationMatrix& projectionMatrix, const TransformationMatrix& drawMatrix, + float width, float height, float opacity, + int matrixLocation, int alphaLocation) +{ + static float glMatrix[16]; + + TransformationMatrix renderMatrix = drawMatrix; + + // Apply a scaling factor to size the quad from 1x1 to its intended size. + renderMatrix.scale3d(width, height, 1); + + // Apply the projection matrix before sending the transform over to the shader. + renderMatrix.multiply(projectionMatrix); + + toGLMatrix(&glMatrix[0], renderMatrix); + + GLC(context, context->uniformMatrix4fv(matrixLocation, false, &glMatrix[0], 1)); + + if (alphaLocation != -1) + GLC(context, context->uniform1f(alphaLocation, opacity)); + + GLC(context, context->drawElements(GraphicsContext3D::TRIANGLES, 6, GraphicsContext3D::UNSIGNED_SHORT, 0)); +} + +void LayerChromium::drawDebugBorder() +{ + static float glMatrix[16]; + if (!borderColor().alpha()) + return; + + ASSERT(layerRenderer()); + const SharedValues* sv = layerRenderer()->layerSharedValues(); + ASSERT(sv && sv->initialized()); + layerRenderer()->useShader(sv->borderShaderProgram()); + TransformationMatrix renderMatrix = drawTransform(); + renderMatrix.scale3d(bounds().width(), bounds().height(), 1); + renderMatrix.multiply(layerRenderer()->projectionMatrix()); + toGLMatrix(&glMatrix[0], renderMatrix); + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->uniformMatrix4fv(sv->borderShaderMatrixLocation(), false, &glMatrix[0], 1)); + + GLC(context, context->uniform4f(sv->borderShaderColorLocation(), borderColor().red() / 255.0, borderColor().green() / 255.0, borderColor().blue() / 255.0, 1)); + + GLC(context, context->lineWidth(borderWidth())); + + // The indices for the line are stored in the same array as the triangle indices. + GLC(context, context->drawElements(GraphicsContext3D::LINE_LOOP, 4, GraphicsContext3D::UNSIGNED_SHORT, 6 * sizeof(unsigned short))); +} + +const IntRect LayerChromium::getDrawRect() const +{ + // Form the matrix used by the shader to map the corners of the layer's + // bounds into the view space. + FloatRect layerRect(-0.5 * bounds().width(), -0.5 * bounds().height(), bounds().width(), bounds().height()); + IntRect mappedRect = enclosingIntRect(drawTransform().mapRect(layerRect)); + return mappedRect; +} + +// Returns true if any of the layer's descendants has drawable content. +bool LayerChromium::descendantsDrawContent() +{ + const Vector<RefPtr<LayerChromium> >& sublayers = getSublayers(); + for (size_t i = 0; i < sublayers.size(); ++i) + if (sublayers[i]->descendantsDrawContentRecursive()) + return true; + return false; +} + +// Returns true if either this layer or one of its descendants has drawable content. +bool LayerChromium::descendantsDrawContentRecursive() +{ + if (drawsContent()) + return true; + + const Vector<RefPtr<LayerChromium> >& sublayers = getSublayers(); + for (size_t i = 0; i < sublayers.size(); ++i) + if (sublayers[i]->descendantsDrawContentRecursive()) + return true; + return false; +} + +// static +void LayerChromium::prepareForDraw(const SharedValues* sv) +{ + GraphicsContext3D* context = sv->context(); + GLC(context, context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, sv->quadVerticesVbo())); + GLC(context, context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, sv->quadElementsVbo())); + unsigned offset = 0; + GLC(context, context->vertexAttribPointer(s_positionAttribLocation, 3, GraphicsContext3D::FLOAT, false, 5 * sizeof(float), offset)); + offset += 3 * sizeof(float); + GLC(context, context->vertexAttribPointer(s_texCoordAttribLocation, 2, GraphicsContext3D::FLOAT, false, 5 * sizeof(float), offset)); + GLC(context, context->enableVertexAttribArray(s_positionAttribLocation)); + GLC(context, context->enableVertexAttribArray(s_texCoordAttribLocation)); +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/LayerChromium.h b/Source/WebCore/platform/graphics/chromium/LayerChromium.h new file mode 100644 index 0000000..a0a690f --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerChromium.h @@ -0,0 +1,307 @@ +/* + * 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 LayerChromium_h +#define LayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "FloatPoint.h" +#include "GraphicsContext.h" +#include "GraphicsLayerChromium.h" +#include "PlatformString.h" +#include "RenderSurfaceChromium.h" +#include "TransformationMatrix.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + + +namespace skia { +class PlatformCanvas; +} + +namespace WebCore { + +class GraphicsContext3D; +class LayerRendererChromium; + +// Base class for composited layers. Special layer types are derived from +// this class. +class LayerChromium : public RefCounted<LayerChromium> { + friend class LayerRendererChromium; + friend class LayerTilerChromium; +public: + static PassRefPtr<LayerChromium> create(GraphicsLayerChromium* owner = 0); + + virtual ~LayerChromium(); + + const LayerChromium* rootLayer() const; + LayerChromium* superlayer() const; + void addSublayer(PassRefPtr<LayerChromium>); + void insertSublayer(PassRefPtr<LayerChromium>, size_t index); + void replaceSublayer(LayerChromium* reference, PassRefPtr<LayerChromium> newLayer); + void removeFromSuperlayer(); + void removeAllSublayers(); + void setSublayers(const Vector<RefPtr<LayerChromium> >&); + const Vector<RefPtr<LayerChromium> >& getSublayers() const { return m_sublayers; } + + void setAnchorPoint(const FloatPoint& anchorPoint) { m_anchorPoint = anchorPoint; setNeedsCommit(); } + FloatPoint anchorPoint() const { return m_anchorPoint; } + + void setAnchorPointZ(float anchorPointZ) { m_anchorPointZ = anchorPointZ; setNeedsCommit(); } + float anchorPointZ() const { return m_anchorPointZ; } + + void setBackgroundColor(const Color& color) { m_backgroundColor = color; setNeedsCommit(); } + Color backgroundColor() const { return m_backgroundColor; } + + void setBorderColor(const Color& color) { m_borderColor = color; setNeedsCommit(); } + Color borderColor() const { return m_borderColor; } + + void setBorderWidth(float width) { m_borderWidth = width; setNeedsCommit(); } + float borderWidth() const { return m_borderWidth; } + + void setBounds(const IntSize&); + IntSize bounds() const { return m_bounds; } + + void setClearsContext(bool clears) { m_clearsContext = clears; setNeedsCommit(); } + bool clearsContext() const { return m_clearsContext; } + + void setDoubleSided(bool doubleSided) { m_doubleSided = doubleSided; setNeedsCommit(); } + bool doubleSided() const { return m_doubleSided; } + + void setFrame(const FloatRect&); + FloatRect frame() const { return m_frame; } + + void setHidden(bool hidden) { m_hidden = hidden; setNeedsCommit(); } + bool isHidden() const { return m_hidden; } + + void setMasksToBounds(bool masksToBounds) { m_masksToBounds = masksToBounds; } + bool masksToBounds() const { return m_masksToBounds; } + + void setName(const String& name) { m_name = name; } + String name() const { return m_name; } + + void setNeedsDisplay(const FloatRect& dirtyRect); + void setNeedsDisplay(); + const FloatRect& dirtyRect() const { return m_dirtyRect; } + void resetNeedsDisplay(); + + void setNeedsDisplayOnBoundsChange(bool needsDisplay) { m_needsDisplayOnBoundsChange = needsDisplay; } + + void setOpacity(float opacity) { m_opacity = opacity; setNeedsCommit(); } + float opacity() const { return m_opacity; } + + void setOpaque(bool opaque) { m_opaque = opaque; setNeedsCommit(); } + bool opaque() const { return m_opaque; } + + void setPosition(const FloatPoint& position) { m_position = position; setNeedsCommit(); } + FloatPoint position() const { return m_position; } + + void setZPosition(float zPosition) { m_zPosition = zPosition; setNeedsCommit(); } + float zPosition() const { return m_zPosition; } + + void setSublayerTransform(const TransformationMatrix& transform) { m_sublayerTransform = transform; setNeedsCommit(); } + const TransformationMatrix& sublayerTransform() const { return m_sublayerTransform; } + + void setTransform(const TransformationMatrix& transform) { m_transform = transform; setNeedsCommit(); } + const TransformationMatrix& transform() const { return m_transform; } + + // FIXME: This setting is currently ignored. + void setGeometryFlipped(bool flipped) { m_geometryFlipped = flipped; setNeedsCommit(); } + bool geometryFlipped() const { return m_geometryFlipped; } + + const TransformationMatrix& drawTransform() const { return m_drawTransform; } + float drawOpacity() const { return m_drawOpacity; } + + bool preserves3D() { return m_owner && m_owner->preserves3D(); } + + // Derived types must override this method if they need to react to a change + // in the LayerRendererChromium. + virtual void setLayerRenderer(LayerRendererChromium*); + + void setOwner(GraphicsLayerChromium* owner) { m_owner = owner; } + + // Returns the rect containtaining this layer in the current view's coordinate system. + const IntRect getDrawRect() const; + + // These methods typically need to be overwritten by derived classes. + virtual bool drawsContent() { return false; } + virtual void updateContentsIfDirty() { } + virtual void draw() { } + + void drawDebugBorder(); + + RenderSurfaceChromium* createRenderSurface(); + + // Stores values that are shared between instances of this class that are + // associated with the same LayerRendererChromium (and hence the same GL + // context). + class SharedValues { + public: + explicit SharedValues(GraphicsContext3D*); + ~SharedValues(); + + GraphicsContext3D* context() const { return m_context; } + unsigned quadVerticesVbo() const { return m_quadVerticesVbo; } + unsigned quadElementsVbo() const { return m_quadElementsVbo; } + int maxTextureSize() const { return m_maxTextureSize; } + unsigned borderShaderProgram() const { return m_borderShaderProgram; } + int borderShaderMatrixLocation() const { return m_borderShaderMatrixLocation; } + int borderShaderColorLocation() const { return m_borderShaderColorLocation; } + bool initialized() const { return m_initialized; } + + private: + GraphicsContext3D* m_context; + unsigned m_quadVerticesVbo; + unsigned m_quadElementsVbo; + int m_maxTextureSize; + unsigned m_borderShaderProgram; + int m_borderShaderMatrixLocation; + int m_borderShaderColorLocation; + bool m_initialized; + }; + + static void prepareForDraw(const SharedValues*); + + LayerRendererChromium* layerRenderer() const { return m_layerRenderer.get(); } + + static unsigned createShaderProgram(GraphicsContext3D*, const char* vertexShaderSource, const char* fragmentShaderSource); + + static void drawTexturedQuad(GraphicsContext3D*, const TransformationMatrix& projectionMatrix, const TransformationMatrix& layerMatrix, + float width, float height, float opacity, + int matrixLocation, int alphaLocation); + +protected: + GraphicsLayerChromium* m_owner; + LayerChromium(GraphicsLayerChromium* owner); + + // This is called to clean up resources being held in the same context as + // layerRendererContext(). Subclasses should override this method if they + // hold context-dependent resources such as textures. + virtual void cleanupResources(); + + GraphicsContext3D* layerRendererContext() const; + + // Returns true if any of the layer's descendants has content to draw. + bool descendantsDrawContent(); + + static void toGLMatrix(float*, const TransformationMatrix&); + + IntSize m_bounds; + FloatRect m_dirtyRect; + bool m_contentsDirty; + + // Render surface this layer draws into. This is a surface that can belong + // either to this layer (if m_targetRenderSurface == m_renderSurface) or + // to an ancestor of this layer. The target render surface determines the + // coordinate system the layer's transforms are relative to. + RenderSurfaceChromium* m_targetRenderSurface; + + // All layer shaders share the same attribute locations for the vertex positions + // and texture coordinates. This allows switching shaders without rebinding attribute + // arrays. + static const unsigned s_positionAttribLocation; + static const unsigned s_texCoordAttribLocation; + +private: + void setNeedsCommit(); + + void setSuperlayer(LayerChromium* superlayer) { m_superlayer = superlayer; } + + size_t numSublayers() const + { + return m_sublayers.size(); + } + + // Returns the index of the sublayer or -1 if not found. + int indexOfSublayer(const LayerChromium*); + + // This should only be called from removeFromSuperlayer. + void removeSublayer(LayerChromium*); + + bool descendantsDrawContentRecursive(); + + Vector<RefPtr<LayerChromium> > m_sublayers; + LayerChromium* m_superlayer; + + // Layer properties. + IntSize m_backingStoreSize; + FloatPoint m_position; + FloatPoint m_anchorPoint; + Color m_backgroundColor; + Color m_borderColor; + float m_opacity; + float m_zPosition; + float m_anchorPointZ; + float m_borderWidth; + float m_drawOpacity; + bool m_clearsContext; + bool m_doubleSided; + bool m_hidden; + bool m_masksToBounds; + bool m_opaque; + bool m_geometryFlipped; + bool m_needsDisplayOnBoundsChange; + + // The global depth value of the center of the layer. This value is used + // to sort layers from back to front. + float m_drawDepth; + + // Points to the layer renderer that updates and draws this layer. + RefPtr<LayerRendererChromium> m_layerRenderer; + + FloatRect m_frame; + TransformationMatrix m_transform; + TransformationMatrix m_sublayerTransform; + TransformationMatrix m_drawTransform; + + // The scissor rectangle that should be used when this layer is drawn. + // Inherited by the parent layer and further restricted if this layer masks + // to bounds. + IntRect m_scissorRect; + + // Render surface associated with this layer. The layer and its descendants + // will render to this surface. + OwnPtr<RenderSurfaceChromium> m_renderSurface; + + // Hierarchical bounding rect containing the layer and its descendants. + IntRect m_drawableContentRect; + + String m_name; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp new file mode 100644 index 0000000..8d77bea --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp @@ -0,0 +1,803 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) +#include "LayerRendererChromium.h" + +#include "Canvas2DLayerChromium.h" +#include "GraphicsContext3D.h" +#include "LayerChromium.h" +#include "LayerTexture.h" +#include "NotImplemented.h" +#include "TextureManager.h" +#include "WebGLLayerChromium.h" +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#elif PLATFORM(CG) +#include <CoreGraphics/CGBitmapContext.h> +#endif + +namespace WebCore { + +// FIXME: Make this limit adjustable and give it a useful value. +static size_t textureMemoryLimitBytes = 64 * 1024 * 1024; + +static TransformationMatrix orthoMatrix(float left, float right, float bottom, float top) +{ + float deltaX = right - left; + float deltaY = top - bottom; + TransformationMatrix ortho; + if (!deltaX || !deltaY) + return ortho; + ortho.setM11(2.0f / deltaX); + ortho.setM41(-(right + left) / deltaX); + ortho.setM22(2.0f / deltaY); + ortho.setM42(-(top + bottom) / deltaY); + + // Z component of vertices is always set to zero as we don't use the depth buffer + // while drawing. + ortho.setM33(0); + + return ortho; +} + +// Returns true if the matrix has no rotation, skew or perspective components to it. +static bool isScaleOrTranslation(const TransformationMatrix& m) +{ + return !m.m12() && !m.m13() && !m.m14() + && !m.m21() && !m.m23() && !m.m24() + && !m.m31() && !m.m32() && !m.m43() + && m.m44(); + +} + +bool LayerRendererChromium::compareLayerZ(const LayerChromium* a, const LayerChromium* b) +{ + return a->m_drawDepth < b->m_drawDepth; +} + +PassRefPtr<LayerRendererChromium> LayerRendererChromium::create(PassRefPtr<GraphicsContext3D> context) +{ + if (!context) + return 0; + + RefPtr<LayerRendererChromium> layerRenderer(adoptRef(new LayerRendererChromium(context))); + if (!layerRenderer->hardwareCompositing()) + return 0; + + return layerRenderer.release(); +} + +LayerRendererChromium::LayerRendererChromium(PassRefPtr<GraphicsContext3D> context) + : m_rootLayerTextureWidth(0) + , m_rootLayerTextureHeight(0) + , m_rootLayer(0) + , m_scrollPosition(IntPoint(-1, -1)) + , m_currentShader(0) + , m_currentRenderSurface(0) + , m_offscreenFramebufferId(0) + , m_compositeOffscreen(false) + , m_context(context) + , m_defaultRenderSurface(0) +{ + m_hardwareCompositing = initializeSharedObjects(); + m_rootLayerTiler = LayerTilerChromium::create(this, IntSize(256, 256)); + ASSERT(m_rootLayerTiler); +} + +LayerRendererChromium::~LayerRendererChromium() +{ + cleanupSharedObjects(); + + // Because the tilers need to clean up textures, clean them up explicitly + // before the GraphicsContext3D is destroyed. + m_rootLayerTiler.clear(); + m_horizontalScrollbarTiler.clear(); + m_verticalScrollbarTiler.clear(); +} + +GraphicsContext3D* LayerRendererChromium::context() +{ + return m_context.get(); +} + +void LayerRendererChromium::debugGLCall(GraphicsContext3D* context, const char* command, const char* file, int line) +{ + unsigned long error = context->getError(); + if (error != GraphicsContext3D::NO_ERROR) + LOG_ERROR("GL command failed: File: %s\n\tLine %d\n\tcommand: %s, error %x\n", file, line, command, static_cast<int>(error)); +} + +void LayerRendererChromium::useShader(unsigned programId) +{ + if (programId != m_currentShader) { + GLC(m_context.get(), m_context->useProgram(programId)); + m_currentShader = programId; + } +} + +IntRect LayerRendererChromium::verticalScrollbarRect(const IntRect& visibleRect, const IntRect& contentRect) +{ + IntRect verticalScrollbar(IntPoint(contentRect.right(), contentRect.y()), IntSize(visibleRect.width() - contentRect.width(), visibleRect.height())); + return verticalScrollbar; +} + +IntRect LayerRendererChromium::horizontalScrollbarRect(const IntRect& visibleRect, const IntRect& contentRect) +{ + IntRect horizontalScrollbar(IntPoint(contentRect.x(), contentRect.bottom()), IntSize(visibleRect.width(), visibleRect.height() - contentRect.height())); + return horizontalScrollbar; +} + +void LayerRendererChromium::invalidateRootLayerRect(const IntRect& dirtyRect, const IntRect& visibleRect, const IntRect& contentRect) +{ + if (contentRect.intersects(dirtyRect)) + m_rootLayerTiler->invalidateRect(dirtyRect); + if (m_horizontalScrollbarTiler) { + IntRect scrollbar = horizontalScrollbarRect(visibleRect, contentRect); + if (dirtyRect.intersects(scrollbar)) { + m_horizontalScrollbarTiler->setLayerPosition(scrollbar.location()); + m_horizontalScrollbarTiler->invalidateRect(dirtyRect); + } + } + if (m_verticalScrollbarTiler) { + IntRect scrollbar = verticalScrollbarRect(visibleRect, contentRect); + if (dirtyRect.intersects(scrollbar)) { + m_verticalScrollbarTiler->setLayerPosition(scrollbar.location()); + m_verticalScrollbarTiler->invalidateRect(dirtyRect); + } + } +} + +void LayerRendererChromium::updateAndDrawRootLayer(TilePaintInterface& tilePaint, TilePaintInterface& scrollbarPaint, const IntRect& visibleRect, const IntRect& contentRect) +{ + m_rootLayerTiler->update(tilePaint, visibleRect); + m_rootLayerTiler->draw(visibleRect); + + if (visibleRect.width() > contentRect.width()) { + IntRect verticalScrollbar = verticalScrollbarRect(visibleRect, contentRect); + IntSize tileSize = verticalScrollbar.size().shrunkTo(IntSize(m_maxTextureSize, m_maxTextureSize)); + if (!m_verticalScrollbarTiler) + m_verticalScrollbarTiler = LayerTilerChromium::create(this, tileSize); + else + m_verticalScrollbarTiler->setTileSize(tileSize); + m_verticalScrollbarTiler->setLayerPosition(verticalScrollbar.location()); + m_verticalScrollbarTiler->update(scrollbarPaint, visibleRect); + m_verticalScrollbarTiler->draw(visibleRect); + } + + if (visibleRect.height() > contentRect.height()) { + IntRect horizontalScrollbar = horizontalScrollbarRect(visibleRect, contentRect); + IntSize tileSize = horizontalScrollbar.size().shrunkTo(IntSize(m_maxTextureSize, m_maxTextureSize)); + if (!m_horizontalScrollbarTiler) + m_horizontalScrollbarTiler = LayerTilerChromium::create(this, tileSize); + else + m_horizontalScrollbarTiler->setTileSize(tileSize); + m_horizontalScrollbarTiler->setLayerPosition(horizontalScrollbar.location()); + m_horizontalScrollbarTiler->update(scrollbarPaint, visibleRect); + m_horizontalScrollbarTiler->draw(visibleRect); + } +} + +void LayerRendererChromium::drawLayers(const IntRect& visibleRect, const IntRect& contentRect, + const IntPoint& scrollPosition, TilePaintInterface& tilePaint, + TilePaintInterface& scrollbarPaint) +{ + ASSERT(m_hardwareCompositing); + + if (!m_rootLayer) + return; + + makeContextCurrent(); + + // If the size of the visible area has changed then allocate a new texture + // to store the contents of the root layer and adjust the projection matrix + // and viewport. + int visibleRectWidth = visibleRect.width(); + int visibleRectHeight = visibleRect.height(); + + if (!m_rootLayer->m_renderSurface) + m_rootLayer->createRenderSurface(); + m_rootLayer->m_renderSurface->m_contentRect = IntRect(0, 0, visibleRectWidth, visibleRectHeight); + + if (visibleRectWidth != m_rootLayerTextureWidth || visibleRectHeight != m_rootLayerTextureHeight) { + m_rootLayerTextureWidth = visibleRectWidth; + m_rootLayerTextureHeight = visibleRectHeight; + + // Reset the current render surface to force an update of the viewport and + // projection matrix next time useRenderSurface is called. + m_currentRenderSurface = 0; + } + + // The GL viewport covers the entire visible area, including the scrollbars. + GLC(m_context.get(), m_context->viewport(0, 0, visibleRectWidth, visibleRectHeight)); + + // Bind the common vertex attributes used for drawing all the layers. + LayerChromium::prepareForDraw(layerSharedValues()); + + // FIXME: These calls can be made once, when the compositor context is initialized. + GLC(m_context.get(), m_context->disable(GraphicsContext3D::DEPTH_TEST)); + GLC(m_context.get(), m_context->disable(GraphicsContext3D::CULL_FACE)); + + // Blending disabled by default. Root layer alpha channel on Windows is incorrect when Skia uses ClearType. + GLC(m_context.get(), m_context->disable(GraphicsContext3D::BLEND)); + + m_scrollPosition = scrollPosition; + + ASSERT(m_rootLayer->m_renderSurface); + m_defaultRenderSurface = m_rootLayer->m_renderSurface.get(); + + useRenderSurface(m_defaultRenderSurface); + + // Clear to blue to make it easier to spot unrendered regions. + m_context->clearColor(0, 0, 1, 1); + m_context->colorMask(true, true, true, true); + m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT); + // Mask out writes to alpha channel: subpixel antialiasing via Skia results in invalid + // zero alpha values on text glyphs. The root layer is always opaque. + m_context->colorMask(true, true, true, false); + + updateAndDrawRootLayer(tilePaint, scrollbarPaint, visibleRect, contentRect); + + // Set the root visible/content rects --- used by subsequent drawLayers calls. + m_rootVisibleRect = visibleRect; + m_rootContentRect = contentRect; + + // Scissor out the scrollbars to avoid rendering on top of them. + IntRect rootScissorRect(contentRect); + // The scissorRect should not include the scroll offset. + rootScissorRect.move(-m_scrollPosition.x(), -m_scrollPosition.y()); + m_rootLayer->m_scissorRect = rootScissorRect; + + Vector<LayerChromium*> renderSurfaceLayerList; + renderSurfaceLayerList.append(m_rootLayer.get()); + + TransformationMatrix identityMatrix; + m_defaultRenderSurface->m_layerList.clear(); + updateLayersRecursive(m_rootLayer.get(), identityMatrix, renderSurfaceLayerList, m_defaultRenderSurface->m_layerList); + + // The shader used to render layers returns pre-multiplied alpha colors + // so we need to send the blending mode appropriately. + GLC(m_context.get(), m_context->enable(GraphicsContext3D::BLEND)); + GLC(m_context.get(), m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA)); + GLC(m_context.get(), m_context->enable(GraphicsContext3D::SCISSOR_TEST)); + + // Update the contents of the render surfaces. We traverse the array from + // back to front to guarantee that nested render surfaces get rendered in the + // correct order. + for (int surfaceIndex = renderSurfaceLayerList.size() - 1; surfaceIndex >= 0 ; --surfaceIndex) { + LayerChromium* renderSurfaceLayer = renderSurfaceLayerList[surfaceIndex]; + ASSERT(renderSurfaceLayer->m_renderSurface); + + // Render surfaces whose drawable area has zero width or height + // will have no layers associated with them and should be skipped. + if (!renderSurfaceLayer->m_renderSurface->m_layerList.size()) + continue; + + if (useRenderSurface(renderSurfaceLayer->m_renderSurface.get())) { + if (renderSurfaceLayer != m_rootLayer) { + GLC(m_context.get(), m_context->disable(GraphicsContext3D::SCISSOR_TEST)); + GLC(m_context.get(), m_context->clearColor(0, 0, 0, 0)); + GLC(m_context.get(), m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT)); + GLC(m_context.get(), m_context->enable(GraphicsContext3D::SCISSOR_TEST)); + } + + Vector<LayerChromium*>& layerList = renderSurfaceLayer->m_renderSurface->m_layerList; + ASSERT(layerList.size()); + for (unsigned layerIndex = 0; layerIndex < layerList.size(); ++layerIndex) + drawLayer(layerList[layerIndex], renderSurfaceLayer->m_renderSurface.get()); + } + } + + GLC(m_context.get(), m_context->disable(GraphicsContext3D::SCISSOR_TEST)); + GLC(m_context.get(), m_context->disable(GraphicsContext3D::BLEND)); +} + +void LayerRendererChromium::finish() +{ + m_context->finish(); +} + +void LayerRendererChromium::present() +{ + // We're done! Time to swapbuffers! + + // Note that currently this has the same effect as swapBuffers; we should + // consider exposing a different entry point on GraphicsContext3D. + m_context->prepareTexture(); +} + +void LayerRendererChromium::setRootLayer(PassRefPtr<LayerChromium> layer) +{ + m_rootLayer = layer; + m_rootLayerTiler->invalidateEntireLayer(); + if (m_horizontalScrollbarTiler) + m_horizontalScrollbarTiler->invalidateEntireLayer(); + if (m_verticalScrollbarTiler) + m_verticalScrollbarTiler->invalidateEntireLayer(); +} + +void LayerRendererChromium::getFramebufferPixels(void *pixels, const IntRect& rect) +{ + ASSERT(rect.right() <= rootLayerTextureSize().width() + && rect.bottom() <= rootLayerTextureSize().height()); + + if (!pixels) + return; + + makeContextCurrent(); + + GLC(m_context.get(), m_context->readPixels(rect.x(), rect.y(), rect.width(), rect.height(), + GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels)); +} + +// FIXME: This method should eventually be replaced by a proper texture manager. +unsigned LayerRendererChromium::createLayerTexture() +{ + unsigned textureId = 0; + GLC(m_context.get(), textureId = m_context->createTexture()); + GLC(m_context.get(), m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId)); + // Do basic linear filtering on resize. + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR)); + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR)); + // NPOT textures in GL ES only work when the wrap mode is set to GraphicsContext3D::CLAMP_TO_EDGE. + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE)); + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE)); + return textureId; +} + +void LayerRendererChromium::deleteLayerTexture(unsigned textureId) +{ + if (!textureId) + return; + + GLC(m_context.get(), m_context->deleteTexture(textureId)); +} + +// Returns true if any part of the layer falls within the visibleRect +bool LayerRendererChromium::isLayerVisible(LayerChromium* layer, const TransformationMatrix& matrix, const IntRect& visibleRect) +{ + // Form the matrix used by the shader to map the corners of the layer's + // bounds into clip space. + TransformationMatrix renderMatrix = matrix; + renderMatrix.scale3d(layer->bounds().width(), layer->bounds().height(), 1); + renderMatrix.multiply(m_projectionMatrix); + + FloatRect layerRect(-0.5, -0.5, 1, 1); + FloatRect mappedRect = renderMatrix.mapRect(layerRect); + + // The layer is visible if it intersects any part of a rectangle whose origin + // is at (-1, -1) and size is 2x2. + return mappedRect.intersects(FloatRect(-1, -1, 2, 2)); +} + +// Recursively walks the layer tree starting at the given node and computes all the +// necessary transformations, scissor rectangles, render surfaces, etc. +void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const TransformationMatrix& parentMatrix, Vector<LayerChromium*>& renderSurfaceLayerList, Vector<LayerChromium*>& layerList) +{ + layer->setLayerRenderer(this); + + // Compute the new matrix transformation that will be applied to this layer and + // all its sublayers. It's important to remember that the layer's position + // is the position of the layer's anchor point. Also, the coordinate system used + // assumes that the origin is at the lower left even though the coordinates the browser + // gives us for the layers are for the upper left corner. The Y flip happens via + // the orthographic projection applied at render time. + // The transformation chain for the layer is (using the Matrix x Vector order): + // M = M[p] * Tr[l] * M[l] * Tr[c] + // Where M[p] is the parent matrix passed down to the function + // Tr[l] is the translation matrix locating the layer's anchor point + // Tr[c] is the translation offset between the anchor point and the center of the layer + // M[l] is the layer's matrix (applied at the anchor point) + // This transform creates a coordinate system whose origin is the center of the layer. + // Note that the final matrix used by the shader for the layer is P * M * S . This final product + // is computed in drawTexturedQuad(). + // Where: P is the projection matrix + // M is the layer's matrix computed above + // S is the scale adjustment (to scale up to the layer size) + IntSize bounds = layer->bounds(); + FloatPoint anchorPoint = layer->anchorPoint(); + FloatPoint position = layer->position(); + + // Offset between anchor point and the center of the quad. + float centerOffsetX = (0.5 - anchorPoint.x()) * bounds.width(); + float centerOffsetY = (0.5 - anchorPoint.y()) * bounds.height(); + + TransformationMatrix layerLocalTransform; + // LT = Tr[l] + layerLocalTransform.translate3d(position.x(), position.y(), layer->anchorPointZ()); + // LT = Tr[l] * M[l] + layerLocalTransform.multLeft(layer->transform()); + // LT = Tr[l] * M[l] * Tr[c] + layerLocalTransform.translate3d(centerOffsetX, centerOffsetY, -layer->anchorPointZ()); + + TransformationMatrix combinedTransform = parentMatrix; + combinedTransform = combinedTransform.multLeft(layerLocalTransform); + + FloatRect layerRect(-0.5 * layer->bounds().width(), -0.5 * layer->bounds().height(), layer->bounds().width(), layer->bounds().height()); + IntRect transformedLayerRect; + + // The layer and its descendants render on a new RenderSurface if any of + // these conditions hold: + // 1. The layer clips its descendants and its transform is not a simple translation. + // 2. If the layer has opacity != 1 and does not have a preserves-3d transform style. + // If a layer preserves-3d then we don't create a RenderSurface for it to avoid flattening + // out its children. The opacity value of the children layers is multiplied by the opacity + // of their parent. + bool useSurfaceForClipping = layer->masksToBounds() && !isScaleOrTranslation(combinedTransform); + bool useSurfaceForOpacity = layer->opacity() != 1 && !layer->preserves3D(); + if ((useSurfaceForClipping || useSurfaceForOpacity) && layer->descendantsDrawContent()) { + RenderSurfaceChromium* renderSurface = layer->m_renderSurface.get(); + if (!renderSurface) + renderSurface = layer->createRenderSurface(); + + // The origin of the new surface is the upper left corner of the layer. + layer->m_drawTransform = TransformationMatrix(); + layer->m_drawTransform.translate3d(0.5 * bounds.width(), 0.5 * bounds.height(), 0); + + transformedLayerRect = IntRect(0, 0, bounds.width(), bounds.height()); + + // Layer's opacity will be applied when drawing the render surface. + renderSurface->m_drawOpacity = layer->opacity(); + if (layer->superlayer()->preserves3D()) + renderSurface->m_drawOpacity *= layer->superlayer()->drawOpacity(); + layer->m_drawOpacity = 1; + + TransformationMatrix layerOriginTransform = combinedTransform; + layerOriginTransform.translate3d(-0.5 * bounds.width(), -0.5 * bounds.height(), 0); + renderSurface->m_originTransform = layerOriginTransform; + if (layerOriginTransform.isInvertible() && layer->superlayer()) { + TransformationMatrix parentToLayer = layerOriginTransform.inverse(); + + layer->m_scissorRect = parentToLayer.mapRect(layer->superlayer()->m_scissorRect); + } else + layer->m_scissorRect = IntRect(); + + // The render surface scissor rect is the scissor rect that needs to + // be applied before drawing the render surface onto its containing + // surface and is therefore expressed in the superlayer's coordinate system. + renderSurface->m_scissorRect = layer->superlayer()->m_scissorRect; + + renderSurface->m_layerList.clear(); + + renderSurfaceLayerList.append(layer); + } else { + // DT = M[p] * LT + layer->m_drawTransform = combinedTransform; + transformedLayerRect = enclosingIntRect(layer->m_drawTransform.mapRect(layerRect)); + + layer->m_drawOpacity = layer->opacity(); + + if (layer->superlayer()) { + if (layer->superlayer()->preserves3D()) + layer->m_drawOpacity *= layer->superlayer()->m_drawOpacity; + + // Layers inherit the scissor rect from their superlayer. + layer->m_scissorRect = layer->superlayer()->m_scissorRect; + + layer->m_targetRenderSurface = layer->superlayer()->m_targetRenderSurface; + } + + if (layer != m_rootLayer) + layer->m_renderSurface = 0; + + if (layer->masksToBounds()) + layer->m_scissorRect.intersect(transformedLayerRect); + } + + if (layer->m_renderSurface) + layer->m_targetRenderSurface = layer->m_renderSurface.get(); + else { + ASSERT(layer->superlayer()); + layer->m_targetRenderSurface = layer->superlayer()->m_targetRenderSurface; + } + + // m_drawableContentRect is always stored in the coordinate system of the + // RenderSurface the layer draws into. + if (layer->drawsContent()) + layer->m_drawableContentRect = transformedLayerRect; + else + layer->m_drawableContentRect = IntRect(); + + TransformationMatrix sublayerMatrix = layer->m_drawTransform; + + // Flatten to 2D if the layer doesn't preserve 3D. + if (!layer->preserves3D()) { + sublayerMatrix.setM13(0); + sublayerMatrix.setM23(0); + sublayerMatrix.setM31(0); + sublayerMatrix.setM32(0); + sublayerMatrix.setM33(1); + sublayerMatrix.setM34(0); + sublayerMatrix.setM43(0); + } + + // Apply the sublayer transform at the center of the layer. + sublayerMatrix.multLeft(layer->sublayerTransform()); + + // The origin of the sublayers is the top left corner of the layer, not the + // center. The matrix passed down to the sublayers is therefore: + // M[s] = M * Tr[-center] + sublayerMatrix.translate3d(-bounds.width() * 0.5, -bounds.height() * 0.5, 0); + + Vector<LayerChromium*>& descendants = (layer->m_renderSurface ? layer->m_renderSurface->m_layerList : layerList); + descendants.append(layer); + unsigned thisLayerIndex = descendants.size() - 1; + + const Vector<RefPtr<LayerChromium> >& sublayers = layer->getSublayers(); + for (size_t i = 0; i < sublayers.size(); ++i) { + LayerChromium* sublayer = sublayers[i].get(); + updateLayersRecursive(sublayer, sublayerMatrix, renderSurfaceLayerList, descendants); + + if (sublayer->m_renderSurface) { + RenderSurfaceChromium* sublayerRenderSurface = sublayer->m_renderSurface.get(); + const IntRect& contentRect = sublayerRenderSurface->contentRect(); + FloatRect sublayerRect(-0.5 * contentRect.width(), -0.5 * contentRect.height(), + contentRect.width(), contentRect.height()); + layer->m_drawableContentRect.unite(enclosingIntRect(sublayerRenderSurface->m_drawTransform.mapRect(sublayerRect))); + descendants.append(sublayer); + } else + layer->m_drawableContentRect.unite(sublayer->m_drawableContentRect); + } + + if (layer->masksToBounds()) + layer->m_drawableContentRect.intersect(transformedLayerRect); + + if (layer->m_renderSurface && layer != m_rootLayer) { + RenderSurfaceChromium* renderSurface = layer->m_renderSurface.get(); + renderSurface->m_contentRect = layer->m_drawableContentRect; + FloatPoint surfaceCenter = renderSurface->contentRectCenter(); + + // Restrict the RenderSurface size to the portion that's visible. + FloatSize centerOffsetDueToClipping; + renderSurface->m_contentRect.intersect(layer->m_scissorRect); + FloatPoint clippedSurfaceCenter = renderSurface->contentRectCenter(); + centerOffsetDueToClipping = clippedSurfaceCenter - surfaceCenter; + + // The RenderSurface backing texture cannot exceed the maximum supported + // texture size. + renderSurface->m_contentRect.setWidth(std::min(renderSurface->m_contentRect.width(), m_maxTextureSize)); + renderSurface->m_contentRect.setHeight(std::min(renderSurface->m_contentRect.height(), m_maxTextureSize)); + + if (renderSurface->m_contentRect.isEmpty()) + renderSurface->m_layerList.clear(); + + // Since the layer starts a new render surface we need to adjust its + // scissor rect to be expressed in the new surface's coordinate system. + layer->m_scissorRect = layer->m_drawableContentRect; + + // Adjust the origin of the transform to be the center of the render surface. + renderSurface->m_drawTransform = renderSurface->m_originTransform; + renderSurface->m_drawTransform.translate3d(surfaceCenter.x() + centerOffsetDueToClipping.width(), surfaceCenter.y() + centerOffsetDueToClipping.height(), 0); + } + + // Compute the depth value of the center of the layer which will be used when + // sorting the layers for the preserves-3d property. + TransformationMatrix& layerDrawMatrix = layer->m_renderSurface ? layer->m_renderSurface->m_drawTransform : layer->m_drawTransform; + if (layer->superlayer()) { + if (!layer->superlayer()->preserves3D()) + layer->m_drawDepth = layer->superlayer()->m_drawDepth; + else + layer->m_drawDepth = layerDrawMatrix.m43(); + } else + layer->m_drawDepth = 0; + + // If preserves-3d then sort all the descendants by the Z coordinate of their + // center. If the preserves-3d property is also set on the superlayer then + // skip the sorting as the superlayer will sort all the descendants anyway. + if (layer->preserves3D() && (!layer->superlayer() || !layer->superlayer()->preserves3D())) + std::stable_sort(&descendants.at(thisLayerIndex), descendants.end(), compareLayerZ); +} + +void LayerRendererChromium::setCompositeOffscreen(bool compositeOffscreen) +{ + m_compositeOffscreen = compositeOffscreen; + + if (!m_rootLayer) { + m_compositeOffscreen = false; + return; + } + + if (m_compositeOffscreen) { + // Need to explicitly set a LayerRendererChromium for the layer with the offscreen texture, + // or else the call to prepareContentsTexture() in useRenderSurface() will fail. + m_rootLayer->setLayerRenderer(this); + } else + m_rootLayer->m_renderSurface.clear(); +} + +bool LayerRendererChromium::useRenderSurface(RenderSurfaceChromium* renderSurface) +{ + if (m_currentRenderSurface == renderSurface) + return true; + + m_currentRenderSurface = renderSurface; + + if (renderSurface == m_defaultRenderSurface && !m_compositeOffscreen) { + GLC(m_context.get(), m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0)); + setDrawViewportRect(renderSurface->m_contentRect, true); + return true; + } + + GLC(m_context.get(), m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_offscreenFramebufferId)); + + if (!renderSurface->prepareContentsTexture()) + return false; + + renderSurface->m_contentsTexture->framebufferTexture2D(); + +#if !defined ( NDEBUG ) + if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { + ASSERT_NOT_REACHED(); + return false; + } +#endif + + setDrawViewportRect(renderSurface->m_contentRect, false); + return true; +} + +void LayerRendererChromium::drawLayer(LayerChromium* layer, RenderSurfaceChromium* targetSurface) +{ + if (layer->m_renderSurface && layer->m_renderSurface != targetSurface) { + layer->m_renderSurface->draw(); + return; + } + + if (layer->m_bounds.isEmpty()) + return; + + setScissorToRect(layer->m_scissorRect); + + // Check if the layer falls within the visible bounds of the page. + IntRect layerRect = layer->getDrawRect(); + bool isLayerVisible = layer->m_scissorRect.intersects(layerRect); + if (!isLayerVisible) + return; + + // FIXME: Need to take into account the transform of the containing + // RenderSurface here, otherwise single-sided layers that draw on + // transformed surfaces won't always be culled properly. + if (!layer->doubleSided() && layer->m_drawTransform.m33() < 0) + return; + + if (layer->drawsContent()) { + // Update the contents of the layer if necessary. + layer->updateContentsIfDirty(); + m_context->makeContextCurrent(); + layer->draw(); + } + + // Draw the debug border if there is one. + layer->drawDebugBorder(); +} + +// Sets the scissor region to the given rectangle. The coordinate system for the +// scissorRect has its origin at the top left corner of the current visible rect. +void LayerRendererChromium::setScissorToRect(const IntRect& scissorRect) +{ + // The scissor coordinates must be supplied in viewport space so we need to offset + // by the relative position of the top left corner of the current render surface. + int scissorX = scissorRect.x() - m_currentRenderSurface->m_contentRect.x(); + // When rendering to the default render surface we're rendering upside down so the top + // of the GL scissor is the bottom of our layer. + // But, if rendering to offscreen texture, we reverse our sense of 'upside down'. + int scissorY; + if (m_currentRenderSurface == m_defaultRenderSurface && !m_compositeOffscreen) + scissorY = m_currentRenderSurface->m_contentRect.height() - (scissorRect.bottom() - m_currentRenderSurface->m_contentRect.y()); + else + scissorY = scissorRect.y() - m_currentRenderSurface->m_contentRect.y(); + GLC(m_context.get(), m_context->scissor(scissorX, scissorY, scissorRect.width(), scissorRect.height())); +} + +bool LayerRendererChromium::makeContextCurrent() +{ + m_context->makeContextCurrent(); + return true; +} + +// Checks whether a given size is within the maximum allowed texture size range. +bool LayerRendererChromium::checkTextureSize(const IntSize& textureSize) +{ + if (textureSize.width() > m_maxTextureSize || textureSize.height() > m_maxTextureSize) + return false; + return true; +} + +// Sets the coordinate range of content that ends being drawn onto the target render surface. +// The target render surface is assumed to have an origin at 0, 0 and the width and height of +// of the drawRect. +void LayerRendererChromium::setDrawViewportRect(const IntRect& drawRect, bool flipY) +{ + if (flipY) + m_projectionMatrix = orthoMatrix(drawRect.x(), drawRect.right(), drawRect.bottom(), drawRect.y()); + else + m_projectionMatrix = orthoMatrix(drawRect.x(), drawRect.right(), drawRect.y(), drawRect.bottom()); + GLC(m_context.get(), m_context->viewport(0, 0, drawRect.width(), drawRect.height())); +} + + + +void LayerRendererChromium::resizeOnscreenContent(const IntSize& size) +{ + if (m_context) + m_context->reshape(size.width(), size.height()); +} + +bool LayerRendererChromium::initializeSharedObjects() +{ + makeContextCurrent(); + + // Get the max texture size supported by the system. + m_maxTextureSize = 0; + GLC(m_context.get(), m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize)); + + // Create an FBO for doing offscreen rendering. + GLC(m_context.get(), m_offscreenFramebufferId = m_context->createFramebuffer()); + + m_layerSharedValues = adoptPtr(new LayerChromium::SharedValues(m_context.get())); + m_contentLayerSharedValues = adoptPtr(new ContentLayerChromium::SharedValues(m_context.get())); + m_canvasLayerSharedValues = adoptPtr(new CanvasLayerChromium::SharedValues(m_context.get())); + m_videoLayerSharedValues = adoptPtr(new VideoLayerChromium::SharedValues(m_context.get())); + m_pluginLayerSharedValues = adoptPtr(new PluginLayerChromium::SharedValues(m_context.get())); + m_renderSurfaceSharedValues = adoptPtr(new RenderSurfaceChromium::SharedValues(m_context.get())); + + if (!m_layerSharedValues->initialized() || !m_contentLayerSharedValues->initialized() || !m_canvasLayerSharedValues->initialized() + || !m_videoLayerSharedValues->initialized() || !m_pluginLayerSharedValues->initialized() || !m_renderSurfaceSharedValues->initialized()) { + cleanupSharedObjects(); + return false; + } + + m_textureManager = TextureManager::create(m_context.get(), textureMemoryLimitBytes, m_maxTextureSize); + return true; +} + +void LayerRendererChromium::cleanupSharedObjects() +{ + makeContextCurrent(); + + m_layerSharedValues.clear(); + m_contentLayerSharedValues.clear(); + m_canvasLayerSharedValues.clear(); + m_videoLayerSharedValues.clear(); + m_pluginLayerSharedValues.clear(); + m_renderSurfaceSharedValues.clear(); + if (m_offscreenFramebufferId) + GLC(m_context.get(), m_context->deleteFramebuffer(m_offscreenFramebufferId)); + + m_textureManager.clear(); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.h b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.h new file mode 100644 index 0000000..3d3e784 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.h @@ -0,0 +1,221 @@ +/* + * 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 LayerRendererChromium_h +#define LayerRendererChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CanvasLayerChromium.h" +#include "ContentLayerChromium.h" +#include "IntRect.h" +#include "LayerChromium.h" +#include "LayerTilerChromium.h" +#include "PluginLayerChromium.h" +#include "RenderSurfaceChromium.h" +#include "SkBitmap.h" +#include "VideoLayerChromium.h" +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +#if PLATFORM(CG) +#include <CoreGraphics/CGContext.h> +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +class GraphicsContext3D; + +// Class that handles drawing of composited render layers using GL. +class LayerRendererChromium : public RefCounted<LayerRendererChromium> { +public: + static PassRefPtr<LayerRendererChromium> create(PassRefPtr<GraphicsContext3D> graphicsContext3D); + + ~LayerRendererChromium(); + + GraphicsContext3D* context(); + + void invalidateRootLayerRect(const IntRect& dirtyRect, const IntRect& visibleRect, const IntRect& contentRect); + + // updates and draws the current layers onto the backbuffer + void drawLayers(const IntRect& visibleRect, const IntRect& contentRect, + const IntPoint& scrollPosition, TilePaintInterface& tilePaint, + TilePaintInterface& scrollbarPaint); + + // waits for rendering to finish + void finish(); + + // puts backbuffer onscreen + void present(); + + void setRootLayer(PassRefPtr<LayerChromium> layer); + LayerChromium* rootLayer() { return m_rootLayer.get(); } + void transferRootLayer(LayerRendererChromium* other) { other->m_rootLayer = m_rootLayer.release(); } + + bool hardwareCompositing() const { return m_hardwareCompositing; } + + void setCompositeOffscreen(bool); + bool isCompositingOffscreen() { return m_compositeOffscreen; } + LayerTexture* getOffscreenLayerTexture() { return m_compositeOffscreen ? m_rootLayer->m_renderSurface->m_contentsTexture.get() : 0; } + + void setRootLayerCanvasSize(const IntSize&); + + GraphicsContext* rootLayerGraphicsContext() const { return m_rootLayerGraphicsContext.get(); } + + unsigned createLayerTexture(); + void deleteLayerTexture(unsigned); + + static void debugGLCall(GraphicsContext3D*, const char* command, const char* file, int line); + + const TransformationMatrix& projectionMatrix() const { return m_projectionMatrix; } + + void useShader(unsigned); + + bool checkTextureSize(const IntSize&); + + const LayerChromium::SharedValues* layerSharedValues() const { return m_layerSharedValues.get(); } + const ContentLayerChromium::SharedValues* contentLayerSharedValues() const { return m_contentLayerSharedValues.get(); } + const CanvasLayerChromium::SharedValues* canvasLayerSharedValues() const { return m_canvasLayerSharedValues.get(); } + const VideoLayerChromium::SharedValues* videoLayerSharedValues() const { return m_videoLayerSharedValues.get(); } + const PluginLayerChromium::SharedValues* pluginLayerSharedValues() const { return m_pluginLayerSharedValues.get(); } + const RenderSurfaceChromium::SharedValues* renderSurfaceSharedValues() const { return m_renderSurfaceSharedValues.get(); } + + void resizeOnscreenContent(const IntSize&); + + IntSize rootLayerTextureSize() const { return IntSize(m_rootLayerTextureWidth, m_rootLayerTextureHeight); } + IntRect rootLayerContentRect() const { return m_rootContentRect; } + void getFramebufferPixels(void *pixels, const IntRect& rect); + + TextureManager* textureManager() const { return m_textureManager.get(); } + + void setScissorToRect(const IntRect&); + +private: + explicit LayerRendererChromium(PassRefPtr<GraphicsContext3D> graphicsContext3D); + void updateLayersRecursive(LayerChromium* layer, const TransformationMatrix& parentMatrix, Vector<LayerChromium*>& renderSurfaceLayerList, Vector<LayerChromium*>& layerList); + + void drawLayer(LayerChromium*, RenderSurfaceChromium*); + + void updateAndDrawRootLayer(TilePaintInterface& tilePaint, TilePaintInterface& scrollbarPaint, const IntRect& visibleRect, const IntRect& contentRect); + + bool isLayerVisible(LayerChromium*, const TransformationMatrix&, const IntRect& visibleRect); + + void setDrawViewportRect(const IntRect&, bool flipY); + + bool useRenderSurface(RenderSurfaceChromium*); + + bool makeContextCurrent(); + + static bool compareLayerZ(const LayerChromium*, const LayerChromium*); + + bool initializeSharedObjects(); + void cleanupSharedObjects(); + + static IntRect verticalScrollbarRect(const IntRect& visibleRect, const IntRect& contentRect); + static IntRect horizontalScrollbarRect(const IntRect& visibleRect, const IntRect& contentRect); + + int m_rootLayerTextureWidth; + int m_rootLayerTextureHeight; + + TransformationMatrix m_projectionMatrix; + + RefPtr<LayerChromium> m_rootLayer; + OwnPtr<LayerTilerChromium> m_rootLayerTiler; + OwnPtr<LayerTilerChromium> m_horizontalScrollbarTiler; + OwnPtr<LayerTilerChromium> m_verticalScrollbarTiler; + + IntPoint m_scrollPosition; + bool m_hardwareCompositing; + + unsigned m_currentShader; + RenderSurfaceChromium* m_currentRenderSurface; + + unsigned m_offscreenFramebufferId; + bool m_compositeOffscreen; + +#if PLATFORM(SKIA) + OwnPtr<skia::PlatformCanvas> m_rootLayerCanvas; + OwnPtr<PlatformContextSkia> m_rootLayerSkiaContext; + OwnPtr<GraphicsContext> m_rootLayerGraphicsContext; +#elif PLATFORM(CG) + Vector<uint8_t> m_rootLayerBackingStore; + RetainPtr<CGContextRef> m_rootLayerCGContext; + OwnPtr<GraphicsContext> m_rootLayerGraphicsContext; +#endif + + IntSize m_rootLayerCanvasSize; + + IntRect m_rootVisibleRect; + IntRect m_rootContentRect; + + // Maximum texture dimensions supported. + int m_maxTextureSize; + + // Store values that are shared between instances of each layer type + // associated with this instance of the compositor. Since there can be + // multiple instances of the compositor running in the same renderer process + // we cannot store these values in static variables. + OwnPtr<LayerChromium::SharedValues> m_layerSharedValues; + OwnPtr<ContentLayerChromium::SharedValues> m_contentLayerSharedValues; + OwnPtr<CanvasLayerChromium::SharedValues> m_canvasLayerSharedValues; + OwnPtr<VideoLayerChromium::SharedValues> m_videoLayerSharedValues; + OwnPtr<PluginLayerChromium::SharedValues> m_pluginLayerSharedValues; + OwnPtr<RenderSurfaceChromium::SharedValues> m_renderSurfaceSharedValues; + + OwnPtr<TextureManager> m_textureManager; + + RefPtr<GraphicsContext3D> m_context; + + RenderSurfaceChromium* m_defaultRenderSurface; +}; + +// Setting DEBUG_GL_CALLS to 1 will call glGetError() after almost every GL +// call made by the compositor. Useful for debugging rendering issues but +// will significantly degrade performance. +#define DEBUG_GL_CALLS 0 + +#if DEBUG_GL_CALLS && !defined ( NDEBUG ) +#define GLC(context, x) { (x), LayerRendererChromium::debugGLCall(context, #x, __FILE__, __LINE__); } +#else +#define GLC(context, x) (x) +#endif + + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp b/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp new file mode 100644 index 0000000..32bfa0b --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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(ACCELERATED_COMPOSITING) + +#include "LayerTexture.h" + +#include "GraphicsContext3D.h" +#include "TextureManager.h" + +namespace WebCore { + +LayerTexture::LayerTexture(GraphicsContext3D* context, TextureManager* manager) + : m_context(context) + , m_textureManager(manager) + , m_token(0) + , m_format(0) + , m_textureId(0) +{ +} + +LayerTexture::~LayerTexture() +{ + if (m_token) + m_textureManager->releaseToken(m_token); +} + +bool LayerTexture::isValid(const IntSize& size, unsigned format) +{ + return m_token && size == m_size && format == m_format && m_textureManager->hasTexture(m_token); +} + +bool LayerTexture::reserve(const IntSize& size, unsigned format) +{ + if (!m_token) + m_token = m_textureManager->getToken(); + + if (size == m_size && format == m_format && m_textureManager->hasTexture(m_token)) + m_textureManager->protectTexture(m_token); + else { + m_textureId = m_textureManager->requestTexture(m_token, size, format); + if (m_textureId) { + m_size = size; + m_format = format; + } + } + + return m_textureId; +} + +void LayerTexture::unreserve() +{ + if (m_token) + m_textureManager->unprotectTexture(m_token); +} + +void LayerTexture::bindTexture() +{ + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textureId); +} + +void LayerTexture::framebufferTexture2D() +{ + m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_textureId, 0); +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) + diff --git a/Source/WebCore/platform/graphics/chromium/LayerTexture.h b/Source/WebCore/platform/graphics/chromium/LayerTexture.h new file mode 100644 index 0000000..312adfa --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerTexture.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 LayerTexture_h +#define LayerTexture_h + +#include "IntSize.h" +#include "TextureManager.h" + +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class GraphicsContext3D; +class TextureManager; + +class LayerTexture : public Noncopyable { +public: + static PassOwnPtr<LayerTexture> create(GraphicsContext3D* context, TextureManager* manager) + { + return adoptPtr(new LayerTexture(context, manager)); + } + ~LayerTexture(); + + bool isValid(const IntSize&, unsigned format); + bool reserve(const IntSize&, unsigned format); + void unreserve(); + + void bindTexture(); + void framebufferTexture2D(); + +private: + LayerTexture(GraphicsContext3D*, TextureManager*); + + RefPtr<GraphicsContext3D> m_context; + TextureManager* m_textureManager; + TextureToken m_token; + IntSize m_size; + unsigned m_format; + unsigned m_textureId; +}; + +} + +#endif + diff --git a/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp new file mode 100644 index 0000000..31649a4 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerTilerChromium.h" + +#include "GraphicsContext.h" +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" + +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#include "PlatformContextSkia.h" +#elif PLATFORM(CG) +#include <CoreGraphics/CGBitmapContext.h> +#endif + +#include <wtf/PassOwnArrayPtr.h> + +namespace WebCore { + +PassOwnPtr<LayerTilerChromium> LayerTilerChromium::create(LayerRendererChromium* layerRenderer, const IntSize& tileSize) +{ + if (!layerRenderer || tileSize.isEmpty()) + return 0; + + return adoptPtr(new LayerTilerChromium(layerRenderer, tileSize)); +} + +LayerTilerChromium::LayerTilerChromium(LayerRendererChromium* layerRenderer, const IntSize& tileSize) + : m_layerRenderer(layerRenderer) +{ + setTileSize(tileSize); +} + +LayerTilerChromium::~LayerTilerChromium() +{ + reset(); +} + +GraphicsContext3D* LayerTilerChromium::layerRendererContext() const +{ + ASSERT(layerRenderer()); + return layerRenderer()->context(); +} + +void LayerTilerChromium::setTileSize(const IntSize& size) +{ + if (m_tileSize == size) + return; + + reset(); + + m_tileSize = size; + m_tilePixels = adoptArrayPtr(new uint8_t[m_tileSize.width() * m_tileSize.height() * 4]); +} + +void LayerTilerChromium::reset() +{ + for (size_t i = 0; i < m_tiles.size(); ++i) { + if (!m_tiles[i]) + continue; + layerRenderer()->deleteLayerTexture(m_tiles[i]->releaseTextureId()); + } + m_tiles.clear(); + for (size_t i = 0; i < m_unusedTiles.size(); ++i) { + if (!m_unusedTiles[i]) + continue; + layerRenderer()->deleteLayerTexture(m_unusedTiles[i]->releaseTextureId()); + } + m_unusedTiles.clear(); + + m_layerSize = IntSize(); + m_layerTileSize = IntSize(); + m_lastUpdateLayerRect = IntRect(); +} + +LayerTilerChromium::Tile* LayerTilerChromium::createTile(int i, int j) +{ + const int index = tileIndex(i, j); + ASSERT(!m_tiles[index]); + + if (m_unusedTiles.size() > 0) { + m_tiles[index] = m_unusedTiles.last().release(); + m_unusedTiles.removeLast(); + } else { + const unsigned int textureId = layerRenderer()->createLayerTexture(); + OwnPtr<Tile> tile = adoptPtr(new Tile(textureId)); + + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, m_tileSize.width(), m_tileSize.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE)); + + m_tiles[index] = tile.release(); + } + + m_tiles[index]->m_dirtyLayerRect = tileLayerRect(i, j); + return m_tiles[index].get(); +} + +void LayerTilerChromium::invalidateTiles(const IntRect& oldLayerRect, const IntRect& newLayerRect) +{ + if (!m_tiles.size()) + return; + + IntRect oldContentRect = layerRectToContentRect(oldLayerRect); + int oldLeft, oldTop, oldRight, oldBottom; + contentRectToTileIndices(oldContentRect, oldLeft, oldTop, oldRight, oldBottom); + + IntRect newContentRect = layerRectToContentRect(newLayerRect); + int newLeft, newTop, newRight, newBottom; + contentRectToTileIndices(newContentRect, newLeft, newTop, newRight, newBottom); + + // Iterating through just the old tile indices is an optimization to avoid + // iterating through the entire m_tiles array. + for (int j = oldTop; j <= oldBottom; ++j) { + for (int i = oldLeft; i <= oldRight; ++i) { + if (i >= newLeft && i <= newRight && j >= newTop && j <= newBottom) + continue; + + const int index = tileIndex(i, j); + if (m_tiles[index]) + m_unusedTiles.append(m_tiles[index].release()); + } + } +} + +void LayerTilerChromium::contentRectToTileIndices(const IntRect& contentRect, int &left, int &top, int &right, int &bottom) const +{ + const IntRect layerRect = contentRectToLayerRect(contentRect); + + left = layerRect.x() / m_tileSize.width(); + top = layerRect.y() / m_tileSize.height(); + right = (layerRect.right() - 1) / m_tileSize.width(); + bottom = (layerRect.bottom() - 1) / m_tileSize.height(); +} + +IntRect LayerTilerChromium::contentRectToLayerRect(const IntRect& contentRect) const +{ + IntPoint pos(contentRect.x() - m_layerPosition.x(), contentRect.y() - m_layerPosition.y()); + IntRect layerRect(pos, contentRect.size()); + + // Clip to the position. + if (pos.x() < 0 || pos.y() < 0) + layerRect = IntRect(IntPoint(0, 0), IntSize(contentRect.width() + pos.x(), contentRect.height() + pos.y())); + return layerRect; +} + +IntRect LayerTilerChromium::layerRectToContentRect(const IntRect& layerRect) const +{ + IntRect contentRect = layerRect; + contentRect.move(m_layerPosition.x(), m_layerPosition.y()); + return contentRect; +} + +int LayerTilerChromium::tileIndex(int i, int j) const +{ + ASSERT(i >= 0 && j >= 0 && i < m_layerTileSize.width() && j < m_layerTileSize.height()); + return i + j * m_layerTileSize.width(); +} + +IntRect LayerTilerChromium::tileContentRect(int i, int j) const +{ + IntPoint anchor(m_layerPosition.x() + i * m_tileSize.width(), m_layerPosition.y() + j * m_tileSize.height()); + IntRect tile(anchor, m_tileSize); + return tile; +} + +IntRect LayerTilerChromium::tileLayerRect(int i, int j) const +{ + IntPoint anchor(i * m_tileSize.width(), j * m_tileSize.height()); + IntRect tile(anchor, m_tileSize); + return tile; +} + +void LayerTilerChromium::invalidateRect(const IntRect& contentRect) +{ + if (contentRect.isEmpty()) + return; + + growLayerToContain(contentRect); + + // Dirty rects are always in layer space, as the layer could be repositioned + // after invalidation. + IntRect layerRect = contentRectToLayerRect(contentRect); + + int left, top, right, bottom; + contentRectToTileIndices(contentRect, left, top, right, bottom); + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + Tile* tile = m_tiles[tileIndex(i, j)].get(); + if (!tile) + continue; + IntRect bound = tileLayerRect(i, j); + bound.intersect(layerRect); + tile->m_dirtyLayerRect.unite(bound); + } + } +} + +void LayerTilerChromium::invalidateEntireLayer() +{ + for (size_t i = 0; i < m_tiles.size(); ++i) { + if (m_tiles[i]) + m_unusedTiles.append(m_tiles[i].release()); + } + m_tiles.clear(); + + m_layerSize = IntSize(); + m_layerTileSize = IntSize(); + m_lastUpdateLayerRect = IntRect(); +} + +void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& contentRect) +{ + // Invalidate old tiles that were previously used but aren't in use this + // frame so that they can get reused for new tiles. + IntRect layerRect = contentRectToLayerRect(contentRect); + invalidateTiles(m_lastUpdateLayerRect, layerRect); + m_lastUpdateLayerRect = layerRect; + + growLayerToContain(contentRect); + + // Create tiles as needed, expanding a dirty rect to contain all + // the dirty regions currently being drawn. + IntRect dirtyLayerRect; + int left, top, right, bottom; + contentRectToTileIndices(contentRect, left, top, right, bottom); + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + Tile* tile = m_tiles[tileIndex(i, j)].get(); + if (!tile) + tile = createTile(i, j); + dirtyLayerRect.unite(tile->m_dirtyLayerRect); + } + } + + if (dirtyLayerRect.isEmpty()) + return; + + const IntRect paintRect = layerRectToContentRect(dirtyLayerRect); + GraphicsContext3D* context = layerRendererContext(); +#if PLATFORM(SKIA) + OwnPtr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas(paintRect.width(), paintRect.height(), false)); + OwnPtr<PlatformContextSkia> skiaContext(new PlatformContextSkia(canvas.get())); + OwnPtr<GraphicsContext> graphicsContext(new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(skiaContext.get()))); + + // Bring the canvas into the coordinate system of the paint rect. + canvas->translate(static_cast<SkScalar>(-paintRect.x()), static_cast<SkScalar>(-paintRect.y())); + + painter.paint(*graphicsContext, paintRect); + + // Get the contents of the updated rect. + const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(false); + ASSERT(bitmap.width() == paintRect.width() && bitmap.height() == paintRect.height()); + uint8_t* paintPixels = static_cast<uint8_t*>(bitmap.getPixels()); +#elif PLATFORM(CG) + Vector<uint8_t> canvasPixels; + int rowBytes = 4 * paintRect.width(); + canvasPixels.resize(rowBytes * paintRect.height()); + memset(canvasPixels.data(), 0, canvasPixels.size()); + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGContextRef> m_cgContext; + m_cgContext.adoptCF(CGBitmapContextCreate(canvasPixels.data(), + paintRect.width(), paintRect.height(), 8, rowBytes, + colorSpace.get(), + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + CGContextTranslateCTM(m_cgContext.get(), 0, paintRect.height()); + CGContextScaleCTM(m_cgContext.get(), 1, -1); + OwnPtr<GraphicsContext> m_graphicsContext(new GraphicsContext(m_cgContext.get())); + + // Bring the CoreGraphics context into the coordinate system of the paint rect. + CGContextTranslateCTM(m_cgContext.get(), -paintRect.x(), -paintRect.y()); + painter.paint(*m_graphicsContext, paintRect); + + // Get the contents of the updated rect. + ASSERT(static_cast<int>(CGBitmapContextGetWidth(m_cgContext.get())) == paintRect.width() && static_cast<int>(CGBitmapContextGetHeight(m_cgContext.get())) == paintRect.height()); + uint8_t* paintPixels = static_cast<uint8_t*>(canvasPixels.data()); +#else +#error "Need to implement for your platform." +#endif + + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + Tile* tile = m_tiles[tileIndex(i, j)].get(); + if (!tile->dirty()) + continue; + + // Calculate page-space rectangle to copy from. + IntRect sourceRect = tileContentRect(i, j); + const IntPoint anchor = sourceRect.location(); + sourceRect.intersect(layerRectToContentRect(tile->m_dirtyLayerRect)); + + // Calculate tile-space rectangle to upload into. + IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size()); + + // Offset from paint rectangle to this tile's dirty rectangle. + IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y()); + + uint8_t* pixelSource; + if (paintRect.width() == sourceRect.width() && !paintOffset.x()) + pixelSource = &paintPixels[4 * paintOffset.y() * paintRect.width()]; + else { + // Strides not equal, so do a row-by-row memcpy from the + // paint results into a temp buffer for uploading. + for (int row = 0; row < destRect.height(); ++row) + memcpy(&m_tilePixels[destRect.width() * 4 * row], + &paintPixels[4 * (paintOffset.x() + (paintOffset.y() + row) * paintRect.width())], + destRect.width() * 4); + + pixelSource = &m_tilePixels[0]; + } + + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, tile->textureId())); + GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, destRect.x(), destRect.y(), destRect.width(), destRect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixelSource)); + + tile->clearDirty(); + } + } +} + +void LayerTilerChromium::setLayerPosition(const IntPoint& layerPosition) +{ + m_layerPosition = layerPosition; +} + +void LayerTilerChromium::draw(const IntRect& contentRect) +{ + // We reuse the shader program used by ContentLayerChromium. + GraphicsContext3D* context = layerRendererContext(); + const ContentLayerChromium::SharedValues* contentLayerValues = layerRenderer()->contentLayerSharedValues(); + layerRenderer()->useShader(contentLayerValues->contentShaderProgram()); + GLC(context, context->uniform1i(contentLayerValues->shaderSamplerLocation(), 0)); + + int left, top, right, bottom; + contentRectToTileIndices(contentRect, left, top, right, bottom); + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + Tile* tile = m_tiles[tileIndex(i, j)].get(); + ASSERT(tile); + + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, tile->textureId())); + + TransformationMatrix tileMatrix; + IntRect tileRect = tileContentRect(i, j); + tileMatrix.translate3d(tileRect.x() - contentRect.x() + tileRect.width() / 2.0, tileRect.y() - contentRect.y() + tileRect.height() / 2.0, 0); + + LayerChromium::drawTexturedQuad(context, layerRenderer()->projectionMatrix(), tileMatrix, m_tileSize.width(), m_tileSize.height(), 1, contentLayerValues->shaderMatrixLocation(), contentLayerValues->shaderAlphaLocation()); + } + } +} + +void LayerTilerChromium::resizeLayer(const IntSize& size) +{ + if (m_layerSize == size) + return; + + int width = (size.width() + m_tileSize.width() - 1) / m_tileSize.width(); + int height = (size.height() + m_tileSize.height() - 1) / m_tileSize.height(); + + Vector<OwnPtr<Tile> > newTiles; + newTiles.resize(width * height); + for (int j = 0; j < m_layerTileSize.height(); ++j) + for (int i = 0; i < m_layerTileSize.width(); ++i) + newTiles[i + j * width].swap(m_tiles[i + j * m_layerTileSize.width()]); + + m_tiles.swap(newTiles); + m_layerSize = size; + m_layerTileSize = IntSize(width, height); +} + +void LayerTilerChromium::growLayerToContain(const IntRect& contentRect) +{ + // Grow the tile array to contain this content rect. + IntRect layerRect = contentRectToLayerRect(contentRect); + IntSize layerSize = IntSize(layerRect.right(), layerRect.bottom()); + + IntSize newSize = layerSize.expandedTo(m_layerSize); + resizeLayer(newSize); +} + +LayerTilerChromium::Tile::~Tile() +{ + // Each tile doesn't have a reference to the context, so can't clean up + // its own texture. If this assert is hit, then the LayerTilerChromium + // destructor didn't clean this up. + ASSERT(!m_textureId); +} + +unsigned int LayerTilerChromium::Tile::releaseTextureId() +{ + unsigned int id = m_textureId; + m_textureId = 0; + return id; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.h b/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.h new file mode 100644 index 0000000..c066fdf --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef LayerTilerChromium_h +#define LayerTilerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerChromium.h" +#include <wtf/OwnArrayPtr.h> + +namespace WebCore { + +class GraphicsContext3D; +class LayerRendererChromium; + +class TilePaintInterface { +public: + virtual void paint(GraphicsContext& context, const IntRect& contentRect) = 0; +}; + +class LayerTilerChromium : public Noncopyable { +public: + static PassOwnPtr<LayerTilerChromium> create(LayerRendererChromium* layerRenderer, const IntSize& tileSize); + + ~LayerTilerChromium(); + + void invalidateRect(const IntRect& contentRect); + void invalidateEntireLayer(); + void update(TilePaintInterface& painter, const IntRect& contentRect); + void draw(const IntRect& contentRect); + + // Set position of this tiled layer in content space. + void setLayerPosition(const IntPoint& position); + // Change the tile size. This may invalidate all the existing tiles. + void setTileSize(const IntSize& size); + +private: + LayerTilerChromium(LayerRendererChromium* layerRenderer, const IntSize& tileSize); + + class Tile { + public: + explicit Tile(unsigned int textureId) : m_textureId(textureId) { } + ~Tile(); + + unsigned int textureId() const { return m_textureId; } + unsigned int releaseTextureId(); + + bool dirty() const { return !m_dirtyLayerRect.isEmpty(); } + void clearDirty() { m_dirtyLayerRect = IntRect(); } + + // Layer-space dirty rectangle that needs to be repainted. + IntRect m_dirtyLayerRect; + private: + unsigned int m_textureId; + }; + + void resizeLayer(const IntSize& size); + // Grow layer size to contain this rectangle. + void growLayerToContain(const IntRect& contentRect); + + LayerRendererChromium* layerRenderer() const { return m_layerRenderer; } + GraphicsContext3D* layerRendererContext() const; + Tile* createTile(int i, int j); + // Invalidate any tiles which do not intersect with the newLayerRect. + void invalidateTiles(const IntRect& oldLayerRect, const IntRect& newLayerRect); + void reset(); + void contentRectToTileIndices(const IntRect& contentRect, int &left, int &top, int &right, int &bottom) const; + IntRect contentRectToLayerRect(const IntRect& contentRect) const; + IntRect layerRectToContentRect(const IntRect& layerRect) const; + + // Returns the index into m_tiles for a given tile location. + int tileIndex(int i, int j) const; + // Returns the bounds in content space for a given tile location. + IntRect tileContentRect(int i, int j) const; + // Returns the bounds in layer space for a given tile location. + IntRect tileLayerRect(int i, int j) const; + + IntSize m_tileSize; + IntSize m_layerSize; + IntSize m_layerTileSize; + IntRect m_lastUpdateLayerRect; + IntPoint m_layerPosition; + + // Logical 2D array of tiles (dimensions of m_layerTileSize) + Vector<OwnPtr<Tile> > m_tiles; + // Linear array of unused tiles. + Vector<OwnPtr<Tile> > m_unusedTiles; + + // Cache a tile-sized pixel buffer to draw into. + OwnArrayPtr<uint8_t> m_tilePixels; + + LayerRendererChromium* m_layerRenderer; +}; + +} + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h b/Source/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h new file mode 100644 index 0000000..534244d --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008, 2009, 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 MediaPlayerPrivateChromium_h +#define MediaPlayerPrivateChromium_h + +#if ENABLE(VIDEO) + +#include "MediaPlayerPrivate.h" + +namespace WebCore { + +class MediaPlayerPrivate { +public: + static void registerMediaEngine(MediaEngineRegistrar); +}; + +} // namespace WebCore + +#endif + +#endif // MediaPlayerPrivateChromium_h diff --git a/Source/WebCore/platform/graphics/chromium/PlatformIcon.h b/Source/WebCore/platform/graphics/chromium/PlatformIcon.h new file mode 100644 index 0000000..51613b8 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/PlatformIcon.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008, 2009, 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 PlatformIcon_h +#define PlatformIcon_h + +typedef struct HICON__* HICON; + +namespace WebCore { + +typedef HICON PlatformIcon; + +} // namespace WebCore + +#endif // PlatformIcon_h diff --git a/Source/WebCore/platform/graphics/chromium/PluginLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/PluginLayerChromium.cpp new file mode 100644 index 0000000..878c142 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/PluginLayerChromium.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "PluginLayerChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#include <GLES2/gl2.h> + +namespace WebCore { + +PluginLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_shaderProgram(0) + , m_shaderSamplerLocation(-1) + , m_shaderMatrixLocation(-1) + , m_shaderAlphaLocation(-1) + , m_initialized(false) +{ + char vertexShaderString[] = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + + char fragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, vec2(v_texCoord.x, v_texCoord.y)); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" + "} \n"; + + m_shaderProgram = createShaderProgram(m_context, vertexShaderString, fragmentShaderString); + if (!m_shaderProgram) { + LOG_ERROR("PluginLayerChromium: Failed to create shader program"); + return; + } + + m_shaderSamplerLocation = m_context->getUniformLocation(m_shaderProgram, "s_texture"); + m_shaderMatrixLocation = m_context->getUniformLocation(m_shaderProgram, "matrix"); + m_shaderAlphaLocation = m_context->getUniformLocation(m_shaderProgram, "alpha"); + ASSERT(m_shaderSamplerLocation != -1); + ASSERT(m_shaderMatrixLocation != -1); + ASSERT(m_shaderAlphaLocation != -1); + + m_initialized = true; +} + +PluginLayerChromium::SharedValues::~SharedValues() +{ + if (m_shaderProgram) + GLC(m_context, m_context->deleteProgram(m_shaderProgram)); +} + +PassRefPtr<PluginLayerChromium> PluginLayerChromium::create(GraphicsLayerChromium* owner) +{ + return adoptRef(new PluginLayerChromium(owner)); +} + +PluginLayerChromium::PluginLayerChromium(GraphicsLayerChromium* owner) + : LayerChromium(owner) +{ +} + +void PluginLayerChromium::setTextureId(unsigned id) +{ + m_textureId = id; +} + +void PluginLayerChromium::updateContentsIfDirty() +{ +} + +void PluginLayerChromium::draw() +{ + ASSERT(layerRenderer()); + const PluginLayerChromium::SharedValues* sv = layerRenderer()->pluginLayerSharedValues(); + ASSERT(sv && sv->initialized()); + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GL_TEXTURE0)); + GLC(context, context->bindTexture(GL_TEXTURE_2D, m_textureId)); + + // FIXME: setting the texture parameters every time is redundant. Move this code somewhere + // where it will only happen once per texture. + GLC(context, context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLC(context, context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLC(context, context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLC(context, context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + layerRenderer()->useShader(sv->shaderProgram()); + GLC(context, context->uniform1i(sv->shaderSamplerLocation(), 0)); + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), + bounds().width(), bounds().height(), drawOpacity(), + sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/PluginLayerChromium.h b/Source/WebCore/platform/graphics/chromium/PluginLayerChromium.h new file mode 100644 index 0000000..853b328 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/PluginLayerChromium.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PluginLayerChromium_h +#define PluginLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerChromium.h" + +namespace WebCore { + +// A Layer containing a the rendered output of a plugin instance. +class PluginLayerChromium : public LayerChromium { +public: + static PassRefPtr<PluginLayerChromium> create(GraphicsLayerChromium* owner = 0); + virtual bool drawsContent() { return true; } + virtual void updateContentsIfDirty(); + virtual void draw(); + + void setTextureId(unsigned textureId); + + class SharedValues { + public: + SharedValues(GraphicsContext3D* context); + ~SharedValues(); + + unsigned shaderProgram() const { return m_shaderProgram; } + int shaderSamplerLocation() const { return m_shaderSamplerLocation; } + int shaderMatrixLocation() const { return m_shaderMatrixLocation; } + int shaderAlphaLocation() const { return m_shaderAlphaLocation; } + bool initialized() const { return m_initialized; } + + private: + GraphicsContext3D* m_context; + unsigned m_shaderProgram; + int m_shaderSamplerLocation; + int m_shaderMatrixLocation; + int m_shaderAlphaLocation; + bool m_initialized; + }; + +private: + PluginLayerChromium(GraphicsLayerChromium* owner); + unsigned m_textureId; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp new file mode 100644 index 0000000..e8b9a12 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "RenderSurfaceChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#include "LayerTexture.h" + +namespace WebCore { + +RenderSurfaceChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_shaderProgram(0) + , m_shaderSamplerLocation(-1) + , m_shaderMatrixLocation(-1) + , m_shaderAlphaLocation(-1) + , m_initialized(false) +{ + // The following program composites layers whose contents are the results of a previous + // render operation and therefore doesn't perform any color swizzling. It is used + // in scrolling and for compositing offscreen textures. + char renderSurfaceVertexShaderString[] = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; + char renderSurfaceFragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" + "} \n"; + + m_shaderProgram = LayerChromium::createShaderProgram(m_context, renderSurfaceVertexShaderString, renderSurfaceFragmentShaderString); + if (!m_shaderProgram) { + LOG_ERROR("RenderSurfaceChromium: Failed to create shader program"); + return; + } + + GLC(m_context, m_shaderSamplerLocation = m_context->getUniformLocation(m_shaderProgram, "s_texture")); + GLC(m_context, m_shaderMatrixLocation = m_context->getUniformLocation(m_shaderProgram, "matrix")); + GLC(m_context, m_shaderAlphaLocation = m_context->getUniformLocation(m_shaderProgram, "alpha")); + if (m_shaderSamplerLocation == -1 || m_shaderMatrixLocation == -1 || m_shaderAlphaLocation == -1) { + LOG_ERROR("Failed to initialize texture layer shader."); + return; + } + m_initialized = true; +} + +RenderSurfaceChromium::SharedValues::~SharedValues() +{ + if (m_shaderProgram) + GLC(m_context, m_context->deleteProgram(m_shaderProgram)); +} + +RenderSurfaceChromium::RenderSurfaceChromium(LayerChromium* owningLayer) + : m_owningLayer(owningLayer) + , m_skipsDraw(false) +{ +} + +RenderSurfaceChromium::~RenderSurfaceChromium() +{ + cleanupResources(); +} + +void RenderSurfaceChromium::cleanupResources() +{ + if (!m_contentsTexture) + return; + + ASSERT(layerRenderer()); + + m_contentsTexture.clear(); +} + +LayerRendererChromium* RenderSurfaceChromium::layerRenderer() +{ + ASSERT(m_owningLayer); + return m_owningLayer->layerRenderer(); +} + +bool RenderSurfaceChromium::prepareContentsTexture() +{ + IntSize requiredSize(m_contentRect.size()); + TextureManager* textureManager = layerRenderer()->textureManager(); + + if (!m_contentsTexture) + m_contentsTexture = LayerTexture::create(layerRenderer()->context(), textureManager); + + if (!m_contentsTexture->reserve(requiredSize, GraphicsContext3D::RGBA)) { + m_skipsDraw = true; + return false; + } + + m_skipsDraw = false; + return true; +} + +void RenderSurfaceChromium::draw() +{ + if (m_skipsDraw || !m_contentsTexture) + return; + + m_contentsTexture->bindTexture(); + + const RenderSurfaceChromium::SharedValues* sv = layerRenderer()->renderSurfaceSharedValues(); + ASSERT(sv && sv->initialized()); + + layerRenderer()->useShader(sv->shaderProgram()); + layerRenderer()->setScissorToRect(m_scissorRect); + + LayerChromium::drawTexturedQuad(layerRenderer()->context(), layerRenderer()->projectionMatrix(), m_drawTransform, + m_contentRect.width(), m_contentRect.height(), m_drawOpacity, + sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + + m_contentsTexture->unreserve(); +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h new file mode 100644 index 0000000..a93218f --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef RenderSurfaceChromium_h +#define RenderSurfaceChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "FloatRect.h" +#include "IntRect.h" +#include "TextureManager.h" +#include "TransformationMatrix.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class LayerChromium; +class LayerRendererChromium; +class LayerTexture; + +class RenderSurfaceChromium : public Noncopyable { + friend class LayerRendererChromium; +public: + explicit RenderSurfaceChromium(LayerChromium*); + ~RenderSurfaceChromium(); + + bool prepareContentsTexture(); + void cleanupResources(); + void draw(); + + FloatPoint contentRectCenter() const { return FloatRect(m_contentRect).center(); } + IntRect contentRect() const { return m_contentRect; } + + // Stores values that are shared between instances of this class that are + // associated with the same LayerRendererChromium (and hence the same GL + // context). + class SharedValues { + public: + explicit SharedValues(GraphicsContext3D*); + ~SharedValues(); + + unsigned shaderProgram() const { return m_shaderProgram; } + int shaderSamplerLocation() const { return m_shaderSamplerLocation; } + int shaderMatrixLocation() const { return m_shaderMatrixLocation; } + int shaderAlphaLocation() const { return m_shaderAlphaLocation; } + bool initialized() const { return m_initialized; } + + private: + GraphicsContext3D* m_context; + + unsigned m_shaderProgram; + int m_shaderSamplerLocation; + int m_shaderMatrixLocation; + int m_shaderAlphaLocation; + bool m_initialized; + }; + +private: + LayerRendererChromium* layerRenderer(); + + LayerChromium* m_owningLayer; + IntRect m_contentRect; + bool m_skipsDraw; + OwnPtr<LayerTexture> m_contentsTexture; + float m_drawOpacity; + TransformationMatrix m_drawTransform; + TransformationMatrix m_originTransform; + IntRect m_scissorRect; + Vector<LayerChromium*> m_layerList; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp new file mode 100644 index 0000000..204c565 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All Rights Reserved. + * Copyright (c) 2008, 2009, 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 "SimpleFontData.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "FontCache.h" +#include "FloatRect.h" +#include "FontDescription.h" +#include <wtf/MathExtras.h> + +#include <unicode/uchar.h> +#include <unicode/unorm.h> +#include <objidl.h> +#include <mlang.h> + +namespace WebCore { + +static inline float scaleEmToUnits(float x, int unitsPerEm) +{ + return unitsPerEm ? x / static_cast<float>(unitsPerEm) : x; +} + +void SimpleFontData::platformInit() +{ + if (!m_platformData.size()) { + m_ascent = 0; + m_descent = 0; + m_lineGap = 0; + m_lineSpacing = 0; + m_avgCharWidth = 0; + m_maxCharWidth = 0; + m_xHeight = 0; + m_unitsPerEm = 0; + return; + } + + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); + + TEXTMETRIC textMetric = {0}; + if (!GetTextMetrics(dc, &textMetric)) { + if (ChromiumBridge::ensureFontLoaded(m_platformData.hfont())) { + // Retry GetTextMetrics. + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401. + if (!GetTextMetrics(dc, &textMetric)) + LOG_ERROR("Unable to get the text metrics after second attempt"); + } + } + + m_avgCharWidth = textMetric.tmAveCharWidth; + m_maxCharWidth = textMetric.tmMaxCharWidth; + + m_ascent = textMetric.tmAscent; + m_descent = textMetric.tmDescent; + m_lineGap = textMetric.tmExternalLeading; + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. + + OUTLINETEXTMETRIC outlineTextMetric; + if (GetOutlineTextMetrics(dc, sizeof(outlineTextMetric), &outlineTextMetric) > 0) { + // This is a TrueType font. We might be able to get an accurate xHeight. + GLYPHMETRICS glyphMetrics = {0}; + MAT2 identityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; + DWORD len = GetGlyphOutlineW(dc, 'x', GGO_METRICS, &glyphMetrics, 0, 0, &identityMatrix); + if (len != GDI_ERROR && glyphMetrics.gmBlackBoxY > 0) + m_xHeight = static_cast<float>(glyphMetrics.gmBlackBoxY); + } + + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); +} + +void SimpleFontData::platformCharWidthInit() +{ + // charwidths are set in platformInit. +} + +void SimpleFontData::platformDestroy() +{ +} + +SimpleFontData* SimpleFontData::scaledFontData(const FontDescription& fontDescription, float scaleFactor) const +{ + LOGFONT winFont; + GetObject(m_platformData.hfont(), sizeof(LOGFONT), &winFont); + float scaledSize = scaleFactor * fontDescription.computedSize(); + winFont.lfHeight = -lroundf(scaledSize); + HFONT hfont = CreateFontIndirect(&winFont); + return new SimpleFontData(FontPlatformData(hfont, scaledSize), isCustomFont(), false); +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->smallCaps) + m_derivedFontData->smallCaps = scaledFontData(fontDescription, .7); + + return m_derivedFontData->smallCaps.get(); +} + +SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->emphasisMark) + m_derivedFontData->emphasisMark = scaledFontData(fontDescription, .5); + + return m_derivedFontData->emphasisMark.get(); +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + // This used to be implemented with IMLangFontLink2, but since that code has + // been disabled, this would always return false anyway. + return false; +} + +void SimpleFontData::determinePitch() +{ + // TEXTMETRICS have this. Set m_treatAsFixedPitch based off that. + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); + + // Yes, this looks backwards, but the fixed pitch bit is actually set if the font + // is *not* fixed pitch. Unbelievable but true. + TEXTMETRIC textMetric = {0}; + if (!GetTextMetrics(dc, &textMetric)) { + if (ChromiumBridge::ensureFontLoaded(m_platformData.hfont())) { + // Retry GetTextMetrics. + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401. + if (!GetTextMetrics(dc, &textMetric)) + LOG_ERROR("Unable to get the text metrics after second attempt"); + } + } + + m_treatAsFixedPitch = ((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0); + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); +} + +FloatRect SimpleFontData::platformBoundsForGlyph(Glyph) const +{ + return FloatRect(); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + if (!m_platformData.size()) + return 0; + + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); + + int width = 0; + if (!GetCharWidthI(dc, glyph, 1, 0, &width)) { + // Ask the browser to preload the font and retry. + if (ChromiumBridge::ensureFontLoaded(m_platformData.hfont())) { + // FIXME: Handle gracefully the error if this call also fails. + // See http://crbug.com/6401. + if (!GetCharWidthI(dc, glyph, 1, 0, &width)) + LOG_ERROR("Unable to get the char width after second attempt"); + } + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + return static_cast<float>(width); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp b/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp new file mode 100644 index 0000000..355d837 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2008, 2009, 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 "SimpleFontData.h" + +#include "Font.h" +#include "FontCache.h" +#include "FloatRect.h" +#include "FontDescription.h" +#include "Logging.h" +#include "VDMXParser.h" + +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkTypes.h" + +namespace WebCore { + +// Smallcaps versions of fonts are 70% the size of the normal font. +static const float smallCapsFraction = 0.7f; +static const float emphasisMarkFraction = .5; +// This is the largest VDMX table which we'll try to load and parse. +static const size_t maxVDMXTableSize = 1024 * 1024; // 1 MB + +void SimpleFontData::platformInit() +{ + if (!m_platformData.size()) { + m_ascent = 0; + m_descent = 0; + m_lineGap = 0; + m_lineSpacing = 0; + m_avgCharWidth = 0; + m_maxCharWidth = 0; + m_xHeight = 0; + m_unitsPerEm = 0; + return; + } + + SkPaint paint; + SkPaint::FontMetrics metrics; + + m_platformData.setupPaint(&paint); + paint.getFontMetrics(&metrics); + const SkFontID fontID = m_platformData.uniqueID(); + + static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); + int pixelSize = m_platformData.size() + 0.5; + int vdmxAscent, vdmxDescent; + bool isVDMXValid = false; + + size_t vdmxSize = SkFontHost::GetTableSize(fontID, vdmxTag); + if (vdmxSize && vdmxSize < maxVDMXTableSize) { + uint8_t* vdmxTable = (uint8_t*) fastMalloc(vdmxSize); + if (vdmxTable + && SkFontHost::GetTableData(fontID, vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize + && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) + isVDMXValid = true; + fastFree(vdmxTable); + } + + // Beware those who step here: This code is designed to match Win32 font + // metrics *exactly*. + if (isVDMXValid) { + m_ascent = vdmxAscent; + m_descent = -vdmxDescent; + } else { + SkScalar height = -metrics.fAscent + metrics.fDescent + metrics.fLeading; + m_ascent = SkScalarRound(-metrics.fAscent); + m_descent = SkScalarRound(height) - m_ascent; + } + + if (metrics.fXHeight) + m_xHeight = metrics.fXHeight; + else { + // hack taken from the Windows port + m_xHeight = static_cast<float>(m_ascent) * 0.56; + } + + m_lineGap = SkScalarRound(metrics.fLeading); + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + if (m_orientation == Vertical) { + static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); + static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); + size_t vheaSize = SkFontHost::GetTableSize(fontID, vheaTag); + size_t vorgSize = SkFontHost::GetTableSize(fontID, vorgTag); + if ((vheaSize <= 0) && (vorgSize <= 0)) + m_orientation = Horizontal; + } + + // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is + // calculated for us, but we need to calculate m_maxCharWidth and + // m_avgCharWidth in order for text entry widgets to be sized correctly. + + SkScalar xRange = metrics.fXMax - metrics.fXMin; + m_maxCharWidth = SkScalarRound(xRange * SkScalarRound(m_platformData.size())); + + if (metrics.fAvgCharWidth) + m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); + else { + m_avgCharWidth = m_xHeight; + + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + + if (glyphPageZero) { + static const UChar32 x_char = 'x'; + const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(x_char).glyph; + + if (xGlyph) + m_avgCharWidth = widthForGlyph(xGlyph); + } + } +} + +void SimpleFontData::platformCharWidthInit() +{ + // charwidths are set in platformInit. +} + +void SimpleFontData::platformDestroy() +{ +} + +SimpleFontData* SimpleFontData::scaledFontData(const FontDescription& fontDescription, float scaleFactor) const +{ + const float scaledSize = lroundf(fontDescription.computedSize() * scaleFactor); + return new SimpleFontData(FontPlatformData(m_platformData, scaledSize), isCustomFont(), false); +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->smallCaps) + m_derivedFontData->smallCaps = scaledFontData(fontDescription, smallCapsFraction); + + return m_derivedFontData->smallCaps.get(); +} + +SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->emphasisMark) + m_derivedFontData->emphasisMark = scaledFontData(fontDescription, emphasisMarkFraction); + + return m_derivedFontData->emphasisMark.get(); +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + SkPaint paint; + static const unsigned maxBufferCount = 64; + uint16_t glyphs[maxBufferCount]; + + m_platformData.setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + while (length > 0) { + int n = SkMin32(length, SK_ARRAY_COUNT(glyphs)); + + // textToGlyphs takes a byte count so we double the character count. + int count = paint.textToGlyphs(characters, n * 2, glyphs); + for (int i = 0; i < count; i++) { + if (0 == glyphs[i]) + return false; // missing glyph + } + + characters += n; + length -= n; + } + + return true; +} + +void SimpleFontData::determinePitch() +{ + m_treatAsFixedPitch = platformData().isFixedPitch(); +} + +FloatRect SimpleFontData::platformBoundsForGlyph(Glyph) const +{ + return FloatRect(); +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + if (!m_platformData.size()) + return 0; + + SkASSERT(sizeof(glyph) == 2); // compile-time assert + + SkPaint paint; + + m_platformData.setupPaint(&paint); + + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + SkScalar width = paint.measureText(&glyph, 2); + + // Though WebKit supports non-integral advances, Skia only supports them + // for "subpixel" (distinct from LCD subpixel antialiasing) text, which + // we don't use. + return round(SkScalarToFloat(width)); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/TextureManager.cpp b/Source/WebCore/platform/graphics/chromium/TextureManager.cpp new file mode 100644 index 0000000..9579ef9 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/TextureManager.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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(ACCELERATED_COMPOSITING) + +#include "TextureManager.h" + +#include "LayerRendererChromium.h" + +namespace WebCore { + +static size_t memoryUseBytes(IntSize size, unsigned textureFormat) +{ + // FIXME: This assumes all textures are 4 bytes/pixel, like RGBA. + return size.width() * size.height() * 4; +} + +TextureManager::TextureManager(GraphicsContext3D* context, size_t memoryLimitBytes, int maxTextureSize) + : m_context(context) + , m_memoryLimitBytes(memoryLimitBytes) + , m_memoryUseBytes(0) + , m_maxTextureSize(maxTextureSize) + , m_nextToken(1) +{ +} + +TextureToken TextureManager::getToken() +{ + return m_nextToken++; +} + +void TextureManager::releaseToken(TextureToken token) +{ + TextureMap::iterator it = m_textures.find(token); + if (it != m_textures.end()) + removeTexture(token, it->second); +} + +bool TextureManager::hasTexture(TextureToken token) +{ + if (m_textures.contains(token)) { + // If someone asks about a texture put it at the end of the LRU list. + m_textureLRUSet.remove(token); + m_textureLRUSet.add(token); + return true; + } + return false; +} + +void TextureManager::protectTexture(TextureToken token) +{ + ASSERT(hasTexture(token)); + ASSERT(!m_textures.get(token).isProtected); + TextureInfo info = m_textures.take(token); + info.isProtected = true; + m_textures.add(token, info); +} + +void TextureManager::unprotectTexture(TextureToken token) +{ + TextureMap::iterator it = m_textures.find(token); + if (it != m_textures.end()) { + TextureInfo info = it->second; + if (info.isProtected) { + info.isProtected = false; + m_textures.remove(it); + m_textures.add(token, info); + } + } +} + +bool TextureManager::reduceMemoryToLimit(size_t limit) +{ + while (m_memoryUseBytes > limit) { + ASSERT(!m_textureLRUSet.isEmpty()); + bool foundCandidate = false; + for (ListHashSet<TextureToken>::iterator lruIt = m_textureLRUSet.begin(); lruIt != m_textureLRUSet.end(); ++lruIt) { + TextureToken token = *lruIt; + TextureInfo info = m_textures.get(token); + if (info.isProtected) + continue; + removeTexture(token, info); + foundCandidate = true; + break; + } + if (!foundCandidate) + return false; + } + return true; +} + +void TextureManager::addTexture(TextureToken token, TextureInfo info) +{ + ASSERT(!m_textureLRUSet.contains(token)); + ASSERT(!m_textures.contains(token)); + m_memoryUseBytes += memoryUseBytes(info.size, info.format); + m_textures.set(token, info); + m_textureLRUSet.add(token); +} + +void TextureManager::removeTexture(TextureToken token, TextureInfo info) +{ + ASSERT(m_textureLRUSet.contains(token)); + ASSERT(m_textures.contains(token)); + m_memoryUseBytes -= memoryUseBytes(info.size, info.format); + m_textures.remove(token); + ASSERT(m_textureLRUSet.contains(token)); + m_textureLRUSet.remove(token); + GLC(m_context.get(), m_context->deleteTexture(info.textureId)); +} + +unsigned TextureManager::requestTexture(TextureToken token, IntSize size, unsigned format, bool* newTexture) +{ + if (size.width() > m_maxTextureSize || size.height() > m_maxTextureSize) + return 0; + + TextureMap::iterator it = m_textures.find(token); + if (it != m_textures.end()) { + ASSERT(it->second.size != size || it->second.format != format); + removeTexture(token, it->second); + } + + size_t memoryRequiredBytes = memoryUseBytes(size, format); + if (memoryRequiredBytes > m_memoryLimitBytes || !reduceMemoryToLimit(m_memoryLimitBytes - memoryRequiredBytes)) + return 0; + + unsigned textureId = m_context->createTexture(); + GLC(m_context.get(), textureId = m_context->createTexture()); + GLC(m_context.get(), m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId)); + // Do basic linear filtering on resize. + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR)); + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR)); + // NPOT textures in GL ES only work when the wrap mode is set to GraphicsContext3D::CLAMP_TO_EDGE. + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE)); + GLC(m_context.get(), m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE)); + GLC(m_context.get(), m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, format, size.width(), size.height(), 0, format, GraphicsContext3D::UNSIGNED_BYTE)); + TextureInfo info; + info.size = size; + info.format = format; + info.textureId = textureId; + info.isProtected = true; + addTexture(token, info); + return textureId; +} + +} + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/TextureManager.h b/Source/WebCore/platform/graphics/chromium/TextureManager.h new file mode 100644 index 0000000..1e850cd --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/TextureManager.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 TextureManager_h +#define TextureManager_h + +#include "GraphicsContext3D.h" +#include "IntRect.h" +#include "IntSize.h" + +#include <wtf/HashMap.h> +#include <wtf/ListHashSet.h> + +namespace WebCore { + +typedef int TextureToken; + +class TextureManager : public Noncopyable { +public: + static PassOwnPtr<TextureManager> create(GraphicsContext3D* context, size_t memoryLimitBytes, int maxTextureSize) + { + return adoptPtr(new TextureManager(context, memoryLimitBytes, maxTextureSize)); + } + + TextureToken getToken(); + void releaseToken(TextureToken); + bool hasTexture(TextureToken); + + unsigned requestTexture(TextureToken, IntSize, unsigned textureFormat, bool* newTexture = 0); + + void protectTexture(TextureToken); + void unprotectTexture(TextureToken); + +private: + TextureManager(GraphicsContext3D*, size_t memoryLimitBytes, int maxTextureSize); + + struct TextureInfo { + IntSize size; + unsigned format; + unsigned textureId; + bool isProtected; + }; + + bool reduceMemoryToLimit(size_t); + void addTexture(TextureToken, TextureInfo); + void removeTexture(TextureToken, TextureInfo); + + RefPtr<GraphicsContext3D> m_context; + + typedef HashMap<TextureToken, TextureInfo> TextureMap; + TextureMap m_textures; + ListHashSet<TextureToken> m_textureLRUSet; + + size_t m_memoryLimitBytes; + size_t m_memoryUseBytes; + int m_maxTextureSize; + TextureToken m_nextToken; +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp new file mode 100644 index 0000000..4dc2157 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2009 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 <windows.h> + +#include "AffineTransform.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" +#include "TransparencyWin.h" + +#include "SkColorPriv.h" +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +namespace { + +// The maximum size in pixels of the buffer we'll keep around for drawing text +// into. Buffers larger than this will be destroyed when we're done with them. +const int maxCachedBufferPixelSize = 65536; + +inline skia::PlatformCanvas* canvasForContext(const GraphicsContext& context) +{ + return context.platformContext()->canvas(); +} + +inline const SkBitmap& bitmapForContext(const GraphicsContext& context) +{ + return canvasForContext(context)->getTopPlatformDevice().accessBitmap(false); +} + +void compositeToCopy(const GraphicsContext& sourceLayers, + GraphicsContext& destContext, + const AffineTransform& matrix) +{ + // Make a list of all devices. The iterator goes top-down, and we want + // bottom-up. Note that each layer can also have an offset in canvas + // coordinates, which is the (x, y) position. + struct DeviceInfo { + DeviceInfo(SkDevice* d, int lx, int ly) + : device(d) + , x(lx) + , y(ly) {} + SkDevice* device; + int x; + int y; + }; + Vector<DeviceInfo> devices; + SkCanvas* sourceCanvas = canvasForContext(sourceLayers); + SkCanvas::LayerIter iter(sourceCanvas, false); + while (!iter.done()) { + devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); + iter.next(); + } + + // Create a temporary canvas for the compositing into the destination. + SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); + SkCanvas destCanvas(*destBmp); + destCanvas.setMatrix(matrix); + + for (int i = devices.size() - 1; i >= 0; i--) { + const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); + + SkRect destRect; + destRect.fLeft = devices[i].x; + destRect.fTop = devices[i].y; + destRect.fRight = destRect.fLeft + srcBmp.width(); + destRect.fBottom = destRect.fTop + srcBmp.height(); + + destCanvas.drawBitmapRect(srcBmp, 0, destRect); + } +} + +} // namespace + +// If either of these pointers is non-null, both must be valid and point to +// bitmaps of the same size. +class TransparencyWin::OwnedBuffers { +public: + OwnedBuffers(const IntSize& size, bool needReferenceBuffer) + { + m_destBitmap = ImageBuffer::create(size); + + if (needReferenceBuffer) { + m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); + m_referenceBitmap.allocPixels(); + m_referenceBitmap.eraseARGB(0, 0, 0, 0); + } + } + + ImageBuffer* destBitmap() { return m_destBitmap.get(); } + + // This bitmap will be empty if you don't specify needReferenceBuffer to the + // constructor. + SkBitmap* referenceBitmap() { return &m_referenceBitmap; } + + // Returns whether the current layer will fix a buffer of the given size. + bool canHandleSize(const IntSize& size) const + { + return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height(); + } + +private: + // The destination bitmap we're drawing into. + OwnPtr<ImageBuffer> m_destBitmap; + + // This could be an ImageBuffer but this is an optimization. Since this is + // only ever used as a reference, we don't need to make a full + // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap + // is much faster since it's just a Malloc rather than a GDI call. + SkBitmap m_referenceBitmap; +}; + +TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0; + +TransparencyWin::TransparencyWin() + : m_destContext(0) + , m_orgTransform() + , m_layerMode(NoLayer) + , m_transformMode(KeepTransform) + , m_drawContext(0) + , m_savedOnDrawContext(false) + , m_layerBuffer(0) + , m_referenceBitmap(0) + , m_validLayer(false) +{ +} + +TransparencyWin::~TransparencyWin() +{ + // This should be false, since calling composite() is mandatory. + ASSERT(!m_savedOnDrawContext); +} + +void TransparencyWin::composite() +{ + // Matches the save() in initializeNewTextContext (or the constructor for + // SCALE) to put the context back into the same state we found it. + if (m_savedOnDrawContext) { + m_drawContext->restore(); + m_savedOnDrawContext = false; + } + + switch (m_layerMode) { + case NoLayer: + break; + case OpaqueCompositeLayer: + case WhiteLayer: + compositeOpaqueComposite(); + break; + case TextComposite: + compositeTextComposite(); + break; + } +} + +void TransparencyWin::init(GraphicsContext* dest, + LayerMode layerMode, + TransformMode transformMode, + const IntRect& region) +{ + m_destContext = dest; + m_orgTransform = dest->getCTM(); + m_layerMode = layerMode; + m_transformMode = transformMode; + m_sourceRect = region; + + computeLayerSize(); + setupLayer(); + setupTransform(region); +} + +void TransparencyWin::computeLayerSize() +{ + if (m_transformMode == Untransform) { + // The meaning of the "transformed" source rect is a little ambigous + // here. The rest of the code doesn't care about it in the Untransform + // case since we're using our own happy coordinate system. So we set it + // to be the source rect since that matches how the code below actually + // uses the variable: to determine how to translate things to account + // for the offset of the layer. + m_transformedSourceRect = m_sourceRect; + m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height()); + } else { + m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); + m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); + } +} + +void TransparencyWin::setupLayer() +{ + switch (m_layerMode) { + case NoLayer: + setupLayerForNoLayer(); + break; + case OpaqueCompositeLayer: + setupLayerForOpaqueCompositeLayer(); + break; + case TextComposite: + setupLayerForTextComposite(); + break; + case WhiteLayer: + setupLayerForWhiteLayer(); + break; + } +} + +void TransparencyWin::setupLayerForNoLayer() +{ + m_drawContext = m_destContext; // Draw to the source context. + m_validLayer = true; +} + +void TransparencyWin::setupLayerForOpaqueCompositeLayer() +{ + initializeNewContext(); + if (!m_validLayer) + return; + + AffineTransform mapping; + mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); + if (m_transformMode == Untransform){ + // Compute the inverse mapping from the canvas space to the + // coordinate space of our bitmap. + mapping = m_orgTransform.inverse() * mapping; + } + compositeToCopy(*m_destContext, *m_drawContext, mapping); + + // Save the reference layer so we can tell what changed. + SkCanvas referenceCanvas(*m_referenceBitmap); + referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0); + // Layer rect represents the part of the original layer. +} + +void TransparencyWin::setupLayerForTextComposite() +{ + ASSERT(m_transformMode == KeepTransform); + // Fall through to filling with white. + setupLayerForWhiteLayer(); +} + +void TransparencyWin::setupLayerForWhiteLayer() +{ + initializeNewContext(); + if (!m_validLayer) + return; + + m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white, ColorSpaceDeviceRGB); + // Layer rect represents the part of the original layer. +} + +void TransparencyWin::setupTransform(const IntRect& region) +{ + switch (m_transformMode) { + case KeepTransform: + setupTransformForKeepTransform(region); + break; + case Untransform: + setupTransformForUntransform(); + break; + case ScaleTransform: + setupTransformForScaleTransform(); + break; + } +} + +void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) +{ + if (!m_validLayer) + return; + + if (m_layerMode != NoLayer) { + // Need to save things since we're modifying the transform. + m_drawContext->save(); + m_savedOnDrawContext = true; + + // Account for the fact that the layer may be offset from the + // original. This only happens when we create a layer that has the + // same coordinate space as the parent. + AffineTransform xform; + xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); + + // We're making a layer, so apply the old transform to the new one + // so it's maintained. We know the new layer has the identity + // transform now, we we can just multiply it. + xform = m_orgTransform * xform; + m_drawContext->concatCTM(xform); + } + m_drawRect = m_sourceRect; +} + +void TransparencyWin::setupTransformForUntransform() +{ + ASSERT(m_layerMode != NoLayer); + // We now have a new layer with the identity transform, which is the + // Untransformed space we'll use for drawing. + m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); +} + +void TransparencyWin::setupTransformForScaleTransform() +{ + if (!m_validLayer) + return; + + if (m_layerMode == NoLayer) { + // Need to save things since we're modifying the layer. + m_drawContext->save(); + m_savedOnDrawContext = true; + + // Undo the transform on the current layer when we're re-using the + // current one. + m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); + + // We're drawing to the original layer with just a different size. + m_drawRect = m_transformedSourceRect; + } else { + // Just go ahead and use the layer's coordinate space to draw into. + // It will have the scaled size, and an identity transform loaded. + m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); + } +} + +void TransparencyWin::setTextCompositeColor(Color color) +{ + m_textCompositeColor = color; +} + +void TransparencyWin::initializeNewContext() +{ + int pixelSize = m_layerSize.width() * m_layerSize.height(); + if (pixelSize <= 0) + return; + + if (pixelSize > maxCachedBufferPixelSize) { + // Create a 1-off buffer for drawing into. We only need the reference + // buffer if we're making an OpaqueCompositeLayer. + bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; + m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap)); + m_layerBuffer = m_ownedBuffers->destBitmap(); + if (!m_layerBuffer) + return; + + m_drawContext = m_layerBuffer->context(); + if (needReferenceBitmap) { + m_referenceBitmap = m_ownedBuffers->referenceBitmap(); + if (!m_referenceBitmap || !m_referenceBitmap->getPixels()) + return; + } + m_validLayer = true; + return; + } + + if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) { + // We can re-use the existing buffer. We don't need to clear it since + // all layer modes will clear it in their initialization. + m_layerBuffer = m_cachedBuffers->destBitmap(); + m_drawContext = m_cachedBuffers->destBitmap()->context(); + bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); + m_referenceBitmap = m_cachedBuffers->referenceBitmap(); + m_referenceBitmap->eraseARGB(0, 0, 0, 0); + m_validLayer = true; + return; + } + + // Create a new cached buffer. + if (m_cachedBuffers) + delete m_cachedBuffers; + m_cachedBuffers = new OwnedBuffers(m_layerSize, true); + + m_layerBuffer = m_cachedBuffers->destBitmap(); + m_drawContext = m_cachedBuffers->destBitmap()->context(); + m_referenceBitmap = m_cachedBuffers->referenceBitmap(); + m_validLayer = true; +} + +void TransparencyWin::compositeOpaqueComposite() +{ + if (!m_validLayer) + return; + + SkCanvas* destCanvas = canvasForContext(*m_destContext); + destCanvas->save(); + + SkBitmap* bitmap = const_cast<SkBitmap*>( + &bitmapForContext(*m_layerBuffer->context())); + + // This function will be called for WhiteLayer as well, which we don't want + // to change. + if (m_layerMode == OpaqueCompositeLayer) { + // Fix up our bitmap, making it contain only the pixels which changed + // and transparent everywhere else. + SkAutoLockPixels sourceLock(*m_referenceBitmap); + SkAutoLockPixels lock(*bitmap); + for (int y = 0; y < bitmap->height(); y++) { + uint32_t* source = m_referenceBitmap->getAddr32(0, y); + uint32_t* dest = bitmap->getAddr32(0, y); + for (int x = 0; x < bitmap->width(); x++) { + // Clear out any pixels that were untouched. + if (dest[x] == source[x]) + dest[x] = 0; + else + dest[x] |= (0xFF << SK_A32_SHIFT); + } + } + } else + makeLayerOpaque(); + + SkRect destRect; + if (m_transformMode != Untransform) { + // We want to use Untransformed space. + // + // Note that we DON'T call m_layerBuffer->image() here. This actually + // makes a copy of the image, which is unnecessary and slow. Instead, we + // just draw the image from inside the destination context. + SkMatrix identity; + identity.reset(); + destCanvas->setMatrix(identity); + + destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom()); + } else + destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom()); + + SkPaint paint; + paint.setFilterBitmap(true); + paint.setAntiAlias(true); + + // Note that we need to specify the source layer subset, since the bitmap + // may have been cached and it could be larger than what we're using. + SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; + destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); + destCanvas->restore(); +} + +void TransparencyWin::compositeTextComposite() +{ + if (!m_validLayer) + return; + + const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true); + SkColor textColor = m_textCompositeColor.rgb(); + for (int y = 0; y < m_layerSize.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < m_layerSize.width(); x++) { + // The alpha is the average of the R, G, and B channels. + int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3; + + // Apply that alpha to the text color and write the result. + row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); + } + } + + // Now the layer has text with the proper color and opacity. + SkCanvas* destCanvas = canvasForContext(*m_destContext); + destCanvas->save(); + + // We want to use Untransformed space (see above) + SkMatrix identity; + identity.reset(); + destCanvas->setMatrix(identity); + SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() }; + + // Note that we need to specify the source layer subset, since the bitmap + // may have been cached and it could be larger than what we're using. + SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; + destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0); + destCanvas->restore(); +} + +void TransparencyWin::makeLayerOpaque() +{ + if (!m_validLayer) + return; + + SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> + canvas()->getTopPlatformDevice().accessBitmap(true)); + for (int y = 0; y < m_layerSize.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < m_layerSize.width(); x++) + row[x] |= 0xFF000000; + } +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/TransparencyWin.h b/Source/WebCore/platform/graphics/chromium/TransparencyWin.h new file mode 100644 index 0000000..b6bef91 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/TransparencyWin.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2009 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 TransparencyWin_h +#define TransparencyWin_h + +#include <windows.h> + +#include "AffineTransform.h" +#include "ImageBuffer.h" +#include "Noncopyable.h" +#include "wtf/OwnPtr.h" + +class SkBitmap; +class SkCanvas; + +namespace WebCore { + +class GraphicsContext; +class TransparencyWin_NoLayer_Test; +class TransparencyWin_WhiteLayer_Test; +class TransparencyWin_TextComposite_Test; +class TransparencyWin_OpaqueCompositeLayer_Test; + +// Helper class that abstracts away drawing ClearType text and Windows form +// controls either to the original context directly, or to an offscreen context +// that is composited later manually. This is to get around Windows' inability +// to handle the alpha channel, semitransparent text, and transformed form +// controls. +class TransparencyWin : public Noncopyable { +public: + enum LayerMode { + // No extra layer is created. Drawing will happen to the source. + // Valid only with KeepTransform and ScaleTransform. The region being + // drawn onto must be opaque, since the modified region will be forced + // to opaque when drawing is complete. + NoLayer, + + // Makes a temporary layer consisting of the composited layers below + // it. This result must be opaque. When complete, the result will be + // compared to the original, and the difference will be added to a thee + // destination layer. + // + // This mode only works if the lower layers are opque (normally the + // case for a web page) and layers are only drawn in the stack order, + // meaning you can never draw underneath a layer. + // + // This doesn't technically produce the correct answer in all cases. If + // you have an opaque base, a transparency layer, than a semitransparent + // drawing on top, the result will actually be blended in twice. But + // this isn't a very important case. This mode is used for form + // controls which are always opaque except for occationally some + // antialiasing. It means form control antialiasing will be too light in + // some cases, but only if you have extra layers. + OpaqueCompositeLayer, + + // Allows semitransparent text to be drawn on any background (even if it + // is itself semitransparent), but disables ClearType. + // + // It makes a trmporary layer filled with white. This is composited with + // the lower layer with a custom color applied to produce the result. + // The caller must draw the text in black, and set the desired final + // text color by calling setTextCompositeColor(). + // + // Only valid with KeepTransform, which is the only mode where drawing + // text in this fashion makes sense. + TextComposite, + + // Makes a temporary layer filled with white. When complete, the layer + // will be forced to be opqaue (since Windows may have messed up the + // alpha channel) and composited down. Any areas not drawn into will + // remain white. + // + // This is the mode of last resort. If the opacity of the final image + // is unknown and we can't do the text trick (since we know its color), + // then we have to live with potential white halos. This is used for + // form control drawing, for example. + WhiteLayer, + }; + + enum TransformMode { + // There are no changes to the transform. Use this when drawing + // horizontal text. The current transform must not have rotation. + KeepTransform, + + // Drawing happens in an Untransformed space, and then that bitmap is + // transformed according to the current context when it is copied down. + // Requires that a layer be created (layer mode is not NoLayer). + Untransform, + + // When the current transform only has a scaling factor applied and + // you're drawing form elements, use this parameter. This will unscale + // the coordinate space, so the OS will just draw the form controls + // larger or smaller depending on the destination size. + ScaleTransform, + }; + + // You MUST call init() below. + // |region| is expressed relative to the current transformation. + TransparencyWin(); + ~TransparencyWin(); + + // Initializes the members if you use the 0-argument constructor. Don't call + // this if you use the multiple-argument constructor. + void init(GraphicsContext* dest, + LayerMode layerMode, + TransformMode transformMode, + const IntRect& region); + + // Combines the source and destination bitmaps using the given mode. + // Calling this function before the destructor runs is mandatory in most + // cases, and harmless otherwise. The mandatory cases are: + // (m_layerMode != NoLayer) || (m_transformMode == ScaleTransform) + void composite(); + + // Returns the context for drawing into, which may be the destination + // context, or a temporary one. + GraphicsContext* context() const { return m_drawContext; } + + PlatformGraphicsContext* platformContext() const { return m_drawContext ? m_drawContext->platformContext() : 0; } + + // When the mode is TextComposite, this sets the color that the text will + // get. See the enum above for more. + void setTextCompositeColor(Color color); + + // Returns the input bounds translated into the destination space. This is + // not necessary for KeepTransform since the rectangle will be unchanged. + const IntRect& drawRect() { return m_drawRect; } + +private: + friend TransparencyWin_NoLayer_Test; + friend TransparencyWin_WhiteLayer_Test; + friend TransparencyWin_TextComposite_Test; + friend TransparencyWin_OpaqueCompositeLayer_Test; + + class OwnedBuffers; + + void computeLayerSize(); + + // Sets up a new layer, if any. setupLayer() will call the appopriate layer- + // specific helper. Must be called after computeLayerSize(); + void setupLayer(); + void setupLayerForNoLayer(); + void setupLayerForOpaqueCompositeLayer(); + void setupLayerForTextComposite(); + void setupLayerForWhiteLayer(); + + // Sets up the transformation on the newly created layer. setupTransform() + // will call the appropriate transform-specific helper. Must be called after + // setupLayer(). + void setupTransform(const IntRect& region); + void setupTransformForKeepTransform(const IntRect& region); + void setupTransformForUntransform(); + void setupTransformForScaleTransform(); + + void initializeNewContext(); + + void compositeOpaqueComposite(); + void compositeTextComposite(); + + // Fixes the alpha channel to make the region inside m_transformedRect + // opaque. + void makeLayerOpaque(); + + // The context our drawing will eventually end up in. + GraphicsContext* m_destContext; + + // The original transform from the destination context. + AffineTransform m_orgTransform; + + LayerMode m_layerMode; + TransformMode m_transformMode; + + // The rectangle we're drawing in the destination's coordinate space + IntRect m_sourceRect; + + // The source rectangle transformed into pixels in the final image. For + // Untransform this has no meaning, since the destination might not be a + // rectangle. + IntRect m_transformedSourceRect; + + // The size of the layer we created. If there's no layer, this is the size + // of the region we're using in the source. + IntSize m_layerSize; + + // The rectangle we're drawing to in the draw context's coordinate space. + // This will be the same as the source rectangle except for ScaleTransform + // where we create a new virtual coordinate space for the layer. + IntRect m_drawRect; + + // Points to the graphics context to draw text to, which will either be + // the original context or the copy, depending on our mode. + GraphicsContext* m_drawContext; + + // This flag is set when we call save() on the draw context during + // initialization. It allows us to avoid doing an extra save()/restore() + // when one is unnecessary. + bool m_savedOnDrawContext; + + // Used only when m_mode = TextComposite, this is the color that the text + // will end up being once we figure out the transparency. + Color m_textCompositeColor; + + // Layer we're drawing to. + ImageBuffer* m_layerBuffer; + + // When the layer type is OpaqueCompositeLayer, this will contain a copy + // of the original contents of the m_layerBuffer before Windows drew on it. + // It allows us to re-create what Windows did to the layer. It is an + // SkBitmap instead of an ImageBuffer because an SkBitmap is lighter-weight + // (ImageBuffers are also GDI surfaces, which we don't need here). + SkBitmap* m_referenceBitmap; + + // If the given size of bitmap can be cached, they will be stored here. Both + // the bitmap and the reference are guaranteed to be allocated if this + // member is non-null. + static OwnedBuffers* m_cachedBuffers; + + // If a buffer was too big to be cached, it will be created temporarily, and + // this member tracks its scope to make sure it gets deleted. Always use + // m_layerBuffer, which will either point to this object, or the statically + // cached one. Don't access directly. + OwnPtr<OwnedBuffers> m_ownedBuffers; + + // Sometimes we're asked to create layers that have negative dimensions. + // This API is not designed to fail to initialize, so we hide the fact + // that they are illegal and can't be rendered (failing silently, drawing + // nothing). + bool m_validLayer; +}; + +} // namespace WebCore + +#endif // TransaprencyWin_h diff --git a/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp b/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp new file mode 100644 index 0000000..dda84a9 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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 "UniscribeHelper.h" + +#include <windows.h> + +#include "FontUtilsChromiumWin.h" +#include "PlatformContextSkia.h" +#include "SkiaFontWin.h" +#include "SkPoint.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +// This function is used to see where word spacing should be applied inside +// runs. Note that this must match Font::treatAsSpace so we all agree where +// and how much space this is, so we don't want to do more general Unicode +// "is this a word break" thing. +static bool treatAsSpace(UChar c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; +} + +// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid +// and blank glyphs. Just because ScriptShape succeeds does not mean +// that a text run is rendered correctly. Some characters may be rendered +// with default/invalid/blank glyphs. Therefore, we need to check if the glyph +// array returned by ScriptShape contains any of those glyphs to make +// sure that the text run is rendered successfully. +static bool containsMissingGlyphs(WORD *glyphs, + int length, + SCRIPT_FONTPROPERTIES* properties) +{ + for (int i = 0; i < length; ++i) { + if (glyphs[i] == properties->wgDefault + || (glyphs[i] == properties->wgInvalid + && glyphs[i] != properties->wgBlank)) + return true; + } + + return false; +} + +// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque +// handle and we can't directly query it to make a new HFONT sharing +// its characteristics (height, style, etc) except for family name. +// This function uses GetObject to convert HFONT back to LOGFONT, +// resets the fields of LOGFONT and calculates style to use later +// for the creation of a font identical to HFONT other than family name. +static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) +{ + ASSERT(hfont && logfont); + if (!hfont || !logfont) + return; + + GetObject(hfont, sizeof(LOGFONT), logfont); + // We reset these fields to values appropriate for CreateFontIndirect. + // while keeping lfHeight, which is the most important value in creating + // a new font similar to hfont. + logfont->lfWidth = 0; + logfont->lfEscapement = 0; + logfont->lfOrientation = 0; + logfont->lfCharSet = DEFAULT_CHARSET; + logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; + logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings. + logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + if (style) + *style = getStyleFromLogfont(logfont); +} + +UniscribeHelper::UniscribeHelper(const UChar* input, + int inputLength, + bool isRtl, + HFONT hfont, + SCRIPT_CACHE* scriptCache, + SCRIPT_FONTPROPERTIES* fontProperties) + : m_input(input) + , m_inputLength(inputLength) + , m_isRtl(isRtl) + , m_hfont(hfont) + , m_scriptCache(scriptCache) + , m_fontProperties(fontProperties) + , m_directionalOverride(false) + , m_inhibitLigate(false) + , m_letterSpacing(0) + , m_spaceWidth(0) + , m_wordSpacing(0) + , m_ascent(0) + , m_disableFontFallback(false) + +{ + m_logfont.lfFaceName[0] = 0; +} + +UniscribeHelper::~UniscribeHelper() +{ +} + +void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection) +{ + // We cap the input length and just don't do anything. We'll allocate a lot + // of things of the size of the number of characters, so the allocated + // memory will be several times the input length. Plus shaping such a large + // buffer may be a form of denial of service. No legitimate text should be + // this long. It also appears that Uniscribe flatly rejects very long + // strings, so we don't lose anything by doing this. + // + // The input length protection may be disabled by the unit tests to cause + // an error condition. + static const int kMaxInputLength = 65535; + if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength)) + return; + + fillRuns(); + fillShapes(); + fillScreenOrder(); +} + +int UniscribeHelper::width() const +{ + int width = 0; + for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++) + width += advanceForItem(itemIndex); + return width; +} + +void UniscribeHelper::justify(int additionalSpace) +{ + // Count the total number of glyphs we have so we know how big to make the + // buffers below. + int totalGlyphs = 0; + for (size_t run = 0; run < m_runs.size(); run++) { + int runIndex = m_screenOrder[run]; + totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength()); + } + if (totalGlyphs == 0) + return; // Nothing to do. + + // We make one big buffer in screen order of all the glyphs we are drawing + // across runs so that the justification function will adjust evenly across + // all glyphs. + Vector<SCRIPT_VISATTR, 64> visualAttributes; + visualAttributes.resize(totalGlyphs); + Vector<int, 64> advances; + advances.resize(totalGlyphs); + Vector<int, 64> justify; + justify.resize(totalGlyphs); + + // Build the packed input. + int destIndex = 0; + for (size_t run = 0; run < m_runs.size(); run++) { + int runIndex = m_screenOrder[run]; + const Shaping& shaping = m_shapes[runIndex]; + + for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) { + memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i], + sizeof(SCRIPT_VISATTR)); + advances[destIndex] = shaping.m_advance[i]; + } + } + + // The documentation for Scriptjustify is wrong, the parameter is the space + // to add and not the width of the column you want. + const int minKashida = 1; // How do we decide what this should be? + ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs, + additionalSpace, minKashida, &justify[0]); + + // Now we have to unpack the justification amounts back into the runs so + // the glyph indices match. + int globalGlyphIndex = 0; + for (size_t run = 0; run < m_runs.size(); run++) { + int runIndex = m_screenOrder[run]; + Shaping& shaping = m_shapes[runIndex]; + + shaping.m_justify.resize(shaping.glyphLength()); + for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++) + shaping.m_justify[i] = justify[globalGlyphIndex]; + } +} + +int UniscribeHelper::characterToX(int offset) const +{ + HRESULT hr; + ASSERT(offset <= m_inputLength); + + // Our algorithm is to traverse the items in screen order from left to + // right, adding in each item's screen width until we find the item with + // the requested character in it. + int width = 0; + for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { + // Compute the length of this run. + int itemIndex = m_screenOrder[screenIndex]; + const SCRIPT_ITEM& item = m_runs[itemIndex]; + const Shaping& shaping = m_shapes[itemIndex]; + int itemLength = shaping.charLength(); + + if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) { + // Character offset is in this run. + int charLength = offset - item.iCharPos; + + int curX = 0; + hr = ScriptCPtoX(charLength, FALSE, itemLength, + shaping.glyphLength(), + &shaping.m_logs[0], &shaping.m_visualAttributes[0], + shaping.effectiveAdvances(), &item.a, &curX); + if (FAILED(hr)) + return 0; + + width += curX + shaping.m_prePadding; + ASSERT(width >= 0); + return width; + } + + // Move to the next item. + width += advanceForItem(itemIndex); + } + ASSERT(width >= 0); + return width; +} + +int UniscribeHelper::xToCharacter(int x) const +{ + // We iterate in screen order until we find the item with the given pixel + // position in it. When we find that guy, we ask Uniscribe for the + // character index. + HRESULT hr; + for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { + int itemIndex = m_screenOrder[screenIndex]; + int itemAdvance = advanceForItem(itemIndex); + + // Note that the run may be empty if shaping failed, so we want to skip + // over it. + const Shaping& shaping = m_shapes[itemIndex]; + int itemLength = shaping.charLength(); + if (x <= itemAdvance && itemLength > 0) { + // The requested offset is within this item. + const SCRIPT_ITEM& item = m_runs[itemIndex]; + + // Account for the leading space we've added to this run that + // Uniscribe doesn't know about. + x -= shaping.m_prePadding; + + int charX = 0; + int trailing; + hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(), + &shaping.m_logs[0], &shaping.m_visualAttributes[0], + shaping.effectiveAdvances(), &item.a, &charX, + &trailing); + + // The character offset is within the item. We need to add the + // item's offset to transform it into the space of the TextRun + return charX + item.iCharPos; + } + + // The offset is beyond this item, account for its length and move on. + x -= itemAdvance; + } + + // Error condition, we don't know what to do if we don't have that X + // position in any of our items. + return 0; +} + +void UniscribeHelper::draw(GraphicsContext* graphicsContext, + HDC dc, int x, int y, int from, int to) +{ + HGDIOBJ oldFont = 0; + int curX = x; + bool firstRun = true; + bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext); + + for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { + int itemIndex = m_screenOrder[screenIndex]; + const SCRIPT_ITEM& item = m_runs[itemIndex]; + const Shaping& shaping = m_shapes[itemIndex]; + + // Character offsets within this run. THESE MAY NOT BE IN RANGE and may + // be negative, etc. The code below handles this. + int fromChar = from - item.iCharPos; + int toChar = to - item.iCharPos; + + // See if we need to draw any characters in this item. + if (shaping.charLength() == 0 || + fromChar >= shaping.charLength() || toChar <= 0) { + // No chars in this item to display. + curX += advanceForItem(itemIndex); + continue; + } + + // Compute the starting glyph within this span. |from| and |to| are + // global offsets that may intersect arbitrarily with our local run. + int fromGlyph, afterGlyph; + if (item.a.fRTL) { + // To compute the first glyph when going RTL, we use |to|. + if (toChar >= shaping.charLength()) + // The end of the text is after (to the left) of us. + fromGlyph = 0; + else { + // Since |to| is exclusive, the first character we draw on the + // left is actually the one right before (to the right) of + // |to|. + fromGlyph = shaping.m_logs[toChar - 1]; + } + + // The last glyph is actually the first character in the range. + if (fromChar <= 0) { + // The first character to draw is before (to the right) of this + // span, so draw all the way to the end. + afterGlyph = shaping.glyphLength(); + } else { + // We want to draw everything up until the character to the + // right of |from|. To the right is - 1, so we look that up + // (remember our character could be more than one glyph, so we + // can't look up our glyph and add one). + afterGlyph = shaping.m_logs[fromChar - 1]; + } + } else { + // Easy case, everybody agrees about directions. We only need to + // handle boundary conditions to get a range inclusive at the + // beginning, and exclusive at the ending. We have to do some + // computation to see the glyph one past the end. + fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar]; + if (toChar >= shaping.charLength()) + afterGlyph = shaping.glyphLength(); + else + afterGlyph = shaping.m_logs[toChar]; + } + + // Account for the characters that were skipped in this run. When + // WebKit asks us to draw a subset of the run, it actually tells us + // to draw at the X offset of the beginning of the run, since it + // doesn't know the internal position of any of our characters. + const int* effectiveAdvances = shaping.effectiveAdvances(); + int innerOffset = 0; + for (int i = 0; i < fromGlyph; i++) + innerOffset += effectiveAdvances[i]; + + // Actually draw the glyphs we found. + int glyphCount = afterGlyph - fromGlyph; + if (fromGlyph >= 0 && glyphCount > 0) { + // Account for the preceding space we need to add to this run. We + // don't need to count for the following space because that will be + // counted in advanceForItem below when we move to the next run. + innerOffset += shaping.m_prePadding; + + // Pass 0 in when there is no justification. + const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph]; + + if (useWindowsDrawing) { + if (firstRun) { + oldFont = SelectObject(dc, shaping.m_hfont); + firstRun = false; + } else + SelectObject(dc, shaping.m_hfont); + } + + // Fonts with different ascents can be used to render different + // runs. 'Across-runs' y-coordinate correction needs to be + // adjusted for each font. + bool textOutOk = false; + for (int executions = 0; executions < 2; ++executions) { + if (useWindowsDrawing) { + HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache, + curX + innerOffset, + y - shaping.m_ascentOffset, + 0, 0, &item.a, 0, 0, + &shaping.m_glyphs[fromGlyph], + glyphCount, + &shaping.m_advance[fromGlyph], + justify, + &shaping.m_offsets[fromGlyph]); + textOutOk = (hr == S_OK); + } else { + SkPoint origin; + origin.fX = curX + + innerOffset; + origin.fY = y + m_ascent; + textOutOk = paintSkiaText(graphicsContext, + shaping.m_hfont, + glyphCount, + &shaping.m_glyphs[fromGlyph], + &shaping.m_advance[fromGlyph], + &shaping.m_offsets[fromGlyph], + &origin); + } + + if (!textOutOk && 0 == executions) { + // If TextOut is called from the renderer it might fail + // because the sandbox is preventing it from opening the + // font files. If we are running in the renderer, + // TryToPreloadFont is overridden to ask the browser to + // preload the font for us so we can access it. + tryToPreloadFont(shaping.m_hfont); + continue; + } + break; + } + } + + curX += advanceForItem(itemIndex); + } + + if (oldFont) + SelectObject(dc, oldFont); +} + +WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const +{ + // Find the run for the given character. + for (int i = 0; i < static_cast<int>(m_runs.size()); i++) { + int firstChar = m_runs[i].iCharPos; + const Shaping& shaping = m_shapes[i]; + int localOffset = charOffset - firstChar; + if (localOffset >= 0 && localOffset < shaping.charLength()) { + // The character is in this run, return the first glyph for it + // (should generally be the only glyph). It seems Uniscribe gives + // glyph 0 for empty, which is what we want to return in the + // "missing" case. + size_t glyphIndex = shaping.m_logs[localOffset]; + if (glyphIndex >= shaping.m_glyphs.size()) { + // The glyph should be in this run, but the run has too few + // actual characters. This can happen when shaping the run + // fails, in which case, we should have no data in the logs at + // all. + ASSERT(shaping.m_glyphs.size() == 0); + return 0; + } + return shaping.m_glyphs[glyphIndex]; + } + } + + return 0; +} + +void UniscribeHelper::fillRuns() +{ + HRESULT hr; + m_runs.resize(UNISCRIBE_HELPER_STACK_RUNS); + + SCRIPT_STATE inputState; + inputState.uBidiLevel = m_isRtl; + inputState.fOverrideDirection = m_directionalOverride; + inputState.fInhibitSymSwap = false; + inputState.fCharShape = false; // Not implemented in Uniscribe + inputState.fDigitSubstitute = false; // Do we want this for Arabic? + inputState.fInhibitLigate = m_inhibitLigate; + inputState.fDisplayZWG = false; // Don't draw control characters. + inputState.fArabicNumContext = m_isRtl; // Do we want this for Arabic? + inputState.fGcpClusters = false; + inputState.fReserved = 0; + inputState.fEngineReserved = 0; + // The psControl argument to ScriptItemize should be non-0 for RTL text, + // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a + // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the + // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx + static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage :16; + 0, // fContextDigits :1; + 0, // fInvertPreBoundDir :1; + 0, // fInvertPostBoundDir :1; + 0, // fLinkStringBefore :1; + 0, // fLinkStringAfter :1; + 0, // fNeutralOverride :1; + 0, // fNumericOverride :1; + 0, // fLegacyBidiClass :1; + 0, // fMergeNeutralItems :1; + 0};// fReserved :7; + // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState) + // here would be appropriate if we wanted to set the language ID, and get + // local digit substitution behavior. For now, don't do it. + + while (true) { + int numberOfItems = 0; + + // Ideally, we would have a way to know the runs before and after this + // one, and put them into the control parameter of ScriptItemize. This + // would allow us to shape characters properly that cross style + // boundaries (WebKit bug 6148). + // + // We tell ScriptItemize that the output list of items is one smaller + // than it actually is. According to Mozilla bug 366643, if there is + // not enough room in the array on pre-SP2 systems, ScriptItemize will + // write one past the end of the buffer. + // + // ScriptItemize is very strange. It will often require a much larger + // ITEM buffer internally than it will give us as output. For example, + // it will say a 16-item buffer is not big enough, and will write + // interesting numbers into all those items. But when we give it a 32 + // item buffer and it succeeds, it only has one item output. + // + // It seems to be doing at least two passes, the first where it puts a + // lot of intermediate data into our items, and the second where it + // collates them. + hr = ScriptItemize(m_input, m_inputLength, + static_cast<int>(m_runs.size()) - 1, &inputControl, + &inputState, + &m_runs[0], &numberOfItems); + if (SUCCEEDED(hr)) { + m_runs.resize(numberOfItems); + break; + } + if (hr != E_OUTOFMEMORY) { + // Some kind of unexpected error. + m_runs.resize(0); + break; + } + // There was not enough items for it to write into, expand. + m_runs.resize(m_runs.size() * 2); + } +} + +bool UniscribeHelper::shape(const UChar* input, + int itemLength, + int numGlyphs, + SCRIPT_ITEM& run, + Shaping& shaping) +{ + HFONT hfont = m_hfont; + SCRIPT_CACHE* scriptCache = m_scriptCache; + SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties; + int ascent = m_ascent; + HDC tempDC = 0; + HGDIOBJ oldFont = 0; + HRESULT hr; + // When used to fill up glyph pages for simple scripts in non-BMP, + // we don't want any font fallback in this class. The simple script + // font path can take care of font fallback. + bool lastFallbackTried = m_disableFontFallback; + bool result; + + int generatedGlyphs = 0; + + // In case HFONT passed in ctor cannot render this run, we have to scan + // other fonts from the beginning of the font list. + resetFontIndex(); + + // Compute shapes. + while (true) { + shaping.m_logs.resize(itemLength); + shaping.m_glyphs.resize(numGlyphs); + shaping.m_visualAttributes.resize(numGlyphs); + +#ifdef PURIFY + // http://code.google.com/p/chromium/issues/detail?id=5309 + // Purify isn't able to track the assignments that ScriptShape makes to + // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it + // writes, will be considered un-initialized data. + // + // This hack avoid the false-positive UMRs by marking the buffer as + // initialized. + // + // FIXME: A better solution would be to use Purify's API and mark only + // the populated range as initialized: + // + // PurifyMarkAsInitialized( + // &shaping.m_glyphs[0], + // sizeof(shaping.m_glyphs[0] * generatedGlyphs); + + ZeroMemory(&shaping.m_glyphs[0], + sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size()); +#endif + + // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true + // here. Is that what we want? It will display control characters. + hr = ScriptShape(tempDC, scriptCache, input, itemLength, + numGlyphs, &run.a, + &shaping.m_glyphs[0], &shaping.m_logs[0], + &shaping.m_visualAttributes[0], &generatedGlyphs); + if (hr == E_PENDING) { + // Allocate the DC. + tempDC = GetDC(0); + oldFont = SelectObject(tempDC, hfont); + continue; + } else if (hr == E_OUTOFMEMORY) { + numGlyphs *= 2; + continue; + } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(&shaping.m_glyphs[0], generatedGlyphs, fontProperties))) + break; + + // The current font can't render this run. clear DC and try + // next font. + if (tempDC) { + SelectObject(tempDC, oldFont); + ReleaseDC(0, tempDC); + tempDC = 0; + } + + if (!m_disableFontFallback && + nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { + // The primary font does not support this run. Try next font. + // In case of web page rendering, they come from fonts specified in + // CSS stylesheets. + continue; + } else if (!lastFallbackTried) { + lastFallbackTried = true; + + // Generate a last fallback font based on the script of + // a character to draw while inheriting size and styles + // from the primary font + if (!m_logfont.lfFaceName[0]) + setLogFontAndStyle(m_hfont, &m_logfont, &m_style); + + // TODO(jungshik): generic type should come from webkit for + // UniscribeHelperTextRun (a derived class used in webkit). + const UChar *family = getFallbackFamily(input, itemLength, + FontDescription::StandardFamily, 0, 0); + bool fontOk = getDerivedFontData(family, m_style, &m_logfont, + &ascent, &hfont, &scriptCache); + + if (!fontOk) { + // If this GetDerivedFontData is called from the renderer it + // might fail because the sandbox is preventing it from opening + // the font files. If we are running in the renderer, + // TryToPreloadFont is overridden to ask the browser to preload + // the font for us so we can access it. + tryToPreloadFont(hfont); + + // Try again. + fontOk = getDerivedFontData(family, m_style, &m_logfont, + &ascent, &hfont, &scriptCache); + ASSERT(fontOk); + } + + // TODO(jungshik) : Currently GetDerivedHFont always returns a + // a valid HFONT, but in the future, I may change it to return 0. + ASSERT(hfont); + + // We don't need a font_properties for the last resort fallback font + // because we don't have anything more to try and are forced to + // accept empty glyph boxes. If we tried a series of fonts as + // 'last-resort fallback', we'd need it, but currently, we don't. + continue; + } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { + run.a.eScript = SCRIPT_UNDEFINED; + continue; + } else if (FAILED(hr)) { + // Error shaping. + generatedGlyphs = 0; + result = false; + goto cleanup; + } + } + + // Sets Windows font data for this run to those corresponding to + // a font supporting this run. we don't need to store font_properties + // because it's not used elsewhere. + shaping.m_hfont = hfont; + shaping.m_scriptCache = scriptCache; + + // The ascent of a font for this run can be different from + // that of the primary font so that we need to keep track of + // the difference per run and take that into account when calling + // ScriptTextOut in |draw|. Otherwise, different runs rendered by + // different fonts would not be aligned vertically. + shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0; + result = true; + + cleanup: + shaping.m_glyphs.resize(generatedGlyphs); + shaping.m_visualAttributes.resize(generatedGlyphs); + shaping.m_advance.resize(generatedGlyphs); + shaping.m_offsets.resize(generatedGlyphs); + if (tempDC) { + SelectObject(tempDC, oldFont); + ReleaseDC(0, tempDC); + } + // On failure, our logs don't mean anything, so zero those out. + if (!result) + shaping.m_logs.clear(); + + return result; +} + +void UniscribeHelper::fillShapes() +{ + m_shapes.resize(m_runs.size()); + for (size_t i = 0; i < m_runs.size(); i++) { + int startItem = m_runs[i].iCharPos; + int itemLength = m_inputLength - startItem; + if (i < m_runs.size() - 1) + itemLength = m_runs[i + 1].iCharPos - startItem; + + int numGlyphs; + if (itemLength < UNISCRIBE_HELPER_STACK_CHARS) { + // We'll start our buffer sizes with the current stack space + // available in our buffers if the current input fits. As long as + // it doesn't expand past that we'll save a lot of time mallocing. + numGlyphs = UNISCRIBE_HELPER_STACK_CHARS; + } else { + // When the input doesn't fit, give up with the stack since it will + // almost surely not be enough room (unless the input actually + // shrinks, which is unlikely) and just start with the length + // recommended by the Uniscribe documentation as a "usually fits" + // size. + numGlyphs = itemLength * 3 / 2 + 16; + } + + // Convert a string to a glyph string trying the primary font, fonts in + // the fallback list and then script-specific last resort font. + Shaping& shaping = m_shapes[i]; + if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping)) + continue; + + // At the moment, the only time m_disableFontFallback is set is + // when we look up glyph indices for non-BMP code ranges. So, + // we can skip the glyph placement. When that becomes not the case + // any more, we have to add a new flag to control glyph placement. + if (m_disableFontFallback) + continue; + + // Compute placements. Note that offsets is documented incorrectly + // and is actually an array. + + // DC that we lazily create if Uniscribe commands us to. + // (this does not happen often because scriptCache is already + // updated when calling ScriptShape). + HDC tempDC = 0; + HGDIOBJ oldFont = 0; + HRESULT hr; + while (true) { + shaping.m_prePadding = 0; + hr = ScriptPlace(tempDC, shaping.m_scriptCache, + &shaping.m_glyphs[0], + static_cast<int>(shaping.m_glyphs.size()), + &shaping.m_visualAttributes[0], &m_runs[i].a, + &shaping.m_advance[0], &shaping.m_offsets[0], + &shaping.m_abc); + if (hr != E_PENDING) + break; + + // Allocate the DC and run the loop again. + tempDC = GetDC(0); + oldFont = SelectObject(tempDC, shaping.m_hfont); + } + + if (FAILED(hr)) { + // Some error we don't know how to handle. Nuke all of our data + // since we can't deal with partially valid data later. + m_runs.clear(); + m_shapes.clear(); + m_screenOrder.clear(); + } + + if (tempDC) { + SelectObject(tempDC, oldFont); + ReleaseDC(0, tempDC); + } + } + + adjustSpaceAdvances(); + + if (m_letterSpacing != 0 || m_wordSpacing != 0) + applySpacing(); +} + +void UniscribeHelper::fillScreenOrder() +{ + m_screenOrder.resize(m_runs.size()); + + // We assume that the input has only one text direction in it. + // TODO(brettw) are we sure we want to keep this restriction? + if (m_isRtl) { + for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++) + m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i; + } else { + for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++) + m_screenOrder[i] = i; + } +} + +void UniscribeHelper::adjustSpaceAdvances() +{ + if (m_spaceWidth == 0) + return; + + int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing; + + // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem. + for (size_t run = 0; run < m_runs.size(); run++) { + Shaping& shaping = m_shapes[run]; + + for (int i = 0; i < shaping.charLength(); i++) { + if (!treatAsSpace(m_input[m_runs[run].iCharPos + i])) + continue; + + int glyphIndex = shaping.m_logs[i]; + int currentAdvance = shaping.m_advance[glyphIndex]; + + // currentAdvance does not include additional letter-spacing, but + // space_width does. Here we find out how off we are from the + // correct width for the space not including letter-spacing, then + // just subtract that diff. + int diff = currentAdvance - spaceWidthWithoutLetterSpacing; + // The shaping can consist of a run of text, so only subtract the + // difference in the width of the glyph. + shaping.m_advance[glyphIndex] -= diff; + shaping.m_abc.abcB -= diff; + } + } +} + +void UniscribeHelper::applySpacing() +{ + for (size_t run = 0; run < m_runs.size(); run++) { + Shaping& shaping = m_shapes[run]; + bool isRtl = m_runs[run].a.fRTL; + + if (m_letterSpacing != 0) { + // RTL text gets padded to the left of each character. We increment + // the run's advance to make this happen. This will be balanced out + // by NOT adding additional advance to the last glyph in the run. + if (isRtl) + shaping.m_prePadding += m_letterSpacing; + + // Go through all the glyphs in this run and increase the "advance" + // to account for letter spacing. We adjust letter spacing only on + // cluster boundaries. + // + // This works for most scripts, but may have problems with some + // indic scripts. This behavior is better than Firefox or IE for + // Hebrew. + for (int i = 0; i < shaping.glyphLength(); i++) { + if (shaping.m_visualAttributes[i].fClusterStart) { + // Ick, we need to assign the extra space so that the glyph + // comes first, then is followed by the space. This is + // opposite for RTL. + if (isRtl) { + if (i != shaping.glyphLength() - 1) { + // All but the last character just get the spacing + // applied to their advance. The last character + // doesn't get anything, + shaping.m_advance[i] += m_letterSpacing; + shaping.m_abc.abcB += m_letterSpacing; + } + } else { + // LTR case is easier, we just add to the advance. + shaping.m_advance[i] += m_letterSpacing; + shaping.m_abc.abcB += m_letterSpacing; + } + } + } + } + + // Go through all the characters to find whitespace and insert the + // extra wordspacing amount for the glyphs they correspond to. + if (m_wordSpacing != 0) { + for (int i = 0; i < shaping.charLength(); i++) { + if (!treatAsSpace(m_input[m_runs[run].iCharPos + i])) + continue; + + // The char in question is a word separator... + int glyphIndex = shaping.m_logs[i]; + + // Spaces will not have a glyph in Uniscribe, it will just add + // additional advance to the character to the left of the + // space. The space's corresponding glyph will be the character + // following it in reading order. + if (isRtl) { + // In RTL, the glyph to the left of the space is the same + // as the first glyph of the following character, so we can + // just increment it. + shaping.m_advance[glyphIndex] += m_wordSpacing; + shaping.m_abc.abcB += m_wordSpacing; + } else { + // LTR is actually more complex here, we apply it to the + // previous character if there is one, otherwise we have to + // apply it to the leading space of the run. + if (glyphIndex == 0) + shaping.m_prePadding += m_wordSpacing; + else { + shaping.m_advance[glyphIndex - 1] += m_wordSpacing; + shaping.m_abc.abcB += m_wordSpacing; + } + } + } + } // m_wordSpacing != 0 + + // Loop for next run... + } +} + +// The advance is the ABC width of the run +int UniscribeHelper::advanceForItem(int itemIndex) const +{ + int accum = 0; + const Shaping& shaping = m_shapes[itemIndex]; + + if (shaping.m_justify.size() == 0) { + // Easy case with no justification, the width is just the ABC width of + // the run. (The ABC width is the sum of the advances). + return shaping.m_abc.abcA + shaping.m_abc.abcB + + shaping.m_abc.abcC + shaping.m_prePadding; + } + + // With justification, we use the justified amounts instead. The + // justification array contains both the advance and the extra space + // added for justification, so is the width we want. + int justification = 0; + for (size_t i = 0; i < shaping.m_justify.size(); i++) + justification += shaping.m_justify[i]; + + return shaping.m_prePadding + justification; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/UniscribeHelper.h b/Source/WebCore/platform/graphics/chromium/UniscribeHelper.h new file mode 100644 index 0000000..ffd57db --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/UniscribeHelper.h @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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. + */ + +// A wrapper around Uniscribe that provides a reasonable API. + +#ifndef UniscribeHelper_h +#define UniscribeHelper_h + +#include <windows.h> +#include <usp10.h> +#include <map> + +#include <unicode/uchar.h> +#include <wtf/Vector.h> + +class UniscribeTest_TooBig_Test; // A gunit test for UniscribeHelper. + +namespace WebCore { + +class GraphicsContext; + +#define UNISCRIBE_HELPER_STACK_RUNS 8 +#define UNISCRIBE_HELPER_STACK_CHARS 32 + +// This object should be safe to create & destroy frequently, as long as the +// caller preserves the script_cache when possible (this data may be slow to +// compute). +// +// This object is "kind of large" (~1K) because it reserves a lot of space for +// working with to avoid expensive heap operations. Therefore, not only should +// you not worry about creating and destroying it, you should try to not keep +// them around. +class UniscribeHelper { +public: + // Initializes this Uniscribe run with the text pointed to by |run| with + // |length|. The input is NOT null terminated. + // + // The is_rtl flag should be set if the input script is RTL. It is assumed + // that the caller has already divided up the input text (using ICU, for + // example) into runs of the same direction of script. This avoids + // disagreements between the caller and Uniscribe later (see FillItems). + // + // A script cache should be provided by the caller that is initialized to + // NULL. When the caller is done with the cache (it may be stored between + // runs as long as it is used consistently with the same HFONT), it should + // call ScriptFreeCache(). + UniscribeHelper(const UChar* input, + int inputLength, + bool isRtl, + HFONT, + SCRIPT_CACHE*, + SCRIPT_FONTPROPERTIES*); + + virtual ~UniscribeHelper(); + + // Sets Uniscribe's directional override flag. False by default. + bool directionalOverride() const + { + return m_directionalOverride; + } + void setDirectionalOverride(bool override) + { + m_directionalOverride = override; + } + + // Set's Uniscribe's no-ligate override flag. False by default. + bool inhibitLigate() const + { + return m_inhibitLigate; + } + void setInhibitLigate(bool inhibit) + { + m_inhibitLigate = inhibit; + } + + // Set letter spacing. We will try to insert this much space between + // graphemes (one or more glyphs perceived as a single unit by ordinary + // users of a script). Positive values increase letter spacing, negative + // values decrease it. 0 by default. + int letterSpacing() const + { + return m_letterSpacing; + } + void setLetterSpacing(int letterSpacing) + { + m_letterSpacing = letterSpacing; + } + + // Set the width of a standard space character. We use this to normalize + // space widths. Windows will make spaces after Hindi characters larger than + // other spaces. A space_width of 0 means to use the default space width. + // + // Must be set before Init() is called. + int spaceWidth() const + { + return m_spaceWidth; + } + void setSpaceWidth(int spaceWidth) + { + m_spaceWidth = spaceWidth; + } + + // Set word spacing. We will try to insert this much extra space between + // each word in the input (beyond whatever whitespace character separates + // words). Positive values lead to increased letter spacing, negative values + // decrease it. 0 by default. + // + // Must be set before Init() is called. + int wordSpacing() const + { + return m_wordSpacing; + } + void setWordSpacing(int wordSpacing) + { + m_wordSpacing = wordSpacing; + } + + void setAscent(int ascent) + { + m_ascent = ascent; + } + + // When set to true, this class is used only to look up glyph + // indices for a range of Unicode characters without glyph placement. + // By default, it's false. This should be set to true when this + // class is used for glyph index look-up for non-BMP characters + // in GlyphPageNodeChromiumWin.cpp. + void setDisableFontFallback(bool disableFontFallback) + { + m_disableFontFallback = true; + } + + // You must call this after setting any options but before doing any + // other calls like asking for widths or drawing. + void init() + { + initWithOptionalLengthProtection(true); + } + + // Returns the total width in pixels of the text run. + int width() const; + + // Call to justify the text, with the amount of space that should be ADDED + // to get the desired width that the column should be justified to. + // Normally, spaces are inserted, but for Arabic there will be kashidas + // (extra strokes) inserted instead. + // + // This function MUST be called AFTER Init(). + void justify(int additionalSpace); + + // Computes the given character offset into a pixel offset of the beginning + // of that character. + int characterToX(int offset) const; + + // Converts the given pixel X position into a logical character offset into + // the run. For positions appearing before the first character, this will + // return -1. + int xToCharacter(int x) const; + + // Draws the given characters to (x, y) in the given DC. The font will be + // handled by this function, but the font color and other attributes should + // be pre-set. + // + // The y position is the upper left corner, NOT the baseline. + void draw(GraphicsContext* graphicsContext, HDC dc, int x, int y, int from, + int to); + + // Returns the first glyph assigned to the character at the given offset. + // This function is used to retrieve glyph information when Uniscribe is + // being used to generate glyphs for non-complex, non-BMP (above U+FFFF) + // characters. These characters are not otherwise special and have no + // complex shaping rules, so we don't otherwise need Uniscribe, except + // Uniscribe is the only way to get glyphs for non-BMP characters. + // + // Returns 0 if there is no glyph for the given character. + WORD firstGlyphForCharacter(int charOffset) const; + +protected: + // Backend for init. The flag allows the unit test to specify whether we + // should fail early for very long strings like normal, or try to pass the + // long string to Uniscribe. The latter provides a way to force failure of + // shaping. + void initWithOptionalLengthProtection(bool lengthProtection); + + // Tries to preload the font when the it is not accessible. + // This is the default implementation and it does not do anything. + virtual void tryToPreloadFont(HFONT) {} + +private: + friend class UniscribeTest_TooBig_Test; + + // An array corresponding to each item in runs_ containing information + // on each of the glyphs that were generated. Like runs_, this is in + // reading order. However, for rtl text, the characters within each + // item will be reversed. + struct Shaping { + Shaping() + : m_prePadding(0) + , m_hfont(NULL) + , m_scriptCache(NULL) + , m_ascentOffset(0) { + m_abc.abcA = 0; + m_abc.abcB = 0; + m_abc.abcC = 0; + } + + // Returns the number of glyphs (which will be drawn to the screen) + // in this run. + int glyphLength() const + { + return static_cast<int>(m_glyphs.size()); + } + + // Returns the number of characters (that we started with) in this run. + int charLength() const + { + return static_cast<int>(m_logs.size()); + } + + // Returns the advance array that should be used when measuring glyphs. + // The returned pointer will indicate an array with glyph_length() + // elements and the advance that should be used for each one. This is + // either the real advance, or the justified advances if there is one, + // and is the array we want to use for measurement. + const int* effectiveAdvances() const + { + if (m_advance.size() == 0) + return 0; + if (m_justify.size() == 0) + return &m_advance[0]; + return &m_justify[0]; + } + + // This is the advance amount of space that we have added to the + // beginning of the run. It is like the ABC's |A| advance but one that + // we create and must handle internally whenever computing with pixel + // offsets. + int m_prePadding; + + // Glyph indices in the font used to display this item. These indices + // are in screen order. + Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_glyphs; + + // For each input character, this tells us the first glyph index it + // generated. This is the only array with size of the input chars. + // + // All offsets are from the beginning of this run. Multiple characters + // can generate one glyph, in which case there will be adjacent + // duplicates in this list. One character can also generate multiple + // glyphs, in which case there will be skipped indices in this list. + Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_logs; + + // Flags and such for each glyph. + Vector<SCRIPT_VISATTR, UNISCRIBE_HELPER_STACK_CHARS> m_visualAttributes; + + // Horizontal advances for each glyph listed above, this is basically + // how wide each glyph is. + Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_advance; + + // This contains glyph offsets, from the nominal position of a glyph. + // It is used to adjust the positions of multiple combining characters + // around/above/below base characters in a context-sensitive manner so + // that they don't bump against each other and the base character. + Vector<GOFFSET, UNISCRIBE_HELPER_STACK_CHARS> m_offsets; + + // Filled by a call to Justify, this is empty for nonjustified text. + // If nonempty, this contains the array of justify characters for each + // character as returned by ScriptJustify. + // + // This is the same as the advance array, but with extra space added + // for some characters. The difference between a glyph's |justify| + // width and it's |advance| width is the extra space added. + Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_justify; + + // Sizing information for this run. This treats the entire run as a + // character with a preceeding advance, width, and ending advance. The + // B width is the sum of the |advance| array, and the A and C widths + // are any extra spacing applied to each end. + // + // It is unclear from the documentation what this actually means. From + // experimentation, it seems that the sum of the character advances is + // always the sum of the ABC values, and I'm not sure what you're + // supposed to do with the ABC values. + ABC m_abc; + + // Pointers to windows font data used to render this run. + HFONT m_hfont; + SCRIPT_CACHE* m_scriptCache; + + // Ascent offset between the ascent of the primary font + // and that of the fallback font. The offset needs to be applied, + // when drawing a string, to align multiple runs rendered with + // different fonts. + int m_ascentOffset; + }; + + // Computes the runs_ array from the text run. + void fillRuns(); + + // Computes the shapes_ array given an runs_ array already filled in. + void fillShapes(); + + // Fills in the screen_order_ array (see below). + void fillScreenOrder(); + + // Called to update the glyph positions based on the current spacing + // options that are set. + void applySpacing(); + + // Normalizes all advances for spaces to the same width. This keeps windows + // from making spaces after Hindi characters larger, which is then + // inconsistent with our meaure of the width since WebKit doesn't include + // spaces in text-runs sent to uniscribe unless white-space:pre. + void adjustSpaceAdvances(); + + // Returns the total width of a single item. + int advanceForItem(int) const; + + // Shapes a run (pointed to by |input|) using |hfont| first. + // Tries a series of fonts specified retrieved with NextWinFontData + // and finally a font covering characters in |*input|. A string pointed + // by |input| comes from ScriptItemize and is supposed to contain + // characters belonging to a single script aside from characters common to + // all scripts (e.g. space). + bool shape(const UChar* input, int itemLength, int numGlyphs, SCRIPT_ITEM& run, Shaping&); + + // Gets Windows font data for the next best font to try in the list + // of fonts. When there's no more font available, returns false + // without touching any of out params. Need to call ResetFontIndex + // to start scanning of the font list from the beginning. + virtual bool nextWinFontData(HFONT*, SCRIPT_CACHE**, SCRIPT_FONTPROPERTIES**, int* ascent) + { + return false; + } + + // Resets the font index to the first in the list of fonts to try after the + // primaryFont turns out not to work. With fontIndex reset, + // NextWinFontData scans fallback fonts from the beginning. + virtual void resetFontIndex() {} + + // The input data for this run of Uniscribe. See the constructor. + const UChar* m_input; + const int m_inputLength; + const bool m_isRtl; + + // Windows font data for the primary font. In a sense, m_logfont and m_style + // are redundant because m_hfont contains all the information. However, + // invoking GetObject, everytime we need the height and the style, is rather + // expensive so that we cache them. Would it be better to add getter and + // (virtual) setter for the height and the style of the primary font, + // instead of m_logfont? Then, a derived class ctor can set m_ascent, + // m_height and m_style if they're known. Getters for them would have to + // 'infer' their values from m_hfont ONLY when they're not set. + HFONT m_hfont; + SCRIPT_CACHE* m_scriptCache; + SCRIPT_FONTPROPERTIES* m_fontProperties; + int m_ascent; + LOGFONT m_logfont; + int m_style; + + // Options, see the getters/setters above. + bool m_directionalOverride; + bool m_inhibitLigate; + int m_letterSpacing; + int m_spaceWidth; + int m_wordSpacing; + bool m_disableFontFallback; + + // Uniscribe breaks the text into Runs. These are one length of text that is + // in one script and one direction. This array is in reading order. + Vector<SCRIPT_ITEM, UNISCRIBE_HELPER_STACK_RUNS> m_runs; + + Vector<Shaping, UNISCRIBE_HELPER_STACK_RUNS> m_shapes; + + // This is a mapping between reading order and screen order for the items. + // Uniscribe's items array are in reading order. For right-to-left text, + // or mixed (although WebKit's |TextRun| should really be only one + // direction), this makes it very difficult to compute character offsets + // and positions. This list is in screen order from left to right, and + // gives the index into the |m_runs| and |m_shapes| arrays of each + // subsequent item. + Vector<int, UNISCRIBE_HELPER_STACK_RUNS> m_screenOrder; +}; + +} // namespace WebCore + +#endif // UniscribeHelper_h diff --git a/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp b/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp new file mode 100644 index 0000000..f801c13 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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 "UniscribeHelperTextRun.h" + +#include "ChromiumBridge.h" +#include "Font.h" +#include "SimpleFontData.h" + +namespace WebCore { + +UniscribeHelperTextRun::UniscribeHelperTextRun(const TextRun& run, + const Font& font) + : UniscribeHelper(run.characters(), run.length(), run.rtl(), + font.primaryFont()->platformData().hfont(), + font.primaryFont()->platformData().scriptCache(), + font.primaryFont()->platformData().scriptFontProperties()) + , m_font(&font) + , m_fontIndex(0) +{ + setDirectionalOverride(run.directionalOverride()); + setLetterSpacing(font.letterSpacing()); + setSpaceWidth(font.spaceWidth()); + setWordSpacing(font.wordSpacing()); + setAscent(font.primaryFont()->ascent()); + + init(); + + // Padding is the amount to add to make justification happen. This + // should be done after Init() so all the runs are already measured. + if (run.padding() > 0) + justify(run.padding()); +} + +UniscribeHelperTextRun::UniscribeHelperTextRun( + const wchar_t* input, + int inputLength, + bool isRtl, + HFONT hfont, + SCRIPT_CACHE* scriptCache, + SCRIPT_FONTPROPERTIES* fontProperties) + : UniscribeHelper(input, inputLength, isRtl, hfont, + scriptCache, fontProperties) + , m_font(0) + , m_fontIndex(-1) +{ +} + +void UniscribeHelperTextRun::tryToPreloadFont(HFONT font) +{ + // Ask the browser to get the font metrics for this font. + // That will preload the font and it should now be accessible + // from the renderer. + ChromiumBridge::ensureFontLoaded(font); +} + +bool UniscribeHelperTextRun::nextWinFontData( + HFONT* hfont, + SCRIPT_CACHE** scriptCache, + SCRIPT_FONTPROPERTIES** fontProperties, + int* ascent) +{ + // This check is necessary because NextWinFontData can be called again + // after we already ran out of fonts. fontDataAt behaves in a strange + // manner when the difference between param passed and # of fonts stored in + // WebKit::Font is larger than one. We can avoid this check by setting + // font_index_ to # of elements in hfonts_ when we run out of font. In that + // case, we'd have to go through a couple of more checks before returning + // false. + if (m_fontIndex == -1 || !m_font) + return false; + + // If the font data for a fallback font requested is not yet retrieved, add + // them to our vectors. Note that '>' rather than '>=' is used to test that + // condition. primaryFont is not stored in hfonts_, and friends so that + // indices for fontDataAt and our vectors for font data are 1 off from each + // other. That is, when fully populated, hfonts_ and friends have one font + // fewer than what's contained in font_. + if (static_cast<size_t>(++m_fontIndex) > m_hfonts.size()) { + const FontData *fontData = m_font->fontDataAt(m_fontIndex); + if (!fontData) { + // Ran out of fonts. + m_fontIndex = -1; + return false; + } + + // FIXME: this won't work for SegmentedFontData + // http://crbug.com/6425 + const SimpleFontData* simpleFontData = + fontData->fontDataForCharacter(' '); + + m_hfonts.append(simpleFontData->platformData().hfont()); + m_scriptCaches.append(simpleFontData->platformData().scriptCache()); + m_fontProperties.append(simpleFontData->platformData().scriptFontProperties()); + m_ascents.append(simpleFontData->ascent()); + } + + *hfont = m_hfonts[m_fontIndex - 1]; + *scriptCache = m_scriptCaches[m_fontIndex - 1]; + *fontProperties = m_fontProperties[m_fontIndex - 1]; + *ascent = m_ascents[m_fontIndex - 1]; + return true; +} + +void UniscribeHelperTextRun::resetFontIndex() +{ + m_fontIndex = 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h b/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h new file mode 100644 index 0000000..b5c54a0 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006, 2007, 2008, 2009, 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 UniscribeHelperTextRun_h +#define UniscribeHelperTextRun_h + +#include "UniscribeHelper.h" + +namespace WebCore { + +class Font; +class TextRun; + +// Wrapper around the Uniscribe helper that automatically sets it up with the +// WebKit types we supply. +class UniscribeHelperTextRun : public UniscribeHelper { +public: + // Regular constructor used for WebCore text run processing. + UniscribeHelperTextRun(const TextRun&, const Font&); + + // Constructor with the same interface as the gfx::UniscribeState. Using + // this constructor will not give you font fallback, but it will provide + // the ability to load fonts that may not be in the OS cache + // ("TryToPreloadFont") if the caller does not have a TextRun/Font. + UniscribeHelperTextRun(const wchar_t* input, + int inputLength, + bool isRtl, + HFONT hfont, + SCRIPT_CACHE*, + SCRIPT_FONTPROPERTIES*); + +protected: + virtual void tryToPreloadFont(HFONT); + +private: + // This function retrieves the Windows font data (HFONT, etc) for the next + // WebKit font in the list. If the font data corresponding to font_index_ + // has been obtained before, returns the values stored in our internal + // vectors (hfonts_, etc). Otherwise, it gets next SimpleFontData from + // WebKit and adds them to in hfonts_ and friends so that font data can be + // returned quickly next time they're requested. + virtual bool nextWinFontData(HFONT*, SCRIPT_CACHE**, SCRIPT_FONTPROPERTIES**, int* ascent); + virtual void resetFontIndex(); + + // Reference to WebKit::Font that contains all the information about fonts + // we can use to render this input run of text. It is used in + // NextWinFontData to retrieve Windows font data for a series of + // non-primary fonts. + // + // This pointer can be NULL for no font fallback handling. + const Font* m_font; + + // It's rare that many fonts are listed in stylesheets. + // Four would be large enough in most cases. + const static size_t kNumberOfFonts = 4; + + // These vectors are used to store Windows font data for non-primary fonts. + Vector<HFONT, kNumberOfFonts> m_hfonts; + Vector<SCRIPT_CACHE*, kNumberOfFonts> m_scriptCaches; + Vector<SCRIPT_FONTPROPERTIES*, kNumberOfFonts> m_fontProperties; + Vector<int, kNumberOfFonts> m_ascents; + + // Index of the fallback font we're currently using for NextWinFontData. + int m_fontIndex; +}; + +} // namespace WebCore + +#endif // UniscribeHelperTextRun_h diff --git a/Source/WebCore/platform/graphics/chromium/VDMXParser.cpp b/Source/WebCore/platform/graphics/chromium/VDMXParser.cpp new file mode 100644 index 0000000..bd30a97 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VDMXParser.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2008, 2009, 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 <stdint.h> +#include <stdlib.h> +#include <string.h> + +// For htons/ntohs +#include <arpa/inet.h> + +// Buffer helper class +// +// This class perform some trival buffer operations while checking for +// out-of-bounds errors. As a family they return false if anything is amiss, +// updating the current offset otherwise. +class Buffer { +public: + Buffer(const uint8_t* buffer, size_t length) + : m_buffer(buffer) + , m_length(length) + , m_offset(0) { } + + bool skip(size_t numBytes) + { + if (m_offset + numBytes > m_length) + return false; + m_offset += numBytes; + return true; + } + + bool readU8(uint8_t* value) + { + if (m_offset + sizeof(uint8_t) > m_length) + return false; + *value = m_buffer[m_offset]; + m_offset += sizeof(uint8_t); + return true; + } + + bool readU16(uint16_t* value) + { + if (m_offset + sizeof(uint16_t) > m_length) + return false; + memcpy(value, m_buffer + m_offset, sizeof(uint16_t)); + *value = ntohs(*value); + m_offset += sizeof(uint16_t); + return true; + } + + bool readS16(int16_t* value) + { + return readU16(reinterpret_cast<uint16_t*>(value)); + } + + size_t offset() const + { + return m_offset; + } + + void setOffset(size_t newoffset) + { + m_offset = newoffset; + } + +private: + const uint8_t *const m_buffer; + const size_t m_length; + size_t m_offset; +}; + +// VDMX parsing code. +// +// VDMX tables are found in some TrueType/OpenType fonts and contain +// ascender/descender overrides for certain (usually small) sizes. This is +// needed in order to match font metrics on Windows. +// +// Freetype does not parse these tables so we do so here. + +namespace WebCore { + +// Parse a TrueType VDMX table. +// yMax: (output) the ascender value from the table +// yMin: (output) the descender value from the table (negative!) +// vdmx: the table bytes +// vdmxLength: length of @vdmx, in bytes +// targetPixelSize: the pixel size of the font (e.g. 16) +// +// Returns true iff a suitable match are found. Otherwise, *yMax and *yMin are +// untouched. size_t must be 32-bits to avoid overflow. +// +// See http://www.microsoft.com/opentype/otspec/vdmx.htm +bool parseVDMX(int* yMax, int* yMin, + const uint8_t* vdmx, size_t vdmxLength, + unsigned targetPixelSize) +{ + Buffer buf(vdmx, vdmxLength); + + // We ignore the version. Future tables should be backwards compatible with + // this layout. + uint16_t numRatios; + if (!buf.skip(4) || !buf.readU16(&numRatios)) + return false; + + // Now we have two tables. Firstly we have @numRatios Ratio records, then a + // matching array of @numRatios offsets. We save the offset of the beginning + // of this second table. + // + // Range 6 <= x <= 262146 + unsigned long offsetTableOffset = + buf.offset() + 4 /* sizeof struct ratio */ * numRatios; + + unsigned desiredRatio = 0xffffffff; + // We read 4 bytes per record, so the offset range is + // 6 <= x <= 524286 + for (unsigned i = 0; i < numRatios; ++i) { + uint8_t xRatio, yRatio1, yRatio2; + + if (!buf.skip(1) + || !buf.readU8(&xRatio) + || !buf.readU8(&yRatio1) + || !buf.readU8(&yRatio2)) + return false; + + // This either covers 1:1, or this is the default entry (0, 0, 0) + if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1) + || (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) { + desiredRatio = i; + break; + } + } + + if (desiredRatio == 0xffffffff) // no ratio found + return false; + + // Range 10 <= x <= 393216 + buf.setOffset(offsetTableOffset + sizeof(uint16_t) * desiredRatio); + + // Now we read from the offset table to get the offset of another array + uint16_t groupOffset; + if (!buf.readU16(&groupOffset)) + return false; + // Range 0 <= x <= 65535 + buf.setOffset(groupOffset); + + uint16_t numRecords; + if (!buf.readU16(&numRecords) || !buf.skip(sizeof(uint16_t))) + return false; + + // We read 6 bytes per record, so the offset range is + // 4 <= x <= 458749 + for (unsigned i = 0; i < numRecords; ++i) { + uint16_t pixelSize; + if (!buf.readU16(&pixelSize)) + return false; + // the entries are sorted, so we can abort early if need be + if (pixelSize > targetPixelSize) + return false; + + if (pixelSize == targetPixelSize) { + int16_t tempYMax, tempYMin; + if (!buf.readS16(&tempYMax) + || !buf.readS16(&tempYMin)) + return false; + *yMin = tempYMin; + *yMax = tempYMax; + return true; + } + if (!buf.skip(2 * sizeof(int16_t))) + return false; + } + + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/VDMXParser.h b/Source/WebCore/platform/graphics/chromium/VDMXParser.h new file mode 100644 index 0000000..ef625b7 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VDMXParser.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2009, 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 VDMXParser_h +#define VDMXParser_h + +namespace WebCore { + bool parseVDMX(int* ymax, int* ymin, + const uint8_t* vdmx, size_t vdmxLength, + unsigned targetPixelSize); +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/VideoFrameChromium.cpp b/Source/WebCore/platform/graphics/chromium/VideoFrameChromium.cpp new file mode 100644 index 0000000..43b40e2 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VideoFrameChromium.cpp @@ -0,0 +1,47 @@ +/* + * 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" + +#include "VideoFrameChromium.h" + +namespace WebCore { + +const unsigned VideoFrameChromium::maxPlanes = 3; +const unsigned VideoFrameChromium::numRGBPlanes = 1; +const unsigned VideoFrameChromium::rgbPlane = 0; +const unsigned VideoFrameChromium::numYUVPlanes = 3; +const unsigned VideoFrameChromium::yPlane = 0; +const unsigned VideoFrameChromium::uPlane = 1; +const unsigned VideoFrameChromium::vPlane = 2; + +} // namespace WebCore + + diff --git a/Source/WebCore/platform/graphics/chromium/VideoFrameChromium.h b/Source/WebCore/platform/graphics/chromium/VideoFrameChromium.h new file mode 100644 index 0000000..e176b0c --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VideoFrameChromium.h @@ -0,0 +1,82 @@ +/* + * 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 VideoFrameChromium_h +#define VideoFrameChromium_h + +#include "IntSize.h" + +namespace WebCore { + +// A class that represents a video frame in chromium. +class VideoFrameChromium { +public: + static const unsigned maxPlanes; + static const unsigned numRGBPlanes; + static const unsigned rgbPlane; + static const unsigned numYUVPlanes; + static const unsigned yPlane; + static const unsigned uPlane; + static const unsigned vPlane; + + // These enums must be kept in sync with WebKit::WebVideoFrame. + enum Format { + Invalid, + RGB555, + RGB565, + RGB24, + RGB32, + RGBA, + YV12, + YV16, + NV12, + Empty, + ASCII, + }; + + enum SurfaceType { + TypeSystemMemory, + TypeTexture, + }; + + virtual SurfaceType surfaceType() const = 0; + virtual Format format() const = 0; + virtual unsigned width() const = 0; + virtual unsigned height() const = 0; + virtual unsigned planes() const = 0; + virtual int stride(unsigned plane) const = 0; + virtual const void* data(unsigned plane) const = 0; + virtual unsigned texture(unsigned plane) const = 0; + virtual const IntSize requiredTextureSize(unsigned plane) const = 0; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/VideoFrameProvider.h b/Source/WebCore/platform/graphics/chromium/VideoFrameProvider.h new file mode 100644 index 0000000..c596f87 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VideoFrameProvider.h @@ -0,0 +1,57 @@ +/* + * 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 VideoFrameProvider_h +#define VideoFrameProvider_h + +#include "VideoFrameChromium.h" + +namespace WebCore { + +class VideoFrameProvider { +public: + virtual ~VideoFrameProvider() { } + + // This function returns a pointer to a VideoFrameChromium, which is + // the WebCore wrapper for a video frame in Chromium. getCurrentFrame() + // places a lock on the frame in Chromium. Calls to this method should + // always be followed with a call to putCurrentFrame(). + // The ownership of the object is not transferred to the caller and + // the caller should not free the returned object. + virtual VideoFrameChromium* getCurrentFrame() = 0; + // This function releases the lock on the video frame in chromium. It should + // always be called after getCurrentFrame(). Frames passed into this method + // should no longer be referenced after the call is made. + virtual void putCurrentFrame(VideoFrameChromium*) = 0; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp new file mode 100644 index 0000000..81264b3 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp @@ -0,0 +1,427 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) +#include "VideoLayerChromium.h" + +#include "Extensions3DChromium.h" +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#include "NotImplemented.h" +#include "RenderLayerBacking.h" +#include "VideoFrameChromium.h" +#include "VideoFrameProvider.h" + +namespace WebCore { + +// These values are magic numbers that are used in the transformation +// from YUV to RGB color values. +const float VideoLayerChromium::yuv2RGB[9] = { + 1.f, 1.f, 1.f, + 0.f, -.344f, 1.772f, + 1.403f, -.714f, 0.f, +}; + +VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_yuvShaderProgram(0) + , m_rgbaShaderProgram(0) + , m_yuvShaderMatrixLocation(0) + , m_yuvWidthScaleFactorLocation(0) + , m_rgbaShaderMatrixLocation(0) + , m_rgbaWidthScaleFactorLocation(0) + , m_ccMatrixLocation(0) + , m_yTextureLocation(0) + , m_uTextureLocation(0) + , m_vTextureLocation(0) + , m_rgbaTextureLocation(0) + , m_yuvAlphaLocation(0) + , m_rgbaAlphaLocation(0) + , m_initialized(false) +{ + // Frame textures are allocated based on stride width, not visible frame + // width, such that there is a guarantee that the frame rows line up + // properly and are not shifted by (stride - width) pixels. To hide the + // "padding" pixels between the edge of the visible frame width and the end + // of the stride, we give the shader a widthScaleFactor (<=1.0) of how much + // of the width of the texture should be shown when drawing the texture onto + // the vertices. + char vertexShaderString[] = + "precision mediump float; \n" + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "uniform float widthScaleFactor; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = vec2(widthScaleFactor * a_texCoord.x, a_texCoord.y); \n" + "} \n"; + + char yuvFragmentShaderString[] = + "precision mediump float; \n" + "precision mediump int; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D y_texture; \n" + "uniform sampler2D u_texture; \n" + "uniform sampler2D v_texture; \n" + "uniform float alpha; \n" + "uniform mat3 cc_matrix; \n" + "void main() \n" + "{ \n" + " float y = texture2D(y_texture, v_texCoord).x; \n" + " float u = texture2D(u_texture, v_texCoord).r - .5; \n" + " float v = texture2D(v_texture, v_texCoord).r - .5; \n" + " vec3 rgb = cc_matrix * vec3(y, u, v); \n" + " gl_FragColor = vec4(rgb.x, rgb.y, rgb.z, 1.0) * alpha; \n" + "} \n"; + + char rgbaFragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D rgba_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(rgba_texture, vec2(v_texCoord.x, 1.0 - v_texCoord.y)); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" + "} \n"; + + m_rgbaShaderProgram = createShaderProgram(m_context, vertexShaderString, rgbaFragmentShaderString); + if (!m_rgbaShaderProgram) { + LOG_ERROR("VideoLayerChromium: Failed to create rgba shader program"); + return; + } + + m_yuvShaderProgram = createShaderProgram(m_context, vertexShaderString, yuvFragmentShaderString); + if (!m_yuvShaderProgram) { + LOG_ERROR("VideoLayerChromium: Failed to create yuv shader program"); + return; + } + + m_yuvShaderMatrixLocation = m_context->getUniformLocation(m_yuvShaderProgram, "matrix"); + m_yuvWidthScaleFactorLocation = m_context->getUniformLocation(m_yuvShaderProgram, "widthScaleFactor"); + m_yTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "y_texture"); + m_uTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "u_texture"); + m_vTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "v_texture"); + m_ccMatrixLocation = m_context->getUniformLocation(m_yuvShaderProgram, "cc_matrix"); + m_yuvAlphaLocation = m_context->getUniformLocation(m_yuvShaderProgram, "alpha"); + + ASSERT(m_yuvShaderMatrixLocation != -1); + ASSERT(m_yuvWidthScaleFactorLocation != -1); + ASSERT(m_yTextureLocation != -1); + ASSERT(m_uTextureLocation != -1); + ASSERT(m_vTextureLocation != -1); + ASSERT(m_ccMatrixLocation != -1); + ASSERT(m_yuvAlphaLocation != -1); + + m_rgbaShaderMatrixLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "matrix"); + m_rgbaTextureLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "rgba_texture"); + m_rgbaWidthScaleFactorLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "widthScaleFactor"); + m_rgbaAlphaLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "alpha"); + + ASSERT(m_rgbaShaderMatrixLocation != -1); + ASSERT(m_rgbaTextureLocation != -1); + ASSERT(m_rgbaWidthScaleFactorLocation != -1); + ASSERT(m_rgbaAlphaLocation != -1); + + m_initialized = true; +} + +VideoLayerChromium::SharedValues::~SharedValues() +{ + if (m_yuvShaderProgram) + GLC(m_context, m_context->deleteProgram(m_yuvShaderProgram)); + if (m_rgbaShaderProgram) + GLC(m_context, m_context->deleteProgram(m_rgbaShaderProgram)); +} + +PassRefPtr<VideoLayerChromium> VideoLayerChromium::create(GraphicsLayerChromium* owner, + VideoFrameProvider* provider) +{ + return adoptRef(new VideoLayerChromium(owner, provider)); +} + +VideoLayerChromium::VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameProvider* provider) + : LayerChromium(owner) + , m_skipsDraw(true) + , m_frameFormat(VideoFrameChromium::Invalid) + , m_provider(provider) + , m_currentFrame(0) +{ + resetFrameParameters(); +} + +VideoLayerChromium::~VideoLayerChromium() +{ + cleanupResources(); +} + +void VideoLayerChromium::cleanupResources() +{ + LayerChromium::cleanupResources(); + releaseCurrentFrame(); + if (!layerRenderer()) + return; + + GraphicsContext3D* context = layerRendererContext(); + for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { + if (m_textures[plane]) + GLC(context, context->deleteTexture(m_textures[plane])); + } +} + +void VideoLayerChromium::updateContentsIfDirty() +{ + if (!m_contentsDirty) + return; + + RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client()); + if (!backing || backing->paintingGoesToWindow()) + return; + + ASSERT(drawsContent()); + + m_skipsDraw = false; + VideoFrameChromium* frame = m_provider->getCurrentFrame(); + if (!frame) { + m_skipsDraw = true; + m_provider->putCurrentFrame(frame); + return; + } + + m_frameFormat = frame->format(); + unsigned textureFormat = determineTextureFormat(frame); + if (textureFormat == GraphicsContext3D::INVALID_VALUE) { + // FIXME: Implement other paths. + notImplemented(); + m_skipsDraw = true; + m_provider->putCurrentFrame(frame); + return; + } + + if (frame->surfaceType() == VideoFrameChromium::TypeTexture) { + releaseCurrentFrame(); + saveCurrentFrame(frame); + m_dirtyRect.setSize(FloatSize()); + m_contentsDirty = false; + return; + } + + // Allocate textures for planes if they are not allocated already, or + // reallocate textures that are the wrong size for the frame. + GraphicsContext3D* context = layerRendererContext(); + bool texturesAllocated = allocateTexturesIfNeeded(context, frame, textureFormat); + if (!texturesAllocated) { + m_skipsDraw = true; + m_provider->putCurrentFrame(frame); + return; + } + + // Update texture planes. + for (unsigned plane = 0; plane < frame->planes(); plane++) { + ASSERT(frame->requiredTextureSize(plane) == m_textureSizes[plane]); + updateTexture(context, m_textures[plane], frame->requiredTextureSize(plane), textureFormat, frame->data(plane)); + } + + m_dirtyRect.setSize(FloatSize()); + m_contentsDirty = false; + + m_provider->putCurrentFrame(frame); +} + +unsigned VideoLayerChromium::determineTextureFormat(VideoFrameChromium* frame) +{ + switch (frame->format()) { + case VideoFrameChromium::YV12: + return GraphicsContext3D::LUMINANCE; + case VideoFrameChromium::RGBA: + return GraphicsContext3D::RGBA; + default: + break; + } + return GraphicsContext3D::INVALID_VALUE; +} + +bool VideoLayerChromium::allocateTexturesIfNeeded(GraphicsContext3D* context, VideoFrameChromium* frame, unsigned textureFormat) +{ + ASSERT(context); + ASSERT(frame); + + for (unsigned plane = 0; plane < frame->planes(); plane++) { + IntSize planeTextureSize = frame->requiredTextureSize(plane); + + // If the renderer cannot handle this large of a texture, return false. + // FIXME: Remove this test when tiled layers are implemented. + if (!layerRenderer()->checkTextureSize(planeTextureSize)) + return false; + + if (!m_textures[plane]) + m_textures[plane] = layerRenderer()->createLayerTexture(); + + if (!planeTextureSize.isZero() && planeTextureSize != m_textureSizes[plane]) { + allocateTexture(context, m_textures[plane], planeTextureSize, textureFormat); + m_textureSizes[plane] = planeTextureSize; + m_frameSizes[plane] = IntSize(frame->width(), frame->height()); + } + } + return true; +} + +void VideoLayerChromium::allocateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned textureFormat) +{ + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId)); + GLC(context, context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, textureFormat, dimensions.width(), dimensions.height(), 0, textureFormat, GraphicsContext3D::UNSIGNED_BYTE)); +} + +void VideoLayerChromium::updateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned format, const void* data) +{ + ASSERT(context); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId)); + void* mem = static_cast<Extensions3DChromium*>(context->getExtensions())->mapTexSubImage2DCHROMIUM(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, dimensions.width(), dimensions.height(), format, GraphicsContext3D::UNSIGNED_BYTE, Extensions3DChromium::WRITE_ONLY); + if (mem) { + memcpy(mem, data, dimensions.width() * dimensions.height()); + GLC(context, static_cast<Extensions3DChromium*>(context->getExtensions())->unmapTexSubImage2DCHROMIUM(mem)); + } else { + // FIXME: We should have some sort of code to handle the case when + // mapTexSubImage2D fails. + m_skipsDraw = true; + } +} + +void VideoLayerChromium::draw() +{ + if (m_skipsDraw) + return; + + ASSERT(layerRenderer()); + const VideoLayerChromium::SharedValues* sv = layerRenderer()->videoLayerSharedValues(); + ASSERT(sv && sv->initialized()); + + switch (m_frameFormat) { + case VideoFrameChromium::YV12: + drawYUV(sv); + break; + case VideoFrameChromium::RGBA: + drawRGBA(sv); + break; + default: + // FIXME: Implement other paths. + notImplemented(); + break; + } + releaseCurrentFrame(); +} + +void VideoLayerChromium::releaseCurrentFrame() +{ + if (!m_currentFrame) + return; + + m_provider->putCurrentFrame(m_currentFrame); + m_currentFrame = 0; + resetFrameParameters(); +} + +void VideoLayerChromium::drawYUV(const SharedValues* sv) +{ + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE1)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::yPlane])); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE2)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::uPlane])); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE3)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::vPlane])); + + layerRenderer()->useShader(sv->yuvShaderProgram()); + unsigned frameWidth = m_frameSizes[VideoFrameChromium::yPlane].width(); + unsigned textureWidth = m_textureSizes[VideoFrameChromium::yPlane].width(); + float widthScaleFactor = static_cast<float>(frameWidth) / textureWidth; + GLC(context, context->uniform1f(sv->yuvWidthScaleFactorLocation(), widthScaleFactor)); + + GLC(context, context->uniform1i(sv->yTextureLocation(), 1)); + GLC(context, context->uniform1i(sv->uTextureLocation(), 2)); + GLC(context, context->uniform1i(sv->vTextureLocation(), 3)); + + GLC(context, context->uniformMatrix3fv(sv->ccMatrixLocation(), 0, const_cast<float*>(yuv2RGB), 1)); + + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), + bounds().width(), bounds().height(), drawOpacity(), + sv->yuvShaderMatrixLocation(), sv->yuvAlphaLocation()); + + // Reset active texture back to texture 0. + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); +} + +void VideoLayerChromium::drawRGBA(const SharedValues* sv) +{ + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::rgbPlane])); + + layerRenderer()->useShader(sv->rgbaShaderProgram()); + unsigned frameWidth = m_frameSizes[VideoFrameChromium::rgbPlane].width(); + unsigned textureWidth = m_textureSizes[VideoFrameChromium::rgbPlane].width(); + float widthScaleFactor = static_cast<float>(frameWidth) / textureWidth; + GLC(context, context->uniform1f(sv->rgbaWidthScaleFactorLocation(), widthScaleFactor)); + + GLC(context, context->uniform1i(sv->rgbaTextureLocation(), 0)); + + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), + bounds().width(), bounds().height(), drawOpacity(), + sv->rgbaShaderMatrixLocation(), sv->rgbaAlphaLocation()); +} + +void VideoLayerChromium::resetFrameParameters() +{ + for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { + m_textures[plane] = 0; + m_textureSizes[plane] = IntSize(); + m_frameSizes[plane] = IntSize(); + } +} + +void VideoLayerChromium::saveCurrentFrame(VideoFrameChromium* frame) +{ + ASSERT(!m_currentFrame); + m_currentFrame = frame; + for (unsigned plane = 0; plane < frame->planes(); plane++) { + m_textures[plane] = frame->texture(plane); + m_textureSizes[plane] = frame->requiredTextureSize(plane); + m_frameSizes[plane] = m_textureSizes[plane]; + } +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h new file mode 100644 index 0000000..0992ab7 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h @@ -0,0 +1,124 @@ +/* + * 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 VideoLayerChromium_h +#define VideoLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerChromium.h" +#include "VideoFrameProvider.h" + +namespace WebCore { + +// A Layer that contains a Video element. +class VideoLayerChromium : public LayerChromium { +public: + static PassRefPtr<VideoLayerChromium> create(GraphicsLayerChromium* owner = 0, + VideoFrameProvider* = 0); + virtual ~VideoLayerChromium(); + virtual void updateContentsIfDirty(); + virtual bool drawsContent() { return true; } + virtual void draw(); + + // This function is called by VideoFrameProvider. When this method is called + // putCurrentFrame() must be called to return the frame currently held. + void releaseCurrentFrame(); + + class SharedValues { + public: + explicit SharedValues(GraphicsContext3D*); + ~SharedValues(); + unsigned yuvShaderProgram() const { return m_yuvShaderProgram; } + unsigned rgbaShaderProgram() const { return m_rgbaShaderProgram; } + int yuvShaderMatrixLocation() const { return m_yuvShaderMatrixLocation; } + int rgbaShaderMatrixLocation() const { return m_rgbaShaderMatrixLocation; } + int yuvWidthScaleFactorLocation() const { return m_yuvWidthScaleFactorLocation; } + int rgbaWidthScaleFactorLocation() const { return m_rgbaWidthScaleFactorLocation; } + int yTextureLocation() const { return m_yTextureLocation; } + int uTextureLocation() const { return m_uTextureLocation; } + int vTextureLocation() const { return m_vTextureLocation; } + int yuvAlphaLocation() const { return m_yuvAlphaLocation; } + int rgbaAlphaLocation() const { return m_rgbaAlphaLocation; } + int rgbaTextureLocation() const { return m_rgbaTextureLocation; } + int ccMatrixLocation() const { return m_ccMatrixLocation; } + bool initialized() const { return m_initialized; } + private: + GraphicsContext3D* m_context; + unsigned m_yuvShaderProgram; + unsigned m_rgbaShaderProgram; + int m_yuvShaderMatrixLocation; + int m_yuvWidthScaleFactorLocation; + int m_rgbaShaderMatrixLocation; + int m_rgbaWidthScaleFactorLocation; + int m_ccMatrixLocation; + int m_yTextureLocation; + int m_uTextureLocation; + int m_vTextureLocation; + int m_rgbaTextureLocation; + int m_yuvAlphaLocation; + int m_rgbaAlphaLocation; + bool m_initialized; + }; + +protected: + virtual void cleanupResources(); + +private: + VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameProvider*); + + static unsigned determineTextureFormat(VideoFrameChromium*); + bool allocateTexturesIfNeeded(GraphicsContext3D*, VideoFrameChromium*, unsigned textureFormat); + void updateYUVContents(GraphicsContext3D*, const VideoFrameChromium*); + void updateRGBAContents(GraphicsContext3D*, const VideoFrameChromium*); + void allocateTexture(GraphicsContext3D*, unsigned textureId, const IntSize& dimensions, unsigned textureFormat); + void updateTexture(GraphicsContext3D*, unsigned textureId, const IntSize& dimensions, unsigned textureFormat, const void* data); + void drawYUV(const SharedValues*); + void drawRGBA(const SharedValues*); + void resetFrameParameters(); + void saveCurrentFrame(VideoFrameChromium*); + + static const float yuv2RGB[9]; + + bool m_skipsDraw; + VideoFrameChromium::Format m_frameFormat; + VideoFrameProvider* m_provider; + VideoFrameChromium* m_currentFrame; + + unsigned m_textures[3]; + IntSize m_textureSizes[3]; + IntSize m_frameSizes[3]; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif diff --git a/Source/WebCore/platform/graphics/chromium/WebGLLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/WebGLLayerChromium.cpp new file mode 100644 index 0000000..5b34bb9 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/WebGLLayerChromium.cpp @@ -0,0 +1,88 @@ +/* + * 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 USE(ACCELERATED_COMPOSITING) + +#include "WebGLLayerChromium.h" + +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" + +namespace WebCore { + +PassRefPtr<WebGLLayerChromium> WebGLLayerChromium::create(GraphicsLayerChromium* owner) +{ + return adoptRef(new WebGLLayerChromium(owner)); +} + +WebGLLayerChromium::WebGLLayerChromium(GraphicsLayerChromium* owner) + : CanvasLayerChromium(owner) + , m_context(0) +{ +} + +void WebGLLayerChromium::updateContentsIfDirty() +{ + if (!m_contentsDirty) + return; + + GraphicsContext3D* rendererContext = layerRendererContext(); + ASSERT(m_context); + if (m_textureChanged) { + rendererContext->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textureId); + // Set the min-mag filters to linear and wrap modes to GL_CLAMP_TO_EDGE + // to get around NPOT texture limitations of GLES. + rendererContext->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); + rendererContext->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); + rendererContext->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); + rendererContext->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); + m_textureChanged = false; + } + // Update the contents of the texture used by the compositor. + if (m_contentsDirty) { + m_context->prepareTexture(); + m_contentsDirty = false; + } +} + +void WebGLLayerChromium::setContext(const GraphicsContext3D* context) +{ + m_context = const_cast<GraphicsContext3D*>(context); + + unsigned int textureId = m_context->platformTexture(); + if (textureId != m_textureId) + m_textureChanged = true; + m_textureId = textureId; +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/WebGLLayerChromium.h b/Source/WebCore/platform/graphics/chromium/WebGLLayerChromium.h new file mode 100644 index 0000000..c67cc2c --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/WebGLLayerChromium.h @@ -0,0 +1,60 @@ +/* + * 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 WebGLLayerChromium_h +#define WebGLLayerChromium_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CanvasLayerChromium.h" + +namespace WebCore { + +class GraphicsContext3D; + +// A Layer containing a WebGL canvas +class WebGLLayerChromium : public CanvasLayerChromium { +public: + static PassRefPtr<WebGLLayerChromium> create(GraphicsLayerChromium* owner = 0); + virtual bool drawsContent() { return m_context; } + virtual void updateContentsIfDirty(); + + void setContext(const GraphicsContext3D* context); + +private: + explicit WebGLLayerChromium(GraphicsLayerChromium* owner); + GraphicsContext3D* m_context; +}; + +} +#endif // USE(ACCELERATED_COMPOSITING) + +#endif |
