diff options
Diffstat (limited to 'WebCore/platform/graphics/mac')
15 files changed, 991 insertions, 1595 deletions
diff --git a/WebCore/platform/graphics/mac/CoreTextController.cpp b/WebCore/platform/graphics/mac/ComplexTextController.cpp index b2682e4..265b2c3 100644 --- a/WebCore/platform/graphics/mac/CoreTextController.cpp +++ b/WebCore/platform/graphics/mac/ComplexTextController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,30 +10,24 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" -#include "CoreTextController.h" - -#if USE(CORE_TEXT) +#include "ComplexTextController.h" #include "CharacterNames.h" #include "Font.h" -#include "FontCache.h" -#include "SimpleFontData.h" #include "TextBreakIterator.h" -#include <wtf/MathExtras.h> using namespace std; @@ -53,54 +47,7 @@ static inline CGFloat ceilCGFloat(CGFloat f) return static_cast<CGFloat>(ceil(f)); } -CoreTextController::CoreTextRun::CoreTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) - : m_CTRun(ctRun) - , m_fontData(fontData) - , m_characters(characters) - , m_stringLocation(stringLocation) - , m_stringLength(stringLength) -{ - m_glyphCount = CTRunGetGlyphCount(ctRun); - m_indices = CTRunGetStringIndicesPtr(ctRun); - if (!m_indices) { - m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); - CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex)); - m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get())); - CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices)); - } -} - -// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on -// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path. -CoreTextController::CoreTextRun::CoreTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) - : m_fontData(fontData) - , m_characters(characters) - , m_stringLocation(stringLocation) - , m_stringLength(stringLength) -{ - Vector<CFIndex, 16> indices; - unsigned r = 0; - while (r < stringLength) { - indices.append(r); - if (U_IS_SURROGATE(characters[r])) { - ASSERT(r + 1 < stringLength); - ASSERT(U_IS_SURROGATE_LEAD(characters[r])); - ASSERT(U_IS_TRAIL(characters[r + 1])); - r += 2; - } else - r++; - } - m_glyphCount = indices.size(); - if (!ltr) { - for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) - std::swap(indices[r], indices[end]); - } - m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); - CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex)); - m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get())); -} - -CoreTextController::CoreTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts) +ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts) : m_font(*font) , m_run(run) , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) @@ -111,6 +58,7 @@ CoreTextController::CoreTextController(const Font* font, const TextRun& run, boo , m_numGlyphsSoFar(0) , m_currentRun(0) , m_glyphInCurrentRun(0) + , m_characterInCurrentGlyph(0) , m_finalRoundingWidth(0) , m_fallbackFonts(fallbackFonts) , m_lastRoundingGlyph(0) @@ -130,16 +78,12 @@ CoreTextController::CoreTextController(const Font* font, const TextRun& run, boo m_padPerSpace = ceilf(m_run.padding() / numSpaces); } - collectCoreTextRuns(); + collectComplexTextRuns(); adjustGlyphsAndAdvances(); } -int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs) +int ComplexTextController::offsetForPosition(int h, bool includePartialGlyphs) { - // FIXME: For positions occurring within a ligature, we should return the closest "ligature caret" or - // approximate it by dividing the width of the ligature by the number of characters it encompasses. - // However, Core Text does not expose a low-level API for directly finding - // out how many characters a ligature encompasses (the "attachment count"). if (h >= m_totalWidth) return m_run.ltr() ? m_end : 0; if (h < 0) @@ -147,17 +91,27 @@ int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs) CGFloat x = h; - size_t runCount = m_coreTextRuns.size(); + size_t runCount = m_complexTextRuns.size(); size_t offsetIntoAdjustedGlyphs = 0; for (size_t r = 0; r < runCount; ++r) { - const CoreTextRun& coreTextRun = m_coreTextRuns[r]; - for (unsigned j = 0; j < coreTextRun.glyphCount(); ++j) { + const ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; + for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) { CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width; - if (x <= adjustedAdvance) { - CFIndex hitIndex = coreTextRun.indexAt(j); - int stringLength = coreTextRun.stringLength(); - TextBreakIterator* cursorPositionIterator = cursorMovementIterator(coreTextRun.characters(), stringLength); + if (x < adjustedAdvance) { + CFIndex hitGlyphStart = complexTextRun.indexAt(j); + CFIndex hitGlyphEnd; + if (m_run.ltr()) + hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.stringLength()); + else + hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.stringLength()); + + // FIXME: Instead of dividing the glyph's advance equially between the characters, this + // could use the glyph's "ligature carets". However, there is no Core Text API to get the + // ligature carets. + CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance); + int stringLength = complexTextRun.stringLength(); + TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength); int clusterStart; if (isTextBreak(cursorPositionIterator, hitIndex)) clusterStart = hitIndex; @@ -168,45 +122,49 @@ int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs) } if (!includePartialGlyphs) - return coreTextRun.stringLocation() + clusterStart; + return complexTextRun.stringLocation() + clusterStart; int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex); if (clusterEnd == TextBreakDone) clusterEnd = stringLength; - CGFloat clusterWidth = adjustedAdvance; - // FIXME: The search stops at the boundaries of coreTextRun. In theory, it should go on into neighboring CoreTextRuns + CGFloat clusterWidth; + // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no // reordering and on font fallback should occur within a CTLine. if (clusterEnd - clusterStart > 1) { + clusterWidth = adjustedAdvance; int firstGlyphBeforeCluster = j - 1; - while (firstGlyphBeforeCluster >= 0 && coreTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { + while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width; clusterWidth += width; x += width; firstGlyphBeforeCluster--; } unsigned firstGlyphAfterCluster = j + 1; - while (firstGlyphAfterCluster < coreTextRun.glyphCount() && coreTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { + while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width; firstGlyphAfterCluster++; } + } else { + clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart); + x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1); } if (x <= clusterWidth / 2) - return coreTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); else - return coreTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); } x -= adjustedAdvance; } - offsetIntoAdjustedGlyphs += coreTextRun.glyphCount(); + offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); } ASSERT_NOT_REACHED(); return 0; } -void CoreTextController::collectCoreTextRuns() +void ComplexTextController::collectComplexTextRuns() { if (!m_end) return; @@ -227,7 +185,7 @@ void CoreTextController::collectCoreTextRuns() static const UChar hyphen = '-'; if (hasTrailingSoftHyphen && m_run.rtl()) { - collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); + collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); indexOfFontTransition--; curr--; } @@ -290,7 +248,7 @@ void CoreTextController::collectCoreTextRuns() if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) { int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition; int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; - collectCoreTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); + collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); indexOfFontTransition = index; } } @@ -298,19 +256,15 @@ void CoreTextController::collectCoreTextRuns() int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0); if (itemLength) { int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; - collectCoreTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); + collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); } if (hasTrailingSoftHyphen && m_run.ltr()) - collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); + collectComplexTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); } -void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) +void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) { - // FIXME: For offsets falling inside a ligature, we should advance only as far as the appropriate "ligature caret" - // or divide the width of the ligature by the number of offsets it encompasses and make an advance proportional - // to the offsets into the ligature. However, Core Text does not expose a low-level API for - // directly finding out how many characters a ligature encompasses (the "attachment count"). if (static_cast<int>(offset) > m_end) offset = m_end; @@ -319,24 +273,44 @@ void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) m_currentCharacter = offset; - size_t runCount = m_coreTextRuns.size(); + size_t runCount = m_complexTextRuns.size(); bool ltr = m_run.ltr(); unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar; while (m_currentRun < runCount) { - const CoreTextRun& coreTextRun = m_coreTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun]; - size_t glyphCount = coreTextRun.glyphCount(); + const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun]; + size_t glyphCount = complexTextRun.glyphCount(); unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun; while (m_glyphInCurrentRun < glyphCount) { - if (coreTextRun.indexAt(g) + coreTextRun.stringLocation() >= m_currentCharacter) - return; + unsigned glyphStartOffset = complexTextRun.indexAt(g); + unsigned glyphEndOffset; + if (ltr) + glyphEndOffset = max<unsigned>(glyphStartOffset, g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.stringLength()); + else + glyphEndOffset = max<unsigned>(glyphStartOffset, g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.stringLength()); + CGSize adjustedAdvance = m_adjustedAdvances[k]; - if (glyphBuffer) - glyphBuffer->add(m_adjustedGlyphs[k], coreTextRun.fontData(), adjustedAdvance); - m_runWidthSoFar += adjustedAdvance.width; + + if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter) + return; + + if (glyphBuffer && !m_characterInCurrentGlyph) + glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance); + + unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; + m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset; + // FIXME: Instead of dividing the glyph's advance equially between the characters, this + // could use the glyph's "ligature carets". However, there is no Core Text API to get the + // ligature carets. + m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset); + + if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter) + return; + m_numGlyphsSoFar++; m_glyphInCurrentRun++; + m_characterInCurrentGlyph = 0; if (ltr) { g++; k++; @@ -352,100 +326,35 @@ void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) m_runWidthSoFar += m_finalRoundingWidth; } -void CoreTextController::collectCoreTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) -{ - if (!fontData) { - // Create a run of missing glyphs from the primary font. - m_coreTextRuns.append(CoreTextRun(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr())); - return; - } - - if (m_fallbackFonts && fontData != m_font.primaryFont()) - m_fallbackFonts->add(fontData); - - RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull)); - - RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes(m_font.fontDescription().textRenderingMode()))); - - RetainPtr<CTTypesetterRef> typesetter; - - if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) { - static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; - static const void* ltrOptionValues[] = { kCFBooleanFalse }; - static const void* rtlOptionValues[] = { kCFBooleanTrue }; - static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); - } else - typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get())); - - RetainPtr<CTLineRef> line(AdoptCF, CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0))); - - CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); - - CFIndex runCount = CFArrayGetCount(runArray); - - for (CFIndex r = 0; r < runCount; r++) { - CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); - ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); - m_coreTextRuns.append(CoreTextRun(ctRun, fontData, cp, stringLocation, length)); - } -} - -void CoreTextController::adjustGlyphsAndAdvances() +void ComplexTextController::adjustGlyphsAndAdvances() { - size_t runCount = m_coreTextRuns.size(); + size_t runCount = m_complexTextRuns.size(); for (size_t r = 0; r < runCount; ++r) { - const CoreTextRun& coreTextRun = m_coreTextRuns[r]; - unsigned glyphCount = coreTextRun.glyphCount(); - const SimpleFontData* fontData = coreTextRun.fontData(); - - Vector<CGGlyph, 256> glyphsVector; - const CGGlyph* glyphs; - - Vector<CGSize, 256> advancesVector; - const CGSize* advances; - - if (coreTextRun.ctRun()) { - glyphs = CTRunGetGlyphsPtr(coreTextRun.ctRun()); - if (!glyphs) { - glyphsVector.grow(glyphCount); - CTRunGetGlyphs(coreTextRun.ctRun(), CFRangeMake(0, 0), glyphsVector.data()); - glyphs = glyphsVector.data(); - } + const ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; + unsigned glyphCount = complexTextRun.glyphCount(); + const SimpleFontData* fontData = complexTextRun.fontData(); - advances = CTRunGetAdvancesPtr(coreTextRun.ctRun()); - if (!advances) { - advancesVector.grow(glyphCount); - CTRunGetAdvances(coreTextRun.ctRun(), CFRangeMake(0, 0), advancesVector.data()); - advances = advancesVector.data(); - } - } else { - // Synthesize a run of missing glyphs. - glyphsVector.fill(0, glyphCount); - glyphs = glyphsVector.data(); - advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), glyphCount); - advances = advancesVector.data(); - } + const CGGlyph* glyphs = complexTextRun.glyphs(); + const CGSize* advances = complexTextRun.advances(); bool lastRun = r + 1 == runCount; - const UChar* cp = coreTextRun.characters(); + const UChar* cp = complexTextRun.characters(); CGFloat roundedSpaceWidth = roundCGFloat(fontData->spaceWidth()); bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances(); bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled(); for (unsigned i = 0; i < glyphCount; i++) { - CFIndex characterIndex = coreTextRun.indexAt(i); + CFIndex characterIndex = complexTextRun.indexAt(i); UChar ch = *(cp + characterIndex); bool lastGlyph = lastRun && i + 1 == glyphCount; UChar nextCh; if (lastGlyph) nextCh = ' '; else if (i + 1 < glyphCount) - nextCh = *(cp + coreTextRun.indexAt(i + 1)); + nextCh = *(cp + complexTextRun.indexAt(i + 1)); else - nextCh = *(m_coreTextRuns[r + 1].characters() + m_coreTextRuns[r + 1].indexAt(0)); + nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0)); bool treatAsSpace = Font::treatAsSpace(ch); CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i]; @@ -533,5 +442,3 @@ void CoreTextController::adjustGlyphsAndAdvances() } } // namespace WebCore - -#endif // USE(CORE_TEXT) diff --git a/WebCore/platform/graphics/mac/ComplexTextController.h b/WebCore/platform/graphics/mac/ComplexTextController.h new file mode 100644 index 0000000..7a915e2 --- /dev/null +++ b/WebCore/platform/graphics/mac/ComplexTextController.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 ComplexTextController_h +#define ComplexTextController_h + +#include "GlyphBuffer.h" +#include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RetainPtr.h> +#include <wtf/Vector.h> +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + +class Font; +class SimpleFontData; +class TextRun; + +class ComplexTextController { +public: + ComplexTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const SimpleFontData*>* fallbackFonts = 0); + + // Advance and emit glyphs up to the specified character. + void advance(unsigned to, GlyphBuffer* = 0); + + // Compute the character offset for a given x coordinate. + int offsetForPosition(int x, bool includePartialGlyphs); + + // Returns the width of everything we've consumed so far. + float runWidthSoFar() const { return m_runWidthSoFar; } + + float totalWidth() const { return m_totalWidth; } + + // Extra width to the left of the leftmost glyph. + float finalRoundingWidth() const { return m_finalRoundingWidth; } + +private: + class ComplexTextRun : public RefCounted<ComplexTextRun> { + public: +#if USE(CORE_TEXT) + static PassRefPtr<ComplexTextRun> create(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) + { + return adoptRef(new ComplexTextRun(ctRun, fontData, characters, stringLocation, stringLength)); + } +#elif USE(ATSUI) + static PassRefPtr<ComplexTextRun> create(ATSUTextLayout atsuTextLayout, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr, bool directionalOverride) + { + return adoptRef(new ComplexTextRun(atsuTextLayout, fontData, characters, stringLocation, stringLength, ltr, directionalOverride)); + } +#endif + static PassRefPtr<ComplexTextRun> create(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) + { + return adoptRef(new ComplexTextRun(fontData, characters, stringLocation, stringLength, ltr)); + } + + unsigned glyphCount() const { return m_glyphCount; } + const SimpleFontData* fontData() const { return m_fontData; } + const UChar* characters() const { return m_characters; } + unsigned stringLocation() const { return m_stringLocation; } + size_t stringLength() const { return m_stringLength; } + CFIndex indexAt(size_t i) const { return m_indices[i]; } + const CGGlyph* glyphs() const { return m_glyphs; } + const CGSize* advances() const { return m_advances; } + + private: +#if USE(CORE_TEXT) + ComplexTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength); +#elif USE(ATSUI) + ComplexTextRun(ATSUTextLayout, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr, bool directionalOverride); +#endif + ComplexTextRun(const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr); + +#if USE(ATSUI) +#ifdef BUILDING_ON_TIGER + typedef UInt32 URefCon; +#endif + static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector, ATSULineRef, URefCon, void*, ATSULayoutOperationCallbackStatus*); +#endif + +#if USE(CORE_TEXT) + RetainPtr<CTRunRef> m_CTRun; +#endif + unsigned m_glyphCount; + const SimpleFontData* m_fontData; + const UChar* m_characters; + unsigned m_stringLocation; + size_t m_stringLength; +#if USE(CORE_TEXT) + RetainPtr<CFMutableDataRef> m_indicesData; + const CFIndex* m_indices; +#elif USE(ATSUI) + Vector<CFIndex, 64> m_indices; +#endif + Vector<CGGlyph, 64> m_glyphsVector; + const CGGlyph* m_glyphs; + Vector<CGSize, 64> m_advancesVector; + const CGSize* m_advances; +#if USE(ATSUI) + bool m_ltr; + bool m_directionalOverride; +#endif + }; + + void collectComplexTextRuns(); + void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void adjustGlyphsAndAdvances(); + + const Font& m_font; + const TextRun& m_run; + bool m_mayUseNaturalWritingDirection; + + Vector<UChar, 256> m_smallCapsBuffer; + + Vector<RefPtr<ComplexTextRun>, 16> m_complexTextRuns; + Vector<CGSize, 256> m_adjustedAdvances; + Vector<CGGlyph, 256> m_adjustedGlyphs; + + unsigned m_currentCharacter; + int m_end; + + CGFloat m_totalWidth; + + float m_runWidthSoFar; + unsigned m_numGlyphsSoFar; + size_t m_currentRun; + unsigned m_glyphInCurrentRun; + unsigned m_characterInCurrentGlyph; + float m_finalRoundingWidth; + float m_padding; + float m_padPerSpace; + + HashSet<const SimpleFontData*>* m_fallbackFonts; + + unsigned m_lastRoundingGlyph; +}; + +} // namespace WebCore + +#endif // ComplexTextController_h diff --git a/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp b/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp new file mode 100644 index 0000000..78c588f --- /dev/null +++ b/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "ComplexTextController.h" + +#if USE(ATSUI) + +#include "CharacterNames.h" +#include "Font.h" +#include "ShapeArabic.h" + +#ifdef __LP64__ +// ATSUTextInserted() is SPI in 64-bit. +extern "C" { +OSStatus ATSUTextInserted(ATSUTextLayout iTextLayout, UniCharArrayOffset iInsertionLocation, UniCharCount iInsertionLength); +} +#endif + +using namespace WTF::Unicode; + +namespace WebCore { + +OSStatus ComplexTextController::ComplexTextRun::overrideLayoutOperation(ATSULayoutOperationSelector, ATSULineRef atsuLineRef, URefCon refCon, void*, ATSULayoutOperationCallbackStatus* callbackStatus) +{ + ComplexTextRun* complexTextRun = reinterpret_cast<ComplexTextRun*>(refCon); + OSStatus status; + ItemCount count; + ATSLayoutRecord *layoutRecords; + + status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(atsuLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, reinterpret_cast<void**>(&layoutRecords), &count); + if (status != noErr) { + *callbackStatus = kATSULayoutOperationCallbackStatusContinue; + return status; + } + + count--; + ItemCount j = 0; + CFIndex indexOffset = 0; + + if (complexTextRun->m_directionalOverride) { + j++; + count -= 2; + indexOffset = -1; + } + + complexTextRun->m_glyphCount = count; + complexTextRun->m_glyphsVector.reserveCapacity(count); + complexTextRun->m_advancesVector.reserveCapacity(count); + complexTextRun->m_indices.reserveCapacity(count); + + bool atBeginning = true; + CGFloat lastX = 0; + + for (ItemCount i = 0; i < count; ++i, ++j) { + if (layoutRecords[j].glyphID == kATSDeletedGlyphcode) { + complexTextRun->m_glyphCount--; + continue; + } + complexTextRun->m_glyphsVector.uncheckedAppend(layoutRecords[j].glyphID); + complexTextRun->m_indices.uncheckedAppend(layoutRecords[j].originalOffset / 2 + indexOffset); + CGFloat x = FixedToFloat(layoutRecords[j].realPos); + if (!atBeginning) + complexTextRun->m_advancesVector.uncheckedAppend(CGSizeMake(x - lastX, 0)); + lastX = x; + atBeginning = false; + } + + complexTextRun->m_advancesVector.uncheckedAppend(CGSizeMake(FixedToFloat(layoutRecords[j].realPos) - lastX, 0)); + + complexTextRun->m_glyphs = complexTextRun->m_glyphsVector.data(); + complexTextRun->m_advances = complexTextRun->m_advancesVector.data(); + + status = ATSUDirectReleaseLayoutDataArrayPtr(atsuLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, reinterpret_cast<void**>(&layoutRecords)); + *callbackStatus = kATSULayoutOperationCallbackStatusContinue; + return noErr; +} + +static inline bool isArabicLamWithAlefLigature(UChar c) +{ + return c >= 0xfef5 && c <= 0xfefc; +} + +static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength) +{ + unsigned shapingStart = 0; + while (shapingStart < totalLength) { + unsigned shapingEnd; + // We do not want to pass a Lam with Alef ligature followed by a space to the shaper, + // since we want to be able to identify this sequence as the result of shaping a Lam + // followed by an Alef and padding with a space. + bool foundLigatureSpace = false; + for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd) + foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' '; + shapingEnd++; + + UErrorCode shapingError = U_ZERO_ERROR; + unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError); + + if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) { + for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) { + if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ') + dest[++j] = zeroWidthSpace; + } + if (foundLigatureSpace) { + dest[shapingEnd] = ' '; + shapingEnd++; + } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) { + // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef, + // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR. + ASSERT(dest[shapingStart] == ' '); + dest[shapingStart] = zeroWidthSpace; + } + } else { + // Something went wrong. Abandon shaping and just copy the rest of the buffer. + LOG_ERROR("u_shapeArabic failed(%d)", shapingError); + shapingEnd = totalLength; + memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar)); + } + shapingStart = shapingEnd; + } +} + +ComplexTextController::ComplexTextRun::ComplexTextRun(ATSUTextLayout atsuTextLayout, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr, bool directionalOverride) + : m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) + , m_ltr(ltr) + , m_directionalOverride(directionalOverride) +{ + OSStatus status; + + status = ATSUSetTextLayoutRefCon(atsuTextLayout, reinterpret_cast<URefCon>(this)); + + ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers; + + Boolean rtl = !ltr; + + Vector<UChar, 256> substituteCharacters; + bool shouldCheckForMirroring = !ltr && !fontData->m_ATSUMirrors; + bool shouldCheckForArabic = !fontData->shapesArabic(); + bool shouldShapeArabic = false; + + bool mirrored = false; + for (size_t i = 0; i < stringLength; ++i) { + if (shouldCheckForMirroring) { + UChar mirroredChar = u_charMirror(characters[i]); + if (mirroredChar != characters[i]) { + if (!mirrored) { + mirrored = true; + substituteCharacters.grow(stringLength); + memcpy(substituteCharacters.data(), characters, stringLength * sizeof(UChar)); + ATSUTextMoved(atsuTextLayout, substituteCharacters.data()); + } + substituteCharacters[i] = mirroredChar; + } + } + if (shouldCheckForArabic && isArabicChar(characters[i])) { + shouldCheckForArabic = false; + shouldShapeArabic = true; + } + } + + if (shouldShapeArabic) { + Vector<UChar, 256> shapedArabic(stringLength); + shapeArabic(substituteCharacters.isEmpty() ? characters : substituteCharacters.data(), shapedArabic.data(), stringLength); + substituteCharacters.swap(shapedArabic); + ATSUTextMoved(atsuTextLayout, substituteCharacters.data()); + } + + if (directionalOverride) { + UChar override = ltr ? leftToRightOverride : rightToLeftOverride; + if (substituteCharacters.isEmpty()) { + substituteCharacters.grow(stringLength + 2); + substituteCharacters[0] = override; + memcpy(substituteCharacters.data() + 1, characters, stringLength * sizeof(UChar)); + substituteCharacters[stringLength + 1] = popDirectionalFormatting; + ATSUTextMoved(atsuTextLayout, substituteCharacters.data()); + } else { + substituteCharacters.prepend(override); + substituteCharacters.append(popDirectionalFormatting); + } + ATSUTextInserted(atsuTextLayout, 0, 2); + } + + ATSULayoutOperationOverrideSpecifier overrideSpecifier; + overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment; + overrideSpecifier.overrideUPP = overrideLayoutOperation; + + ATSUAttributeTag tags[] = { kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag }; + ByteCount sizes[] = { sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) }; + ATSUAttributeValuePtr values[] = { &lineLayoutOptions, &rtl, &overrideSpecifier }; + + status = ATSUSetLayoutControls(atsuTextLayout, 3, tags, sizes, values); + + ItemCount boundsCount; + status = ATSUGetGlyphBounds(atsuTextLayout, 0, 0, 0, m_stringLength, kATSUseFractionalOrigins, 0, 0, &boundsCount); + + status = ATSUDisposeTextLayout(atsuTextLayout); +} + +ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) + : m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) +{ + m_indices.reserveCapacity(stringLength); + unsigned r = 0; + while (r < stringLength) { + m_indices.uncheckedAppend(r); + if (U_IS_SURROGATE(characters[r])) { + ASSERT(r + 1 < stringLength); + ASSERT(U_IS_SURROGATE_LEAD(characters[r])); + ASSERT(U_IS_TRAIL(characters[r + 1])); + r += 2; + } else + r++; + } + m_glyphCount = m_indices.size(); + if (!ltr) { + for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) + std::swap(m_indices[r], m_indices[end]); + } + + m_glyphsVector.fill(0, m_glyphCount); + m_glyphs = m_glyphsVector.data(); + m_advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), m_glyphCount); + m_advances = m_advancesVector.data(); +} + +static bool fontHasMirroringInfo(ATSUFontID fontID) +{ + ByteCount propTableSize; + OSStatus status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize); + if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info + return true; + else if (status != kATSInvalidFontTableAccess) // anything other than a missing table is logged as an error + LOG_ERROR("ATSFontGetTable failed (%d)", static_cast<int>(status)); + + return false; +} + +static void disableLigatures(const SimpleFontData* fontData, TextRenderingMode textMode) +{ + // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are + // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example. + // See bugzilla 5166. + if (textMode == OptimizeLegibility || textMode == GeometricPrecision || fontData->platformData().allowsLigatures()) + return; + + ATSUFontFeatureType featureTypes[] = { kLigaturesType }; + ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector }; + OSStatus status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors); + if (status != noErr) + LOG_ERROR("ATSUSetFontFeatures failed (%d) -- ligatures remain enabled", static_cast<int>(status)); +} + +static void initializeATSUStyle(const SimpleFontData* fontData, TextRenderingMode textMode) +{ + if (fontData->m_ATSUStyleInitialized) + return; + + ATSUFontID fontID = fontData->platformData().m_atsuFontID; + if (!fontID) { + LOG_ERROR("unable to get ATSUFontID for %p", fontData->platformData().font()); + return; + } + + OSStatus status = ATSUCreateStyle(&fontData->m_ATSUStyle); + if (status != noErr) + LOG_ERROR("ATSUCreateStyle failed (%d)", static_cast<int>(status)); + + Fixed fontSize = FloatToFixed(fontData->platformData().m_size); + Fract kerningInhibitFactor = FloatToFract(1); + static CGAffineTransform verticalFlip = CGAffineTransformMakeScale(1, -1); + + ByteCount styleSizes[4] = { sizeof(fontSize), sizeof(fontID), sizeof(verticalFlip), sizeof(kerningInhibitFactor) }; + ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag }; + ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &verticalFlip, &kerningInhibitFactor }; + + bool allowKerning = textMode == OptimizeLegibility || textMode == GeometricPrecision; + status = ATSUSetAttributes(fontData->m_ATSUStyle, allowKerning ? 3 : 4, styleTags, styleSizes, styleValues); + if (status != noErr) + LOG_ERROR("ATSUSetAttributes failed (%d)", static_cast<int>(status)); + + fontData->m_ATSUMirrors = fontHasMirroringInfo(fontID); + + disableLigatures(fontData, textMode); + + fontData->m_ATSUStyleInitialized = true; +} + +void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +{ + if (!fontData) { + // Create a run of missing glyphs from the primary font. + m_complexTextRuns.append(ComplexTextRun::create(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr())); + return; + } + + if (m_fallbackFonts && fontData != m_font.primaryFont()) + m_fallbackFonts->add(fontData); + + initializeATSUStyle(fontData, m_font.fontDescription().textRenderingMode()); + + OSStatus status; + ATSUTextLayout atsuTextLayout; + UniCharCount runLength = length; + + status = ATSUCreateTextLayoutWithTextPtr(cp, 0, length, length, 1, &runLength, &fontData->m_ATSUStyle, &atsuTextLayout); + if (status != noErr) { + LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed with error %d", static_cast<int>(status)); + return; + } + m_complexTextRuns.append(ComplexTextRun::create(atsuTextLayout, fontData, cp, stringLocation, length, m_run.ltr(), m_run.directionalOverride())); +} + +} // namespace WebCore + +#endif // USE(ATSUI) diff --git a/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp b/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp new file mode 100644 index 0000000..c9daf84 --- /dev/null +++ b/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ComplexTextController.h" + +#if USE(CORE_TEXT) + +#include "Font.h" + +namespace WebCore { + +ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) + : m_CTRun(ctRun) + , m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) +{ + m_glyphCount = CTRunGetGlyphCount(m_CTRun.get()); + m_indices = CTRunGetStringIndicesPtr(m_CTRun.get()); + if (!m_indices) { + m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); + CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex)); + m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get())); + CTRunGetStringIndices(m_CTRun.get(), CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices)); + } + + m_glyphs = CTRunGetGlyphsPtr(m_CTRun.get()); + if (!m_glyphs) { + m_glyphsVector.grow(m_glyphCount); + CTRunGetGlyphs(m_CTRun.get(), CFRangeMake(0, 0), m_glyphsVector.data()); + m_glyphs = m_glyphsVector.data(); + } + + m_advances = CTRunGetAdvancesPtr(m_CTRun.get()); + if (!m_advances) { + m_advancesVector.grow(m_glyphCount); + CTRunGetAdvances(m_CTRun.get(), CFRangeMake(0, 0), m_advancesVector.data()); + m_advances = m_advancesVector.data(); + } + +} + +// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on +// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path. +ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) + : m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) +{ + Vector<CFIndex, 16> indices; + unsigned r = 0; + while (r < stringLength) { + indices.append(r); + if (U_IS_SURROGATE(characters[r])) { + ASSERT(r + 1 < stringLength); + ASSERT(U_IS_SURROGATE_LEAD(characters[r])); + ASSERT(U_IS_TRAIL(characters[r + 1])); + r += 2; + } else + r++; + } + m_glyphCount = indices.size(); + if (!ltr) { + for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) + std::swap(indices[r], indices[end]); + } + m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); + CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex)); + m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get())); + + // Synthesize a run of missing glyphs. + m_glyphsVector.fill(0, m_glyphCount); + m_glyphs = m_glyphsVector.data(); + m_advancesVector.fill(CGSizeMake(m_fontData->widthForGlyph(0), 0), m_glyphCount); + m_advances = m_advancesVector.data(); +} + +void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +{ + if (!fontData) { + // Create a run of missing glyphs from the primary font. + m_complexTextRuns.append(ComplexTextRun::create(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr())); + return; + } + + if (m_fallbackFonts && fontData != m_font.primaryFont()) + m_fallbackFonts->add(fontData); + + RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull)); + + RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes(m_font.fontDescription().textRenderingMode()))); + + RetainPtr<CTTypesetterRef> typesetter; + + if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) { + static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; + static const void* ltrOptionValues[] = { kCFBooleanFalse }; + static const void* rtlOptionValues[] = { kCFBooleanTrue }; + static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); + } else + typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get())); + + RetainPtr<CTLineRef> line(AdoptCF, CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0))); + + CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); + + CFIndex runCount = CFArrayGetCount(runArray); + + for (CFIndex r = 0; r < runCount; r++) { + CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); + ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); + m_complexTextRuns.append(ComplexTextRun::create(ctRun, fontData, cp, stringLocation, length)); + } +} + +} // namespace WebCore + +#endif // USE(CORE_TEXT) diff --git a/WebCore/platform/graphics/mac/CoreTextController.h b/WebCore/platform/graphics/mac/CoreTextController.h deleted file mode 100644 index 4dd6f93..0000000 --- a/WebCore/platform/graphics/mac/CoreTextController.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CoreTextController_h -#define CoreTextController_h - -#if USE(CORE_TEXT) - -#include "Font.h" -#include "GlyphBuffer.h" -#include <wtf/RetainPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class CoreTextController { -public: - CoreTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const SimpleFontData*>* fallbackFonts = 0); - - // Advance and emit glyphs up to the specified character. - void advance(unsigned to, GlyphBuffer* = 0); - - // Compute the character offset for a given x coordinate. - int offsetForPosition(int x, bool includePartialGlyphs); - - // Returns the width of everything we've consumed so far. - float runWidthSoFar() const { return m_runWidthSoFar; } - - float totalWidth() const { return m_totalWidth; } - - // Extra width to the left of the leftmost glyph. - float finalRoundingWidth() const { return m_finalRoundingWidth; } - -private: - class CoreTextRun { - public: - CoreTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength); - CoreTextRun(const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr); - - CTRunRef ctRun() const { return m_CTRun.get(); } - unsigned glyphCount() const { return m_glyphCount; } - const SimpleFontData* fontData() const { return m_fontData; } - const UChar* characters() const { return m_characters; } - unsigned stringLocation() const { return m_stringLocation; } - size_t stringLength() const { return m_stringLength; } - CFIndex indexAt(size_t i) const { return m_indices[i]; } - - private: - RetainPtr<CTRunRef> m_CTRun; - unsigned m_glyphCount; - const SimpleFontData* m_fontData; - const UChar* m_characters; - unsigned m_stringLocation; - size_t m_stringLength; - const CFIndex* m_indices; - // Used only if CTRunGet*Ptr fails or if this is a missing glyphs run. - RetainPtr<CFMutableDataRef> m_indicesData; - }; - - void collectCoreTextRuns(); - void collectCoreTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); - void adjustGlyphsAndAdvances(); - - const Font& m_font; - const TextRun& m_run; - bool m_mayUseNaturalWritingDirection; - - Vector<UChar, 256> m_smallCapsBuffer; - - Vector<CoreTextRun, 16> m_coreTextRuns; - Vector<CGSize, 256> m_adjustedAdvances; - Vector<CGGlyph, 256> m_adjustedGlyphs; - - unsigned m_currentCharacter; - int m_end; - - CGFloat m_totalWidth; - - float m_runWidthSoFar; - unsigned m_numGlyphsSoFar; - size_t m_currentRun; - unsigned m_glyphInCurrentRun; - float m_finalRoundingWidth; - float m_padding; - float m_padPerSpace; - - HashSet<const SimpleFontData*>* m_fallbackFonts; - - unsigned m_lastRoundingGlyph; -}; - -} // namespace WebCore -#endif // USE(CORE_TEXT) -#endif // CoreTextController_h diff --git a/WebCore/platform/graphics/mac/FontMacCoreText.cpp b/WebCore/platform/graphics/mac/FontComplexTextMac.cpp index 9dffc7a..0db2601 100644 --- a/WebCore/platform/graphics/mac/FontMacCoreText.cpp +++ b/WebCore/platform/graphics/mac/FontComplexTextMac.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,25 +10,22 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "Font.h" -#if USE(CORE_TEXT) - -#include "CoreTextController.h" +#include "ComplexTextController.h" #include "FontFallbackList.h" #include "GlyphBuffer.h" #include "GraphicsContext.h" @@ -41,7 +38,7 @@ namespace WebCore { FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const { - CoreTextController controller(this, run); + ComplexTextController controller(this, run); controller.advance(from); float beforeWidth = controller.runWidthSoFar(); controller.advance(to); @@ -63,7 +60,7 @@ void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const F GlyphBuffer glyphBuffer; float startX = point.x(); - CoreTextController controller(this, run); + ComplexTextController controller(this, run); controller.advance(from); float beforeWidth = controller.runWidthSoFar(); controller.advance(to, &glyphBuffer); @@ -88,15 +85,14 @@ void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const F float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts) const { - CoreTextController controller(this, run, true, fallbackFonts); + ComplexTextController controller(this, run, true, fallbackFonts); return controller.totalWidth(); } int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const { - CoreTextController controller(this, run); + ComplexTextController controller(this, run); return controller.offsetForPosition(x, includePartialGlyphs); } -} -#endif // USE(CORE_TEXT) +} // namespace WebCore diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp index 5e72101..256b5a4 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -24,6 +24,7 @@ #include <ApplicationServices/ApplicationServices.h> #include "SharedBuffer.h" #include "FontPlatformData.h" +#include "OpenTypeSanitizer.h" namespace WebCore { @@ -43,6 +44,14 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); +#if ENABLE(OPENTYPE_SANITIZER) + OpenTypeSanitizer sanitizer(buffer); + RefPtr<SharedBuffer> transcodeBuffer = sanitizer.sanitize(); + if (!transcodeBuffer) + return 0; // validation failed. + buffer = transcodeBuffer.get(); +#endif + ATSFontContainerRef containerRef = 0; ATSFontRef fontRef = 0; diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm index b2b9a5c..bb9561e 100644 --- a/WebCore/platform/graphics/mac/FontMac.mm +++ b/WebCore/platform/graphics/mac/FontMac.mm @@ -115,6 +115,7 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons IntSize shadowSize; int shadowBlur; Color shadowColor; + ColorSpace fillColorSpace = context->fillColorSpace(); context->getShadow(shadowSize, shadowBlur, shadowColor); bool hasSimpleShadow = context->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; @@ -123,14 +124,14 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons context->clearShadow(); Color fillColor = context->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); - context->setFillColor(shadowFillColor); + context->setFillColor(shadowFillColor, fillColorSpace); CGContextSetTextPosition(cgContext, point.x() + shadowSize.width(), point.y() + shadowSize.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + shadowSize.width() + font->syntheticBoldOffset(), point.y() + shadowSize.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } - context->setFillColor(fillColor); + context->setFillColor(fillColor, fillColorSpace); } CGContextSetTextPosition(cgContext, point.x(), point.y()); @@ -141,7 +142,7 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons } if (hasSimpleShadow) - context->setShadow(shadowSize, shadowBlur, shadowColor); + context->setShadow(shadowSize, shadowBlur, shadowColor, fillColorSpace); if (originalShouldUseFontSmoothing != newShouldUseFontSmoothing) CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing); diff --git a/WebCore/platform/graphics/mac/FontMacATSUI.mm b/WebCore/platform/graphics/mac/FontMacATSUI.mm deleted file mode 100644 index 409bda4..0000000 --- a/WebCore/platform/graphics/mac/FontMacATSUI.mm +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#import "config.h" -#import "Font.h" - -#if USE(ATSUI) - -#import "CharacterNames.h" -#import "GraphicsContext.h" -#import "Logging.h" -#import "ShapeArabic.h" -#import "SimpleFontData.h" -#import <AppKit/NSGraphicsContext.h> -#import <wtf/OwnArrayPtr.h> - -#define SYNTHETIC_OBLIQUE_ANGLE 14 - -#ifdef __LP64__ -#define URefCon void* -#else -#define URefCon UInt32 -#endif - -using namespace std; - -namespace WebCore { - -struct ATSULayoutParameters : Noncopyable -{ - ATSULayoutParameters(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts = 0) - : m_run(run) - , m_font(0) - , m_hasSyntheticBold(false) - , m_syntheticBoldPass(false) - , m_padPerSpace(0) - , m_fallbackFonts(fallbackFonts) - { - } - - ~ATSULayoutParameters() - { - ATSUDisposeTextLayout(m_layout); - } - - void initialize(const Font*, const GraphicsContext* = 0); - - const TextRun& m_run; - - const Font* m_font; - - ATSUTextLayout m_layout; - OwnArrayPtr<const SimpleFontData*> m_fonts; - - OwnArrayPtr<UChar> m_charBuffer; - bool m_hasSyntheticBold; - bool m_syntheticBoldPass; - float m_padPerSpace; - HashSet<const SimpleFontData*>* m_fallbackFonts; -}; - -static TextRun copyRunForDirectionalOverrideIfNecessary(const TextRun& run, OwnArrayPtr<UChar>& charactersWithOverride) -{ - if (!run.directionalOverride()) - return run; - - charactersWithOverride.set(new UChar[run.length() + 2]); - charactersWithOverride[0] = run.rtl() ? rightToLeftOverride : leftToRightOverride; - memcpy(&charactersWithOverride[1], run.data(0), sizeof(UChar) * run.length()); - charactersWithOverride[run.length() + 1] = popDirectionalFormatting; - - TextRun result = run; - result.setText(charactersWithOverride.get(), run.length() + 2); - return result; -} - -static bool fontHasMirroringInfo(ATSUFontID fontID) -{ - ByteCount propTableSize; - OSStatus status = ATSFontGetTable(fontID, 'prop', 0, 0, 0, &propTableSize); - if (status == noErr) // naively assume that if a 'prop' table exists then it contains mirroring info - return true; - else if (status != kATSInvalidFontTableAccess) // anything other than a missing table is logged as an error - LOG_ERROR("ATSFontGetTable failed (%d)", status); - - return false; -} - -static void disableLigatures(const SimpleFontData* fontData, TextRenderingMode textMode) -{ - // Don't be too aggressive: if the font doesn't contain 'a', then assume that any ligatures it contains are - // in characters that always go through ATSUI, and therefore allow them. Geeza Pro is an example. - // See bugzilla 5166. - if (textMode == OptimizeLegibility || textMode == GeometricPrecision || fontData->platformData().allowsLigatures()) - return; - - ATSUFontFeatureType featureTypes[] = { kLigaturesType }; - ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector }; - OSStatus status = ATSUSetFontFeatures(fontData->m_ATSUStyle, 1, featureTypes, featureSelectors); - if (status != noErr) - LOG_ERROR("ATSUSetFontFeatures failed (%d) -- ligatures remain enabled", status); -} - -static void initializeATSUStyle(const SimpleFontData* fontData, TextRenderingMode textMode) -{ - if (fontData->m_ATSUStyleInitialized) - return; - - ATSUFontID fontID = fontData->platformData().m_atsuFontID; - if (!fontID) { - LOG_ERROR("unable to get ATSUFontID for %@", fontData->platformData().font()); - return; - } - - OSStatus status = ATSUCreateStyle(&fontData->m_ATSUStyle); - if (status != noErr) - // Who knows how many ATSU functions will crash when passed a NULL style... - LOG_ERROR("ATSUCreateStyle failed (%d)", status); - - CGAffineTransform transform = CGAffineTransformMakeScale(1, -1); - if (fontData->platformData().m_syntheticOblique) - transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); - Fixed fontSize = FloatToFixed(fontData->platformData().m_size); - ByteCount styleSizes[4] = { sizeof(Fixed), sizeof(ATSUFontID), sizeof(CGAffineTransform), sizeof(Fract) }; - - bool allowKerning = textMode == OptimizeLegibility || textMode == GeometricPrecision; - if (!allowKerning) { - // Turn off automatic kerning until it is supported in the CG code path (bug 6136) - Fract kerningInhibitFactor = FloatToFract(1.0); - ATSUAttributeTag styleTags[4] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag, kATSUKerningInhibitFactorTag }; - ATSUAttributeValuePtr styleValues[4] = { &fontSize, &fontID, &transform, &kerningInhibitFactor }; - status = ATSUSetAttributes(fontData->m_ATSUStyle, 4, styleTags, styleSizes, styleValues); - if (status != noErr) - LOG_ERROR("ATSUSetAttributes failed (%d)", status); - } else { - ATSUAttributeTag styleTags[3] = { kATSUSizeTag, kATSUFontTag, kATSUFontMatrixTag }; - ATSUAttributeValuePtr styleValues[3] = { &fontSize, &fontID, &transform, }; - status = ATSUSetAttributes(fontData->m_ATSUStyle, 3, styleTags, styleSizes, styleValues); - if (status != noErr) - LOG_ERROR("ATSUSetAttributes failed (%d)", status); - } - - fontData->m_ATSUMirrors = fontHasMirroringInfo(fontID); - - // Turn off ligatures such as 'fi' to match the CG code path's behavior, until bug 6135 is fixed. - disableLigatures(fontData, textMode); - - fontData->m_ATSUStyleInitialized = true; -} - -static OSStatus overrideLayoutOperation(ATSULayoutOperationSelector, ATSULineRef iLineRef, URefCon iRefCon, void*, ATSULayoutOperationCallbackStatus* oCallbackStatus) -{ - ATSULayoutParameters* params = reinterpret_cast<ATSULayoutParameters*>(iRefCon); - OSStatus status; - ItemCount count; - ATSLayoutRecord *layoutRecords; - - if (params->m_run.applyWordRounding()) { - status = ATSUDirectGetLayoutDataArrayPtrFromLineRef(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, true, (void **)&layoutRecords, &count); - if (status != noErr) { - *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue; - return status; - } - - Fixed lastNativePos = 0; - float lastAdjustedPos = 0; - const UChar* characters = params->m_charBuffer ? params->m_charBuffer.get() : params->m_run.characters(); - const SimpleFontData** renderers = params->m_fonts.get(); - const SimpleFontData* renderer; - const SimpleFontData* lastRenderer = 0; - ByteCount offset = layoutRecords[0].originalOffset; - UChar nextCh = *(UChar *)(((char *)characters)+offset); - bool shouldRound = false; - bool syntheticBoldPass = params->m_syntheticBoldPass; - Fixed syntheticBoldOffset = 0; - bool hasExtraSpacing = (params->m_font->letterSpacing() || params->m_font->wordSpacing() || params->m_run.padding()) && !params->m_run.spacingDisabled(); - float padding = params->m_run.padding(); - // In the CoreGraphics code path, the rounding hack is applied in logical order. - // Here it is applied in visual left-to-right order, which may be better. - ItemCount lastRoundingChar = 0; - ItemCount i; - for (i = 1; i < count; i++) { - bool isLastChar = i == count - 1; - renderer = renderers[offset / 2]; - float width; - if (nextCh == zeroWidthSpace || Font::treatAsZeroWidthSpace(nextCh) && !Font::treatAsSpace(nextCh)) { - width = 0; - layoutRecords[i-1].glyphID = renderer->spaceGlyph(); - } else { - width = FixedToFloat(layoutRecords[i].realPos - lastNativePos); - if (renderer != lastRenderer && width) { - lastRenderer = renderer; - // The CoreGraphics interpretation of NSFontAntialiasedIntegerAdvancementsRenderingMode seems - // to be "round each glyph's width to the nearest integer". This is not the same as ATSUI - // does in any of its device-metrics modes. - shouldRound = renderer->platformData().roundsGlyphAdvances(); - if (syntheticBoldPass) - syntheticBoldOffset = FloatToFixed(renderer->syntheticBoldOffset()); - if (params->m_fallbackFonts && renderer != params->m_font->primaryFont()) - params->m_fallbackFonts->add(renderer); - } - if (shouldRound) - width = roundf(width); - width += renderer->syntheticBoldOffset(); - if (renderer->pitch() == FixedPitch ? width == renderer->spaceWidth() : (layoutRecords[i-1].flags & kATSGlyphInfoIsWhiteSpace)) - width = renderer->adjustedSpaceWidth(); - } - lastNativePos = layoutRecords[i].realPos; - - if (hasExtraSpacing) { - if (width && params->m_font->letterSpacing()) - width +=params->m_font->letterSpacing(); - if (Font::treatAsSpace(nextCh)) { - if (params->m_run.padding()) { - if (padding < params->m_padPerSpace) { - width += padding; - padding = 0; - } else { - width += params->m_padPerSpace; - padding -= params->m_padPerSpace; - } - } - if (offset != 0 && !Font::treatAsSpace(*((UChar *)(((char *)characters)+offset) - 1)) && params->m_font->wordSpacing()) - width += params->m_font->wordSpacing(); - } - } - - UChar ch = nextCh; - offset = layoutRecords[i].originalOffset; - // Use space for nextCh at the end of the loop so that we get inside the rounding hack code. - // We won't actually round unless the other conditions are satisfied. - nextCh = isLastChar ? ' ' : *(UChar *)(((char *)characters)+offset); - - if (Font::isRoundingHackCharacter(ch)) - width = ceilf(width); - lastAdjustedPos = lastAdjustedPos + width; - if (Font::isRoundingHackCharacter(nextCh) && (!isLastChar || params->m_run.applyRunRounding())){ - if (params->m_run.ltr()) - lastAdjustedPos = ceilf(lastAdjustedPos); - else { - float roundingWidth = ceilf(lastAdjustedPos) - lastAdjustedPos; - Fixed rw = FloatToFixed(roundingWidth); - ItemCount j; - for (j = lastRoundingChar; j < i; j++) - layoutRecords[j].realPos += rw; - lastRoundingChar = i; - lastAdjustedPos += roundingWidth; - } - } - if (syntheticBoldPass) { - if (syntheticBoldOffset) - layoutRecords[i-1].realPos += syntheticBoldOffset; - else - layoutRecords[i-1].glyphID = renderer->spaceGlyph(); - } - layoutRecords[i].realPos = FloatToFixed(lastAdjustedPos); - } - - status = ATSUDirectReleaseLayoutDataArrayPtr(iLineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords); - } - *oCallbackStatus = kATSULayoutOperationCallbackStatusHandled; - return noErr; -} - -static inline bool isArabicLamWithAlefLigature(UChar c) -{ - return c >= 0xfef5 && c <= 0xfefc; -} - -static void shapeArabic(const UChar* source, UChar* dest, unsigned totalLength, unsigned shapingStart) -{ - while (shapingStart < totalLength) { - unsigned shapingEnd; - // We do not want to pass a Lam with Alef ligature followed by a space to the shaper, - // since we want to be able to identify this sequence as the result of shaping a Lam - // followed by an Alef and padding with a space. - bool foundLigatureSpace = false; - for (shapingEnd = shapingStart; !foundLigatureSpace && shapingEnd < totalLength - 1; ++shapingEnd) - foundLigatureSpace = isArabicLamWithAlefLigature(source[shapingEnd]) && source[shapingEnd + 1] == ' '; - shapingEnd++; - - UErrorCode shapingError = U_ZERO_ERROR; - unsigned charsWritten = shapeArabic(source + shapingStart, shapingEnd - shapingStart, dest + shapingStart, shapingEnd - shapingStart, U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR, &shapingError); - - if (U_SUCCESS(shapingError) && charsWritten == shapingEnd - shapingStart) { - for (unsigned j = shapingStart; j < shapingEnd - 1; ++j) { - if (isArabicLamWithAlefLigature(dest[j]) && dest[j + 1] == ' ') - dest[++j] = zeroWidthSpace; - } - if (foundLigatureSpace) { - dest[shapingEnd] = ' '; - shapingEnd++; - } else if (isArabicLamWithAlefLigature(dest[shapingEnd - 1])) { - // u_shapeArabic quirk: if the last two characters in the source string are a Lam and an Alef, - // the space is put at the beginning of the string, despite U_SHAPE_LENGTH_FIXED_SPACES_NEAR. - ASSERT(dest[shapingStart] == ' '); - dest[shapingStart] = zeroWidthSpace; - } - } else { - // Something went wrong. Abandon shaping and just copy the rest of the buffer. - LOG_ERROR("u_shapeArabic failed(%d)", shapingError); - shapingEnd = totalLength; - memcpy(dest + shapingStart, source + shapingStart, (shapingEnd - shapingStart) * sizeof(UChar)); - } - shapingStart = shapingEnd; - } -} - -void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext) -{ - m_font = font; - - const SimpleFontData* fontData = font->primaryFont(); - m_fonts.set(new const SimpleFontData*[m_run.length()]); - if (font->isSmallCaps()) - m_charBuffer.set(new UChar[m_run.length()]); - - ATSUTextLayout layout; - OSStatus status; - ATSULayoutOperationOverrideSpecifier overrideSpecifier; - - initializeATSUStyle(fontData, m_font->fontDescription().textRenderingMode()); - - // FIXME: This is currently missing the following required features that the CoreGraphics code path has: - // - \n, \t, and nonbreaking space render as a space. - - UniCharCount runLength = m_run.length(); - - if (m_charBuffer) - memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar)); - - status = ATSUCreateTextLayoutWithTextPtr( - (m_charBuffer ? m_charBuffer.get() : m_run.characters()), - 0, // offset - runLength, // length - runLength, // total length - 1, // styleRunCount - &runLength, // length of style run - &fontData->m_ATSUStyle, - &layout); - if (status != noErr) - LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status); - m_layout = layout; - ATSUSetTextLayoutRefCon(m_layout, (URefCon)this); - - // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext - // measuring text runs with floatWidthForComplexText is one example. - // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0) - // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified! So we're in a bind. - // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context. Nothing we can do about it (yet). - CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - - ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers; - Boolean rtl = m_run.rtl(); - overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment; - overrideSpecifier.overrideUPP = overrideLayoutOperation; - ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag }; - ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) }; - ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier }; - - status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values); - if (status != noErr) - LOG_ERROR("ATSUSetLayoutControls failed(%d)", status); - - status = ATSUSetTransientFontMatching(layout, YES); - if (status != noErr) - LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status); - - m_hasSyntheticBold = false; - ATSUFontID ATSUSubstituteFont; - UniCharArrayOffset substituteOffset = 0; - UniCharCount substituteLength; - UniCharArrayOffset lastOffset; - const SimpleFontData* substituteFontData = 0; - - while (substituteOffset < runLength) { - // FIXME: Using ATSUMatchFontsToText() here results in several problems: the CSS font family list is not necessarily followed for the 2nd - // and onwards unmatched characters; segmented fonts do not work correctly; behavior does not match the simple text and Uniscribe code - // paths. Change this function to use Font::glyphDataForCharacter() for each character instead. - lastOffset = substituteOffset; - status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength); - if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) { - const FontData* fallbackFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength); - substituteFontData = fallbackFontData ? fallbackFontData->fontDataForCharacter(m_run[0]) : 0; - if (substituteFontData) { - initializeATSUStyle(substituteFontData, m_font->fontDescription().textRenderingMode()); - if (substituteFontData->m_ATSUStyle) - ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength); - } else - substituteFontData = fontData; - } else { - substituteOffset = runLength; - substituteLength = 0; - } - - bool shapedArabic = false; - bool isSmallCap = false; - UniCharArrayOffset firstSmallCap = 0; - const SimpleFontData *r = fontData; - UniCharArrayOffset i; - for (i = lastOffset; ; i++) { - if (i == substituteOffset || i == substituteOffset + substituteLength) { - if (isSmallCap) { - isSmallCap = false; - initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription()), m_font->fontDescription().textRenderingMode()); - ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap); - } - if (i == substituteOffset && substituteLength > 0) - r = substituteFontData; - else - break; - } - if (!shapedArabic && WTF::Unicode::isArabicChar(m_run[i]) && !r->shapesArabic()) { - shapedArabic = true; - if (!m_charBuffer) { - m_charBuffer.set(new UChar[runLength]); - memcpy(m_charBuffer.get(), m_run.characters(), i * sizeof(UChar)); - ATSUTextMoved(layout, m_charBuffer.get()); - } - shapeArabic(m_run.characters(), m_charBuffer.get(), runLength, i); - } - if (m_run.rtl() && !r->m_ATSUMirrors) { - UChar mirroredChar = u_charMirror(m_run[i]); - if (mirroredChar != m_run[i]) { - if (!m_charBuffer) { - m_charBuffer.set(new UChar[runLength]); - memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar)); - ATSUTextMoved(layout, m_charBuffer.get()); - } - m_charBuffer[i] = mirroredChar; - } - } - if (m_font->isSmallCaps()) { - const SimpleFontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription()); - UChar c = m_charBuffer[i]; - UChar newC; - if (U_GET_GC_MASK(c) & U_GC_M_MASK) - m_fonts[i] = isSmallCap ? smallCapsData : r; - else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) { - m_charBuffer[i] = newC; - if (!isSmallCap) { - isSmallCap = true; - firstSmallCap = i; - } - m_fonts[i] = smallCapsData; - } else { - if (isSmallCap) { - isSmallCap = false; - initializeATSUStyle(smallCapsData, m_font->fontDescription().textRenderingMode()); - ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap); - } - m_fonts[i] = r; - } - } else - m_fonts[i] = r; - if (m_fonts[i]->syntheticBoldOffset()) - m_hasSyntheticBold = true; - } - substituteOffset += substituteLength; - } - if (m_run.padding()) { - float numSpaces = 0; - unsigned k; - for (k = 0; k < runLength; k++) - if (Font::treatAsSpace(m_run[k])) - numSpaces++; - - if (numSpaces == 0) - m_padPerSpace = 0; - else - m_padPerSpace = ceilf(m_run.padding() / numSpaces); - } else - m_padPerSpace = 0; -} - -FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const -{ - OwnArrayPtr<UChar> charactersWithOverride; - TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); - if (run.directionalOverride()) { - from++; - to++; - } - - ATSULayoutParameters params(adjustedRun); - params.initialize(this); - - ATSTrapezoid firstGlyphBounds; - ItemCount actualNumBounds; - - OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); - if (status != noErr || actualNumBounds != 1) { - static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; - firstGlyphBounds = zeroTrapezoid; - } - - float beforeWidth = min(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x)); - float afterWidth = max(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x)); - - FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); - - return rect; -} - -void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const -{ - OSStatus status; - - int drawPortionLength = to - from; - OwnArrayPtr<UChar> charactersWithOverride; - TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); - if (run.directionalOverride()) - from++; - - ATSULayoutParameters params(adjustedRun); - params.initialize(this, graphicsContext); - - // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0). - CGContextRef context = graphicsContext->platformContext(); - CGContextTranslateCTM(context, point.x(), point.y()); - - IntSize shadowSize; - int shadowBlur; - Color shadowColor; - graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); - - bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; - if (hasSimpleShadow) { - // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. - graphicsContext->clearShadow(); - Color fillColor = graphicsContext->fillColor(); - Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); - graphicsContext->setFillColor(shadowFillColor); - CGContextTranslateCTM(context, shadowSize.width(), shadowSize.height()); - status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); - if (status == noErr && params.m_hasSyntheticBold) { - // Force relayout for the bold pass - ATSUClearLayoutCache(params.m_layout, 0); - params.m_syntheticBoldPass = true; - status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); - // Force relayout for the next pass - ATSUClearLayoutCache(params.m_layout, 0); - params.m_syntheticBoldPass = false; - } - CGContextTranslateCTM(context, -shadowSize.width(), -shadowSize.height()); - graphicsContext->setFillColor(fillColor); - } - - status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); - if (status == noErr && params.m_hasSyntheticBold) { - // Force relayout for the bold pass - ATSUClearLayoutCache(params.m_layout, 0); - params.m_syntheticBoldPass = true; - status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); - } - CGContextTranslateCTM(context, -point.x(), -point.y()); - - if (status != noErr) - // Nothing to do but report the error (dev build only). - LOG_ERROR("ATSUDrawText() failed(%d)", status); - - if (hasSimpleShadow) - graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor); -} - -float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts) const -{ - if (run.length() == 0) - return 0; - - ATSULayoutParameters params(run, fallbackFonts); - params.initialize(this); - - OSStatus status; - - ATSTrapezoid firstGlyphBounds; - ItemCount actualNumBounds; - status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); - if (status != noErr) - LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status); - if (actualNumBounds != 1) - LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds); - - return max(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) - - min(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x)); -} - -int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool /*includePartialGlyphs*/) const -{ - OwnArrayPtr<UChar> charactersWithOverride; - TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); - - ATSULayoutParameters params(adjustedRun); - params.initialize(this); - - UniCharArrayOffset primaryOffset = 0; - - // FIXME: No idea how to avoid including partial glyphs. - // Not even sure if that's the behavior this yields now. - Boolean isLeading; - UniCharArrayOffset secondaryOffset = 0; - OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset); - unsigned offset; - if (status == noErr) { - offset = (unsigned)primaryOffset; - if (run.directionalOverride() && offset > 0) - offset--; - } else - // Failed to find offset! Return 0 offset. - offset = 0; - - return offset; -} - -} -#endif // USE(ATSUI) diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp index 47617d8..41f63a9 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.cpp @@ -30,18 +30,18 @@ #include "GraphicsContext3D.h" #include "CachedImage.h" -#include "CanvasActiveInfo.h" -#include "CanvasArray.h" -#include "CanvasBuffer.h" -#include "CanvasFramebuffer.h" -#include "CanvasFloatArray.h" -#include "CanvasIntArray.h" +#include "WebGLActiveInfo.h" +#include "WebGLArray.h" +#include "WebGLBuffer.h" +#include "WebGLFramebuffer.h" +#include "WebGLFloatArray.h" +#include "WebGLIntArray.h" #include "CanvasObject.h" -#include "CanvasProgram.h" -#include "CanvasRenderbuffer.h" -#include "CanvasShader.h" -#include "CanvasTexture.h" -#include "CanvasUnsignedByteArray.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLShader.h" +#include "WebGLTexture.h" +#include "WebGLUnsignedByteArray.h" #include "CString.h" #include "HTMLCanvasElement.h" #include "HTMLImageElement.h" @@ -175,21 +175,12 @@ GraphicsContext3D::~GraphicsContext3D() } } -void GraphicsContext3D::checkError() const -{ - // FIXME: This needs to only be done in the debug context. It will probably throw an exception - // on error and print the error message to the debug console - GLenum error = ::glGetError(); - if (error != GL_NO_ERROR) - notImplemented(); -} - void GraphicsContext3D::makeContextCurrent() { CGLSetCurrentContext(m_contextObj); } -void GraphicsContext3D::beginPaint(CanvasRenderingContext3D* context) +void GraphicsContext3D::beginPaint(WebGLRenderingContext* context) { UNUSED_PARAM(context); } @@ -246,43 +237,42 @@ void GraphicsContext3D::activeTexture(unsigned long texture) ::glActiveTexture(texture); } -void GraphicsContext3D::attachShader(CanvasProgram* program, CanvasShader* shader) +void GraphicsContext3D::attachShader(WebGLProgram* program, WebGLShader* shader) { - if (!program || !shader) - return; + ASSERT(program); + ASSERT(shader); ensureContext(m_contextObj); ::glAttachShader((GLuint) program->object(), (GLuint) shader->object()); } -void GraphicsContext3D::bindAttribLocation(CanvasProgram* program, unsigned long index, const String& name) +void GraphicsContext3D::bindAttribLocation(WebGLProgram* program, unsigned long index, const String& name) { - if (!program) - return; + ASSERT(program); ensureContext(m_contextObj); ::glBindAttribLocation((GLuint) program->object(), index, name.utf8().data()); } -void GraphicsContext3D::bindBuffer(unsigned long target, CanvasBuffer* buffer) +void GraphicsContext3D::bindBuffer(unsigned long target, WebGLBuffer* buffer) { ensureContext(m_contextObj); ::glBindBuffer(target, buffer ? (GLuint) buffer->object() : 0); } -void GraphicsContext3D::bindFramebuffer(unsigned long target, CanvasFramebuffer* buffer) +void GraphicsContext3D::bindFramebuffer(unsigned long target, WebGLFramebuffer* buffer) { ensureContext(m_contextObj); ::glBindFramebufferEXT(target, buffer ? (GLuint) buffer->object() : m_fbo); } -void GraphicsContext3D::bindRenderbuffer(unsigned long target, CanvasRenderbuffer* renderbuffer) +void GraphicsContext3D::bindRenderbuffer(unsigned long target, WebGLRenderbuffer* renderbuffer) { ensureContext(m_contextObj); - ::glBindBuffer(target, renderbuffer ? (GLuint) renderbuffer->object() : 0); + ::glBindRenderbufferEXT(target, renderbuffer ? (GLuint) renderbuffer->object() : 0); } -void GraphicsContext3D::bindTexture(unsigned long target, CanvasTexture* texture) +void GraphicsContext3D::bindTexture(unsigned long target, WebGLTexture* texture) { ensureContext(m_contextObj); ::glBindTexture(target, texture ? (GLuint) texture->object() : 0); @@ -324,22 +314,22 @@ void GraphicsContext3D::bufferData(unsigned long target, int size, unsigned long ensureContext(m_contextObj); ::glBufferData(target, size, 0, usage); } -void GraphicsContext3D::bufferData(unsigned long target, CanvasArray* array, unsigned long usage) +void GraphicsContext3D::bufferData(unsigned long target, WebGLArray* array, unsigned long usage) { if (!array || !array->length()) return; ensureContext(m_contextObj); - ::glBufferData(target, array->sizeInBytes(), array->baseAddress(), usage); + ::glBufferData(target, array->byteLength(), array->baseAddress(), usage); } -void GraphicsContext3D::bufferSubData(unsigned long target, long offset, CanvasArray* array) +void GraphicsContext3D::bufferSubData(unsigned long target, long offset, WebGLArray* array) { if (!array || !array->length()) return; ensureContext(m_contextObj); - ::glBufferSubData(target, offset, array->sizeInBytes(), array->baseAddress()); + ::glBufferSubData(target, offset, array->byteLength(), array->baseAddress()); } unsigned long GraphicsContext3D::checkFramebufferStatus(unsigned long target) @@ -378,11 +368,9 @@ void GraphicsContext3D::colorMask(bool red, bool green, bool blue, bool alpha) ::glColorMask(red, green, blue, alpha); } -void GraphicsContext3D::compileShader(CanvasShader* shader) +void GraphicsContext3D::compileShader(WebGLShader* shader) { - if (!shader) - return; - + ASSERT(shader); ensureContext(m_contextObj); ::glCompileShader((GLuint) shader->object()); } @@ -423,11 +411,10 @@ void GraphicsContext3D::depthRange(double zNear, double zFar) ::glDepthRange(zNear, zFar); } -void GraphicsContext3D::detachShader(CanvasProgram* program, CanvasShader* shader) +void GraphicsContext3D::detachShader(WebGLProgram* program, WebGLShader* shader) { - if (!program || !shader) - return; - + ASSERT(program); + ASSERT(shader); ensureContext(m_contextObj); ::glDetachShader((GLuint) program->object(), (GLuint) shader->object()); } @@ -480,22 +467,16 @@ void GraphicsContext3D::flush() ::glFlush(); } -void GraphicsContext3D::framebufferRenderbuffer(unsigned long target, unsigned long attachment, unsigned long renderbuffertarget, CanvasRenderbuffer* buffer) +void GraphicsContext3D::framebufferRenderbuffer(unsigned long target, unsigned long attachment, unsigned long renderbuffertarget, WebGLRenderbuffer* buffer) { - if (!buffer) - return; - ensureContext(m_contextObj); - ::glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, (GLuint) buffer->object()); + ::glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, buffer ? (GLuint) buffer->object() : 0); } -void GraphicsContext3D::framebufferTexture2D(unsigned long target, unsigned long attachment, unsigned long textarget, CanvasTexture* texture, long level) +void GraphicsContext3D::framebufferTexture2D(unsigned long target, unsigned long attachment, unsigned long textarget, WebGLTexture* texture, long level) { - if (!texture) - return; - ensureContext(m_contextObj); - ::glFramebufferTexture2DEXT(target, attachment, textarget, (GLuint) texture->object(), level); + ::glFramebufferTexture2DEXT(target, attachment, textarget, texture ? (GLuint) texture->object() : 0, level); } void GraphicsContext3D::frontFace(unsigned long mode) @@ -510,10 +491,12 @@ void GraphicsContext3D::generateMipmap(unsigned long target) ::glGenerateMipmapEXT(target); } -bool GraphicsContext3D::getActiveAttrib(CanvasProgram* program, unsigned long index, ActiveInfo& info) +bool GraphicsContext3D::getActiveAttrib(WebGLProgram* program, unsigned long index, ActiveInfo& info) { - if (!program->object()) + if (!program->object()) { + synthesizeGLError(INVALID_VALUE); return false; + } ensureContext(m_contextObj); GLint maxAttributeSize = 0; ::glGetProgramiv(static_cast<GLuint>(program->object()), GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeSize); @@ -530,10 +513,12 @@ bool GraphicsContext3D::getActiveAttrib(CanvasProgram* program, unsigned long in return true; } -bool GraphicsContext3D::getActiveUniform(CanvasProgram* program, unsigned long index, ActiveInfo& info) +bool GraphicsContext3D::getActiveUniform(WebGLProgram* program, unsigned long index, ActiveInfo& info) { - if (!program->object()) + if (!program->object()) { + synthesizeGLError(INVALID_VALUE); return false; + } ensureContext(m_contextObj); GLint maxUniformSize = 0; ::glGetProgramiv(static_cast<GLuint>(program->object()), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformSize); @@ -550,7 +535,7 @@ bool GraphicsContext3D::getActiveUniform(CanvasProgram* program, unsigned long i return true; } -int GraphicsContext3D::getAttribLocation(CanvasProgram* program, const String& name) +int GraphicsContext3D::getAttribLocation(WebGLProgram* program, const String& name) { if (!program) return -1; @@ -561,6 +546,13 @@ int GraphicsContext3D::getAttribLocation(CanvasProgram* program, const String& n unsigned long GraphicsContext3D::getError() { + if (m_syntheticErrors.size() > 0) { + ListHashSet<unsigned long>::iterator iter = m_syntheticErrors.begin(); + unsigned long err = *iter; + m_syntheticErrors.remove(iter); + return err; + } + ensureContext(m_contextObj); return ::glGetError(); } @@ -577,7 +569,7 @@ void GraphicsContext3D::hint(unsigned long target, unsigned long mode) ::glHint(target, mode); } -bool GraphicsContext3D::isBuffer(CanvasBuffer* buffer) +bool GraphicsContext3D::isBuffer(WebGLBuffer* buffer) { if (!buffer) return false; @@ -592,7 +584,7 @@ bool GraphicsContext3D::isEnabled(unsigned long cap) return ::glIsEnabled(cap); } -bool GraphicsContext3D::isFramebuffer(CanvasFramebuffer* framebuffer) +bool GraphicsContext3D::isFramebuffer(WebGLFramebuffer* framebuffer) { if (!framebuffer) return false; @@ -601,7 +593,7 @@ bool GraphicsContext3D::isFramebuffer(CanvasFramebuffer* framebuffer) return ::glIsFramebufferEXT((GLuint) framebuffer->object()); } -bool GraphicsContext3D::isProgram(CanvasProgram* program) +bool GraphicsContext3D::isProgram(WebGLProgram* program) { if (!program) return false; @@ -610,7 +602,7 @@ bool GraphicsContext3D::isProgram(CanvasProgram* program) return ::glIsProgram((GLuint) program->object()); } -bool GraphicsContext3D::isRenderbuffer(CanvasRenderbuffer* renderbuffer) +bool GraphicsContext3D::isRenderbuffer(WebGLRenderbuffer* renderbuffer) { if (!renderbuffer) return false; @@ -619,7 +611,7 @@ bool GraphicsContext3D::isRenderbuffer(CanvasRenderbuffer* renderbuffer) return ::glIsRenderbufferEXT((GLuint) renderbuffer->object()); } -bool GraphicsContext3D::isShader(CanvasShader* shader) +bool GraphicsContext3D::isShader(WebGLShader* shader) { if (!shader) return false; @@ -628,7 +620,7 @@ bool GraphicsContext3D::isShader(CanvasShader* shader) return ::glIsShader((GLuint) shader->object()); } -bool GraphicsContext3D::isTexture(CanvasTexture* texture) +bool GraphicsContext3D::isTexture(WebGLTexture* texture) { if (!texture) return false; @@ -643,11 +635,9 @@ void GraphicsContext3D::lineWidth(double width) ::glLineWidth(static_cast<float>(width)); } -void GraphicsContext3D::linkProgram(CanvasProgram* program) +void GraphicsContext3D::linkProgram(WebGLProgram* program) { - if (!program) - return; - + ASSERT(program); ensureContext(m_contextObj); ::glLinkProgram((GLuint) program->object()); } @@ -664,7 +654,7 @@ void GraphicsContext3D::polygonOffset(double factor, double units) ::glPolygonOffset(static_cast<float>(factor), static_cast<float>(units)); } -PassRefPtr<CanvasArray> GraphicsContext3D::readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type) +PassRefPtr<WebGLArray> GraphicsContext3D::readPixels(long x, long y, unsigned long width, unsigned long height, unsigned long format, unsigned long type) { ensureContext(m_contextObj); @@ -675,7 +665,7 @@ PassRefPtr<CanvasArray> GraphicsContext3D::readPixels(long x, long y, unsigned l if (type != GL_UNSIGNED_BYTE || format != GL_RGBA) return 0; - RefPtr<CanvasUnsignedByteArray> array = CanvasUnsignedByteArray::create(width * height * 4); + RefPtr<WebGLUnsignedByteArray> array = WebGLUnsignedByteArray::create(width * height * 4); ::glReadPixels(x, y, width, height, format, type, (GLvoid*) array->data()); return array; } @@ -705,10 +695,9 @@ void GraphicsContext3D::scissor(long x, long y, unsigned long width, unsigned lo ::glScissor(x, y, width, height); } -void GraphicsContext3D::shaderSource(CanvasShader* shader, const String& string) +void GraphicsContext3D::shaderSource(WebGLShader* shader, const String& string) { - if (!shader) - return; + ASSERT(shader); ensureContext(m_contextObj); const CString& cs = string.utf8(); @@ -889,19 +878,17 @@ void GraphicsContext3D::uniformMatrix4fv(long location, bool transpose, float* a ::glUniformMatrix4fv(location, size, transpose, array); } -void GraphicsContext3D::useProgram(CanvasProgram* program) +void GraphicsContext3D::useProgram(WebGLProgram* program) { - if (!program) - return; + ASSERT(program); ensureContext(m_contextObj); ::glUseProgram((GLuint) program->object()); } -void GraphicsContext3D::validateProgram(CanvasProgram* program) +void GraphicsContext3D::validateProgram(WebGLProgram* program) { - if (!program) - return; + ASSERT(program); ensureContext(m_contextObj); ::glValidateProgram((GLuint) program->object()); @@ -967,567 +954,156 @@ void GraphicsContext3D::viewport(long x, long y, unsigned long width, unsigned l ::glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height)); } -static int sizeForGetParam(unsigned long pname) -{ - switch(pname) { - case GL_ACTIVE_TEXTURE: return 1; - case GL_ALIASED_LINE_WIDTH_RANGE: return 2; - case GL_ALIASED_POINT_SIZE_RANGE: return 2; - case GL_ALPHA_BITS: return 1; - case GL_ARRAY_BUFFER_BINDING: return 1; // (* actually a CanvasBuffer*) - case GL_BLEND: return 1; - case GL_BLEND_COLOR: return 4; - case GL_BLEND_DST_ALPHA: return 1; - case GL_BLEND_DST_RGB: return 1; - case GL_BLEND_EQUATION_ALPHA: return 1; - case GL_BLEND_EQUATION_RGB: return 1; - case GL_BLEND_SRC_ALPHA: return 1; - case GL_BLEND_SRC_RGB: return 1; - case GL_BLUE_BITS: return 1; - case GL_COLOR_CLEAR_VALUE: return 4; - case GL_COLOR_WRITEMASK: return 4; - case GL_COMPRESSED_TEXTURE_FORMATS: return GL_NUM_COMPRESSED_TEXTURE_FORMATS; - case GL_CULL_FACE: return 1; - case GL_CULL_FACE_MODE: return 1; - case GL_CURRENT_PROGRAM: return 1; // (* actually a CanvasProgram*) - case GL_DEPTH_BITS: return 1; - case GL_DEPTH_CLEAR_VALUE: return 1; - case GL_DEPTH_FUNC: return 1; - case GL_DEPTH_RANGE: return 2; - case GL_DEPTH_TEST: return 1; - case GL_DEPTH_WRITEMASK: return 1; - case GL_DITHER: return 1; - case GL_ELEMENT_ARRAY_BUFFER_BINDING: return 1; // (* actually a CanvasBuffer*) - case GL_FRAMEBUFFER_BINDING_EXT: return 1; // (* actually a CanvasFramebuffer*) - case GL_FRONT_FACE: return 1; - case GL_GENERATE_MIPMAP_HINT: return 1; - case GL_GREEN_BITS: return 1; - //case GL_IMPLEMENTATION_COLOR_READ_FORMAT:return 1; - //case GL_IMPLEMENTATION_COLOR_READ_TYPE: return 1; - case GL_LINE_WIDTH: return 1; - case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:return 1; - case GL_MAX_CUBE_MAP_TEXTURE_SIZE: return 1; - //case GL_MAX_FRAGMENT_UNIFORM_VECTORS: return 1; - case GL_MAX_RENDERBUFFER_SIZE_EXT: return 1; - case GL_MAX_TEXTURE_IMAGE_UNITS: return 1; - case GL_MAX_TEXTURE_SIZE: return 1; - //case GL_MAX_VARYING_VECTORS: return 1; - case GL_MAX_VERTEX_ATTRIBS: return 1; - case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: return 1; - //case GL_MAX_VERTEX_UNIFORM_VECTORS: return 1; - case GL_MAX_VIEWPORT_DIMS: return 2; - case GL_NUM_COMPRESSED_TEXTURE_FORMATS: return 1; - //case GL_NUM_SHADER_BINARY_FORMATS: return 1; - case GL_PACK_ALIGNMENT: return 1; - case GL_POLYGON_OFFSET_FACTOR: return 1; - case GL_POLYGON_OFFSET_FILL: return 1; - case GL_POLYGON_OFFSET_UNITS: return 1; - case GL_RED_BITS: return 1; - case GL_RENDERBUFFER_BINDING_EXT: return 1; // (* actually a CanvasRenderbuffer*) - case GL_SAMPLE_BUFFERS: return 1; - case GL_SAMPLE_COVERAGE_INVERT: return 1; - case GL_SAMPLE_COVERAGE_VALUE: return 1; - case GL_SAMPLES: return 1; - case GL_SCISSOR_BOX: return 4; - case GL_SCISSOR_TEST: return 1; - //case GL_SHADER_BINARY_FORMATS: return GL_NUM_SHADER_BINARY_FORMATS; - //case GL_SHADER_COMPILER: return 1; - case GL_STENCIL_BACK_FAIL: return 1; - case GL_STENCIL_BACK_FUNC: return 1; - case GL_STENCIL_BACK_PASS_DEPTH_FAIL: return 1; - case GL_STENCIL_BACK_PASS_DEPTH_PASS: return 1; - case GL_STENCIL_BACK_REF: return 1; - case GL_STENCIL_BACK_VALUE_MASK: return 1; - case GL_STENCIL_BACK_WRITEMASK: return 1; - case GL_STENCIL_BITS: return 1; - case GL_STENCIL_CLEAR_VALUE: return 1; - case GL_STENCIL_FAIL: return 1; - case GL_STENCIL_FUNC: return 1; - case GL_STENCIL_PASS_DEPTH_FAIL: return 1; - case GL_STENCIL_PASS_DEPTH_PASS: return 1; - case GL_STENCIL_REF: return 1; - case GL_STENCIL_TEST: return 1; - case GL_STENCIL_VALUE_MASK: return 1; - case GL_STENCIL_WRITEMASK: return 1; - case GL_SUBPIXEL_BITS: return 1; - case GL_TEXTURE_BINDING_2D: return 1; // (* actually a CanvasTexture*) - case GL_TEXTURE_BINDING_CUBE_MAP: return 1; // (* actually a CanvasTexture*) - case GL_UNPACK_ALIGNMENT: return 1; - case GL_VIEWPORT: return 4; - } - - return -1; -} - -bool GraphicsContext3D::getBoolean(unsigned long pname) -{ - int size = sizeForGetParam(pname); - if (size < 1) - return 0; - - ensureContext(m_contextObj); - - bool isAlloced = false; - GLboolean buf[4]; - GLboolean* pbuf = buf; - - if (size > 4) { - pbuf = (GLboolean*) malloc(size * sizeof(GLboolean)); - isAlloced = true; - } - - ::glGetBooleanv(pname, pbuf); - - bool value = pbuf[0]; - - if (isAlloced) - free(pbuf); - - return value; -} - -PassRefPtr<CanvasUnsignedByteArray> GraphicsContext3D::getBooleanv(unsigned long pname) -{ - int size = sizeForGetParam(pname); - if (size < 1) - return 0; - - ensureContext(m_contextObj); - - RefPtr<CanvasUnsignedByteArray> array = CanvasUnsignedByteArray::create(size); - bool isAlloced = false; - GLboolean buf[4]; - GLboolean* pbuf = buf; - - if (size > 4) { - pbuf = (GLboolean*) malloc(size * sizeof(GLboolean)); - isAlloced = true; - } - - ::glGetBooleanv(pname, pbuf); - - for (int i = 0; i < size; ++i) - array->set(i, static_cast<unsigned char>(pbuf[i])); - - if (isAlloced) - free(pbuf); - - return array; -} - -float GraphicsContext3D::getFloat(unsigned long pname) +void GraphicsContext3D::getBooleanv(unsigned long pname, unsigned char* value) { - int size = sizeForGetParam(pname); - if (size < 1) - return 0; - ensureContext(m_contextObj); - - bool isAlloced = false; - GLfloat buf[4]; - GLfloat* pbuf = buf; - - if (size > 4) { - pbuf = (GLfloat*) malloc(size * sizeof(GLfloat)); - isAlloced = true; - } - - ::glGetFloatv(pname, pbuf); - - float value = pbuf[0]; - - if (isAlloced) - free(pbuf); - - return value; + ::glGetBooleanv(pname, value); } -PassRefPtr<CanvasFloatArray> GraphicsContext3D::getFloatv(unsigned long pname) +void GraphicsContext3D::getBufferParameteriv(unsigned long target, unsigned long pname, int* value) { - int size = sizeForGetParam(pname); - if (size < 1) - return 0; - ensureContext(m_contextObj); - - RefPtr<CanvasFloatArray> array = CanvasFloatArray::create(size); - bool isAlloced = false; - GLfloat buf[4]; - GLfloat* pbuf = buf; - - if (size > 4) { - pbuf = (GLfloat*) malloc(size * sizeof(GLfloat)); - isAlloced = true; - } - - ::glGetFloatv(pname, pbuf); - - for (int i = 0; i < size; ++i) - array->set(i, static_cast<float>(pbuf[i])); - - if (isAlloced) - free(pbuf); - - return array; + ::glGetBufferParameteriv(target, pname, value); } -int GraphicsContext3D::getInteger(unsigned long pname) +void GraphicsContext3D::getFloatv(unsigned long pname, float* value) { - int size = sizeForGetParam(pname); - if (size < 1) - return 0; - ensureContext(m_contextObj); - - bool isAlloced = false; - GLint buf[4]; - GLint* pbuf = buf; - - if (size > 4) { - pbuf = (GLint*) malloc(size * sizeof(GLint)); - isAlloced = true; - } - - ::glGetIntegerv(pname, pbuf); - - int value = pbuf[0]; - - if (isAlloced) - free(pbuf); - - return value; + ::glGetFloatv(pname, value); } -PassRefPtr<CanvasIntArray> GraphicsContext3D::getIntegerv(unsigned long pname) +void GraphicsContext3D::getFramebufferAttachmentParameteriv(unsigned long target, unsigned long attachment, unsigned long pname, int* value) { - int size = sizeForGetParam(pname); - if (size < 1) - return 0; - ensureContext(m_contextObj); - - RefPtr<CanvasIntArray> array = CanvasIntArray::create(size); - bool isAlloced = false; - GLint buf[4]; - GLint* pbuf = buf; - - if (size > 4) { - pbuf = (GLint*) malloc(size * sizeof(GLint)); - isAlloced = true; - } - - ::glGetIntegerv(pname, pbuf); - - for (int i = 0; i < size; ++i) - array->set(i, static_cast<int>(pbuf[i])); - - if (isAlloced) - free(pbuf); - - return array; + ::glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value); } -int GraphicsContext3D::getBufferParameteri(unsigned long target, unsigned long pname) +void GraphicsContext3D::getIntegerv(unsigned long pname, int* value) { ensureContext(m_contextObj); - GLint data; - ::glGetBufferParameteriv(target, pname, &data); - return data; + ::glGetIntegerv(pname, value); } -PassRefPtr<CanvasIntArray> GraphicsContext3D::getBufferParameteriv(unsigned long target, unsigned long pname) +void GraphicsContext3D::getProgramiv(WebGLProgram* program, unsigned long pname, int* value) { ensureContext(m_contextObj); - RefPtr<CanvasIntArray> array = CanvasIntArray::create(1); - GLint data; - ::glGetBufferParameteriv(target, pname, &data); - array->set(0, static_cast<int>(data)); - - return array; + ::glGetProgramiv((GLuint) program->object(), pname, value); } -int GraphicsContext3D::getFramebufferAttachmentParameteri(unsigned long target, unsigned long attachment, unsigned long pname) +String GraphicsContext3D::getProgramInfoLog(WebGLProgram* program) { - ensureContext(m_contextObj); - GLint data; - ::glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, &data); - return data; -} - -PassRefPtr<CanvasIntArray> GraphicsContext3D::getFramebufferAttachmentParameteriv(unsigned long target, unsigned long attachment, unsigned long pname) -{ - ensureContext(m_contextObj); - RefPtr<CanvasIntArray> array = CanvasIntArray::create(1); - GLint data; - ::glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, &data); - array->set(0, static_cast<int>(data)); - - return array; -} - -int GraphicsContext3D::getProgrami(CanvasProgram* program, unsigned long pname) -{ - ensureContext(m_contextObj); - GLint data; - ::glGetProgramiv((GLuint) program->object(), pname, &data); - return data; -} - -PassRefPtr<CanvasIntArray> GraphicsContext3D::getProgramiv(CanvasProgram* program, unsigned long pname) -{ - ensureContext(m_contextObj); - RefPtr<CanvasIntArray> array = CanvasIntArray::create(1); - GLint data; - ::glGetProgramiv((GLuint) program->object(), pname, &data); - array->set(0, static_cast<int>(data)); - - return array; -} - -String GraphicsContext3D::getProgramInfoLog(CanvasProgram* program) -{ - if (!program) - return String(); + ASSERT(program); ensureContext(m_contextObj); GLint length; ::glGetProgramiv((GLuint) program->object(), GL_INFO_LOG_LENGTH, &length); GLsizei size; - GLchar* info = (GLchar*) malloc(length); + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + ::glGetProgramInfoLog((GLuint) program->object(), length, &size, info); String s(info); - free(info); + fastFree(info); return s; } -int GraphicsContext3D::getRenderbufferParameteri(unsigned long target, unsigned long pname) -{ - ensureContext(m_contextObj); - GLint data; - ::glGetBufferParameteriv(target, pname, &data); - return data; -} - -PassRefPtr<CanvasIntArray> GraphicsContext3D::getRenderbufferParameteriv(unsigned long target, unsigned long pname) +void GraphicsContext3D::getRenderbufferParameteriv(unsigned long target, unsigned long pname, int* value) { ensureContext(m_contextObj); - RefPtr<CanvasIntArray> array = CanvasIntArray::create(1); - GLint data; - ::glGetBufferParameteriv(target, pname, &data); - array->set(0, static_cast<int>(data)); - - return array; + ::glGetRenderbufferParameterivEXT(target, pname, value); } -int GraphicsContext3D::getShaderi(CanvasShader* shader, unsigned long pname) +void GraphicsContext3D::getShaderiv(WebGLShader* shader, unsigned long pname, int* value) { - if (!shader) - return 0; - - ensureContext(m_contextObj); - GLint data; - ::glGetShaderiv((GLuint) shader->object(), pname, &data); - return data; -} - -PassRefPtr<CanvasIntArray> GraphicsContext3D::getShaderiv(CanvasShader* shader, unsigned long pname) -{ - if (!shader) - return 0; + ASSERT(shader); ensureContext(m_contextObj); - RefPtr<CanvasIntArray> array = CanvasIntArray::create(1); - GLint data; - ::glGetShaderiv((GLuint) shader->object(), pname, &data); - array->set(0, static_cast<int>(data)); - - return array; + ::glGetShaderiv((GLuint) shader->object(), pname, value); } -String GraphicsContext3D::getShaderInfoLog(CanvasShader* shader) +String GraphicsContext3D::getShaderInfoLog(WebGLShader* shader) { - if (!shader) - return String(); + ASSERT(shader); ensureContext(m_contextObj); GLint length; ::glGetShaderiv((GLuint) shader->object(), GL_INFO_LOG_LENGTH, &length); GLsizei size; - GLchar* info = (GLchar*) malloc(length); + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + ::glGetShaderInfoLog((GLuint) shader->object(), length, &size, info); String s(info); - free(info); + fastFree(info); return s; } -String GraphicsContext3D::getShaderSource(CanvasShader* shader) +String GraphicsContext3D::getShaderSource(WebGLShader* shader) { - if (!shader) - return String(); - + ASSERT(shader); + ensureContext(m_contextObj); GLint length; ::glGetShaderiv((GLuint) shader->object(), GL_SHADER_SOURCE_LENGTH, &length); GLsizei size; - GLchar* info = (GLchar*) malloc(length); + GLchar* info = (GLchar*) fastMalloc(length); + if (!info) + return ""; + ::glGetShaderSource((GLuint) shader->object(), length, &size, info); String s(info); - free(info); + fastFree(info); return s; } -float GraphicsContext3D::getTexParameterf(unsigned long target, unsigned long pname) +void GraphicsContext3D::getTexParameterfv(unsigned long target, unsigned long pname, float* value) { ensureContext(m_contextObj); - GLfloat data; - ::glGetTexParameterfv(target, pname, &data); - return data; + ::glGetTexParameterfv(target, pname, value); } -PassRefPtr<CanvasFloatArray> GraphicsContext3D::getTexParameterfv(unsigned long target, unsigned long pname) +void GraphicsContext3D::getTexParameteriv(unsigned long target, unsigned long pname, int* value) { ensureContext(m_contextObj); - RefPtr<CanvasFloatArray> array = CanvasFloatArray::create(1); - GLfloat data; - ::glGetTexParameterfv(target, pname, &data); - array->set(0, static_cast<float>(data)); - - return array; + ::glGetTexParameteriv(target, pname, value); } -int GraphicsContext3D::getTexParameteri(unsigned long target, unsigned long pname) +void GraphicsContext3D::getUniformfv(WebGLProgram* program, long location, float* value) { ensureContext(m_contextObj); - GLint data; - ::glGetTexParameteriv(target, pname, &data); - return data; + ::glGetUniformfv((GLuint) program->object(), location, value); } -PassRefPtr<CanvasIntArray> GraphicsContext3D::getTexParameteriv(unsigned long target, unsigned long pname) +void GraphicsContext3D::getUniformiv(WebGLProgram* program, long location, int* value) { ensureContext(m_contextObj); - RefPtr<CanvasIntArray> array = CanvasIntArray::create(1); - GLint data; - ::glGetTexParameteriv(target, pname, &data); - array->set(0, static_cast<int>(data)); - - return array; -} - -float GraphicsContext3D::getUniformf(CanvasProgram* program, long location) -{ - // FIXME: We need to query glGetUniformLocation to determine the size needed - UNUSED_PARAM(program); - UNUSED_PARAM(location); - notImplemented(); - return 0; + ::glGetUniformiv((GLuint) program->object(), location, value); } -PassRefPtr<CanvasFloatArray> GraphicsContext3D::getUniformfv(CanvasProgram* program, long location) +long GraphicsContext3D::getUniformLocation(WebGLProgram* program, const String& name) { - // FIXME: We need to query glGetUniformLocation to determine the size needed - UNUSED_PARAM(program); - UNUSED_PARAM(location); - notImplemented(); - return 0; -} - -int GraphicsContext3D::getUniformi(CanvasProgram* program, long location) -{ - // FIXME: We need to query glGetUniformLocation to determine the size needed - UNUSED_PARAM(program); - UNUSED_PARAM(location); - notImplemented(); - return 0; -} - -PassRefPtr<CanvasIntArray> GraphicsContext3D::getUniformiv(CanvasProgram* program, long location) -{ - // FIXME: We need to query glGetUniformLocation to determine the size needed - UNUSED_PARAM(program); - UNUSED_PARAM(location); - notImplemented(); - return 0; -} - -long GraphicsContext3D::getUniformLocation(CanvasProgram* program, const String& name) -{ - if (!program) - return -1; + ASSERT(program); ensureContext(m_contextObj); return ::glGetUniformLocation((GLuint) program->object(), name.utf8().data()); } -static int sizeForGetVertexAttribParam(unsigned long pname) -{ - switch(pname) { - case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: return 1; // (* actually a CanvasBuffer*) - case GL_VERTEX_ATTRIB_ARRAY_ENABLED: return 1; - case GL_VERTEX_ATTRIB_ARRAY_SIZE: return 1; - case GL_VERTEX_ATTRIB_ARRAY_STRIDE: return 1; - case GL_VERTEX_ATTRIB_ARRAY_TYPE: return 1; - case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: return 1; - case GL_CURRENT_VERTEX_ATTRIB: return 4; - } - - return -1; -} - -float GraphicsContext3D::getVertexAttribf(unsigned long index, unsigned long pname) -{ - ensureContext(m_contextObj); - GLfloat buf[4]; - ::glGetVertexAttribfv(index, pname, buf); - return buf[0]; -} - -PassRefPtr<CanvasFloatArray> GraphicsContext3D::getVertexAttribfv(unsigned long index, unsigned long pname) -{ - int size = sizeForGetVertexAttribParam(pname); - if (size < 1) - return 0; - - ensureContext(m_contextObj); - - RefPtr<CanvasFloatArray> array = CanvasFloatArray::create(size); - GLfloat buf[4]; - ::glGetVertexAttribfv(index, pname, buf); - - for (int i = 0; i < size; ++i) - array->set(i, static_cast<float>(buf[i])); - - return array; -} - -int GraphicsContext3D::getVertexAttribi(unsigned long index, unsigned long pname) +void GraphicsContext3D::getVertexAttribfv(unsigned long index, unsigned long pname, float* value) { ensureContext(m_contextObj); - GLint buf[4]; - ::glGetVertexAttribiv(index, pname, buf); - return buf[0]; + ::glGetVertexAttribfv(index, pname, value); } -PassRefPtr<CanvasIntArray> GraphicsContext3D::getVertexAttribiv(unsigned long index, unsigned long pname) +void GraphicsContext3D::getVertexAttribiv(unsigned long index, unsigned long pname, int* value) { - int size = sizeForGetVertexAttribParam(pname); - if (size < 1) - return 0; - ensureContext(m_contextObj); - - RefPtr<CanvasIntArray> array = CanvasIntArray::create(size); - GLint buf[4]; - ::glGetVertexAttribiv(index, pname, buf); - - for (int i = 0; i < size; ++i) - array->set(i, static_cast<int>(buf[i])); - - return array; + ::glGetVertexAttribiv(index, pname, value); } long GraphicsContext3D::getVertexAttribOffset(unsigned long index, unsigned long pname) @@ -1552,18 +1128,21 @@ static void imageToTexture(Image* image, unsigned target, unsigned level) size_t textureWidth = CGImageGetWidth(textureImage); size_t textureHeight = CGImageGetHeight(textureImage); - GLubyte* textureData = (GLubyte*) malloc(textureWidth * textureHeight * 4); + GLubyte* textureData = (GLubyte*) fastMalloc(textureWidth * textureHeight * 4); + if (!textureData) + return; + CGContextRef textureContext = CGBitmapContextCreate(textureData, textureWidth, textureHeight, 8, textureWidth * 4, CGImageGetColorSpace(textureImage), kCGImageAlphaPremultipliedLast); - + CGContextSetBlendMode(textureContext, kCGBlendModeCopy); CGContextDrawImage(textureContext, CGRectMake(0, 0, (CGFloat)textureWidth, (CGFloat)textureHeight), textureImage); CGContextRelease(textureContext); ::glTexImage2D(target, level, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData); - free(textureData); + fastFree(textureData); } -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, CanvasArray* pixels) +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned width, unsigned height, unsigned border, unsigned format, unsigned type, WebGLArray* pixels) { // FIXME: Need to do bounds checking on the buffer here. ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels->baseAddress()); @@ -1585,39 +1164,15 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned level, unsigned inte return -1; } -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, HTMLImageElement* image, bool flipY, bool premultiplyAlpha) +int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, bool flipY, bool premultiplyAlpha) { // FIXME: need to support flipY and premultiplyAlpha UNUSED_PARAM(flipY); UNUSED_PARAM(premultiplyAlpha); - - if (!image) - return -1; + ASSERT(image); ensureContext(m_contextObj); - CachedImage* cachedImage = image->cachedImage(); - if (!cachedImage) - return -1; - - imageToTexture(cachedImage->image(), target, level); - return 0; -} - -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, bool flipY, bool premultiplyAlpha) -{ - // FIXME: need to support flipY and premultiplyAlpha - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - - if (!canvas) - return -1; - - ensureContext(m_contextObj); - ImageBuffer* buffer = canvas->buffer(); - if (!buffer) - return -1; - - imageToTexture(buffer->image(), target, level); + imageToTexture(image, target, level); return 0; } @@ -1634,7 +1189,7 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned level, HTMLVideoElem return -1; } -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, unsigned format, unsigned type, CanvasArray* pixels) +int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, unsigned format, unsigned type, WebGLArray* pixels) { // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size UNUSED_PARAM(target); @@ -1664,7 +1219,7 @@ int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned x return -1; } -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, HTMLImageElement* image, bool flipY, bool premultiplyAlpha) +int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, Image* image, bool flipY, bool premultiplyAlpha) { // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size UNUSED_PARAM(target); @@ -1681,23 +1236,6 @@ int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned x return -1; } -int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, HTMLCanvasElement* canvas, bool flipY, bool premultiplyAlpha) -{ - // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(xoff); - UNUSED_PARAM(yoff); - UNUSED_PARAM(width); - UNUSED_PARAM(height); - UNUSED_PARAM(canvas); - - // FIXME: need to support flipY and premultiplyAlpha - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - return -1; -} - int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoff, unsigned yoff, unsigned width, unsigned height, HTMLVideoElement* video, bool flipY, bool premultiplyAlpha) { // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size @@ -1745,7 +1283,7 @@ unsigned GraphicsContext3D::createRenderbuffer() return o; } -unsigned GraphicsContext3D::createShader(ShaderType type) +unsigned GraphicsContext3D::createShader(unsigned long type) { ensureContext(m_contextObj); return glCreateShader((type == FRAGMENT_SHADER) ? GL_FRAGMENT_SHADER : GL_VERTEX_SHADER); @@ -1817,6 +1355,11 @@ int GraphicsContext3D::sizeInBytes(int type) } } +void GraphicsContext3D::synthesizeGLError(unsigned long error) +{ + m_syntheticErrors.add(error); +} + } #endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h index 8cf51b4..8024091 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.h +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -54,6 +54,7 @@ public: // for hosting this GraphicsLayer in a native layer hierarchy virtual NativeLayer nativeLayer() const; + virtual bool setChildren(const Vector<GraphicsLayer*>&); virtual void addChild(GraphicsLayer*); virtual void addChildAtIndex(GraphicsLayer*, int index); virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index b351956..dea6bfc 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -47,6 +47,7 @@ #import "WebLayer.h" #import "WebTiledLayer.h" #import <limits.h> +#import <objc/objc-auto.h> #import <wtf/CurrentTime.h> #import <wtf/UnusedParam.h> #import <wtf/RetainPtr.h> @@ -298,6 +299,18 @@ static void clearLayerBackgroundColor(PlatformLayer* layer) [layer setBackgroundColor:0]; } +static void safeSetSublayers(CALayer* layer, NSArray* sublayers) +{ + // Workaround for <rdar://problem/7390716>: -[CALayer setSublayers:] crashes if sublayers is an empty array, or nil, under GC. + if (objc_collectingEnabled() && ![sublayers count]) { + while ([[layer sublayers] count]) + [[[layer sublayers] objectAtIndex:0] removeFromSuperlayer]; + return; + } + + [layer setSublayers:sublayers]; +} + static bool caValueFunctionSupported() { static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; @@ -315,18 +328,6 @@ GraphicsLayer::CompositingCoordinatesOrientation GraphicsLayer::compositingCoord return CompositingCoordinatesBottomUp; } -bool GraphicsLayer::showDebugBorders() -{ - static bool showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; - return showDebugBorders; -} - -bool GraphicsLayer::showRepaintCounter() -{ - static bool showRepaintCounter = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerRepaintCounter"]; - return showRepaintCounter; -} - static NSDictionary* nullActionsDictionary() { NSNull* nullValue = [NSNull null]; @@ -352,13 +353,13 @@ PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) } GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) -: GraphicsLayer(client) -, m_contentsLayerPurpose(NoContentsLayer) -, m_contentsLayerHasBackgroundColor(false) -, m_uncommittedChanges(NoChange) + : GraphicsLayer(client) + , m_contentsLayerPurpose(NoContentsLayer) + , m_contentsLayerHasBackgroundColor(false) + , m_uncommittedChanges(NoChange) #if ENABLE(3D_CANVAS) -, m_platformGraphicsContext3D(NullPlatformGraphicsContext3D) -, m_platformTexture(NullPlatform3DObject) + , m_platformGraphicsContext3D(NullPlatformGraphicsContext3D) + , m_platformTexture(NullPlatform3DObject) #endif { BEGIN_BLOCK_OBJC_EXCEPTIONS @@ -409,6 +410,15 @@ NativeLayer GraphicsLayerCA::nativeLayer() const return m_layer.get(); } +bool GraphicsLayerCA::setChildren(const Vector<GraphicsLayer*>& children) +{ + bool childrenChanged = GraphicsLayer::setChildren(children); + if (childrenChanged) + noteLayerPropertyChanged(ChildrenChanged); + + return childrenChanged; +} + void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) { GraphicsLayer::addChild(childLayer); @@ -882,17 +892,16 @@ void GraphicsLayerCA::updateSublayerList() [newSublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; if (m_transformLayer) { - [m_transformLayer.get() setSublayers:newSublayers]; + safeSetSublayers(m_transformLayer.get(), 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.get() setSublayers:nil]; + safeSetSublayers(m_layer.get(), nil); [m_layer.get() addSublayer:m_contentsLayer.get()]; } - } else { - [m_layer.get() setSublayers:newSublayers]; - } + } else + safeSetSublayers(m_layer.get(), newSublayers); [newSublayers release]; } @@ -1757,7 +1766,7 @@ void GraphicsLayerCA::swapFromOrToTiledLayer(bool useTiledLayer) } [m_layer.get() setLayerOwner:this]; - [m_layer.get() setSublayers:[oldLayer.get() sublayers]]; + safeSetSublayers(m_layer.get(), [oldLayer.get() sublayers]); [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; diff --git a/WebCore/platform/graphics/mac/IconMac.mm b/WebCore/platform/graphics/mac/IconMac.mm index 63abe59..aee7234 100644 --- a/WebCore/platform/graphics/mac/IconMac.mm +++ b/WebCore/platform/graphics/mac/IconMac.mm @@ -39,27 +39,32 @@ Icon::~Icon() { } -PassRefPtr<Icon> Icon::createIconForFile(const String& filename) +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) { - // Don't pass relative filenames -- we don't want a result that depends on the current directory. - // Need 0U here to disambiguate String::operator[] from operator(NSString*, int)[] - if (filename.isEmpty() || filename[0U] != '/') + if (filenames.isEmpty()) return 0; - NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile:filename]; - if (!image) - return 0; + bool useIconFromFirstFile; +#ifdef BUILDING_ON_TIGER + // FIXME: find a better image for multiple files to use on Tiger. + useIconFromFirstFile = true; +#else + useIconFromFirstFile = filenames.size() == 1; +#endif + if (useIconFromFirstFile) { + // Don't pass relative filenames -- we don't want a result that depends on the current directory. + // Need 0U here to disambiguate String::operator[] from operator(NSString*, int)[] + if (filenames[0].isEmpty() || filenames[0][0U] != '/') + return 0; - return adoptRef(new Icon(image)); -} + NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile:filenames[0]]; + if (!image) + return 0; -PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) -{ - if (filenames.isEmpty()) - return 0; + return adoptRef(new Icon(image)); + } #ifdef BUILDING_ON_TIGER - // FIXME: find a better image to use on Tiger. - return createIconForFile(filenames[0]); + return 0; #else NSImage* image = [NSImage imageNamed:NSImageNameMultipleDocuments]; if (!image) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index 0a63626..7aaf95d 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -101,6 +101,9 @@ private: void setVolume(float); void setPreservesPitch(bool); + bool hasClosedCaptions() const; + void setClosedCaptionsVisible(bool); + void setEndTime(float time); int dataRate() const; diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index 30d0c82..dfb5958 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -736,8 +736,13 @@ bool MediaPlayerPrivate::hasAudio() const } bool MediaPlayerPrivate::supportsFullscreen() const -{ +{ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) return true; +#else + // See <rdar://problem/7389945> + return false; +#endif } void MediaPlayerPrivate::setVolume(float volume) @@ -746,6 +751,27 @@ void MediaPlayerPrivate::setVolume(float volume) [m_qtMovie.get() setVolume:volume]; } +bool MediaPlayerPrivate::hasClosedCaptions() const +{ + if (!metaDataAvailable()) + return false; + return wkQTMovieHasClosedCaptions(m_qtMovie.get()); +} + +void MediaPlayerPrivate::setClosedCaptionsVisible(bool closedCaptionsVisible) +{ + if (metaDataAvailable()) { + wkQTMovieSetShowClosedCaptions(m_qtMovie.get(), closedCaptionsVisible); + +#if USE(ACCELERATED_COMPOSITING) && (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + if (closedCaptionsVisible && m_qtVideoLayer) { + // Captions will be rendered upsided down unless we flag the movie as flipped (again). See <rdar://7408440>. + [m_qtVideoLayer.get() setGeometryFlipped:YES]; + } +#endif + } +} + void MediaPlayerPrivate::setRate(float rate) { if (m_qtMovie) @@ -1158,10 +1184,10 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) TextRun textRun(text.characters(), text.length()); const Color color(255, 0, 0); context->scale(FloatSize(1.0f, -1.0f)); - context->setStrokeColor(color); + context->setStrokeColor(color, styleToUse->colorSpace()); context->setStrokeStyle(SolidStroke); context->setStrokeThickness(1.0f); - context->setFillColor(color); + context->setFillColor(color, styleToUse->colorSpace()); context->drawText(styleToUse->font(), textRun, IntPoint(2, -3)); } } |