diff options
| author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
|---|---|---|
| committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
| commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
| tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/mac | |
| parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
| download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 | |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/mac')
34 files changed, 6566 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/mac/ColorMac.h b/Source/WebCore/platform/graphics/mac/ColorMac.h new file mode 100644 index 0000000..b68b157 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ColorMac.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ColorMac_h +#define ColorMac_h + +#include "Color.h" + +#ifdef __OBJC__ +@class NSColor; +#else +class NSColor; +#endif + +namespace WebCore { + + // These functions assume NSColors are in DeviceRGB colorspace + Color colorFromNSColor(NSColor *); + NSColor *nsColor(const Color&); + + bool usesTestModeFocusRingColor(); + void setUsesTestModeFocusRingColor(bool); + + // Focus ring color used for testing purposes. + RGBA32 oldAquaFocusRingColor(); + +} + +#endif diff --git a/Source/WebCore/platform/graphics/mac/ColorMac.mm b/Source/WebCore/platform/graphics/mac/ColorMac.mm new file mode 100644 index 0000000..07d6353 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ColorMac.mm @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "ColorMac.h" + +#import <wtf/RetainPtr.h> +#import <wtf/StdLibExtras.h> + +namespace WebCore { + +// NSColor calls don't throw, so no need to block Cocoa exceptions in this file + +static bool useOldAquaFocusRingColor; + +RGBA32 oldAquaFocusRingColor() +{ + return 0xFF7DADD9; +} + +void setUsesTestModeFocusRingColor(bool newValue) +{ + useOldAquaFocusRingColor = newValue; +} + +bool usesTestModeFocusRingColor() +{ + return useOldAquaFocusRingColor; +} + +static RGBA32 makeRGBAFromNSColor(NSColor *c) +{ + CGFloat redComponent; + CGFloat greenComponent; + CGFloat blueComponent; + CGFloat alpha; + [c getRed:&redComponent green:&greenComponent blue:&blueComponent alpha:&alpha]; + + return makeRGBA(255 * redComponent, 255 * greenComponent, 255 * blueComponent, 255 * alpha); +} + +Color colorFromNSColor(NSColor *c) +{ + return Color(makeRGBAFromNSColor(c)); +} + +NSColor *nsColor(const Color& color) +{ + RGBA32 c = color.rgb(); + switch (c) { + case 0: { + // Need this to avoid returning nil because cachedRGBAValues will default to 0. + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, clearColor, ([NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:0])); + return clearColor.get(); + } + case Color::black: { + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, blackColor, ([NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:1])); + return blackColor.get(); + } + case Color::white: { + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, whiteColor, ([NSColor colorWithDeviceRed:1 green:1 blue:1 alpha:1])); + return whiteColor.get(); + } + default: { + const int cacheSize = 32; + static unsigned cachedRGBAValues[cacheSize]; + static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; + + for (int i = 0; i != cacheSize; ++i) { + if (cachedRGBAValues[i] == c) + return cachedColors[i].get(); + } + + NSColor *result = [NSColor colorWithDeviceRed:static_cast<CGFloat>(color.red()) / 255 + green:static_cast<CGFloat>(color.green()) / 255 + blue:static_cast<CGFloat>(color.blue()) / 255 + alpha:static_cast<CGFloat>(color.alpha()) / 255]; + + static int cursor; + cachedRGBAValues[cursor] = c; + cachedColors[cursor] = result; + if (++cursor == cacheSize) + cursor = 0; + return result; + } + } +} + + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp b/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp new file mode 100644 index 0000000..206fd5f --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ComplexTextController.h" + +#include <ApplicationServices/ApplicationServices.h> +#include "CharacterNames.h" +#include "FloatSize.h" +#include "Font.h" +#include "TextBreakIterator.h" + +#include <wtf/StdLibExtras.h> + +#if defined(BUILDING_ON_LEOPARD) +// Undefined when compiling agains the 10.5 SDK. +#define kCTVersionNumber10_6 0x00030000 +#endif + +using namespace std; + +namespace WebCore { + +static inline CGFloat roundCGFloat(CGFloat f) +{ + if (sizeof(CGFloat) == sizeof(float)) + return roundf(static_cast<float>(f)); + return static_cast<CGFloat>(round(f)); +} + +static inline CGFloat ceilCGFloat(CGFloat f) +{ + if (sizeof(CGFloat) == sizeof(float)) + return ceilf(static_cast<float>(f)); + return static_cast<CGFloat>(ceil(f)); +} + +ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis) + : m_font(*font) + , m_run(run) + , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) + , m_forTextEmphasis(forTextEmphasis) + , m_currentCharacter(0) + , m_end(run.length()) + , m_totalWidth(0) + , m_runWidthSoFar(0) + , m_numGlyphsSoFar(0) + , m_currentRun(0) + , m_glyphInCurrentRun(0) + , m_characterInCurrentGlyph(0) + , m_finalRoundingWidth(0) + , m_padding(run.padding()) + , m_fallbackFonts(fallbackFonts) + , m_minGlyphBoundingBoxX(numeric_limits<float>::max()) + , m_maxGlyphBoundingBoxX(numeric_limits<float>::min()) + , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) + , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) + , m_lastRoundingGlyph(0) +{ + if (!m_padding) + m_padPerSpace = 0; + else { + int numSpaces = 0; + for (int s = 0; s < m_run.length(); s++) { + if (Font::treatAsSpace(m_run[s])) + numSpaces++; + } + + if (!numSpaces) + m_padPerSpace = 0; + else + m_padPerSpace = m_padding / numSpaces; + } + + collectComplexTextRuns(); + adjustGlyphsAndAdvances(); +} + +int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs) +{ + if (h >= m_totalWidth) + return m_run.ltr() ? m_end : 0; + if (h < 0) + return m_run.ltr() ? 0 : m_end; + + CGFloat x = h; + + size_t runCount = m_complexTextRuns.size(); + size_t offsetIntoAdjustedGlyphs = 0; + + for (size_t r = 0; r < runCount; ++r) { + 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 hitGlyphStart = complexTextRun.indexAt(j); + CFIndex hitGlyphEnd; + if (m_run.ltr()) + hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.stringLength())); + else + hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.stringLength())); + + // FIXME: Instead of dividing the glyph's advance equally 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; + else { + clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex); + if (clusterStart == TextBreakDone) + clusterStart = 0; + } + + if (!includePartialGlyphs) + return complexTextRun.stringLocation() + clusterStart; + + int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex); + if (clusterEnd == TextBreakDone) + clusterEnd = stringLength; + + 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 && 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 < 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 complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); + else + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); + } + x -= adjustedAdvance; + } + offsetIntoAdjustedGlyphs += complexTextRun.glyphCount(); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void ComplexTextController::collectComplexTextRuns() +{ + if (!m_end) + return; + + // We break up glyph run generation for the string by FontData and (if needed) the use of small caps. + const UChar* cp = m_run.characters(); + + if (m_font.isSmallCaps()) + m_smallCapsBuffer.resize(m_end); + + unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0; + const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp; + const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end; + + GlyphData glyphData; + GlyphData nextGlyphData; + + bool isSurrogate = U16_IS_SURROGATE(*curr); + if (isSurrogate) { + if (m_run.ltr()) { + if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); + } else { + if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); + } + } else + nextGlyphData = m_font.glyphDataForCharacter(*curr, false); + + UChar newC = 0; + + bool isSmallCaps; + bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; + + if (nextIsSmallCaps) + m_smallCapsBuffer[curr - cp] = newC; + + while (true) { + curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1); + if (curr == end) + break; + + glyphData = nextGlyphData; + isSmallCaps = nextIsSmallCaps; + int index = curr - cp; + isSurrogate = U16_IS_SURROGATE(*curr); + UChar c = *curr; + bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK); + if (isSurrogate) { + if (m_run.ltr()) { + if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); + } else { + if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) + return; + nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); + } + } else + nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant); + + if (!isSurrogate && m_font.isSmallCaps()) { + nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; + if (nextIsSmallCaps) + m_smallCapsBuffer[index] = forceSmallCaps ? c : newC; + } + + if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) { + int itemStart = m_run.rtl() ? index + 1 : static_cast<int>(indexOfFontTransition); + int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; + collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); + indexOfFontTransition = index; + } + } + + int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition; + if (itemLength) { + int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; + collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); + } +} + +#if USE(CORE_TEXT) && USE(ATSUI) +static inline bool shouldUseATSUIAPI() +{ + enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText }; + static TypeRenderingAPIToUse apiToUse = UnInitialized; + + if (UNLIKELY(apiToUse == UnInitialized)) { + if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6) + apiToUse = UseCoreText; + else + apiToUse = UseATSUI; + } + + return apiToUse == UseATSUI; +} +#endif + +CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const +{ +#if USE(CORE_TEXT) && USE(ATSUI) + return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i]; +#elif USE(ATSUI) + return m_atsuiIndices[i]; +#elif USE(CORE_TEXT) + return m_coreTextIndices[i]; +#endif +} + +void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData) +{ +#if USE(CORE_TEXT) && USE(ATSUI) + if (shouldUseATSUIAPI()) + return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); + return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); +#elif USE(ATSUI) + return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData); +#elif USE(CORE_TEXT) + return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData); +#endif +} + +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_isMonotonic(true) +{ +#if USE(CORE_TEXT) && USE(ATSUI) + shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr); +#elif USE(ATSUI) + createTextRunFromFontDataATSUI(ltr); +#elif USE(CORE_TEXT) + createTextRunFromFontDataCoreText(ltr); +#endif +} + +void ComplexTextController::ComplexTextRun::setIsNonMonotonic() +{ + ASSERT(m_isMonotonic); + m_isMonotonic = false; + + Vector<bool, 64> mappedIndices(m_stringLength); + for (size_t i = 0; i < m_glyphCount; ++i) { + ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength)); + mappedIndices[indexAt(i)] = true; + } + + m_glyphEndOffsets.grow(m_glyphCount); + for (size_t i = 0; i < m_glyphCount; ++i) { + CFIndex nextMappedIndex = m_stringLength; + for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) { + if (mappedIndices[j]) { + nextMappedIndex = j; + break; + } + } + m_glyphEndOffsets[i] = nextMappedIndex; + } +} + +void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) +{ + if (static_cast<int>(offset) > m_end) + offset = m_end; + + if (offset <= m_currentCharacter) + return; + + m_currentCharacter = offset; + + 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 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) { + unsigned glyphStartOffset = complexTextRun.indexAt(g); + unsigned glyphEndOffset; + if (complexTextRun.isMonotonic()) { + if (ltr) + glyphEndOffset = max<unsigned>(glyphStartOffset, g + 1 < glyphCount ? static_cast<unsigned>(complexTextRun.indexAt(g + 1)) : complexTextRun.stringLength()); + else + glyphEndOffset = max<unsigned>(glyphStartOffset, g > 0 ? static_cast<unsigned>(complexTextRun.indexAt(g - 1)) : complexTextRun.stringLength()); + } else + glyphEndOffset = complexTextRun.endOffsetAt(g); + + CGSize adjustedAdvance = m_adjustedAdvances[k]; + + 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. + if (glyphStartOffset == glyphEndOffset) { + // When there are multiple glyphs per character we need to advance by the full width of the glyph. + ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph); + m_runWidthSoFar += adjustedAdvance.width; + } else + 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++; + } else { + g--; + k--; + } + } + m_currentRun++; + m_glyphInCurrentRun = 0; + } + if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size()) + m_runWidthSoFar += m_finalRoundingWidth; +} + +void ComplexTextController::adjustGlyphsAndAdvances() +{ + CGFloat widthSinceLastRounding = 0; + size_t runCount = m_complexTextRuns.size(); + for (size_t r = 0; r < runCount; ++r) { + ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; + unsigned glyphCount = complexTextRun.glyphCount(); + const SimpleFontData* fontData = complexTextRun.fontData(); + + const CGGlyph* glyphs = complexTextRun.glyphs(); + const CGSize* advances = complexTextRun.advances(); + + bool lastRun = r + 1 == runCount; + 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(); + CGPoint glyphOrigin = CGPointZero; + CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max(); + bool isMonotonic = true; + + for (unsigned i = 0; i < glyphCount; i++) { + CFIndex characterIndex = complexTextRun.indexAt(i); + if (m_run.ltr()) { + if (characterIndex < lastCharacterIndex) + isMonotonic = false; + } else { + if (characterIndex > lastCharacterIndex) + isMonotonic = false; + } + UChar ch = *(cp + characterIndex); + bool lastGlyph = lastRun && i + 1 == glyphCount; + UChar nextCh; + if (lastGlyph) + nextCh = ' '; + else if (i + 1 < glyphCount) + nextCh = *(cp + complexTextRun.indexAt(i + 1)); + else + nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0)); + + bool treatAsSpace = Font::treatAsSpace(ch); + CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i]; + CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i]; + + if (ch == '\t' && m_run.allowTabs()) { + float tabWidth = m_font.tabWidth(*fontData); + advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastRounding, tabWidth); + } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) { + advance.width = 0; + glyph = fontData->spaceGlyph(); + } + + float roundedAdvanceWidth = roundf(advance.width); + if (roundsAdvances) + advance.width = roundedAdvanceWidth; + + advance.width += fontData->syntheticBoldOffset(); + + // We special case spaces in two ways when applying word rounding. + // First, we round spaces to an adjusted width in all fonts. + // Second, in fixed-pitch fonts we ensure that all glyphs that + // match the width of the space glyph have the same width as the space glyph. + if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()) && m_run.applyWordRounding()) + advance.width = fontData->adjustedSpaceWidth(); + + if (hasExtraSpacing) { + // If we're a glyph with an advance, go ahead and add in letter-spacing. + // That way we weed out zero width lurkers. This behavior matches the fast text code path. + if (advance.width && m_font.letterSpacing()) + advance.width += m_font.letterSpacing(); + + // Handle justification and word-spacing. + if (treatAsSpace) { + // Account for padding. WebCore uses space padding to justify text. + // We distribute the specified padding over the available spaces in the run. + if (m_padding) { + // Use leftover padding if not evenly divisible by number of spaces. + if (m_padding < m_padPerSpace) { + advance.width += m_padding; + m_padding = 0; + } else { + float previousPadding = m_padding; + m_padding -= m_padPerSpace; + advance.width += roundf(previousPadding) - roundf(m_padding); + } + } + + // Account for word-spacing. + if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) + advance.width += m_font.wordSpacing(); + } + } + + // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters + // followed by a character defined by isRoundingHackCharacter()) are always an integer width. + // We adjust the width of the last character of a "word" to ensure an integer width. + // Force characters that are used to determine word boundaries for the rounding hack + // to be integer width, so the following words will start on an integer boundary. + if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch)) + advance.width = ceilCGFloat(advance.width); + + // Check to see if the next character is a "rounding hack character", if so, adjust the + // width so that the total run width will be on an integer boundary. + if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) { + CGFloat totalWidth = widthSinceLastRounding + advance.width; + widthSinceLastRounding = ceilCGFloat(totalWidth); + CGFloat extraWidth = widthSinceLastRounding - totalWidth; + if (m_run.ltr()) + advance.width += extraWidth; + else { + if (m_lastRoundingGlyph) + m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; + else + m_finalRoundingWidth = extraWidth; + m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; + } + m_totalWidth += widthSinceLastRounding; + widthSinceLastRounding = 0; + } else + widthSinceLastRounding += advance.width; + + // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space. + if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK))) + glyph = 0; + + advance.height *= -1; + m_adjustedAdvances.append(advance); + m_adjustedGlyphs.append(glyph); + + FloatRect glyphBounds = fontData->boundsForGlyph(glyph); + glyphBounds.move(glyphOrigin.x, glyphOrigin.y); + m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); + m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.right()); + m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); + m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.bottom()); + glyphOrigin.x += advance.width; + glyphOrigin.y += advance.height; + + lastCharacterIndex = characterIndex; + } + if (!isMonotonic) + complexTextRun.setIsNonMonotonic(); + } + m_totalWidth += widthSinceLastRounding; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextController.h b/Source/WebCore/platform/graphics/mac/ComplexTextController.h new file mode 100644 index 0000000..9cf80a6 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ComplexTextController.h @@ -0,0 +1,193 @@ +/* + * 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 <ApplicationServices/ApplicationServices.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; + +// ComplexTextController is responsible for rendering and measuring glyphs for +// complex scripts on OS X. +// The underlying API can be selected at compile time based on USE(ATSUI) and +// USE(CORE_TEXT). If both are defined then the Core Text APIs are used for +// OS Versions >= 10.6, ATSUI is used otherwise. +class ComplexTextController { +public: + ComplexTextController(const Font*, const TextRun&, bool mayUseNaturalWritingDirection = false, HashSet<const SimpleFontData*>* fallbackFonts = 0, bool forTextEmphasis = false); + + // 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(float x, bool includePartialGlyphs); + + // Returns the width of everything we've consumed so far. + float runWidthSoFar() const { return m_runWidthSoFar; } + + float totalWidth() const { return m_totalWidth; } + + // Extra width to the left of the leftmost glyph. + float finalRoundingWidth() const { return m_finalRoundingWidth; } + + float minGlyphBoundingBoxX() const { return m_minGlyphBoundingBoxX; } + float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; } + float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; } + float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; } + +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)); + } +#endif +#if 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; } + ALWAYS_INLINE CFIndex indexAt(size_t i) const; + CFIndex endOffsetAt(size_t i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; } + const CGGlyph* glyphs() const { return m_glyphs; } + const CGSize* advances() const { return m_advances; } + bool isMonotonic() const { return m_isMonotonic; } + void setIsNonMonotonic(); + + private: +#if USE(CORE_TEXT) + ComplexTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength); + void createTextRunFromFontDataCoreText(bool ltr); +#endif +#if USE(ATSUI) + ComplexTextRun(ATSUTextLayout, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr, bool directionalOverride); + void createTextRunFromFontDataATSUI(bool ltr); +#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_coreTextRun; +#endif + unsigned m_glyphCount; + const SimpleFontData* m_fontData; + const UChar* m_characters; + unsigned m_stringLocation; + size_t m_stringLength; +#if USE(CORE_TEXT) + Vector<CFIndex, 64> m_coreTextIndicesVector; + const CFIndex* m_coreTextIndices; +#endif +#if USE(ATSUI) + Vector<CFIndex, 64> m_atsuiIndices; +#endif + Vector<CFIndex, 64> m_glyphEndOffsets; + Vector<CGGlyph, 64> m_glyphsVector; + const CGGlyph* m_glyphs; + Vector<CGSize, 64> m_advancesVector; + const CGSize* m_advances; +#if USE(ATSUI) + bool m_directionalOverride; +#endif + bool m_isMonotonic; + }; + + void collectComplexTextRuns(); + + // collectComplexTextRunsForCharacters() is a stub function that calls through to the ATSUI or Core Text variants based + // on the API in use. + void collectComplexTextRunsForCharacters(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void collectComplexTextRunsForCharactersATSUI(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void collectComplexTextRunsForCharactersCoreText(const UChar*, unsigned length, unsigned stringLocation, const SimpleFontData*); + void adjustGlyphsAndAdvances(); + + const Font& m_font; + const TextRun& m_run; + bool m_mayUseNaturalWritingDirection; + bool m_forTextEmphasis; + + 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; + + float m_minGlyphBoundingBoxX; + float m_maxGlyphBoundingBoxX; + float m_minGlyphBoundingBoxY; + float m_maxGlyphBoundingBoxY; + + unsigned m_lastRoundingGlyph; +}; + +} // namespace WebCore + +#endif // ComplexTextController_h diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp b/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp new file mode 100644 index 0000000..c24a914 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp @@ -0,0 +1,340 @@ +/* + * 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_atsuiIndices.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_atsuiIndices.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_directionalOverride(directionalOverride) + , m_isMonotonic(true) +{ + 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); +} + +void ComplexTextController::ComplexTextRun::createTextRunFromFontDataATSUI(bool ltr) +{ + m_atsuiIndices.reserveCapacity(m_stringLength); + unsigned r = 0; + while (r < m_stringLength) { + m_atsuiIndices.uncheckedAppend(r); + if (U_IS_SURROGATE(m_characters[r])) { + ASSERT(r + 1 < m_stringLength); + ASSERT(U_IS_SURROGATE_LEAD(m_characters[r])); + ASSERT(U_IS_TRAIL(m_characters[r + 1])); + r += 2; + } else + r++; + } + m_glyphCount = m_atsuiIndices.size(); + if (!ltr) { + for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) + std::swap(m_atsuiIndices[r], m_atsuiIndices[end]); + } + + 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(); +} + +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, ATSUStyle atsuStyle, TypesettingFeatures typesettingFeatures) +{ + // 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 ((typesettingFeatures & Ligatures) || (fontData->orientation() == Horizontal && fontData->platformData().allowsLigatures())) + return; + + ATSUFontFeatureType featureTypes[] = { kLigaturesType }; + ATSUFontFeatureSelector featureSelectors[] = { kCommonLigaturesOffSelector }; + OSStatus status = ATSUSetFontFeatures(atsuStyle, 1, featureTypes, featureSelectors); + if (status != noErr) + LOG_ERROR("ATSUSetFontFeatures failed (%d) -- ligatures remain enabled", static_cast<int>(status)); +} + +static ATSUStyle initializeATSUStyle(const SimpleFontData* fontData, TypesettingFeatures typesettingFeatures) +{ + unsigned key = typesettingFeatures + 1; + pair<HashMap<unsigned, ATSUStyle>::iterator, bool> addResult = fontData->m_ATSUStyleMap.add(key, 0); + ATSUStyle& atsuStyle = addResult.first->second; + if (!addResult.second) + return atsuStyle; + + ATSUFontID fontID = fontData->platformData().ctFont() ? CTFontGetPlatformFont(fontData->platformData().ctFont(), 0) : 0; + if (!fontID) { + LOG_ERROR("unable to get ATSUFontID for %p", fontData->platformData().font()); + fontData->m_ATSUStyleMap.remove(addResult.first); + return 0; + } + + OSStatus status = ATSUCreateStyle(&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 = typesettingFeatures & Kerning; + status = ATSUSetAttributes(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, atsuStyle, typesettingFeatures); + return atsuStyle; +} + +void ComplexTextController::collectComplexTextRunsForCharactersATSUI(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); + + ATSUStyle atsuStyle = initializeATSUStyle(fontData, m_font.typesettingFeatures()); + + OSStatus status; + ATSUTextLayout atsuTextLayout; + UniCharCount runLength = length; + + status = ATSUCreateTextLayoutWithTextPtr(cp, 0, length, length, 1, &runLength, &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/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp b/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp new file mode 100644 index 0000000..42e7897 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp @@ -0,0 +1,173 @@ +/* + * 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" +#include "WebCoreSystemInterface.h" + +#if USE(CORE_TEXT) + +#include "Font.h" + +#if defined(BUILDING_ON_LEOPARD) +// The following symbols are SPI in 10.5. +extern "C" { +void CTRunGetAdvances(CTRunRef run, CFRange range, CGSize buffer[]); +const CGSize* CTRunGetAdvancesPtr(CTRunRef run); +extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; +} +#endif + +namespace WebCore { + +ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) + : m_coreTextRun(ctRun) + , m_fontData(fontData) + , m_characters(characters) + , m_stringLocation(stringLocation) + , m_stringLength(stringLength) + , m_isMonotonic(true) +{ + m_glyphCount = CTRunGetGlyphCount(m_coreTextRun.get()); + m_coreTextIndices = CTRunGetStringIndicesPtr(m_coreTextRun.get()); + if (!m_coreTextIndices) { + m_coreTextIndicesVector.grow(m_glyphCount); + CTRunGetStringIndices(m_coreTextRun.get(), CFRangeMake(0, 0), m_coreTextIndicesVector.data()); + m_coreTextIndices = m_coreTextIndicesVector.data(); + } + + m_glyphs = CTRunGetGlyphsPtr(m_coreTextRun.get()); + if (!m_glyphs) { + m_glyphsVector.grow(m_glyphCount); + CTRunGetGlyphs(m_coreTextRun.get(), CFRangeMake(0, 0), m_glyphsVector.data()); + m_glyphs = m_glyphsVector.data(); + } + + m_advances = CTRunGetAdvancesPtr(m_coreTextRun.get()); + if (!m_advances) { + m_advancesVector.grow(m_glyphCount); + CTRunGetAdvances(m_coreTextRun.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. +void ComplexTextController::ComplexTextRun::createTextRunFromFontDataCoreText(bool ltr) +{ + m_coreTextIndicesVector.reserveInitialCapacity(m_stringLength); + unsigned r = 0; + while (r < m_stringLength) { + m_coreTextIndicesVector.uncheckedAppend(r); + if (U_IS_SURROGATE(m_characters[r])) { + ASSERT(r + 1 < m_stringLength); + ASSERT(U_IS_SURROGATE_LEAD(m_characters[r])); + ASSERT(U_IS_TRAIL(m_characters[r + 1])); + r += 2; + } else + r++; + } + m_glyphCount = m_coreTextIndicesVector.size(); + if (!ltr) { + for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) + std::swap(m_coreTextIndicesVector[r], m_coreTextIndicesVector[end]); + } + m_coreTextIndices = m_coreTextIndicesVector.data(); + + // 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(); +} + +struct ProviderInfo { + const UChar* cp; + unsigned length; + CFDictionaryRef attributes; +}; + +static const UniChar* provideStringAndAttributes(CFIndex stringIndex, CFIndex* charCount, CFDictionaryRef* attributes, void* refCon) +{ + ProviderInfo* info = static_cast<struct ProviderInfo*>(refCon); + if (stringIndex < 0 || static_cast<unsigned>(stringIndex) >= info->length) + return 0; + + *charCount = info->length - stringIndex; + *attributes = info->attributes; + return info->cp + stringIndex; +} + +void ComplexTextController::collectComplexTextRunsForCharactersCoreText(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<CTLineRef> line; + + if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) { + static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; + const short ltrForcedEmbeddingLevelValue = 0; + const short rtlForcedEmbeddingLevelValue = 1; + static const void* ltrOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, <rForcedEmbeddingLevelValue) }; + static const void* rtlOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &rtlForcedEmbeddingLevelValue) }; + static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, WTF_ARRAY_LENGTH(optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, WTF_ARRAY_LENGTH(optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + ProviderInfo info = { cp, length, fontData->getCFStringAttributes(m_font.typesettingFeatures()) }; + RetainPtr<CTTypesetterRef> typesetter(AdoptCF, wkCreateCTTypesetterWithUniCharProviderAndOptions(&provideStringAndAttributes, 0, &info, m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); +#else + RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, cp, length, kCFAllocatorNull)); + RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(kCFAllocatorDefault, string.get(), fontData->getCFStringAttributes(m_font.typesettingFeatures()))); + RetainPtr<CTTypesetterRef> typesetter(AdoptCF, CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions)); +#endif + + line.adoptCF(CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0))); + } else { + ProviderInfo info = { cp, length, fontData->getCFStringAttributes(m_font.typesettingFeatures()) }; + + line.adoptCF(wkCreateCTLineWithUniCharProvider(&provideStringAndAttributes, 0, &info)); + } + + 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/Source/WebCore/platform/graphics/mac/FloatPointMac.mm b/Source/WebCore/platform/graphics/mac/FloatPointMac.mm new file mode 100644 index 0000000..2f73314 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FloatPointMac.mm @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005 Nokia. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FloatPoint.h" + +namespace WebCore { + +#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES + +FloatPoint::FloatPoint(const NSPoint& p) : m_x(p.x), m_y(p.y) +{ +} + +FloatPoint::operator NSPoint() const +{ + return NSMakePoint(m_x, m_y); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/mac/FloatRectMac.mm b/Source/WebCore/platform/graphics/mac/FloatRectMac.mm new file mode 100644 index 0000000..1d6b045 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FloatRectMac.mm @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005 Nokia. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FloatRect.h" + +namespace WebCore { + +#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES + +FloatRect::FloatRect(const NSRect& r) : m_location(r.origin), m_size(r.size) +{ +} + +FloatRect::operator NSRect() const +{ + return NSMakeRect(x(), y(), width(), height()); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/mac/FloatSizeMac.mm b/Source/WebCore/platform/graphics/mac/FloatSizeMac.mm new file mode 100644 index 0000000..01efbe9 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FloatSizeMac.mm @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005 Nokia. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FloatSize.h" + +namespace WebCore { + +#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES + +FloatSize::FloatSize(const NSSize& s) : m_width(s.width), m_height(s.height) +{ +} + +FloatSize::operator NSSize() const +{ + return NSMakeSize(m_width, m_height); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/mac/FontCacheMac.mm b/Source/WebCore/platform/graphics/mac/FontCacheMac.mm new file mode 100644 index 0000000..068bd8e --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "FontCache.h" + +#import "Font.h" +#import "SimpleFontData.h" +#import "FontPlatformData.h" +#import "WebCoreSystemInterface.h" +#import "WebFontCache.h" +#import <AppKit/AppKit.h> +#import <wtf/StdLibExtras.h> + +#ifdef BUILDING_ON_TIGER +typedef int NSInteger; +#endif + +namespace WebCore { + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef) +{ + ASSERT_UNUSED(observer, observer == fontCache()); + ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)); + fontCache()->invalidate(); +} +#else +static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*) +{ + fontCache()->invalidate(); +} +#endif + +void FontCache::platformInit() +{ + wkSetUpFontCache(); +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately); +#else + // kCTFontManagerRegisteredFontsChangedNotification does not exist on Leopard and earlier. + // FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway. + ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0); +#endif +} + +static int toAppKitFontWeight(FontWeight fontWeight) +{ + static int appKitFontWeights[] = { + 2, // FontWeight100 + 3, // FontWeight200 + 4, // FontWeight300 + 5, // FontWeight400 + 6, // FontWeight500 + 8, // FontWeight600 + 9, // FontWeight700 + 10, // FontWeight800 + 12, // FontWeight900 + }; + return appKitFontWeights[fontWeight]; +} + +static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight) +{ + return appKitFontWeight >= 7; +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(characters[0])->platformData(); + NSFont *nsFont = platformData.font(); + + NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) length:length freeWhenDone:NO]; + NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length)); + [string release]; + + if (!substituteFont && length == 1) + substituteFont = wkGetFontInLanguageForCharacter(nsFont, characters[0]); + if (!substituteFont) + return 0; + + // Use the family name from the AppKit-supplied substitute font, requesting the + // traits, weight, and size we want. One way this does better than the original + // AppKit request is that it takes synthetic bold and oblique into account. + // But it does create the possibility that we could end up with a font that + // doesn't actually cover the characters we need. + + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + + NSFontTraitMask traits; + NSInteger weight; + CGFloat size; + + if (nsFont) { + traits = [fontManager traitsOfFont:nsFont]; + if (platformData.m_syntheticBold) + traits |= NSBoldFontMask; + if (platformData.m_syntheticOblique) + traits |= NSFontItalicTrait; + weight = [fontManager weightOfFont:nsFont]; + size = [nsFont pointSize]; + } else { + // For custom fonts nsFont is nil. + traits = font.italic() ? NSFontItalicTrait : 0; + weight = toAppKitFontWeight(font.weight()); + size = font.pixelSize(); + } + + if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size]) + substituteFont = bestVariation; + + substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont]; + + NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont]; + NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont]; + + FontPlatformData alternateFont(substituteFont, platformData.size(), + !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight), + !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait), + platformData.m_orientation); + return getCachedFontData(&alternateFont); +} + +SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + // Attempt to find an appropriate font using a match based on + // the presence of keywords in the the requested names. For example, we'll + // match any name that contains "Arabic" to Geeza Pro. + SimpleFontData* simpleFontData = 0; + const FontFamily* currFamily = &font.fontDescription().family(); + while (currFamily && !simpleFontData) { + if (currFamily->family().length()) { + static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") }; + DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro")); + for (int j = 0; j < 3 && !simpleFontData; ++j) + if (currFamily->family().contains(*matchWords[j], false)) + simpleFontData = getCachedFontData(font.fontDescription(), geezaStr); + } + currFamily = currFamily->next(); + } + + return simpleFontData; +} + +SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) +{ + DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times")); + + // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick + // the default that the user would get without changing any prefs. + SimpleFontData* simpleFontData = getCachedFontData(fontDescription, timesStr); + if (simpleFontData) + return simpleFontData; + + // The Times fallback will almost always work, but in the highly unusual case where + // the user doesn't have it, we fall back on Lucida Grande because that's + // guaranteed to be there, according to Nathan Taylor. This is good enough + // to avoid a crash at least. + DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande")); + return getCachedFontData(fontDescription, lucidaGrandeStr); +} + +void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) +{ + [WebFontCache getTraits:traitsMasks inFamily:familyName]; +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +{ + NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0; + NSInteger weight = toAppKitFontWeight(fontDescription.weight()); + float size = fontDescription.computedPixelSize(); + + NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size]; + if (!nsFont) + return 0; + + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSFontTraitMask actualTraits = 0; + if (fontDescription.italic()) + actualTraits = [fontManager traitsOfFont:nsFont]; + NSInteger actualWeight = [fontManager weightOfFont:nsFont]; + + NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]; + bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); + bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); + + return new FontPlatformData(platformFont, size, syntheticBold, syntheticOblique, fontDescription.orientation()); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp b/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp new file mode 100644 index 0000000..ca006d9 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp @@ -0,0 +1,127 @@ +/* + * 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 + * 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 "Font.h" + +#include "ComplexTextController.h" +#include "FontFallbackList.h" +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "IntRect.h" +#include "SimpleFontData.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, + int from, int to) const +{ + ComplexTextController controller(this, run); + controller.advance(from); + float beforeWidth = controller.runWidthSoFar(); + controller.advance(to); + float afterWidth = controller.runWidthSoFar(); + + // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning + if (run.rtl()) { + float totalWidth = controller.totalWidth(); + return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); + } + + return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); +} + +float Font::getGlyphsAndAdvancesForComplexText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const +{ + float initialAdvance; + + ComplexTextController controller(this, run, false, 0, forTextEmphasis); + controller.advance(from); + float beforeWidth = controller.runWidthSoFar(); + controller.advance(to, &glyphBuffer); + + if (glyphBuffer.isEmpty()) + return 0; + + float afterWidth = controller.runWidthSoFar(); + + if (run.rtl()) { + initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth; + for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) + glyphBuffer.swap(i, end); + } else + initialAdvance = beforeWidth; + + return initialAdvance; +} + +void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const +{ + // This glyph buffer holds our glyphs + advances + font data for each glyph. + GlyphBuffer glyphBuffer; + + float startX = point.x() + getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer); + + // We couldn't generate any glyphs for the run. Give up. + if (glyphBuffer.isEmpty()) + return; + + // Draw the glyph buffer now at the starting point returned in startX. + FloatPoint startPoint(startX, point.y()); + drawGlyphBuffer(context, glyphBuffer, startPoint); +} + +void Font::drawEmphasisMarksForComplexText(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const +{ + GlyphBuffer glyphBuffer; + float initialAdvance = getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer, ForTextEmphasis); + + if (glyphBuffer.isEmpty()) + return; + + drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y())); +} + +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +{ + ComplexTextController controller(this, run, true, fallbackFonts); + if (glyphOverflow) { + glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - ascent()); + glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - descent()); + glyphOverflow->left = max<int>(0, ceilf(-controller.minGlyphBoundingBoxX())); + glyphOverflow->right = max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.totalWidth())); + } + return controller.totalWidth(); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool includePartialGlyphs) const +{ + ComplexTextController controller(this, run); + return controller.offsetForPosition(x, includePartialGlyphs); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp new file mode 100644 index 0000000..d04d0e4 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2007, 2008, 2010 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 "FontCustomPlatformData.h" + +#include "FontPlatformData.h" +#include "OpenTypeSanitizer.h" +#include "SharedBuffer.h" +#include "WOFFFileFormat.h" +#include <ApplicationServices/ApplicationServices.h> + +namespace WebCore { + +FontCustomPlatformData::~FontCustomPlatformData() +{ +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + if (m_atsContainer) + ATSFontDeactivate(m_atsContainer, NULL, kATSOptionFlagsDefault); +#endif + CGFontRelease(m_cgFont); +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontRenderingMode) +{ + return FontPlatformData(m_cgFont, size, bold, italic, orientation); +} + +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(); +#else + RefPtr<SharedBuffer> sfntBuffer; + if (isWOFF(buffer)) { + Vector<char> sfnt; + if (!convertWOFFToSfnt(buffer, sfnt)) + return 0; + + sfntBuffer = SharedBuffer::adoptVector(sfnt); + buffer = sfntBuffer.get(); + } +#endif + + ATSFontContainerRef containerRef = 0; + + RetainPtr<CGFontRef> cgFontRef; + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + RetainPtr<CFDataRef> bufferData(AdoptCF, buffer->createCFData()); + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(bufferData.get())); + + cgFontRef.adoptCF(CGFontCreateWithDataProvider(dataProvider.get())); + if (!cgFontRef) + return 0; +#else + // Use ATS to activate the font. + + // The value "3" means that the font is private and can't be seen by anyone else. + ATSFontActivateFromMemory((void*)buffer->data(), buffer->size(), 3, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &containerRef); + if (!containerRef) + return 0; + ItemCount fontCount; + ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 0, NULL, &fontCount); + + // We just support the first font in the list. + if (fontCount == 0) { + ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); + return 0; + } + + ATSFontRef fontRef = 0; + ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, &fontRef, NULL); + if (!fontRef) { + ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); + return 0; + } + + cgFontRef.adoptCF(CGFontCreateWithPlatformFont(&fontRef)); +#ifndef BUILDING_ON_TIGER + // Workaround for <rdar://problem/5675504>. + if (cgFontRef && !CGFontGetNumberOfGlyphs(cgFontRef.get())) + cgFontRef = 0; +#endif + if (!cgFontRef) { + ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); + return 0; + } +#endif // !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + + return new FontCustomPlatformData(containerRef, cgFontRef.releaseRef()); +} + +bool FontCustomPlatformData::supportsFormat(const String& format) +{ + return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype") || equalIgnoringCase(format, "woff"); +} + +} diff --git a/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h new file mode 100644 index 0000000..c11858c --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 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. + * + */ + +#ifndef FontCustomPlatformData_h +#define FontCustomPlatformData_h + +#include "FontOrientation.h" +#include "FontRenderingMode.h" +#include <CoreFoundation/CFBase.h> +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +typedef struct CGFont* CGFontRef; +typedef UInt32 ATSFontContainerRef; +typedef UInt32 ATSFontRef; + +namespace WebCore { + +class FontPlatformData; +class SharedBuffer; + +struct FontCustomPlatformData : Noncopyable { + FontCustomPlatformData(ATSFontContainerRef container, CGFontRef cgFont) + : m_atsContainer(container) + , m_cgFont(cgFont) + { + } + + ~FontCustomPlatformData(); + + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + + static bool supportsFormat(const String&); + + ATSFontContainerRef m_atsContainer; + CGFontRef m_cgFont; +}; + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); + +} + +#endif diff --git a/Source/WebCore/platform/graphics/mac/FontMac.mm b/Source/WebCore/platform/graphics/mac/FontMac.mm new file mode 100644 index 0000000..8519667 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/FontMac.mm @@ -0,0 +1,209 @@ +/* + * 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, 2010 Apple 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" + +#import "GlyphBuffer.h" +#import "GraphicsContext.h" +#import "Logging.h" +#import "SimpleFontData.h" +#import "WebCoreSystemInterface.h" +#import <AppKit/AppKit.h> + +#define SYNTHETIC_OBLIQUE_ANGLE 14 + +#ifdef __LP64__ +#define URefCon void* +#else +#define URefCon UInt32 +#endif + +using namespace std; + +namespace WebCore { + +bool Font::canReturnFallbackFontsForComplexText() +{ + return true; +} + +static void showGlyphsWithAdvances(const SimpleFontData* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count) +{ + const FontPlatformData& platformData = font->platformData(); + if (!platformData.isColorBitmapFont()) { + CGAffineTransform savedMatrix; + bool isVertical = font->orientation() == Vertical; + + if (isVertical) { + CGAffineTransform rotateLeftTransform = CGAffineTransformMake(0, -1, 1, 0, 0, 0); + + savedMatrix = CGContextGetTextMatrix(context); + CGAffineTransform runMatrix = CGAffineTransformConcat(savedMatrix, rotateLeftTransform); + // Move start point to put glyphs into original region. + runMatrix.tx = savedMatrix.tx + font->ascent(); + runMatrix.ty = savedMatrix.ty + font->descent(); + CGContextSetTextMatrix(context, runMatrix); + } + + CGContextShowGlyphsWithAdvances(context, glyphs, advances, count); + + if (isVertical) + CGContextSetTextMatrix(context, savedMatrix); + } +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + else { + if (!count) + return; + + Vector<CGPoint, 256> positions(count); + CGAffineTransform matrix = CGAffineTransformInvert(CGContextGetTextMatrix(context)); + positions[0] = CGPointZero; + for (size_t i = 1; i < count; ++i) { + CGSize advance = CGSizeApplyAffineTransform(advances[i - 1], matrix); + positions[i].x = positions[i - 1].x + advance.width; + positions[i].y = positions[i - 1].y + advance.height; + } + CTFontDrawGlyphs(platformData.ctFont(), glyphs, positions.data(), count, context); + } +#endif +} + +void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const +{ + CGContextRef cgContext = context->platformContext(); + + bool shouldSmoothFonts = true; + bool changeFontSmoothing = false; + + switch(fontDescription().fontSmoothing()) { + case Antialiased: { + context->setShouldAntialias(true); + shouldSmoothFonts = false; + changeFontSmoothing = true; + break; + } + case SubpixelAntialiased: { + context->setShouldAntialias(true); + shouldSmoothFonts = true; + changeFontSmoothing = true; + break; + } + case NoSmoothing: { + context->setShouldAntialias(false); + shouldSmoothFonts = false; + changeFontSmoothing = true; + break; + } + case AutoSmoothing: { + // For the AutoSmooth case, don't do anything! Keep the default settings. + break; + } + default: + ASSERT_NOT_REACHED(); + } + + if (!shouldUseSmoothing()) { + shouldSmoothFonts = false; + changeFontSmoothing = true; + } + + bool originalShouldUseFontSmoothing = false; + if (changeFontSmoothing) { + originalShouldUseFontSmoothing = wkCGContextGetShouldSmoothFonts(cgContext); + CGContextSetShouldSmoothFonts(cgContext, shouldSmoothFonts); + } + + const FontPlatformData& platformData = font->platformData(); + NSFont* drawFont; + if (!isPrinterFont()) { + drawFont = [platformData.font() screenFont]; + if (drawFont != platformData.font()) + // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually). + LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen. Using screen font anyway, may result in incorrect metrics.", + [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); + } else { + drawFont = [platformData.font() printerFont]; + if (drawFont != platformData.font()) + NSLog(@"Attempting to set non-printer font (%@) when printing. Using printer font anyway, may result in incorrect metrics.", + [[[platformData.font() fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); + } + + CGContextSetFont(cgContext, platformData.cgFont()); + + CGAffineTransform matrix = CGAffineTransformIdentity; + if (drawFont && !platformData.isColorBitmapFont()) + memcpy(&matrix, [drawFont matrix], sizeof(matrix)); + matrix.b = -matrix.b; + matrix.d = -matrix.d; + if (platformData.m_syntheticOblique) + matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, 0, 0)); + CGContextSetTextMatrix(cgContext, matrix); + + if (drawFont) { + wkSetCGFontRenderingMode(cgContext, drawFont); + CGContextSetFontSize(cgContext, 1.0f); + } else + CGContextSetFontSize(cgContext, platformData.m_size); + + + FloatSize shadowOffset; + float shadowBlur; + Color shadowColor; + ColorSpace shadowColorSpace; + ColorSpace fillColorSpace = context->fillColorSpace(); + context->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); + + bool hasSimpleShadow = context->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && !platformData.isColorBitmapFont() && (!context->shadowsIgnoreTransforms() || context->getCTM().isIdentityOrTranslationOrFlipped()); + if (hasSimpleShadow) { + // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. + context->clearShadow(); + Color fillColor = context->fillColor(); + Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); + context->setFillColor(shadowFillColor, shadowColorSpace); + float shadowTextX = point.x() + shadowOffset.width(); + // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. + float shadowTextY = point.y() + shadowOffset.height() * (context->shadowsIgnoreTransforms() ? -1 : 1); + CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY); + showGlyphsWithAdvances(font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + if (font->syntheticBoldOffset()) { + CGContextSetTextPosition(cgContext, shadowTextX + font->syntheticBoldOffset(), shadowTextY); + showGlyphsWithAdvances(font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + } + context->setFillColor(fillColor, fillColorSpace); + } + + CGContextSetTextPosition(cgContext, point.x(), point.y()); + showGlyphsWithAdvances(font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + if (font->syntheticBoldOffset()) { + CGContextSetTextPosition(cgContext, point.x() + font->syntheticBoldOffset(), point.y()); + showGlyphsWithAdvances(font, cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); + } + + if (hasSimpleShadow) + context->setShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); + + if (changeFontSmoothing) + CGContextSetShouldSmoothFonts(cgContext, originalShouldUseFontSmoothing); +} + +} diff --git a/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp b/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp new file mode 100644 index 0000000..5388c24 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GlyphPageTreeNode.h" +#include "Font.h" + +#include "SimpleFontData.h" +#include "WebCoreSystemInterface.h" +#include <ApplicationServices/ApplicationServices.h> + +namespace WebCore { + +#ifndef BUILDING_ON_TIGER +static bool shouldUseCoreText(UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + if (fontData->orientation() == Vertical && !fontData->isBrokenIdeographFont()) { + // Ideographs don't have a vertical variant. + for (unsigned i = 0; i < bufferLength; ++i) { + if (!Font::isCJKIdeograph(buffer[i])) + return true; + } + } + + return false; +} +#endif + +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) +{ + bool haveGlyphs = false; + +#ifndef BUILDING_ON_TIGER + if (!shouldUseCoreText(buffer, bufferLength, fontData)) { + Vector<CGGlyph, 512> glyphs(bufferLength); + wkGetGlyphsForCharacters(fontData->platformData().cgFont(), buffer, glyphs.data(), bufferLength); + for (unsigned i = 0; i < length; ++i) { + if (!glyphs[i]) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyphs[i], fontData); + haveGlyphs = true; + } + } + } else { + // We ask CoreText for possible vertical variant glyphs + RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buffer, bufferLength, kCFAllocatorNull)); + RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(kCFAllocatorDefault, string.get(), fontData->getCFStringAttributes(0))); + RetainPtr<CTLineRef> line(AdoptCF, CTLineCreateWithAttributedString(attributedString.get())); + + CFArrayRef runArray = CTLineGetGlyphRuns(line.get()); + CFIndex runCount = CFArrayGetCount(runArray); + + // Initialize glyph entries + for (unsigned index = 0; index < length; ++index) + setGlyphDataForIndex(offset + index, 0, 0); + + Vector<CGGlyph, 512> glyphVector; + Vector<CFIndex, 512> indexVector; + bool done = false; + for (CFIndex r = 0; r < runCount && !done ; ++r) { + // CTLine could map characters over multiple fonts using its own font fallback list. + // We need to pick runs that use the exact font we need, i.e., fontData->platformData().ctFont(). + CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); + ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); + + CFDictionaryRef attributes = CTRunGetAttributes(ctRun); + CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(attributes, kCTFontAttributeName)); + RetainPtr<CGFontRef> runCGFont(AdoptCF, CTFontCopyGraphicsFont(runFont, 0)); + // Use CGFont here as CFEqual for CTFont counts all attributes for font. + if (CFEqual(fontData->platformData().cgFont(), runCGFont.get())) { + // This run uses the font we want. Extract glyphs. + CFIndex glyphCount = CTRunGetGlyphCount(ctRun); + const CGGlyph* glyphs = CTRunGetGlyphsPtr(ctRun); + if (!glyphs) { + glyphVector.resize(glyphCount); + CTRunGetGlyphs(ctRun, CFRangeMake(0, 0), glyphVector.data()); + glyphs = glyphVector.data(); + } + const CFIndex* stringIndices = CTRunGetStringIndicesPtr(ctRun); + if (!stringIndices) { + indexVector.resize(glyphCount); + CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), indexVector.data()); + stringIndices = indexVector.data(); + } + + for (CFIndex i = 0; i < glyphCount; ++i) { + if (stringIndices[i] >= static_cast<CFIndex>(length)) { + done = true; + break; + } + if (glyphs[i]) { + setGlyphDataForIndex(offset + stringIndices[i], glyphs[i], fontData); + haveGlyphs = true; + } + } + } + } + } +#else + // Use an array of long so we get good enough alignment. + long glyphVector[(GLYPH_VECTOR_SIZE + sizeof(long) - 1) / sizeof(long)]; + + OSStatus status = wkInitializeGlyphVector(GlyphPage::size, &glyphVector); + if (status != noErr) + // This should never happen, perhaps indicates a bad font! If it does the + // font substitution code will find an alternate font. + return false; + + wkConvertCharToGlyphs(fontData->m_styleGroup, buffer, bufferLength, &glyphVector); + + unsigned numGlyphs = wkGetGlyphVectorNumGlyphs(&glyphVector); + if (numGlyphs != length) { + // This should never happen, perhaps indicates a bad font? + // If it does happen, the font substitution code will find an alternate font. + wkClearGlyphVector(&glyphVector); + return false; + } + + ATSLayoutRecord* glyphRecord = (ATSLayoutRecord*)wkGetGlyphVectorFirstRecord(glyphVector); + for (unsigned i = 0; i < length; i++) { + Glyph glyph = glyphRecord->glyphID; + if (!glyph) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyph, fontData); + haveGlyphs = true; + } + glyphRecord = (ATSLayoutRecord *)((char *)glyphRecord + wkGetGlyphVectorRecordSize(glyphVector)); + } + wkClearGlyphVector(&glyphVector); +#endif + + return haveGlyphs; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm b/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm new file mode 100644 index 0000000..321d0ef --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(3D_CANVAS) + +#include "GraphicsContext3D.h" + +#import "BlockExceptions.h" + +#include "ANGLE/ShaderLang.h" +#include "ArrayBuffer.h" +#include "ArrayBufferView.h" +#include "CanvasRenderingContext.h" +#include <CoreGraphics/CGBitmapContext.h> +#include "Extensions3DOpenGL.h" +#include "Float32Array.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include "ImageBuffer.h" +#include "Int32Array.h" +#include "NotImplemented.h" +#include <OpenGL/CGLRenderers.h> +#include <OpenGL/gl.h> +#include "Uint8Array.h" +#include "WebGLLayer.h" +#include "WebGLObject.h" +#include <wtf/UnusedParam.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +static void setPixelFormat(Vector<CGLPixelFormatAttribute>& attribs, int colorBits, int depthBits, bool accelerated, bool supersample, bool closest) +{ + attribs.clear(); + + attribs.append(kCGLPFAColorSize); + attribs.append(static_cast<CGLPixelFormatAttribute>(colorBits)); + attribs.append(kCGLPFADepthSize); + attribs.append(static_cast<CGLPixelFormatAttribute>(depthBits)); + + if (accelerated) + attribs.append(kCGLPFAAccelerated); + else { + attribs.append(kCGLPFARendererID); + attribs.append(static_cast<CGLPixelFormatAttribute>(kCGLRendererGenericFloatID)); + } + + if (supersample) + attribs.append(kCGLPFASupersample); + + if (closest) + attribs.append(kCGLPFAClosestPolicy); + + attribs.append(static_cast<CGLPixelFormatAttribute>(0)); +} + +PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) +{ + // This implementation doesn't currently support rendering directly to the HostWindow. + if (renderStyle == RenderDirectlyToHostWindow) + return 0; + RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attrs, hostWindow, false)); + return context->m_contextObj ? context.release() : 0; +} + +GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, bool) + : m_currentWidth(0) + , m_currentHeight(0) + , m_attrs(attrs) + , m_contextObj(0) + , m_texture(0) + , m_fbo(0) + , m_depthStencilBuffer(0) + , m_boundFBO(0) + , m_multisampleFBO(0) + , m_multisampleDepthStencilBuffer(0) + , m_multisampleColorBuffer(0) +{ + UNUSED_PARAM(hostWindow); + + Vector<CGLPixelFormatAttribute> attribs; + CGLPixelFormatObj pixelFormatObj = 0; + GLint numPixelFormats = 0; + + // We will try: + // + // 1) 32 bit RGBA/32 bit depth/accelerated/supersampled + // 2) 32 bit RGBA/32 bit depth/accelerated + // 3) 32 bit RGBA/16 bit depth/accelerated + // 4) closest to 32 bit RGBA/16 bit depth/software renderer + // + // If none of that works, we simply fail and set m_contextObj to 0. + + setPixelFormat(attribs, 32, 32, true, true, false); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + if (numPixelFormats == 0) { + setPixelFormat(attribs, 32, 32, true, false, false); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + + if (numPixelFormats == 0) { + setPixelFormat(attribs, 32, 16, true, false, false); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + + if (numPixelFormats == 0) { + setPixelFormat(attribs, 32, 16, false, false, true); + CGLChoosePixelFormat(attribs.data(), &pixelFormatObj, &numPixelFormats); + + if (numPixelFormats == 0) { + // Could not find an acceptable renderer - fail + return; + } + } + } + } + + CGLError err = CGLCreateContext(pixelFormatObj, 0, &m_contextObj); + CGLDestroyPixelFormat(pixelFormatObj); + + if (err != kCGLNoError || !m_contextObj) { + // Could not create the context - fail + m_contextObj = 0; + return; + } + + // Set the current context to the one given to us. + CGLSetCurrentContext(m_contextObj); + + validateAttributes(); + + // Create the WebGLLayer + BEGIN_BLOCK_OBJC_EXCEPTIONS + m_webGLLayer.adoptNS([[WebGLLayer alloc] initWithGraphicsContext3D:this]); +#ifndef NDEBUG + [m_webGLLayer.get() setName:@"WebGL Layer"]; +#endif + END_BLOCK_OBJC_EXCEPTIONS + + // create a texture to render into + ::glGenTextures(1, &m_texture); + ::glBindTexture(GL_TEXTURE_2D, m_texture); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + ::glBindTexture(GL_TEXTURE_2D, 0); + + // create an FBO + ::glGenFramebuffersEXT(1, &m_fbo); + ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + + m_boundFBO = m_fbo; + if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth)) + ::glGenRenderbuffersEXT(1, &m_depthStencilBuffer); + + // create an multisample FBO + if (m_attrs.antialias) { + ::glGenFramebuffersEXT(1, &m_multisampleFBO); + ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO); + m_boundFBO = m_multisampleFBO; + ::glGenRenderbuffersEXT(1, &m_multisampleColorBuffer); + if (m_attrs.stencil || m_attrs.depth) + ::glGenRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer); + } + + // ANGLE initialization. + + TBuiltInResource ANGLEResources; + + ANGLEResources.MaxVertexAttribs = 0; + getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs); + ANGLEResources.MaxVertexUniformVectors = 0; + getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors); + ANGLEResources.MaxVaryingVectors = 0; + getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors); + ANGLEResources.MaxVertexTextureImageUnits = 0; + getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits); + ANGLEResources.MaxCombinedTextureImageUnits = 0; + getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits); + ANGLEResources.MaxTextureImageUnits = 0; + getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits); + ANGLEResources.MaxFragmentUniformVectors = 0; + getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors); + + // Always set to 1 for OpenGL ES. + ANGLEResources.MaxDrawBuffers = 1; + + m_compiler.setResources(ANGLEResources); + + ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + ::glEnable(GL_POINT_SPRITE); + + ::glClearColor(0, 0, 0, 0); +} + +GraphicsContext3D::~GraphicsContext3D() +{ + if (m_contextObj) { + CGLSetCurrentContext(m_contextObj); + ::glDeleteTextures(1, &m_texture); + if (m_attrs.antialias) { + ::glDeleteRenderbuffersEXT(1, &m_multisampleColorBuffer); + if (m_attrs.stencil || m_attrs.depth) + ::glDeleteRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer); + ::glDeleteFramebuffersEXT(1, &m_multisampleFBO); + } else { + if (m_attrs.stencil || m_attrs.depth) + ::glDeleteRenderbuffersEXT(1, &m_depthStencilBuffer); + } + ::glDeleteFramebuffersEXT(1, &m_fbo); + CGLSetCurrentContext(0); + CGLDestroyContext(m_contextObj); + } +} + +void GraphicsContext3D::makeContextCurrent() +{ + if (!m_contextObj) + return; + + CGLContextObj currentContext = CGLGetCurrentContext(); + if (currentContext != m_contextObj) + CGLSetCurrentContext(m_contextObj); +} + +bool GraphicsContext3D::isGLES2Compliant() const +{ + return false; +} + +} + +#endif // ENABLE(3D_CANVAS) diff --git a/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm new file mode 100644 index 0000000..6a4fa03 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "GraphicsContext.h" + +#import "GraphicsContextPlatformPrivateCG.h" +#import <AppKit/AppKit.h> +#import <wtf/StdLibExtras.h> + +#import "LocalCurrentGraphicsContext.h" +#import "WebCoreSystemInterface.h" + +@class NSColor; + +// FIXME: More of this should use CoreGraphics instead of AppKit. +// FIXME: More of this should move into GraphicsContextCG.cpp. + +namespace WebCore { + +// NSColor, NSBezierPath, and NSGraphicsContext +// calls in this file are all exception-safe, so we don't block +// exceptions for those. + +static void drawFocusRingToContext(CGContextRef context, CGPathRef focusRingPath, CGColorRef color, int radius) +{ +#ifdef BUILDING_ON_TIGER + CGContextBeginTransparencyLayer(context, 0); +#endif + CGContextBeginPath(context); + CGContextAddPath(context, focusRingPath); + wkDrawFocusRing(context, color, radius); +#ifdef BUILDING_ON_TIGER + CGContextEndTransparencyLayer(context); +#endif +} + +void GraphicsContext::drawFocusRing(const Path& path, int width, int /*offset*/, const Color& color) +{ + // FIXME: Use 'offset' for something? http://webkit.org/b/49909 + + if (paintingDisabled()) + return; + + int radius = (width - 1) / 2; + CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0; + + drawFocusRingToContext(platformContext(), path.platformPath(), colorRef, radius); +} + +void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) +{ + if (paintingDisabled()) + return; + + int radius = (width - 1) / 2; + offset += radius; + CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0; + + RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable()); + unsigned rectCount = rects.size(); + for (unsigned i = 0; i < rectCount; i++) + CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rects[i], -offset, -offset)); + + drawFocusRingToContext(platformContext(), focusRingPath.get(), colorRef, radius); +} + +#ifdef BUILDING_ON_TIGER // Post-Tiger's setPlatformCompositeOperation() is defined in GraphicsContextCG.cpp. +void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) +{ + if (paintingDisabled()) + return; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + [[NSGraphicsContext graphicsContextWithGraphicsPort:platformContext() flipped:YES] + setCompositingOperation:(NSCompositingOperation)op]; + [pool drain]; +} +#endif + +static NSColor* createPatternColor(NSString* name, NSColor* defaultColor, bool& usingDot) +{ + NSImage *image = [NSImage imageNamed:name]; + ASSERT(image); // if image is not available, we want to know + NSColor *color = (image ? [NSColor colorWithPatternImage:image] : nil); + if (color) + usingDot = true; + else + color = defaultColor; + return color; +} + +// WebKit on Mac is a standard platform component, so it must use the standard platform artwork for underline. +void GraphicsContext::drawLineForTextChecking(const IntPoint& point, int width, TextCheckingLineStyle style) +{ + if (paintingDisabled()) + return; + + // These are the same for misspelling or bad grammar. + int patternHeight = cMisspellingLineThickness; + int patternWidth = cMisspellingLinePatternWidth; + + bool usingDot; + NSColor *patternColor; + switch (style) { + case TextCheckingSpellingLineStyle: + { + // Constants for spelling pattern color. + static bool usingDotForSpelling = false; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"SpellingDot", [NSColor redColor], usingDotForSpelling))); + usingDot = usingDotForSpelling; + patternColor = spellingPatternColor.get(); + break; + } + case TextCheckingGrammarLineStyle: + { + // Constants for grammar pattern color. + static bool usingDotForGrammar = false; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, grammarPatternColor, (createPatternColor(@"GrammarDot", [NSColor greenColor], usingDotForGrammar))); + usingDot = usingDotForGrammar; + patternColor = grammarPatternColor.get(); + break; + } +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + // To support correction panel. + case TextCheckingReplacementLineStyle: + { + // Constants for spelling pattern color. + static bool usingDotForSpelling = false; + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, spellingPatternColor, (createPatternColor(@"CorrectionDot", [NSColor blueColor], usingDotForSpelling))); + usingDot = usingDotForSpelling; + patternColor = spellingPatternColor.get(); + break; + } +#endif + default: + return; + } + + // Make sure to draw only complete dots. + // NOTE: Code here used to shift the underline to the left and increase the width + // to make sure everything gets underlined, but that results in drawing out of + // bounds (e.g. when at the edge of a view) and could make it appear that the + // space between adjacent misspelled words was underlined. + if (usingDot) { + // allow slightly more considering that the pattern ends with a transparent pixel + int widthMod = width % patternWidth; + if (patternWidth - widthMod > cMisspellingLinePatternGapWidth) + width -= widthMod; + } + + // FIXME: This code should not use NSGraphicsContext currentContext + // In order to remove this requirement we will need to use CGPattern instead of NSColor + // FIXME: This code should not be using wkSetPatternPhaseInUserSpace, as this approach is wrong + // for transforms. + + // Draw underline. + LocalCurrentGraphicsContext localContext(this); + NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; + CGContextRef context = (CGContextRef)[currentContext graphicsPort]; + CGContextSaveGState(context); + + [patternColor set]; + + wkSetPatternPhaseInUserSpace(context, point); + + NSRectFillUsingOperation(NSMakeRect(point.x(), point.y(), width, patternHeight), NSCompositeSourceOver); + + CGContextRestoreGState(context); +} + +} diff --git a/Source/WebCore/platform/graphics/mac/IconMac.mm b/Source/WebCore/platform/graphics/mac/IconMac.mm new file mode 100644 index 0000000..bc8c312 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/IconMac.mm @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2006, 2007, 2008 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. + * + */ + +#import "config.h" +#import "Icon.h" + +#import "GraphicsContext.h" +#import "LocalCurrentGraphicsContext.h" +#import "PlatformString.h" +#import <wtf/PassRefPtr.h> + +namespace WebCore { + +Icon::Icon(NSImage *image) + : m_nsImage(image) +{ + // Need this because WebCore uses AppKit's flipped coordinate system exclusively. + [image setFlipped:YES]; +} + +Icon::~Icon() +{ +} + +// FIXME: Move the code to ChromeClient::iconForFiles(). +PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) +{ + if (filenames.isEmpty()) + 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; + + NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile:filenames[0]]; + if (!image) + return 0; + + return adoptRef(new Icon(image)); + } +#ifdef BUILDING_ON_TIGER + return 0; +#else + NSImage* image = [NSImage imageNamed:NSImageNameMultipleDocuments]; + if (!image) + return 0; + + return adoptRef(new Icon(image)); +#endif +} + +void Icon::paint(GraphicsContext* context, const IntRect& rect) +{ + if (context->paintingDisabled()) + return; + + LocalCurrentGraphicsContext localCurrentGC(context); + + [m_nsImage.get() drawInRect:rect + fromRect:NSMakeRect(0, 0, [m_nsImage.get() size].width, [m_nsImage.get() size].height) + operation:NSCompositeSourceOver fraction:1.0f]; +} + +} diff --git a/Source/WebCore/platform/graphics/mac/ImageMac.mm b/Source/WebCore/platform/graphics/mac/ImageMac.mm new file mode 100644 index 0000000..6ad3080 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/ImageMac.mm @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "BitmapImage.h" + +#import "FloatRect.h" +#import "GraphicsContext.h" +#import "PlatformString.h" +#import "SharedBuffer.h" + +@interface WebCoreBundleFinder : NSObject +@end + +@implementation WebCoreBundleFinder +@end + +namespace WebCore { + +void BitmapImage::initPlatformData() +{ +} + +void BitmapImage::invalidatePlatformData() +{ + if (m_frames.size() != 1) + return; + + m_nsImage = 0; + m_tiffRep = 0; +} + +PassRefPtr<Image> Image::loadPlatformResource(const char *name) +{ + NSBundle *bundle = [NSBundle bundleForClass:[WebCoreBundleFinder class]]; + NSString *imagePath = [bundle pathForResource:[NSString stringWithUTF8String:name] ofType:@"tiff"]; + NSData *namedImageData = [NSData dataWithContentsOfFile:imagePath]; + if (namedImageData) { + RefPtr<Image> image = BitmapImage::create(); + image->setData(SharedBuffer::wrapNSData(namedImageData), true); + return image.release(); + } + + // We have reports indicating resource loads are failing, but we don't yet know the root cause(s). + // Two theories are bad installs (image files are missing), and too-many-open-files. + // See rdar://5607381 + ASSERT_NOT_REACHED(); + return Image::nullImage(); +} + +CFDataRef BitmapImage::getTIFFRepresentation() +{ + if (m_tiffRep) + return m_tiffRep.get(); + + unsigned numFrames = frameCount(); + + // If numFrames is zero, we know for certain this image doesn't have valid data + // Even though the call to CGImageDestinationCreateWithData will fail and we'll handle it gracefully, + // in certain circumstances that call will spam the console with an error message + if (!numFrames) + return 0; + + Vector<CGImageRef> images; + for (unsigned i = 0; i < numFrames; ++i ) { + CGImageRef cgImage = frameAtIndex(i); + if (cgImage) + images.append(cgImage); + } + + unsigned numValidFrames = images.size(); + + RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(0, 0)); + // FIXME: Use type kCGImageTypeIdentifierTIFF constant once is becomes available in the API + RetainPtr<CGImageDestinationRef> destination(AdoptCF, CGImageDestinationCreateWithData(data.get(), CFSTR("public.tiff"), numValidFrames, 0)); + + if (!destination) + return 0; + + for (unsigned i = 0; i < numValidFrames; ++i) + CGImageDestinationAddImage(destination.get(), images[i], 0); + + CGImageDestinationFinalize(destination.get()); + + m_tiffRep = data; + return m_tiffRep.get(); +} + +NSImage* BitmapImage::getNSImage() +{ + if (m_nsImage) + return m_nsImage.get(); + + CFDataRef data = getTIFFRepresentation(); + if (!data) + return 0; + + m_nsImage.adoptNS([[NSImage alloc] initWithData:(NSData*)data]); + return m_nsImage.get(); +} + +} diff --git a/Source/WebCore/platform/graphics/mac/IntPointMac.mm b/Source/WebCore/platform/graphics/mac/IntPointMac.mm new file mode 100644 index 0000000..7a2e730 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/IntPointMac.mm @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IntPoint.h" + +namespace WebCore { + +#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES + +IntPoint::IntPoint(const NSPoint& p) : m_x(static_cast<int>(p.x)), m_y(static_cast<int>(p.y)) +{ +} + +IntPoint::operator NSPoint() const +{ + return NSMakePoint(m_x, m_y); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/mac/IntRectMac.mm b/Source/WebCore/platform/graphics/mac/IntRectMac.mm new file mode 100644 index 0000000..738618a --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/IntRectMac.mm @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IntRect.h" + +namespace WebCore { + +#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES + +IntRect::operator NSRect() const +{ + return NSMakeRect(x(), y(), width(), height()); +} + +IntRect enclosingIntRect(const NSRect& rect) +{ + int l = static_cast<int>(floorf(rect.origin.x)); + int t = static_cast<int>(floorf(rect.origin.y)); + int r = static_cast<int>(ceilf(NSMaxX(rect))); + int b = static_cast<int>(ceilf(NSMaxY(rect))); + return IntRect(l, t, r - l, b - t); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/mac/IntSizeMac.mm b/Source/WebCore/platform/graphics/mac/IntSizeMac.mm new file mode 100644 index 0000000..c7dcd88 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/IntSizeMac.mm @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IntSize.h" + +namespace WebCore { + +#ifndef NSGEOMETRY_TYPES_SAME_AS_CGGEOMETRY_TYPES + +IntSize::IntSize(const NSSize& s) : m_width(static_cast<int>(s.width)), m_height(static_cast<int>(s.height)) +{ +} + +IntSize::operator NSSize() const +{ + return NSMakeSize(m_width, m_height); +} + +#endif + +} diff --git a/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h new file mode 100644 index 0000000..95ab456 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlayerPrivateQTKit_h +#define MediaPlayerPrivateQTKit_h + +#if ENABLE(VIDEO) + +#include "MediaPlayerPrivate.h" +#include "Timer.h" +#include "FloatSize.h" +#include <wtf/RetainPtr.h> + +#ifdef __OBJC__ +#import <QTKit/QTTime.h> +@class QTMovie; +@class QTMovieView; +@class QTMovieLayer; +@class QTVideoRendererWebKitOnly; +@class WebCoreMovieObserver; +#else +class QTMovie; +class QTMovieView; +class QTTime; +class QTMovieLayer; +class QTVideoRendererWebKitOnly; +class WebCoreMovieObserver; +#endif + +#ifndef DRAW_FRAME_RATE +#define DRAW_FRAME_RATE 0 +#endif + +namespace WebCore { + +class MediaPlayerPrivateQTKit : public MediaPlayerPrivateInterface { +public: + static void registerMediaEngine(MediaEngineRegistrar); + + void repaint(); + void loadStateChanged(); + void rateChanged(); + void sizeChanged(); + void timeChanged(); + void didEnd(); + +private: + MediaPlayerPrivateQTKit(MediaPlayer*); + ~MediaPlayerPrivateQTKit(); + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + + PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer() const; +#endif + + IntSize naturalSize() const; + bool hasVideo() const; + bool hasAudio() const; + bool supportsFullscreen() const; + + void load(const String& url); + void cancelLoad(); + void loadInternal(const String& url); + void resumeLoad(); + + void play(); + void pause(); + void prepareToPlay(); + + bool paused() const; + bool seeking() const; + + float duration() const; + float currentTime() const; + void seek(float time); + + void setRate(float); + void setVolume(float); + void setPreservesPitch(bool); + + bool hasClosedCaptions() const; + void setClosedCaptionsVisible(bool); + + void setPreload(MediaPlayer::Preload); + + MediaPlayer::NetworkState networkState() const { return m_networkState; } + MediaPlayer::ReadyState readyState() const { return m_readyState; } + + PassRefPtr<TimeRanges> buffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + unsigned totalBytes() const; + + void setVisible(bool); + void setSize(const IntSize&); + + virtual bool hasAvailableVideoFrame() const; + + void paint(GraphicsContext*, const IntRect&); + void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); + virtual void prepareForRendering(); + + +#if USE(ACCELERATED_COMPOSITING) + bool supportsAcceleratedRendering() const; + void acceleratedRenderingStateChanged(); +#endif + + bool hasSingleSecurityOrigin() const; + MediaPlayer::MovieLoadType movieLoadType() const; + + void createQTMovie(const String& url); + void createQTMovie(NSURL *, NSDictionary *movieAttributes); + + enum MediaRenderingMode { MediaRenderingNone, MediaRenderingMovieView, MediaRenderingSoftwareRenderer, MediaRenderingMovieLayer }; + MediaRenderingMode currentRenderingMode() const; + MediaRenderingMode preferredRenderingMode() const; + + void setUpVideoRendering(); + void tearDownVideoRendering(); + bool hasSetUpVideoRendering() const; + + void createQTMovieView(); + void detachQTMovieView(); + + enum QTVideoRendererMode { QTVideoRendererModeDefault, QTVideoRendererModeListensForNewImages }; + void createQTVideoRenderer(QTVideoRendererMode rendererMode); + void destroyQTVideoRenderer(); + + void createQTMovieLayer(); + void destroyQTMovieLayer(); + + QTTime createQTTime(float time) const; + + void updateStates(); + void doSeek(); + void cancelSeek(); + void seekTimerFired(Timer<MediaPlayerPrivateQTKit>*); + float maxTimeLoaded() const; + void disableUnsupportedTracks(); + + void sawUnsupportedTracks(); + void cacheMovieScale(); + bool metaDataAvailable() const { return m_qtMovie && m_readyState >= MediaPlayer::HaveMetadata; } + + bool isReadyForVideoSetup() const; + + virtual float mediaTimeForTimeValue(float) const; + + virtual double maximumDurationToCacheMediaTime() const { return 5; } + + MediaPlayer* m_player; + RetainPtr<QTMovie> m_qtMovie; + RetainPtr<QTMovieView> m_qtMovieView; + RetainPtr<QTVideoRendererWebKitOnly> m_qtVideoRenderer; + RetainPtr<WebCoreMovieObserver> m_objcObserver; + String m_movieURL; + float m_seekTo; + Timer<MediaPlayerPrivateQTKit> m_seekTimer; + MediaPlayer::NetworkState m_networkState; + MediaPlayer::ReadyState m_readyState; + IntRect m_rect; + FloatSize m_scaleFactor; + unsigned m_enabledTrackCount; + unsigned m_totalTrackCount; + float m_reportedDuration; + float m_cachedDuration; + float m_timeToRestore; + RetainPtr<QTMovieLayer> m_qtVideoLayer; + MediaPlayer::Preload m_preload; + bool m_startedPlaying; + bool m_isStreaming; + bool m_visible; + bool m_hasUnsupportedTracks; + bool m_videoFrameHasDrawn; + bool m_delayingLoad; + bool m_isAllowedToRender; +#if DRAW_FRAME_RATE + int m_frameCountWhilePlaying; + double m_timeStartedPlaying; + double m_timeStoppedPlaying; +#endif +}; + +} + +#endif +#endif diff --git a/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm new file mode 100644 index 0000000..2361f6a --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -0,0 +1,1673 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" + +#if ENABLE(VIDEO) + +#import "MediaPlayerPrivateQTKit.h" + +#ifdef BUILDING_ON_TIGER +#import "AutodrainedPool.h" +#endif + +#import "BlockExceptions.h" +#import "FrameView.h" +#import "GraphicsContext.h" +#import "KURL.h" +#import "MIMETypeRegistry.h" +#import "SoftLinking.h" +#import "TimeRanges.h" +#import "WebCoreSystemInterface.h" +#import <QTKit/QTKit.h> +#import <objc/objc-runtime.h> +#import <wtf/UnusedParam.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayer.h" +#endif + +#if DRAW_FRAME_RATE +#import "Font.h" +#import "Frame.h" +#import "Document.h" +#import "RenderObject.h" +#import "RenderStyle.h" +#endif + +#ifdef BUILDING_ON_TIGER +static IMP method_setImplementation(Method m, IMP imp) +{ + IMP result = m->method_imp; + m->method_imp = imp; + return result; +} +#endif + +SOFT_LINK_FRAMEWORK(QTKit) + +SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale)) + +SOFT_LINK_CLASS(QTKit, QTMovie) +SOFT_LINK_CLASS(QTKit, QTMovieView) +SOFT_LINK_CLASS(QTKit, QTMovieLayer) + +SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieLoopsAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieHasAudioAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieIsActiveAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieRateChangesPreservePitchAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *) +SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *) +SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *) +#ifndef BUILDING_ON_TIGER +SOFT_LINK_POINTER(QTKit, QTMovieApertureModeClean, NSString *) +SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) +#endif + +#define QTMovie getQTMovieClass() +#define QTMovieView getQTMovieViewClass() +#define QTMovieLayer getQTMovieLayerClass() + +#define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute() +#define QTMediaTypeAttribute getQTMediaTypeAttribute() +#define QTMediaTypeBase getQTMediaTypeBase() +#define QTMediaTypeMPEG getQTMediaTypeMPEG() +#define QTMediaTypeSound getQTMediaTypeSound() +#define QTMediaTypeText getQTMediaTypeText() +#define QTMediaTypeVideo getQTMediaTypeVideo() +#define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute() +#define QTMovieLoopsAttribute getQTMovieLoopsAttribute() +#define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute() +#define QTMovieDidEndNotification getQTMovieDidEndNotification() +#define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute() +#define QTMovieHasAudioAttribute getQTMovieHasAudioAttribute() +#define QTMovieIsActiveAttribute getQTMovieIsActiveAttribute() +#define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute() +#define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification() +#define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute() +#define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute() +#define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute() +#define QTMovieRateChangesPreservePitchAttribute getQTMovieRateChangesPreservePitchAttribute() +#define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() +#define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification() +#define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification() +#define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute() +#define QTMovieURLAttribute getQTMovieURLAttribute() +#define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification() +#define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute() +#define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification() +#ifndef BUILDING_ON_TIGER +#define QTMovieApertureModeClean getQTMovieApertureModeClean() +#define QTMovieApertureModeAttribute getQTMovieApertureModeAttribute() +#endif + +// Older versions of the QTKit header don't have these constants. +#if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0 +enum { + QTMovieLoadStateError = -1L, + QTMovieLoadStateLoaded = 2000L, + QTMovieLoadStatePlayable = 10000L, + QTMovieLoadStatePlaythroughOK = 20000L, + QTMovieLoadStateComplete = 100000L +}; +#endif + +@interface FakeQTMovieView : NSObject +- (WebCoreMovieObserver *)delegate; +@end + +using namespace WebCore; +using namespace std; + +@interface WebCoreMovieObserver : NSObject +{ + MediaPlayerPrivateQTKit* m_callback; + NSView* m_view; + BOOL m_delayCallbacks; +} +-(id)initWithCallback:(MediaPlayerPrivateQTKit*)callback; +-(void)disconnect; +-(void)setView:(NSView*)view; +-(void)repaint; +-(void)setDelayCallbacks:(BOOL)shouldDelay; +-(void)loadStateChanged:(NSNotification *)notification; +-(void)rateChanged:(NSNotification *)notification; +-(void)sizeChanged:(NSNotification *)notification; +-(void)timeChanged:(NSNotification *)notification; +-(void)didEnd:(NSNotification *)notification; +@end + +@protocol WebKitVideoRenderingDetails +-(void)setMovie:(id)movie; +-(void)drawInRect:(NSRect)rect; +@end + +namespace WebCore { + +#ifdef BUILDING_ON_TIGER +static const long minimumQuickTimeVersion = 0x07300000; // 7.3 +#endif + + +MediaPlayerPrivateInterface* MediaPlayerPrivateQTKit::create(MediaPlayer* player) +{ + return new MediaPlayerPrivateQTKit(player); +} + +void MediaPlayerPrivateQTKit::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + +MediaPlayerPrivateQTKit::MediaPlayerPrivateQTKit(MediaPlayer* player) + : m_player(player) + , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) + , m_seekTo(-1) + , m_seekTimer(this, &MediaPlayerPrivateQTKit::seekTimerFired) + , m_networkState(MediaPlayer::Empty) + , m_readyState(MediaPlayer::HaveNothing) + , m_rect() + , m_scaleFactor(1, 1) + , m_enabledTrackCount(0) + , m_totalTrackCount(0) + , m_reportedDuration(-1) + , m_cachedDuration(-1) + , m_timeToRestore(-1) + , m_preload(MediaPlayer::Auto) + , m_startedPlaying(false) + , m_isStreaming(false) + , m_visible(false) + , m_hasUnsupportedTracks(false) + , m_videoFrameHasDrawn(false) + , m_isAllowedToRender(false) +#if DRAW_FRAME_RATE + , m_frameCountWhilePlaying(0) + , m_timeStartedPlaying(0) + , m_timeStoppedPlaying(0) +#endif +{ +} + +MediaPlayerPrivateQTKit::~MediaPlayerPrivateQTKit() +{ + tearDownVideoRendering(); + + [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; + [m_objcObserver.get() disconnect]; +} + +void MediaPlayerPrivateQTKit::createQTMovie(const String& url) +{ + NSURL *cocoaURL = KURL(ParsedURLString, url); + NSMutableDictionary *movieAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys: + cocoaURL, QTMovieURLAttribute, + [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute, + [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, + [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, + [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, + [NSNumber numberWithBool:NO], QTMovieLoopsAttribute, +#ifndef BUILDING_ON_TIGER + QTMovieApertureModeClean, QTMovieApertureModeAttribute, +#endif + nil]; + +#if !defined(BUILDING_ON_LEOPARD) + CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings(); + CFArrayRef proxiesForURL = CFNetworkCopyProxiesForURL((CFURLRef)cocoaURL, proxySettings); + BOOL willUseProxy = YES; + + if (!proxiesForURL || !CFArrayGetCount(proxiesForURL)) + willUseProxy = NO; + + if (CFArrayGetCount(proxiesForURL) == 1) { + CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxiesForURL, 0); + ASSERT(CFGetTypeID(proxy) == CFDictionaryGetTypeID()); + + CFStringRef proxyType = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyTypeKey); + ASSERT(CFGetTypeID(proxyType) == CFStringGetTypeID()); + + if (CFStringCompare(proxyType, kCFProxyTypeNone, 0) == kCFCompareEqualTo) + willUseProxy = NO; + } + + if (!willUseProxy) { + // Only pass the QTMovieOpenForPlaybackAttribute flag if there are no proxy servers, due + // to rdar://problem/7531776. + [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"]; + } + + if (proxiesForURL) + CFRelease(proxiesForURL); + if (proxySettings) + CFRelease(proxySettings); +#endif + + createQTMovie(cocoaURL, movieAttributes); +} + +static void disableComponentsOnce() +{ + static bool sComponentsDisabled = false; + if (sComponentsDisabled) + return; + sComponentsDisabled = true; + + // eat/PDF and grip/PDF components must be disabled twice since they are registered twice + // with different flags. However, there is currently a bug in 64-bit QTKit (<rdar://problem/8378237>) + // which causes subsequent disable component requests of exactly the same type to be ignored if + // QTKitServer has not yet started. As a result, we must pass in exactly the flags we want to + // disable per component. As a failsafe, if in the future these flags change, we will disable the + // PDF components for a third time with a wildcard flags field: + uint32_t componentsToDisable[11][5] = { + {'eat ', 'TEXT', 'text', 0, 0}, + {'eat ', 'TXT ', 'text', 0, 0}, + {'eat ', 'utxt', 'text', 0, 0}, + {'eat ', 'TEXT', 'tx3g', 0, 0}, + {'eat ', 'PDF ', 'vide', 0x44802, 0}, + {'eat ', 'PDF ', 'vide', 0x45802, 0}, + {'eat ', 'PDF ', 'vide', 0, 0}, + {'grip', 'PDF ', 'appl', 0x844a00, 0}, + {'grip', 'PDF ', 'appl', 0x845a00, 0}, + {'grip', 'PDF ', 'appl', 0, 0}, + {'imdc', 'pdf ', 'appl', 0, 0}, + }; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(componentsToDisable); ++i) + wkQTMovieDisableComponent(componentsToDisable[i]); +} + +void MediaPlayerPrivateQTKit::createQTMovie(NSURL *url, NSDictionary *movieAttributes) +{ + disableComponentsOnce(); + + [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; + + bool recreating = false; + if (m_qtMovie) { + recreating = true; + destroyQTVideoRenderer(); + m_qtMovie = 0; + } + + // Disable rtsp streams for now, <rdar://problem/5693967> + if (protocolIs([url scheme], "rtsp")) + return; + + NSError *error = nil; + m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); + + if (!m_qtMovie) + return; + + [m_qtMovie.get() setVolume:m_player->volume()]; + + if (recreating && hasVideo()) + createQTVideoRenderer(QTVideoRendererModeListensForNewImages); + + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(loadStateChanged:) + name:QTMovieLoadStateDidChangeNotification + object:m_qtMovie.get()]; + + // In updateState(), we track when maxTimeLoaded() == duration(). + // In newer version of QuickTime, a notification is emitted when maxTimeLoaded changes. + // In older version of QuickTime, QTMovieLoadStateDidChangeNotification be fired. + if (NSString *maxTimeLoadedChangeNotification = wkQTMovieMaxTimeLoadedChangeNotification()) { + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(loadStateChanged:) + name:maxTimeLoadedChangeNotification + object:m_qtMovie.get()]; + } + + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(rateChanged:) + name:QTMovieRateDidChangeNotification + object:m_qtMovie.get()]; + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(sizeChanged:) + name:QTMovieSizeDidChangeNotification + object:m_qtMovie.get()]; + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(timeChanged:) + name:QTMovieTimeDidChangeNotification + object:m_qtMovie.get()]; + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(didEnd:) + name:QTMovieDidEndNotification + object:m_qtMovie.get()]; +} + +static void mainThreadSetNeedsDisplay(id self, SEL) +{ + id view = [self superview]; + ASSERT(!view || [view isKindOfClass:[QTMovieView class]]); + if (!view || ![view isKindOfClass:[QTMovieView class]]) + return; + + FakeQTMovieView *movieView = static_cast<FakeQTMovieView *>(view); + WebCoreMovieObserver* delegate = [movieView delegate]; + ASSERT(!delegate || [delegate isKindOfClass:[WebCoreMovieObserver class]]); + if (!delegate || ![delegate isKindOfClass:[WebCoreMovieObserver class]]) + return; + + [delegate repaint]; +} + +static Class QTVideoRendererClass() +{ + static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly"); + return QTVideoRendererWebKitOnlyClass; +} + +void MediaPlayerPrivateQTKit::createQTMovieView() +{ + detachQTMovieView(); + + static bool addedCustomMethods = false; + if (!m_player->inMediaDocument() && !addedCustomMethods) { + Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView"); + ASSERT(QTMovieContentViewClass); + + Method mainThreadSetNeedsDisplayMethod = class_getInstanceMethod(QTMovieContentViewClass, @selector(_mainThreadSetNeedsDisplay)); + ASSERT(mainThreadSetNeedsDisplayMethod); + + method_setImplementation(mainThreadSetNeedsDisplayMethod, reinterpret_cast<IMP>(mainThreadSetNeedsDisplay)); + addedCustomMethods = true; + } + + // delay callbacks as we *will* get notifications during setup + [m_objcObserver.get() setDelayCallbacks:YES]; + + m_qtMovieView.adoptNS([[QTMovieView alloc] init]); + setSize(m_player->size()); + NSView* parentView = m_player->frameView()->documentView(); + [parentView addSubview:m_qtMovieView.get()]; +#ifdef BUILDING_ON_TIGER + // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy + [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:m_objcObserver.get()]; +#else + [m_qtMovieView.get() setDelegate:m_objcObserver.get()]; +#endif + [m_objcObserver.get() setView:m_qtMovieView.get()]; + [m_qtMovieView.get() setMovie:m_qtMovie.get()]; + [m_qtMovieView.get() setControllerVisible:NO]; + [m_qtMovieView.get() setPreservesAspectRatio:NO]; + // the area not covered by video should be transparent + [m_qtMovieView.get() setFillColor:[NSColor clearColor]]; + + // If we're in a media document, allow QTMovieView to render in its default mode; + // otherwise tell it to draw synchronously. + // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested. + if (!m_player->inMediaDocument()) + wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); + + [m_objcObserver.get() setDelayCallbacks:NO]; +} + +void MediaPlayerPrivateQTKit::detachQTMovieView() +{ + if (m_qtMovieView) { + [m_objcObserver.get() setView:nil]; +#ifdef BUILDING_ON_TIGER + // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy + [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:nil]; +#else + [m_qtMovieView.get() setDelegate:nil]; +#endif + [m_qtMovieView.get() removeFromSuperview]; + m_qtMovieView = nil; + } +} + +void MediaPlayerPrivateQTKit::createQTVideoRenderer(QTVideoRendererMode rendererMode) +{ + destroyQTVideoRenderer(); + + m_qtVideoRenderer.adoptNS([[QTVideoRendererClass() alloc] init]); + if (!m_qtVideoRenderer) + return; + + // associate our movie with our instance of QTVideoRendererWebKitOnly + [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()]; + + if (rendererMode == QTVideoRendererModeListensForNewImages) { + // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() + selector:@selector(newImageAvailable:) + name:QTVideoRendererWebKitOnlyNewImageAvailableNotification + object:m_qtVideoRenderer.get()]; + } +} + +void MediaPlayerPrivateQTKit::destroyQTVideoRenderer() +{ + if (!m_qtVideoRenderer) + return; + + // stop observing the renderer's notifications before we toss it + [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get() + name:QTVideoRendererWebKitOnlyNewImageAvailableNotification + object:m_qtVideoRenderer.get()]; + + // disassociate our movie from our instance of QTVideoRendererWebKitOnly + [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:nil]; + + m_qtVideoRenderer = nil; +} + +void MediaPlayerPrivateQTKit::createQTMovieLayer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtMovie) + return; + + ASSERT(supportsAcceleratedRendering()); + + if (!m_qtVideoLayer) { + m_qtVideoLayer.adoptNS([[QTMovieLayer alloc] init]); + if (!m_qtVideoLayer) + return; + + [m_qtVideoLayer.get() setMovie:m_qtMovie.get()]; +#ifndef NDEBUG + [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"]; +#endif + // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration(). + } +#endif +} + +void MediaPlayerPrivateQTKit::destroyQTMovieLayer() +{ +#if USE(ACCELERATED_COMPOSITING) + if (!m_qtVideoLayer) + return; + + // disassociate our movie from our instance of QTMovieLayer + [m_qtVideoLayer.get() setMovie:nil]; + m_qtVideoLayer = nil; +#endif +} + +MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::currentRenderingMode() const +{ + if (m_qtMovieView) + return MediaRenderingMovieView; + + if (m_qtVideoLayer) + return MediaRenderingMovieLayer; + + if (m_qtVideoRenderer) + return MediaRenderingSoftwareRenderer; + + return MediaRenderingNone; +} + +MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::preferredRenderingMode() const +{ + if (!m_player->frameView() || !m_qtMovie) + return MediaRenderingNone; + + if (m_player->inMediaDocument() || !QTVideoRendererClass()) + return MediaRenderingMovieView; + +#if USE(ACCELERATED_COMPOSITING) + if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) + return MediaRenderingMovieLayer; +#endif + + return MediaRenderingSoftwareRenderer; +} + +void MediaPlayerPrivateQTKit::setUpVideoRendering() +{ + if (!isReadyForVideoSetup()) + return; + + MediaRenderingMode currentMode = currentRenderingMode(); + MediaRenderingMode preferredMode = preferredRenderingMode(); + if (currentMode == preferredMode && currentMode != MediaRenderingNone) + return; + + if (currentMode != MediaRenderingNone) + tearDownVideoRendering(); + + switch (preferredMode) { + case MediaRenderingMovieView: + createQTMovieView(); + break; + case MediaRenderingNone: + case MediaRenderingSoftwareRenderer: + createQTVideoRenderer(QTVideoRendererModeListensForNewImages); + break; + case MediaRenderingMovieLayer: + createQTMovieLayer(); + break; + } + + // If using a movie layer, inform the client so the compositing tree is updated. + if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) + m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); +} + +void MediaPlayerPrivateQTKit::tearDownVideoRendering() +{ + if (m_qtMovieView) + detachQTMovieView(); + if (m_qtVideoRenderer) + destroyQTVideoRenderer(); + if (m_qtVideoLayer) + destroyQTMovieLayer(); +} + +bool MediaPlayerPrivateQTKit::hasSetUpVideoRendering() const +{ + return m_qtMovieView + || m_qtVideoLayer + || m_qtVideoRenderer; +} + +QTTime MediaPlayerPrivateQTKit::createQTTime(float time) const +{ + if (!metaDataAvailable()) + return QTMakeTime(0, 600); + long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue]; + return QTMakeTime(time * timeScale, timeScale); +} + +void MediaPlayerPrivateQTKit::resumeLoad() +{ + m_delayingLoad = false; + + if (!m_movieURL.isNull()) + loadInternal(m_movieURL); +} + +void MediaPlayerPrivateQTKit::load(const String& url) +{ + m_movieURL = url; + + // If the element is not supposed to load any data return immediately because QTKit + // doesn't have API to throttle loading. + if (m_preload == MediaPlayer::None) { + m_delayingLoad = true; + return; + } + + loadInternal(url); +} + +void MediaPlayerPrivateQTKit::loadInternal(const String& url) +{ + if (m_networkState != MediaPlayer::Loading) { + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + } + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; + m_player->readyStateChanged(); + } + cancelSeek(); + m_videoFrameHasDrawn = false; + + [m_objcObserver.get() setDelayCallbacks:YES]; + + createQTMovie(url); + + [m_objcObserver.get() loadStateChanged:nil]; + [m_objcObserver.get() setDelayCallbacks:NO]; +} + +void MediaPlayerPrivateQTKit::prepareToPlay() +{ + if (!m_qtMovie || m_delayingLoad) + resumeLoad(); +} + +PlatformMedia MediaPlayerPrivateQTKit::platformMedia() const +{ + PlatformMedia pm; + pm.type = PlatformMedia::QTMovieType; + pm.media.qtMovie = m_qtMovie.get(); + return pm; +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* MediaPlayerPrivateQTKit::platformLayer() const +{ + return m_qtVideoLayer.get(); +} +#endif + +void MediaPlayerPrivateQTKit::play() +{ + if (!metaDataAvailable()) + return; + m_startedPlaying = true; +#if DRAW_FRAME_RATE + m_frameCountWhilePlaying = 0; +#endif + [m_objcObserver.get() setDelayCallbacks:YES]; + [m_qtMovie.get() setRate:m_player->rate()]; + [m_objcObserver.get() setDelayCallbacks:NO]; +} + +void MediaPlayerPrivateQTKit::pause() +{ + if (!metaDataAvailable()) + return; + m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; +#endif + [m_objcObserver.get() setDelayCallbacks:YES]; + [m_qtMovie.get() stop]; + [m_objcObserver.get() setDelayCallbacks:NO]; +} + +float MediaPlayerPrivateQTKit::duration() const +{ + if (!metaDataAvailable()) + return 0; + + if (m_cachedDuration != -1.0f) + return m_cachedDuration; + + QTTime time = [m_qtMovie.get() duration]; + if (time.flags == kQTTimeIsIndefinite) + return numeric_limits<float>::infinity(); + return static_cast<float>(time.timeValue) / time.timeScale; +} + +float MediaPlayerPrivateQTKit::currentTime() const +{ + if (!metaDataAvailable()) + return 0; + QTTime time = [m_qtMovie.get() currentTime]; + return static_cast<float>(time.timeValue) / time.timeScale; +} + +void MediaPlayerPrivateQTKit::seek(float time) +{ + // Nothing to do if we are already in the middle of a seek to the same time. + if (time == m_seekTo) + return; + + cancelSeek(); + + if (!metaDataAvailable()) + return; + + if (time > duration()) + time = duration(); + + m_seekTo = time; + if (maxTimeSeekable() >= m_seekTo) + doSeek(); + else + m_seekTimer.start(0, 0.5f); +} + +void MediaPlayerPrivateQTKit::doSeek() +{ + QTTime qttime = createQTTime(m_seekTo); + // setCurrentTime generates several event callbacks, update afterwards + [m_objcObserver.get() setDelayCallbacks:YES]; + float oldRate = [m_qtMovie.get() rate]; + + if (oldRate) + [m_qtMovie.get() setRate:0]; + [m_qtMovie.get() setCurrentTime:qttime]; + + // restore playback only if not at end, otherwise QTMovie will loop + float timeAfterSeek = currentTime(); + if (oldRate && timeAfterSeek < duration()) + [m_qtMovie.get() setRate:oldRate]; + + cancelSeek(); + [m_objcObserver.get() setDelayCallbacks:NO]; +} + +void MediaPlayerPrivateQTKit::cancelSeek() +{ + m_seekTo = -1; + m_seekTimer.stop(); +} + +void MediaPlayerPrivateQTKit::seekTimerFired(Timer<MediaPlayerPrivateQTKit>*) +{ + if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) { + cancelSeek(); + updateStates(); + m_player->timeChanged(); + return; + } + + if (maxTimeSeekable() >= m_seekTo) + doSeek(); + else { + MediaPlayer::NetworkState state = networkState(); + if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) { + cancelSeek(); + updateStates(); + m_player->timeChanged(); + } + } +} + +bool MediaPlayerPrivateQTKit::paused() const +{ + if (!metaDataAvailable()) + return true; + return [m_qtMovie.get() rate] == 0; +} + +bool MediaPlayerPrivateQTKit::seeking() const +{ + if (!metaDataAvailable()) + return false; + return m_seekTo >= 0; +} + +IntSize MediaPlayerPrivateQTKit::naturalSize() const +{ + if (!metaDataAvailable()) + return IntSize(); + + // In spite of the name of this method, return QTMovieNaturalSizeAttribute transformed by the + // initial movie scale because the spec says intrinsic size is: + // + // ... the dimensions of the resource in CSS pixels after taking into account the resource's + // dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the + // format used by the resource + + NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; + return IntSize(naturalSize.width * m_scaleFactor.width(), naturalSize.height * m_scaleFactor.height()); +} + +bool MediaPlayerPrivateQTKit::hasVideo() const +{ + if (!metaDataAvailable()) + return false; + return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue]; +} + +bool MediaPlayerPrivateQTKit::hasAudio() const +{ + if (!m_qtMovie) + return false; + return [[m_qtMovie.get() attributeForKey:QTMovieHasAudioAttribute] boolValue]; +} + +bool MediaPlayerPrivateQTKit::supportsFullscreen() const +{ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + return true; +#else + // See <rdar://problem/7389945> + return false; +#endif +} + +void MediaPlayerPrivateQTKit::setVolume(float volume) +{ + if (m_qtMovie) + [m_qtMovie.get() setVolume:volume]; +} + +bool MediaPlayerPrivateQTKit::hasClosedCaptions() const +{ + if (!metaDataAvailable()) + return false; + return wkQTMovieHasClosedCaptions(m_qtMovie.get()); +} + +void MediaPlayerPrivateQTKit::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 upside down unless we flag the movie as flipped (again). See <rdar://7408440>. + [m_qtVideoLayer.get() setGeometryFlipped:YES]; + } +#endif + } +} + +void MediaPlayerPrivateQTKit::setRate(float rate) +{ + if (m_qtMovie) + [m_qtMovie.get() setRate:rate]; +} + +void MediaPlayerPrivateQTKit::setPreservesPitch(bool preservesPitch) +{ + if (!m_qtMovie) + return; + + // QTMovieRateChangesPreservePitchAttribute cannot be changed dynamically after QTMovie creation. + // If the passed in value is different than what already exists, we need to recreate the QTMovie for it to take effect. + if ([[m_qtMovie.get() attributeForKey:QTMovieRateChangesPreservePitchAttribute] boolValue] == preservesPitch) + return; + + RetainPtr<NSDictionary> movieAttributes(AdoptNS, [[m_qtMovie.get() movieAttributes] mutableCopy]); + ASSERT(movieAttributes); + [movieAttributes.get() setValue:[NSNumber numberWithBool:preservesPitch] forKey:QTMovieRateChangesPreservePitchAttribute]; + m_timeToRestore = currentTime(); + + createQTMovie([movieAttributes.get() valueForKey:QTMovieURLAttribute], movieAttributes.get()); +} + +PassRefPtr<TimeRanges> MediaPlayerPrivateQTKit::buffered() const +{ + RefPtr<TimeRanges> timeRanges = TimeRanges::create(); + float loaded = maxTimeLoaded(); + if (loaded > 0) + timeRanges->add(0, loaded); + return timeRanges.release(); +} + +float MediaPlayerPrivateQTKit::maxTimeSeekable() const +{ + if (!metaDataAvailable()) + return 0; + + // infinite duration means live stream + if (isinf(duration())) + return 0; + + return wkQTMovieMaxTimeSeekable(m_qtMovie.get()); +} + +float MediaPlayerPrivateQTKit::maxTimeLoaded() const +{ + if (!metaDataAvailable()) + return 0; + return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); +} + +unsigned MediaPlayerPrivateQTKit::bytesLoaded() const +{ + float dur = duration(); + if (!dur) + return 0; + return totalBytes() * maxTimeLoaded() / dur; +} + +unsigned MediaPlayerPrivateQTKit::totalBytes() const +{ + if (!metaDataAvailable()) + return 0; + return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue]; +} + +void MediaPlayerPrivateQTKit::cancelLoad() +{ + // FIXME: Is there a better way to check for this? + if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) + return; + + tearDownVideoRendering(); + m_qtMovie = nil; + + updateStates(); +} + +void MediaPlayerPrivateQTKit::cacheMovieScale() +{ + NSSize initialSize = NSZeroSize; + NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + // QTMovieCurrentSizeAttribute is not allowed with instances of QTMovie that have been + // opened with QTMovieOpenForPlaybackAttribute, so ask for the display transform attribute instead. + NSAffineTransform *displayTransform = [m_qtMovie.get() attributeForKey:@"QTMoviePreferredTransformAttribute"]; + if (displayTransform) + initialSize = [displayTransform transformSize:naturalSize]; + else { + initialSize.width = naturalSize.width; + initialSize.height = naturalSize.height; + } +#else + initialSize = [[m_qtMovie.get() attributeForKey:QTMovieCurrentSizeAttribute] sizeValue]; +#endif + + if (naturalSize.width) + m_scaleFactor.setWidth(initialSize.width / naturalSize.width); + if (naturalSize.height) + m_scaleFactor.setHeight(initialSize.height / naturalSize.height); +} + +bool MediaPlayerPrivateQTKit::isReadyForVideoSetup() const +{ + return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); +} + +void MediaPlayerPrivateQTKit::prepareForRendering() +{ + if (m_isAllowedToRender) + return; + m_isAllowedToRender = true; + + if (!hasSetUpVideoRendering()) + setUpVideoRendering(); + + // If using a movie layer, inform the client so the compositing tree is updated. This is crucial if the movie + // has a poster, as it will most likely not have a layer and we will now be rendering frames to the movie layer. + if (currentRenderingMode() == MediaRenderingMovieLayer || preferredRenderingMode() == MediaRenderingMovieLayer) + m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); +} + +void MediaPlayerPrivateQTKit::updateStates() +{ + MediaPlayer::NetworkState oldNetworkState = m_networkState; + MediaPlayer::ReadyState oldReadyState = m_readyState; + + long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError); + + if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { + disableUnsupportedTracks(); + if (m_player->inMediaDocument()) { + if (!m_enabledTrackCount || m_hasUnsupportedTracks) { + // This has a type of media that we do not handle directly with a <video> + // element, eg. a rtsp track or QuickTime VR. Tell the MediaPlayerClient + // that we noticed. + sawUnsupportedTracks(); + return; + } + } else if (!m_enabledTrackCount) + loadState = QTMovieLoadStateError; + + if (loadState != QTMovieLoadStateError) { + wkQTMovieSelectPreferredAlternates(m_qtMovie.get()); + cacheMovieScale(); + MediaPlayer::MovieLoadType movieType = movieLoadType(); + m_isStreaming = movieType == MediaPlayer::StoredStream || movieType == MediaPlayer::LiveStream; + } + } + + // If this movie is reloading and we mean to restore the current time/rate, this might be the right time to do it. + if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::Loaded && m_timeToRestore != -1.0f) { + QTTime qttime = createQTTime(m_timeToRestore); + m_timeToRestore = -1.0f; + + // Disable event callbacks from setCurrentTime for restoring time in a recreated video + [m_objcObserver.get() setDelayCallbacks:YES]; + [m_qtMovie.get() setCurrentTime:qttime]; + [m_qtMovie.get() setRate:m_player->rate()]; + [m_objcObserver.get() setDelayCallbacks:NO]; + } + + BOOL completelyLoaded = !m_isStreaming && (loadState >= QTMovieLoadStateComplete); + + // Note: QT indicates that we are fully loaded with QTMovieLoadStateComplete. + // However newer versions of QT do not, so we check maxTimeLoaded against duration. + if (!completelyLoaded && !m_isStreaming && metaDataAvailable()) + completelyLoaded = maxTimeLoaded() == duration(); + + if (completelyLoaded) { + // "Loaded" is reserved for fully buffered movies, never the case when streaming + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; + } else if (loadState >= QTMovieLoadStatePlaythroughOK) { + m_readyState = MediaPlayer::HaveEnoughData; + m_networkState = MediaPlayer::Loading; + } else if (loadState >= QTMovieLoadStatePlayable) { + // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; + m_networkState = MediaPlayer::Loading; + } else if (loadState >= QTMovieLoadStateLoaded) { + m_readyState = MediaPlayer::HaveMetadata; + m_networkState = MediaPlayer::Loading; + } else if (loadState > QTMovieLoadStateError) { + m_readyState = MediaPlayer::HaveNothing; + m_networkState = MediaPlayer::Loading; + } else { + // Loading or decoding failed. + + if (m_player->inMediaDocument()) { + // Something went wrong in the loading of media within a standalone file. + // This can occur with chained refmovies pointing to streamed media. + sawUnsupportedTracks(); + return; + } + + float loaded = maxTimeLoaded(); + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } + } + + if (isReadyForVideoSetup() && !hasSetUpVideoRendering()) + setUpVideoRendering(); + + if (seeking()) + m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing; + + // Streaming movies don't use the network when paused. + if (m_isStreaming && m_readyState >= MediaPlayer::HaveMetadata && m_networkState >= MediaPlayer::Loading && [m_qtMovie.get() rate] == 0) + m_networkState = MediaPlayer::Idle; + + if (m_networkState != oldNetworkState) + m_player->networkStateChanged(); + + if (m_readyState != oldReadyState) + m_player->readyStateChanged(); + + if (loadState >= QTMovieLoadStateLoaded) { + float dur = duration(); + if (dur != m_reportedDuration) { + if (m_reportedDuration != -1.0f) + m_player->durationChanged(); + m_reportedDuration = dur; + } + } +} + +void MediaPlayerPrivateQTKit::loadStateChanged() +{ + if (!m_hasUnsupportedTracks) + updateStates(); +} + +void MediaPlayerPrivateQTKit::rateChanged() +{ + if (m_hasUnsupportedTracks) + return; + + updateStates(); + m_player->rateChanged(); +} + +void MediaPlayerPrivateQTKit::sizeChanged() +{ + if (!m_hasUnsupportedTracks) + m_player->sizeChanged(); +} + +void MediaPlayerPrivateQTKit::timeChanged() +{ + if (m_hasUnsupportedTracks) + return; + + // It may not be possible to seek to a specific time in a streamed movie. When seeking in a + // stream QuickTime sets the movie time to closest time possible and posts a timechanged + // notification. Update m_seekTo so we can detect when the seek completes. + if (m_seekTo != -1) + m_seekTo = currentTime(); + + m_timeToRestore = -1.0f; + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivateQTKit::didEnd() +{ + if (m_hasUnsupportedTracks) + return; + + m_startedPlaying = false; +#if DRAW_FRAME_RATE + m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; +#endif + + // Hang onto the current time and use it as duration from now on since QuickTime is telling us we + // are at the end. Do this because QuickTime sometimes reports one time for duration and stops + // playback at another time, which causes problems in HTMLMediaElement. QTKit's 'ended' event + // fires when playing in reverse so don't update duration when at time zero! + float now = currentTime(); + if (now > 0) + m_cachedDuration = now; + + updateStates(); + m_player->timeChanged(); +} + +void MediaPlayerPrivateQTKit::setSize(const IntSize&) +{ + // Don't resize the view now because [view setFrame] also resizes the movie itself, and because + // the renderer calls this function immediately when we report a size change (QTMovieSizeDidChangeNotification) + // we can get into a feedback loop observing the size change and resetting the size, and this can cause + // QuickTime to miss resetting a movie's size when the media size changes (as happens with an rtsp movie + // once the rtsp server sends the track sizes). Instead we remember the size passed to paint() and resize + // the view when it changes. + // <rdar://problem/6336092> REGRESSION: rtsp movie does not resize correctly +} + +void MediaPlayerPrivateQTKit::setVisible(bool b) +{ + if (m_visible != b) { + m_visible = b; + if (b) + setUpVideoRendering(); + else + tearDownVideoRendering(); + } +} + +bool MediaPlayerPrivateQTKit::hasAvailableVideoFrame() const +{ + // When using a QTMovieLayer return true as soon as the movie reaches QTMovieLoadStatePlayable + // because although we don't *know* when the first frame has decoded, by the time we get and + // process the notification a frame should have propagated the VisualContext and been set on + // the layer. + if (currentRenderingMode() == MediaRenderingMovieLayer) + return m_readyState >= MediaPlayer::HaveCurrentData; + + // When using the software renderer QuickTime signals that a frame is available so we might as well + // wait until we know that a frame has been drawn. + return m_videoFrameHasDrawn; +} + +void MediaPlayerPrivateQTKit::repaint() +{ + if (m_hasUnsupportedTracks) + return; + +#if DRAW_FRAME_RATE + if (m_startedPlaying) { + m_frameCountWhilePlaying++; + // to eliminate preroll costs from our calculation, + // our frame rate calculation excludes the first frame drawn after playback starts + if (1==m_frameCountWhilePlaying) + m_timeStartedPlaying = [NSDate timeIntervalSinceReferenceDate]; + } +#endif + m_videoFrameHasDrawn = true; + m_player->repaint(); +} + +void MediaPlayerPrivateQTKit::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r) +{ + id qtVideoRenderer = m_qtVideoRenderer.get(); + if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) { + // We're being told to render into a context, but we already have the + // MovieLayer going. This probably means we've been called from <canvas>. + // Set up a QTVideoRenderer to use, but one that doesn't register for + // update callbacks. That way, it won't bother us asking to repaint. + createQTVideoRenderer(QTVideoRendererModeDefault); + qtVideoRenderer = m_qtVideoRenderer.get(); + } + paint(context, r); +} + +void MediaPlayerPrivateQTKit::paint(GraphicsContext* context, const IntRect& r) +{ + if (context->paintingDisabled() || m_hasUnsupportedTracks) + return; + NSView *view = m_qtMovieView.get(); + id qtVideoRenderer = m_qtVideoRenderer.get(); + if (!view && !qtVideoRenderer) + return; + + [m_objcObserver.get() setDelayCallbacks:YES]; + BEGIN_BLOCK_OBJC_EXCEPTIONS; + context->save(); + context->translate(r.x(), r.y() + r.height()); + context->scale(FloatSize(1.0f, -1.0f)); + context->setImageInterpolationQuality(InterpolationLow); + IntRect paintRect(IntPoint(0, 0), IntSize(r.width(), r.height())); + +#ifdef BUILDING_ON_TIGER + AutodrainedPool pool; +#endif + NSGraphicsContext* newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context->platformContext() flipped:NO]; + + // draw the current video frame + if (qtVideoRenderer) { + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:newContext]; + [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect]; + [NSGraphicsContext restoreGraphicsState]; + } else { + if (m_rect != r) { + m_rect = r; + if (m_player->inMediaDocument()) { + // the QTMovieView needs to be placed in the proper location for document mode + [view setFrame:m_rect]; + } + else { + // We don't really need the QTMovieView in any specific location so let's just get it out of the way + // where it won't intercept events or try to bring up the context menu. + IntRect farAwayButCorrectSize(m_rect); + farAwayButCorrectSize.move(-1000000, -1000000); + [view setFrame:farAwayButCorrectSize]; + } + } + + if (m_player->inMediaDocument()) { + // If we're using a QTMovieView in a media document, the view may get layer-backed. AppKit won't update + // the layer hosting correctly if we call displayRectIgnoringOpacity:inContext:, so use displayRectIgnoringOpacity: + // in this case. See <rdar://problem/6702882>. + [view displayRectIgnoringOpacity:paintRect]; + } else + [view displayRectIgnoringOpacity:paintRect inContext:newContext]; + } + +#if DRAW_FRAME_RATE + // Draw the frame rate only after having played more than 10 frames. + if (m_frameCountWhilePlaying > 10) { + Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL; + Document* document = frame ? frame->document() : NULL; + RenderObject* renderer = document ? document->renderer() : NULL; + RenderStyle* styleToUse = renderer ? renderer->style() : NULL; + if (styleToUse) { + double frameRate = (m_frameCountWhilePlaying - 1) / ( m_startedPlaying ? ([NSDate timeIntervalSinceReferenceDate] - m_timeStartedPlaying) : + (m_timeStoppedPlaying - m_timeStartedPlaying) ); + String text = String::format("%1.2f", frameRate); + TextRun textRun(text.characters(), text.length()); + const Color color(255, 0, 0); + context->scale(FloatSize(1.0f, -1.0f)); + context->setStrokeColor(color, styleToUse->colorSpace()); + context->setStrokeStyle(SolidStroke); + context->setStrokeThickness(1.0f); + context->setFillColor(color, styleToUse->colorSpace()); + context->drawText(styleToUse->font(), textRun, IntPoint(2, -3)); + } + } +#endif + + context->restore(); + END_BLOCK_OBJC_EXCEPTIONS; + [m_objcObserver.get() setDelayCallbacks:NO]; +} + +static void addFileTypesToCache(NSArray * fileTypes, HashSet<String> &cache) +{ + int count = [fileTypes count]; + for (int n = 0; n < count; n++) { + CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]); + RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL)); + if (!uti) + continue; + RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType)); + if (mime) + cache.add(mime.get()); + + // -movieFileTypes: returns both file extensions and OSTypes. The later are surrounded by single + // quotes, eg. 'MooV', so don't bother looking at those. + if (CFStringGetCharacterAtIndex(ext, 0) != '\'') { + // UTI is missing many media related MIME types supported by QTKit (see rdar://6434168), and not all + // web servers use the MIME type UTI returns for an extension (see rdar://7875393), so even if UTI + // has a type for this extension add any types in hard coded table in the MIME type regsitry. + Vector<String> typesForExtension = MIMETypeRegistry::getMediaMIMETypesForExtension(ext); + unsigned count = typesForExtension.size(); + for (unsigned ndx = 0; ndx < count; ++ndx) { + if (!cache.contains(typesForExtension[ndx])) + cache.add(typesForExtension[ndx]); + } + } + } +} + +static HashSet<String> mimeCommonTypesCache() +{ + DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + typeListInitialized = true; + NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes]; + addFileTypesToCache(fileTypes, cache); + } + + return cache; +} + +static HashSet<String> mimeModernTypesCache() +{ + DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + typeListInitialized = true; + NSArray* fileTypes = [QTMovie movieFileTypes:(QTMovieFileTypeOptions)wkQTIncludeOnlyModernMediaFileTypes()]; + addFileTypesToCache(fileTypes, cache); + } + + return cache; +} + +void MediaPlayerPrivateQTKit::getSupportedTypes(HashSet<String>& supportedTypes) +{ + supportedTypes = mimeModernTypesCache(); + + // Note: this method starts QTKitServer if it isn't already running when in 64-bit because it has to return the list + // of every MIME type supported by QTKit. + HashSet<String> commonTypes = mimeCommonTypesCache(); + HashSet<String>::const_iterator it = commonTypes.begin(); + HashSet<String>::const_iterator end = commonTypes.end(); + for (; it != end; ++it) + supportedTypes.add(*it); +} + +MediaPlayer::SupportsType MediaPlayerPrivateQTKit::supportsType(const String& type, const String& codecs) +{ + // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type yet. + + // We check the "modern" type cache first, as it doesn't require QTKitServer to start. + if (mimeModernTypesCache().contains(type) || mimeCommonTypesCache().contains(type)) + return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; + + return MediaPlayer::IsNotSupported; +} + +bool MediaPlayerPrivateQTKit::isAvailable() +{ +#ifdef BUILDING_ON_TIGER + SInt32 version; + OSErr result; + result = Gestalt(gestaltQuickTime, &version); + if (result != noErr) { + LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support."); + return false; + } + if (version < minimumQuickTimeVersion) { + LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", version, minimumQuickTimeVersion); + return false; + } + return true; +#else + // On 10.5 and higher, QuickTime will always be new enough for <video> and <audio> support, so we just check that the framework can be loaded. + return QTKitLibrary(); +#endif +} + +void MediaPlayerPrivateQTKit::disableUnsupportedTracks() +{ + if (!m_qtMovie) { + m_enabledTrackCount = 0; + m_totalTrackCount = 0; + return; + } + + static HashSet<String>* allowedTrackTypes = 0; + if (!allowedTrackTypes) { + allowedTrackTypes = new HashSet<String>; + allowedTrackTypes->add(QTMediaTypeVideo); + allowedTrackTypes->add(QTMediaTypeSound); + allowedTrackTypes->add(QTMediaTypeText); + allowedTrackTypes->add(QTMediaTypeBase); + allowedTrackTypes->add(QTMediaTypeMPEG); + allowedTrackTypes->add("clcp"); // Closed caption + allowedTrackTypes->add("sbtl"); // Subtitle + allowedTrackTypes->add("odsm"); // MPEG-4 object descriptor stream + allowedTrackTypes->add("sdsm"); // MPEG-4 scene description stream + allowedTrackTypes->add("tmcd"); // timecode + allowedTrackTypes->add("tc64"); // timcode-64 + allowedTrackTypes->add("tmet"); // timed metadata + } + + NSArray *tracks = [m_qtMovie.get() tracks]; + + m_totalTrackCount = [tracks count]; + m_enabledTrackCount = m_totalTrackCount; + for (unsigned trackIndex = 0; trackIndex < m_totalTrackCount; trackIndex++) { + // Grab the track at the current index. If there isn't one there, then + // we can move onto the next one. + QTTrack *track = [tracks objectAtIndex:trackIndex]; + if (!track) + continue; + + // Check to see if the track is disabled already, we should move along. + // We don't need to re-disable it. + if (![track isEnabled]) { + --m_enabledTrackCount; + continue; + } + + // Get the track's media type. + NSString *mediaType = [track attributeForKey:QTTrackMediaTypeAttribute]; + if (!mediaType) + continue; + + // Test whether the media type is in our white list. + if (!allowedTrackTypes->contains(mediaType)) { + // If this track type is not allowed, then we need to disable it. + [track setEnabled:NO]; + --m_enabledTrackCount; + m_hasUnsupportedTracks = true; + } + + // Disable chapter tracks. These are most likely to lead to trouble, as + // they will be composited under the video tracks, forcing QT to do extra + // work. + QTTrack *chapterTrack = [track performSelector:@selector(chapterlist)]; + if (!chapterTrack) + continue; + + // Try to grab the media for the track. + QTMedia *chapterMedia = [chapterTrack media]; + if (!chapterMedia) + continue; + + // Grab the media type for this track. + id chapterMediaType = [chapterMedia attributeForKey:QTMediaTypeAttribute]; + if (!chapterMediaType) + continue; + + // Check to see if the track is a video track. We don't care about + // other non-video tracks. + if (![chapterMediaType isEqual:QTMediaTypeVideo]) + continue; + + // Check to see if the track is already disabled. If it is, we + // should move along. + if (![chapterTrack isEnabled]) + continue; + + // Disable the evil, evil track. + [chapterTrack setEnabled:NO]; + --m_enabledTrackCount; + m_hasUnsupportedTracks = true; + } +} + +void MediaPlayerPrivateQTKit::sawUnsupportedTracks() +{ + m_hasUnsupportedTracks = true; + m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); +} + +#if USE(ACCELERATED_COMPOSITING) +bool MediaPlayerPrivateQTKit::supportsAcceleratedRendering() const +{ + // Also don't claim to support accelerated rendering when in the media document, as we will then render + // via QTMovieView which is already accelerated. + return isReadyForVideoSetup() && getQTMovieLayerClass() != Nil && !m_player->inMediaDocument(); +} + +void MediaPlayerPrivateQTKit::acceleratedRenderingStateChanged() +{ + // Set up or change the rendering path if necessary. + setUpVideoRendering(); +} +#endif + +bool MediaPlayerPrivateQTKit::hasSingleSecurityOrigin() const +{ + // We tell quicktime to disallow resources that come from different origins + // so we know all media is single origin. + return true; +} + +MediaPlayer::MovieLoadType MediaPlayerPrivateQTKit::movieLoadType() const +{ + if (!m_qtMovie) + return MediaPlayer::Unknown; + + MediaPlayer::MovieLoadType movieType = (MediaPlayer::MovieLoadType)wkQTMovieGetType(m_qtMovie.get()); + + // Can't include WebKitSystemInterface from WebCore so we can't get the enum returned + // by wkQTMovieGetType, but at least verify that the value is in the valid range. + ASSERT(movieType >= MediaPlayer::Unknown && movieType <= MediaPlayer::LiveStream); + + return movieType; +} + +void MediaPlayerPrivateQTKit::setPreload(MediaPlayer::Preload preload) +{ + m_preload = preload; + if (m_delayingLoad && m_preload != MediaPlayer::None) + resumeLoad(); +} + +float MediaPlayerPrivateQTKit::mediaTimeForTimeValue(float timeValue) const +{ + if (!metaDataAvailable()) + return timeValue; + + QTTime qttime = createQTTime(timeValue); + return static_cast<float>(qttime.timeValue) / qttime.timeScale; +} + +} // namespace WebCore + +@implementation WebCoreMovieObserver + +- (id)initWithCallback:(MediaPlayerPrivateQTKit*)callback +{ + m_callback = callback; + return [super init]; +} + +- (void)disconnect +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + m_callback = 0; +} + +-(NSMenu*)menuForEventDelegate:(NSEvent*)theEvent +{ + // Get the contextual menu from the QTMovieView's superview, the frame view + return [[m_view superview] menuForEvent:theEvent]; +} + +-(void)setView:(NSView*)view +{ + m_view = view; +} + +-(void)repaint +{ + if (m_delayCallbacks) + [self performSelector:_cmd withObject:nil afterDelay:0.]; + else if (m_callback) + m_callback->repaint(); +} + +- (void)loadStateChanged:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + if (m_delayCallbacks) + [self performSelector:_cmd withObject:nil afterDelay:0]; + else + m_callback->loadStateChanged(); +} + +- (void)rateChanged:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + if (m_delayCallbacks) + [self performSelector:_cmd withObject:nil afterDelay:0]; + else + m_callback->rateChanged(); +} + +- (void)sizeChanged:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + if (m_delayCallbacks) + [self performSelector:_cmd withObject:nil afterDelay:0]; + else + m_callback->sizeChanged(); +} + +- (void)timeChanged:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + if (m_delayCallbacks) + [self performSelector:_cmd withObject:nil afterDelay:0]; + else + m_callback->timeChanged(); +} + +- (void)didEnd:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + if (m_delayCallbacks) + [self performSelector:_cmd withObject:nil afterDelay:0]; + else + m_callback->didEnd(); +} + +- (void)newImageAvailable:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + [self repaint]; +} + +- (void)setDelayCallbacks:(BOOL)shouldDelay +{ + m_delayCallbacks = shouldDelay; +} + +@end + +#endif diff --git a/Source/WebCore/platform/graphics/mac/MediaPlayerProxy.h b/Source/WebCore/platform/graphics/mac/MediaPlayerProxy.h new file mode 100644 index 0000000..cc7ec95 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/MediaPlayerProxy.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlayerProxy_h +#define MediaPlayerProxy_h + +#ifdef __OBJC__ +@class WebMediaPlayerProxy; +#else +class WebMediaPlayerProxy; +#endif + +enum MediaPlayerProxyNotificationType { + + MediaPlayerNotificationMediaValidated = 1, + MediaPlayerNotificationMediaFailedToValidate, + + MediaPlayerNotificationStartUsingNetwork, + MediaPlayerNotificationStopUsingNetwork, + + MediaPlayerNotificationEnteredFullscreen, + MediaPlayerNotificationExitedFullscreen, + + MediaPlayerNotificationReadyForInspection, + MediaPlayerNotificationReadyForPlayback, + MediaPlayerNotificationDidPlayToTheEnd, + + MediaPlayerNotificationPlaybackFailed, + + MediaPlayerNotificationStreamLikelyToKeepUp, + MediaPlayerNotificationStreamUnlikelyToKeepUp, + MediaPlayerNotificationStreamBufferFull, + MediaPlayerNotificationStreamRanDry, + MediaPlayerNotificationFileLoaded, + + MediaPlayerNotificationSizeDidChange, + MediaPlayerNotificationVolumeDidChange, + MediaPlayerNotificationMutedDidChange, + MediaPlayerNotificationTimeJumped, + + MediaPlayerNotificationPlayPauseButtonPressed, +}; + +#ifdef __OBJC__ +@interface NSObject (WebMediaPlayerProxy) + +- (int)_interfaceVersion; + +- (void)_disconnect; + +- (void)_load:(NSURL *)url; +- (void)_cancelLoad; + +- (void)_setPoster:(NSURL *)url; + +- (void)_play; +- (void)_pause; + +- (NSSize)_naturalSize; + +- (BOOL)_hasVideo; +- (BOOL)_hasAudio; + +- (NSTimeInterval)_duration; + +- (double)_currentTime; +- (void)_setCurrentTime:(double)time; +- (BOOL)_seeking; + +- (void)_setEndTime:(double)time; + +- (float)_rate; +- (void)_setRate:(float)rate; + +- (float)_volume; +- (void)_setVolume:(float)newVolume; + +- (BOOL)_muted; +- (void)_setMuted:(BOOL)muted; + +- (float)_maxTimeBuffered; +- (float)_maxTimeSeekable; +- (NSArray *)_bufferedTimeRanges; + +- (int)_dataRate; + +- (BOOL)_totalBytesKnown; +- (unsigned)_totalBytes; +- (unsigned)_bytesLoaded; + +- (NSArray *)_mimeTypes; + +@end +#endif + +#endif diff --git a/Source/WebCore/platform/graphics/mac/SimpleFontDataATSUI.mm b/Source/WebCore/platform/graphics/mac/SimpleFontDataATSUI.mm new file mode 100644 index 0000000..4b2e7b2 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/SimpleFontDataATSUI.mm @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "SimpleFontData.h" + +#if USE(ATSUI) + +#import "Font.h" +#import "FontCache.h" +#import "FontDescription.h" +#import <ApplicationServices/ApplicationServices.h> +#import <AppKit/AppKit.h> +#import <wtf/Assertions.h> + +using namespace std; + +namespace WebCore { + +void SimpleFontData::checkShapesArabic() const +{ + ASSERT(!m_checkedShapesArabic); + + m_checkedShapesArabic = true; + + ATSUFontID fontID = m_platformData.ctFont() ? CTFontGetPlatformFont(m_platformData.ctFont(), 0) : 0; + if (!fontID) { + LOG_ERROR("unable to get ATSUFontID for %@", m_platformData.font()); + return; + } + + // This function is called only on fonts that contain Arabic glyphs. Our + // heuristic is that if such a font has a glyph metamorphosis table, then + // it includes shaping information for Arabic. + FourCharCode tables[] = { 'morx', 'mort' }; + for (size_t i = 0; i < WTF_ARRAY_LENGTH(tables); ++i) { + ByteCount tableSize; + OSStatus status = ATSFontGetTable(fontID, tables[i], 0, 0, 0, &tableSize); + if (status == noErr) { + m_shapesArabic = true; + return; + } + + if (status != kATSInvalidFontTableAccess) + LOG_ERROR("ATSFontGetTable failed (%d)", status); + } +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp b/Source/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp new file mode 100644 index 0000000..452bd54 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "SimpleFontData.h" + +#import "Font.h" +#import "FontCache.h" +#import "FontDescription.h" +#import <ApplicationServices/ApplicationServices.h> + +using namespace std; + +namespace WebCore { + +CFDictionaryRef SimpleFontData::getCFStringAttributes(TypesettingFeatures typesettingFeatures) const +{ + unsigned key = typesettingFeatures + 1; + pair<HashMap<unsigned, RetainPtr<CFDictionaryRef> >::iterator, bool> addResult = m_CFStringAttributes.add(key, RetainPtr<CFDictionaryRef>()); + RetainPtr<CFDictionaryRef>& attributesDictionary = addResult.first->second; + if (!addResult.second) + return attributesDictionary.get(); + + bool allowLigatures = (orientation() == Horizontal && platformData().allowsLigatures()) || (typesettingFeatures & Ligatures); + + static const int ligaturesNotAllowedValue = 0; + static CFNumberRef ligaturesNotAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesNotAllowedValue); + + static const int ligaturesAllowedValue = 1; + static CFNumberRef ligaturesAllowed = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ligaturesAllowedValue); + + if (!(typesettingFeatures & Kerning)) { + static const float kerningAdjustmentValue = 0; + static CFNumberRef kerningAdjustment = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &kerningAdjustmentValue); + static const void* keysWithKerningDisabled[] = { kCTFontAttributeName, kCTKernAttributeName, kCTLigatureAttributeName, kCTVerticalFormsAttributeName }; + const void* valuesWithKerningDisabled[] = { platformData().ctFont(), kerningAdjustment, allowLigatures + ? ligaturesAllowed : ligaturesNotAllowed, orientation() == Vertical ? kCFBooleanTrue : kCFBooleanFalse }; + attributesDictionary.adoptCF(CFDictionaryCreate(0, keysWithKerningDisabled, valuesWithKerningDisabled, + WTF_ARRAY_LENGTH(keysWithKerningDisabled), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + } else { + // By omitting the kCTKernAttributeName attribute, we get Core Text's standard kerning. + static const void* keysWithKerningEnabled[] = { kCTFontAttributeName, kCTLigatureAttributeName, kCTVerticalFormsAttributeName }; + const void* valuesWithKerningEnabled[] = { platformData().ctFont(), allowLigatures ? ligaturesAllowed : ligaturesNotAllowed, orientation() == Vertical ? kCFBooleanTrue : kCFBooleanFalse }; + attributesDictionary.adoptCF(CFDictionaryCreate(0, keysWithKerningEnabled, valuesWithKerningEnabled, + WTF_ARRAY_LENGTH(keysWithKerningEnabled), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + } + + return attributesDictionary.get(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm new file mode 100644 index 0000000..92585c6 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "SimpleFontData.h" + +#import "BlockExceptions.h" +#import "Color.h" +#import "FloatRect.h" +#import "Font.h" +#import "FontCache.h" +#import "FontDescription.h" +#import "SharedBuffer.h" +#import "WebCoreSystemInterface.h" +#import <AppKit/AppKit.h> +#import <ApplicationServices/ApplicationServices.h> +#import <float.h> +#import <unicode/uchar.h> +#import <wtf/Assertions.h> +#import <wtf/StdLibExtras.h> +#import <wtf/RetainPtr.h> +#import <wtf/UnusedParam.h> + +@interface NSFont (WebAppKitSecretAPI) +- (BOOL)_isFakeFixedPitch; +@end + +using namespace std; + +namespace WebCore { + +const float smallCapsFontSizeMultiplier = 0.7f; +static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x / unitsPerEm; } + +static bool initFontData(SimpleFontData* fontData) +{ + if (!fontData->platformData().cgFont()) + return false; + +#ifdef BUILDING_ON_TIGER + ATSUStyle fontStyle; + if (ATSUCreateStyle(&fontStyle) != noErr) + return false; + + ATSUFontID fontId = fontData->platformData().m_atsuFontID; + if (!fontId) { + ATSUDisposeStyle(fontStyle); + return false; + } + + ATSUAttributeTag tag = kATSUFontTag; + ByteCount size = sizeof(ATSUFontID); + ATSUFontID *valueArray[1] = {&fontId}; + OSStatus status = ATSUSetAttributes(fontStyle, 1, &tag, &size, (void* const*)valueArray); + if (status != noErr) { + ATSUDisposeStyle(fontStyle); + return false; + } + + if (wkGetATSStyleGroup(fontStyle, &fontData->m_styleGroup) != noErr) { + ATSUDisposeStyle(fontStyle); + return false; + } + + ATSUDisposeStyle(fontStyle); +#endif + + return true; +} + +static NSString *webFallbackFontFamily(void) +{ + DEFINE_STATIC_LOCAL(RetainPtr<NSString>, webFallbackFontFamily, ([[NSFont systemFontOfSize:16.0f] familyName])); + return webFallbackFontFamily.get(); +} + +#if !ERROR_DISABLED +#if defined(__LP64__) || (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)) +static NSString* pathFromFont(NSFont*) +{ + // FMGetATSFontRefFromFont is not available. As pathFromFont is only used for debugging purposes, + // returning nil is acceptable. + return nil; +} +#else +static NSString* pathFromFont(NSFont *font) +{ +#ifndef BUILDING_ON_TIGER + ATSFontRef atsFont = FMGetATSFontRefFromFont(CTFontGetPlatformFont(toCTFontRef(font), 0)); +#else + ATSFontRef atsFont = FMGetATSFontRefFromFont(wkGetNSFontATSUFontId(font)); +#endif + FSRef fileRef; + +#ifndef BUILDING_ON_TIGER + OSStatus status = ATSFontGetFileReference(atsFont, &fileRef); + if (status != noErr) + return nil; +#else + FSSpec oFile; + OSStatus status = ATSFontGetFileSpecification(atsFont, &oFile); + if (status != noErr) + return nil; + + status = FSpMakeFSRef(&oFile, &fileRef); + if (status != noErr) + return nil; +#endif + + UInt8 filePathBuffer[PATH_MAX]; + status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX); + if (status == noErr) + return [NSString stringWithUTF8String:(const char*)filePathBuffer]; + + return nil; +} +#endif // __LP64__ +#endif // !ERROR_DISABLED + +void SimpleFontData::platformInit() +{ +#ifdef BUILDING_ON_TIGER + m_styleGroup = 0; +#endif +#if USE(ATSUI) + m_ATSUMirrors = false; + m_checkedShapesArabic = false; + m_shapesArabic = false; +#endif + + m_syntheticBoldOffset = m_platformData.m_syntheticBold ? 1.0f : 0.f; + + bool failedSetup = false; + if (!initFontData(this)) { + // Ack! Something very bad happened, like a corrupt font. + // Try looking for an alternate 'base' font for this renderer. + + // Special case hack to use "Times New Roman" in place of "Times". + // "Times RO" is a common font whose family name is "Times". + // It overrides the normal "Times" family font. + // It also appears to have a corrupt regular variant. + NSString *fallbackFontFamily; + if ([[m_platformData.font() familyName] isEqual:@"Times"]) + fallbackFontFamily = @"Times New Roman"; + else + fallbackFontFamily = webFallbackFontFamily(); + + // Try setting up the alternate font. + // This is a last ditch effort to use a substitute font when something has gone wrong. +#if !ERROR_DISABLED + RetainPtr<NSFont> initialFont = m_platformData.font(); +#endif + if (m_platformData.font()) + m_platformData.setFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toFamily:fallbackFontFamily]); + else + m_platformData.setFont([NSFont fontWithName:fallbackFontFamily size:m_platformData.size()]); +#if !ERROR_DISABLED + NSString *filePath = pathFromFont(initialFont.get()); + if (!filePath) + filePath = @"not known"; +#endif + if (!initFontData(this)) { + if ([fallbackFontFamily isEqual:@"Times New Roman"]) { + // OK, couldn't setup Times New Roman as an alternate to Times, fallback + // on the system font. If this fails we have no alternative left. + m_platformData.setFont([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toFamily:webFallbackFontFamily()]); + if (!initFontData(this)) { + // We tried, Times, Times New Roman, and the system font. No joy. We have to give up. + LOG_ERROR("unable to initialize with font %@ at %@", initialFont.get(), filePath); + failedSetup = true; + } + } else { + // We tried the requested font and the system font. No joy. We have to give up. + LOG_ERROR("unable to initialize with font %@ at %@", initialFont.get(), filePath); + failedSetup = true; + } + } + + // Report the problem. + LOG_ERROR("Corrupt font detected, using %@ in place of %@ located at \"%@\".", + [m_platformData.font() familyName], [initialFont.get() familyName], filePath); + } + + // If all else fails, try to set up using the system font. + // This is probably because Times and Times New Roman are both unavailable. + if (failedSetup) { + m_platformData.setFont([NSFont systemFontOfSize:[m_platformData.font() pointSize]]); + LOG_ERROR("failed to set up font, using system font %s", m_platformData.font()); + initFontData(this); + } + + int iAscent; + int iDescent; + int iLineGap; +#ifdef BUILDING_ON_TIGER + wkGetFontMetrics(m_platformData.cgFont(), &iAscent, &iDescent, &iLineGap, &m_unitsPerEm); +#else + iAscent = CGFontGetAscent(m_platformData.cgFont()); + iDescent = CGFontGetDescent(m_platformData.cgFont()); + iLineGap = CGFontGetLeading(m_platformData.cgFont()); + m_unitsPerEm = CGFontGetUnitsPerEm(m_platformData.cgFont()); +#endif + + float pointSize = m_platformData.m_size; + float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize; + float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize; + float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize; + + // We need to adjust Times, Helvetica, and Courier to closely match the + // vertical metrics of their Microsoft counterparts that are the de facto + // web standard. The AppKit adjustment of 20% is too big and is + // incorrectly added to line spacing, so we use a 15% adjustment instead + // and add it to the ascent. + NSString *familyName = [m_platformData.font() familyName]; + if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"]) + fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); + else if ([familyName isEqualToString:@"Geeza Pro"]) { + // Geeza Pro has glyphs that draw slightly above the ascent or far below the descent. Adjust + // those vertical metrics to better match reality, so that diacritics at the bottom of one line + // do not overlap diacritics at the top of the next line. + fAscent *= 1.08f; + fDescent *= 2.f; + } + + m_ascent = lroundf(fAscent); + m_descent = lroundf(fDescent); + m_lineGap = lroundf(fLineGap); + m_lineSpacing = m_ascent + m_descent + m_lineGap; + + // Hack Hiragino line metrics to allow room for marked text underlines. + // <rdar://problem/5386183> + if (m_descent < 3 && m_lineGap >= 3 && [familyName hasPrefix:@"Hiragino"]) { + m_lineGap -= 3 - m_descent; + m_descent = 3; + } + + if (m_orientation == Vertical) { + // Ignore vertical orientation when the font doesn't support vertical metrics. + // The check doesn't look neat but this is what AppKit does for vertical writing... + RetainPtr<CFArrayRef> tableTags(AdoptCF, CTFontCopyAvailableTables(m_platformData.ctFont(), kCTFontTableOptionExcludeSynthetic)); + CFIndex numTables = CFArrayGetCount(tableTags.get()); + bool found = false; + for (CFIndex index = 0; index < numTables; ++index) { + CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tableTags.get(), index); + if (tag == kCTFontTableVhea || tag == kCTFontTableVORG) { + found = true; + break; + } + } + + if (found == false) + m_orientation = Horizontal; + } + + // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font. + // Unfortunately, NSFont will round this for us so we don't quite get the right value. + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + NSGlyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0; + if (xGlyph) { + CGRect xBox = platformBoundsForGlyph(xGlyph); + // Use the maximum of either width or height because "x" is nearly square + // and web pages that foolishly use this metric for width will be laid out + // poorly if we return an accurate height. Classic case is Times 13 point, + // which has an "x" that is 7x6 pixels. + m_xHeight = static_cast<float>(max(CGRectGetMaxX(xBox), -CGRectGetMinY(xBox))); + } else { +#ifndef BUILDING_ON_TIGER + m_xHeight = static_cast<float>(CGFontGetXHeight(m_platformData.cgFont())) / m_unitsPerEm; +#else + m_xHeight = m_platformData.font() ? [m_platformData.font() xHeight] : 0; +#endif + // CGFontGetXHeight() returns a wrong value for "Apple Symbols" font (a float close to 0, but not strictly 0). + // The following code makes a guess for m_xHeight in that case. + // The int cast is a workaround for the "almost" zero value returned by CGFontGetXHeight(). + if (!static_cast<int>(m_xHeight) && fAscent) + m_xHeight = 2 * fAscent / 3; + } +} + +static CFDataRef copyFontTableForTag(FontPlatformData& platformData, FourCharCode tableName) +{ +#ifdef BUILDING_ON_TIGER + ATSFontRef atsFont = FMGetATSFontRefFromFont(platformData.m_atsuFontID); + + ByteCount tableSize; + if (ATSFontGetTable(atsFont, tableName, 0, 0, NULL, &tableSize) != noErr) + return 0; + + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, tableSize); + if (!data) + return 0; + + CFDataIncreaseLength(data, tableSize); + if (ATSFontGetTable(atsFont, tableName, 0, tableSize, CFDataGetMutableBytePtr(data), &tableSize) != noErr) { + CFRelease(data); + return 0; + } + + return data; +#else + return CGFontCopyTableForTag(platformData.cgFont(), tableName); +#endif +} + +void SimpleFontData::platformCharWidthInit() +{ + m_avgCharWidth = 0; + m_maxCharWidth = 0; + + RetainPtr<CFDataRef> os2Table(AdoptCF, copyFontTableForTag(m_platformData, 'OS/2')); + if (os2Table && CFDataGetLength(os2Table.get()) >= 4) { + const UInt8* os2 = CFDataGetBytePtr(os2Table.get()); + SInt16 os2AvgCharWidth = os2[2] * 256 + os2[3]; + m_avgCharWidth = scaleEmToUnits(os2AvgCharWidth, m_unitsPerEm) * m_platformData.m_size; + } + + RetainPtr<CFDataRef> headTable(AdoptCF, copyFontTableForTag(m_platformData, 'head')); + if (headTable && CFDataGetLength(headTable.get()) >= 42) { + const UInt8* head = CFDataGetBytePtr(headTable.get()); + ushort uxMin = head[36] * 256 + head[37]; + ushort uxMax = head[40] * 256 + head[41]; + SInt16 xMin = static_cast<SInt16>(uxMin); + SInt16 xMax = static_cast<SInt16>(uxMax); + float diff = static_cast<float>(xMax - xMin); + m_maxCharWidth = scaleEmToUnits(diff, m_unitsPerEm) * m_platformData.m_size; + } + + // Fallback to a cross-platform estimate, which will populate these values if they are non-positive. + initCharWidths(); +} + +void SimpleFontData::platformDestroy() +{ + if (!isCustomFont() && m_derivedFontData) { + // These come from the cache. + if (m_derivedFontData->smallCaps) + fontCache()->releaseFontData(m_derivedFontData->smallCaps.leakPtr()); + + if (m_derivedFontData->emphasisMark) + fontCache()->releaseFontData(m_derivedFontData->emphasisMark.leakPtr()); + } + +#ifdef BUILDING_ON_TIGER + if (m_styleGroup) + wkReleaseStyleGroup(m_styleGroup); +#endif +#if USE(ATSUI) + HashMap<unsigned, ATSUStyle>::iterator end = m_ATSUStyleMap.end(); + for (HashMap<unsigned, ATSUStyle>::iterator it = m_ATSUStyleMap.begin(); it != end; ++it) + ATSUDisposeStyle(it->second); +#endif +} + +SimpleFontData* SimpleFontData::scaledFontData(const FontDescription& fontDescription, float scaleFactor) const +{ + if (isCustomFont()) { + FontPlatformData scaledFontData(m_platformData); + scaledFontData.m_size = scaledFontData.m_size * scaleFactor; + return new SimpleFontData(scaledFontData, true, false); + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + float size = m_platformData.size() * scaleFactor; + FontPlatformData scaledFontData([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toSize:size], size); + + // AppKit resets the type information (screen/printer) when you convert a font to a different size. + // We have to fix up the font that we're handed back. + scaledFontData.setFont(fontDescription.usePrinterFont() ? [scaledFontData.font() printerFont] : [scaledFontData.font() screenFont]); + + if (scaledFontData.font()) { + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_platformData.font()]; + + if (m_platformData.m_syntheticBold) + fontTraits |= NSBoldFontMask; + if (m_platformData.m_syntheticOblique) + fontTraits |= NSItalicFontMask; + + NSFontTraitMask scaledFontTraits = [fontManager traitsOfFont:scaledFontData.font()]; + scaledFontData.m_syntheticBold = (fontTraits & NSBoldFontMask) && !(scaledFontTraits & NSBoldFontMask); + scaledFontData.m_syntheticOblique = (fontTraits & NSItalicFontMask) && !(scaledFontTraits & NSItalicFontMask); + + return fontCache()->getCachedFontData(&scaledFontData); + } + END_BLOCK_OBJC_EXCEPTIONS; + + return 0; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->smallCaps) + m_derivedFontData->smallCaps = scaledFontData(fontDescription, smallCapsFontSizeMultiplier); + + return m_derivedFontData->smallCaps.get(); +} + +SimpleFontData* SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const +{ + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->emphasisMark) + m_derivedFontData->emphasisMark = scaledFontData(fontDescription, .5f); + + return m_derivedFontData->emphasisMark.get(); +} + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<unichar*>(characters) length:length freeWhenDone:NO]; + NSCharacterSet *set = [[m_platformData.font() coveredCharacterSet] invertedSet]; + bool result = set && [string rangeOfCharacterFromSet:set].location == NSNotFound; + [string release]; + return result; +} + +void SimpleFontData::determinePitch() +{ + NSFont* f = m_platformData.font(); + // Special case Osaka-Mono. + // According to <rdar://problem/3999467>, we should treat Osaka-Mono as fixed pitch. + // Note that the AppKit does not report Osaka-Mono as fixed pitch. + + // Special case MS-PGothic. + // According to <rdar://problem/4032938>, we should not treat MS-PGothic as fixed pitch. + // Note that AppKit does report MS-PGothic as fixed pitch. + + // Special case MonotypeCorsiva + // According to <rdar://problem/5454704>, we should not treat MonotypeCorsiva as fixed pitch. + // Note that AppKit does report MonotypeCorsiva as fixed pitch. + + NSString *name = [f fontName]; + m_treatAsFixedPitch = ([f isFixedPitch] || [f _isFakeFixedPitch] || + [name caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame) && + [name caseInsensitiveCompare:@"MS-PGothic"] != NSOrderedSame && + [name caseInsensitiveCompare:@"MonotypeCorsiva"] != NSOrderedSame; +} + +FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const +{ + FloatRect boundingBox; +#ifndef BUILDING_ON_TIGER + boundingBox = CTFontGetBoundingRectsForGlyphs(m_platformData.ctFont(), + orientation() == Vertical ? kCTFontVerticalOrientation : kCTFontHorizontalOrientation, &glyph, 0, 1); + boundingBox.setY(-boundingBox.bottom()); +#else + // FIXME: Custom fonts don't have NSFonts, so this function doesn't compute correct bounds for these on Tiger. + if (!m_platformData.font()) + return boundingBox; + boundingBox = [m_platformData.font() boundingRectForGlyph:glyph]; + boundingBox.setY(-boundingBox.bottom()); +#endif + if (m_syntheticBoldOffset) + boundingBox.setWidth(boundingBox.width() + m_syntheticBoldOffset); + + return boundingBox; +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + CGSize advance; + if (orientation() == Horizontal || m_isBrokenIdeographFont) { + NSFont* font = platformData().font(); + float pointSize = platformData().m_size; + CGAffineTransform m = CGAffineTransformMakeScale(pointSize, pointSize); + if (!wkGetGlyphTransformedAdvances(platformData().cgFont(), font, &m, &glyph, &advance)) { + LOG_ERROR("Unable to cache glyph widths for %@ %f", [font displayName], pointSize); + advance.width = 0; + } + } else + CTFontGetAdvancesForGlyphs(m_platformData.ctFont(), kCTFontVerticalOrientation, &glyph, &advance, 1); + + return advance.width + m_syntheticBoldOffset; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/WebGLLayer.h b/Source/WebCore/platform/graphics/mac/WebGLLayer.h new file mode 100644 index 0000000..6440b71 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/WebGLLayer.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLLayer_h +#define WebGLLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import <QuartzCore/QuartzCore.h> + +namespace WebCore { + class GraphicsLayer; + class GraphicsContext3D; +} + +@interface WebGLLayer : CAOpenGLLayer +{ + WebCore::GraphicsLayer* m_layerOwner; + WebCore::GraphicsContext3D* m_context; +} + +- (id)initWithGraphicsContext3D:(WebCore::GraphicsContext3D*)context; + +- (CGImageRef)copyImageSnapshotWithColorSpace:(CGColorSpaceRef)colorSpace; + +@end + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebGLLayer_h diff --git a/Source/WebCore/platform/graphics/mac/WebGLLayer.mm b/Source/WebCore/platform/graphics/mac/WebGLLayer.mm new file mode 100644 index 0000000..c24181b --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/WebGLLayer.mm @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) +#if ENABLE(3D_CANVAS) + +#import "WebGLLayer.h" + +#import "GraphicsContext3D.h" +#import "GraphicsLayer.h" +#import <OpenGL/OpenGL.h> +#import <wtf/FastMalloc.h> +#import <wtf/RetainPtr.h> +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebGLLayer + +-(id)initWithGraphicsContext3D:(GraphicsContext3D*)context +{ + m_context = context; + self = [super init]; + return self; +} + +-(CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask +{ + // FIXME: The mask param tells you which display (on a multi-display system) + // is to be used. But since we are now getting the pixel format from the + // Canvas CGL context, we don't use it. This seems to do the right thing on + // one multi-display system. But there may be cases where this is not the case. + // If needed we will have to set the display mask in the Canvas CGLContext and + // make sure it matches. + UNUSED_PARAM(mask); + return CGLRetainPixelFormat(CGLGetPixelFormat(m_context->platformGraphicsContext3D())); +} + +-(CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat +{ + CGLContextObj contextObj; + CGLCreateContext(pixelFormat, m_context->platformGraphicsContext3D(), &contextObj); + return contextObj; +} + +-(void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp +{ + m_context->prepareTexture(); + + CGLSetCurrentContext(glContext); + + CGRect frame = [self frame]; + + // draw the FBO into the layer + glViewport(0, 0, frame.size.width, frame.size.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-1, 1, -1, 1, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_context->platformTexture()); + + glBegin(GL_TRIANGLE_FAN); + glTexCoord2f(0, 0); + glVertex2f(-1, -1); + glTexCoord2f(1, 0); + glVertex2f(1, -1); + glTexCoord2f(1, 1); + glVertex2f(1, 1); + glTexCoord2f(0, 1); + glVertex2f(-1, 1); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + // Call super to finalize the drawing. By default all it does is call glFlush(). + [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp]; +} + +static void freeData(void *, const void *data, size_t /* size */) +{ + fastFree(const_cast<void *>(data)); +} + +-(CGImageRef)copyImageSnapshotWithColorSpace:(CGColorSpaceRef)colorSpace +{ + CGLSetCurrentContext(m_context->platformGraphicsContext3D()); + + RetainPtr<CGColorSpaceRef> imageColorSpace = colorSpace; + if (!imageColorSpace) + imageColorSpace.adoptCF(CGColorSpaceCreateDeviceRGB()); + + CGRect layerBounds = CGRectIntegral([self bounds]); + + size_t width = layerBounds.size.width; + size_t height = layerBounds.size.height; + + size_t rowBytes = (width * 4 + 15) & ~15; + size_t dataSize = rowBytes * height; + void* data = fastMalloc(dataSize); + if (!data) + return 0; + + glPixelStorei(GL_PACK_ROW_LENGTH, rowBytes / 4); + glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + + CGDataProviderRef provider = CGDataProviderCreateWithData(0, data, dataSize, freeData); + CGImageRef image = CGImageCreate(width, height, 8, 32, rowBytes, imageColorSpace.get(), + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, + provider, 0, true, + kCGRenderingIntentDefault); + CGDataProviderRelease(provider); + return image; +} + +- (void)display +{ + [super display]; + if (m_layerOwner) + m_layerOwner->layerDidDisplay(self); +} + +@end + +@implementation WebGLLayer(WebGLLayerAdditions) + +-(void)setLayerOwner:(GraphicsLayer*)aLayer +{ + m_layerOwner = aLayer; +} + +-(GraphicsLayer*)layerOwner +{ + return m_layerOwner; +} + +@end + +#endif // ENABLE(3D_CANVAS) +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/mac/WebLayer.h b/Source/WebCore/platform/graphics/mac/WebLayer.h new file mode 100644 index 0000000..30bf55b --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/WebLayer.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebLayer_h +#define WebLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import <QuartzCore/QuartzCore.h> + +namespace WebCore { + class GraphicsLayer; + class PlatformCALayerClient; +} + +#if defined(BUILDING_ON_LEOPARD) +@interface CALayer(WebLayerInternal) +- (CGAffineTransform)contentsTransform; +- (void)setContentsTransform:(CGAffineTransform)t; +@end +#endif + +@interface WebLayer : CALayer +{ +} +@end + +// Functions allows us to share implementation across WebTiledLayer and WebLayer +void drawLayerContents(CGContextRef, CALayer *, WebCore::PlatformCALayerClient*); +void setLayerNeedsDisplayInRect(CALayer *, WebCore::PlatformCALayerClient*, CGRect); + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebLayer_h diff --git a/Source/WebCore/platform/graphics/mac/WebLayer.mm b/Source/WebCore/platform/graphics/mac/WebLayer.mm new file mode 100644 index 0000000..06ea9be --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/WebLayer.mm @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebLayer.h" + +#import "GraphicsContext.h" +#import "GraphicsLayerCA.h" +#import "PlatformCALayer.h" +#import <objc/objc-runtime.h> +#import <QuartzCore/QuartzCore.h> +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebLayer + +void drawLayerContents(CGContextRef context, CALayer *layer, WebCore::PlatformCALayerClient* layerContents) +{ + if (!layerContents) + return; + + CGContextSaveGState(context); + + CGRect layerBounds = [layer bounds]; + if (layerContents->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) { + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -layerBounds.size.height); + } + + [NSGraphicsContext saveGraphicsState]; + + // Set up an NSGraphicsContext for the context, so that parts of AppKit that rely on + // the current NSGraphicsContext (e.g. NSCell drawing) get the right one. + NSGraphicsContext* layerContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]; + [NSGraphicsContext setCurrentContext:layerContext]; + + GraphicsContext graphicsContext(context); + + if (!layerContents->platformCALayerContentsOpaque()) { + // Turn off font smoothing to improve the appearance of text rendered onto a transparent background. + graphicsContext.setShouldSmoothFonts(false); + } + + // It's important to get the clip from the context, because it may be significantly + // smaller than the layer bounds (e.g. tiled layers) + CGRect clipBounds = CGContextGetClipBoundingBox(context); + IntRect clip(enclosingIntRect(clipBounds)); + layerContents->platformCALayerPaintContents(graphicsContext, clip); + + [NSGraphicsContext restoreGraphicsState]; + + if (layerContents->platformCALayerShowRepaintCounter()) { + bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; + + char text[16]; // that's a lot of repaints + snprintf(text, sizeof(text), "%d", layerContents->platformCALayerIncrementRepaintCount()); + + CGContextSaveGState(context); + if (isTiledLayer) + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 0.8f); + else + CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); + + CGRect aBounds = layerBounds; + + aBounds.size.width = 10 + 12 * strlen(text); + aBounds.size.height = 25; + CGContextFillRect(context, aBounds); + + CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 1.0f); + + CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, -1.0f)); + CGContextSelectFont(context, "Helvetica", 25, kCGEncodingMacRoman); + CGContextShowTextAtPoint(context, aBounds.origin.x + 3.0f, aBounds.origin.y + 20.0f, text, strlen(text)); + + CGContextRestoreGState(context); + } + + CGContextRestoreGState(context); +} + +void setLayerNeedsDisplayInRect(CALayer *layer, WebCore::PlatformCALayerClient* layerContents, CGRect rect) +{ + if (layerContents && layerContents->platformCALayerDrawsContent()) { + struct objc_super layerSuper = { layer, class_getSuperclass(object_getClass(layer)) }; +#if defined(BUILDING_ON_LEOPARD) + rect = CGRectApplyAffineTransform(rect, [layer contentsTransform]); +#else + if (layerContents->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) + rect.origin.y = [layer bounds].size.height - rect.origin.y - rect.size.height; +#endif + objc_msgSendSuper(&layerSuper, @selector(setNeedsDisplayInRect:), rect); + +#ifndef NDEBUG + if (layerContents->platformCALayerShowRepaintCounter()) { + CGRect bounds = [layer bounds]; + CGRect indicatorRect = CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25); +#if defined(BUILDING_ON_LEOPARD) + indicatorRect = CGRectApplyAffineTransform(indicatorRect, [layer contentsTransform]); +#else + if (layerContents->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesBottomUp) + indicatorRect.origin.y = [layer bounds].size.height - indicatorRect.origin.y - indicatorRect.size.height; +#endif + objc_msgSendSuper(&layerSuper, @selector(setNeedsDisplayInRect:), indicatorRect); + } +#endif + } +} + +// Disable default animations +- (id<CAAction>)actionForKey:(NSString *)key +{ + UNUSED_PARAM(key); + return nil; +} + +- (void)setNeedsDisplay +{ + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer && layer->owner() && layer->owner()->platformCALayerDrawsContent()) + [super setNeedsDisplay]; +} + +- (void)setNeedsDisplayInRect:(CGRect)dirtyRect +{ + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer) + setLayerNeedsDisplayInRect(self, layer->owner(), dirtyRect); +} + +- (void)display +{ + [super display]; + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer && layer->owner()) + layer->owner()->platformCALayerLayerDidDisplay(self); +} + +- (void)drawInContext:(CGContextRef)context +{ + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer) + drawLayerContents(context, self, layer->owner()); +} + +@end // implementation WebLayer + +// MARK: - + +#ifndef NDEBUG + +@implementation CALayer(ExtendedDescription) + +- (NSString*)_descriptionWithPrefix:(NSString*)inPrefix +{ + CGRect aBounds = [self bounds]; + CGPoint aPos = [self position]; + + NSString* selfString = [NSString stringWithFormat:@"%@<%@ 0x%08x> \"%@\" bounds(%.1f, %.1f, %.1f, %.1f) pos(%.1f, %.1f), sublayers=%d masking=%d", + inPrefix, + [self class], + self, + [self name], + aBounds.origin.x, aBounds.origin.y, aBounds.size.width, aBounds.size.height, + aPos.x, aPos.y, + [[self sublayers] count], + [self masksToBounds]]; + + NSMutableString* curDesc = [NSMutableString stringWithString:selfString]; + + if ([[self sublayers] count] > 0) + [curDesc appendString:@"\n"]; + + NSString* sublayerPrefix = [inPrefix stringByAppendingString:@"\t"]; + + NSEnumerator* sublayersEnum = [[self sublayers] objectEnumerator]; + CALayer* curLayer; + while ((curLayer = [sublayersEnum nextObject])) + [curDesc appendString:[curLayer _descriptionWithPrefix:sublayerPrefix]]; + + if ([[self sublayers] count] == 0) + [curDesc appendString:@"\n"]; + + return curDesc; +} + +- (NSString*)extendedDescription +{ + return [self _descriptionWithPrefix:@""]; +} + +@end // implementation WebLayer(ExtendedDescription) + +#endif // NDEBUG + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/mac/WebTiledLayer.h b/Source/WebCore/platform/graphics/mac/WebTiledLayer.h new file mode 100644 index 0000000..6f559e3 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/WebTiledLayer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebTiledLayer_h +#define WebTiledLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebLayer.h" + +@interface WebTiledLayer : CATiledLayer +{ +} + +// implements WebLayerAdditions + +@end + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebTiledLayer_h + diff --git a/Source/WebCore/platform/graphics/mac/WebTiledLayer.mm b/Source/WebCore/platform/graphics/mac/WebTiledLayer.mm new file mode 100644 index 0000000..e9fa5b7 --- /dev/null +++ b/Source/WebCore/platform/graphics/mac/WebTiledLayer.mm @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebTiledLayer.h" + +#import "GraphicsContext.h" +#import "GraphicsLayerCA.h" +#import "PlatformCALayer.h" +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebTiledLayer + +// Set a zero duration for the fade in of tiles ++ (CFTimeInterval)fadeDuration +{ + return 0; +} + +// Make sure that tiles are drawn on the main thread ++ (BOOL)shouldDrawOnMainThread +{ + return YES; +} + +// Disable default animations +- (id<CAAction>)actionForKey:(NSString *)key +{ + UNUSED_PARAM(key); + return nil; +} + +- (void)setNeedsDisplay +{ + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer && layer->owner() && layer->owner()->platformCALayerDrawsContent()) + [super setNeedsDisplay]; +} + +- (void)setNeedsDisplayInRect:(CGRect)dirtyRect +{ + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer) + setLayerNeedsDisplayInRect(self, layer->owner(), dirtyRect); +} + +- (void)display +{ + [super display]; + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer && layer->owner()) + layer->owner()->platformCALayerLayerDidDisplay(self); +} + +- (void)drawInContext:(CGContextRef)context +{ + PlatformCALayer* layer = PlatformCALayer::platformCALayer(self); + if (layer) + drawLayerContents(context, self, layer->owner()); +} + +@end // implementation WebTiledLayer + +#endif // USE(ACCELERATED_COMPOSITING) |
