1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#if USE(ACCELERATED_COMPOSITING)
28#include "CCFontAtlas.h"
29
30#include "CCProxy.h"
31#include "Font.h"
32#include "FontCache.h"
33#include "FontDescription.h"
34#include "GraphicsContext.h"
35#include "ImageBuffer.h"
36#include "TextRun.h"
37
38#define ATLAS_SIZE 128
39#define FONT_HEIGHT 14
40
41namespace WebCore {
42
43using namespace std;
44
45
46CCFontAtlas::CCFontAtlas()
47 : m_fontHeight(FONT_HEIGHT)
48{
49}
50
51static void wrapPositionIfNeeded(IntPoint& position, int textWidth, int textHeight)
52{
53 if (position.x() + textWidth > ATLAS_SIZE)
54 position = IntPoint(0, position.y() + textHeight);
55}
56
57void CCFontAtlas::generateAtlasForFont(GraphicsContext* atlasContext, const FontDescription& fontDescription, const Color& fontColor, const IntPoint& startingPosition, IntRect asciiToAtlasTable[128])
58{
59 ASSERT(CCProxy::isMainThread());
60 ASSERT(m_atlas);
61
62 FontCachePurgePreventer fontCachePurgePreventer;
63
64 IntPoint position = startingPosition;
65 int textHeight = fontDescription.computedPixelSize();
66 // This is a dirty little trick to account for overhang letters like g, p, j.
67 int inflation = textHeight / 3;
68
69 Font font(fontDescription, 0, 0);
70 font.update(0);
71
72 atlasContext->setStrokeColor(fontColor, ColorSpaceDeviceRGB);
73 atlasContext->setFillColor(fontColor, ColorSpaceDeviceRGB);
74
75 // First, draw a generic rect that will be used for special and unknown characters that have nothing else to render.
76 {
77 int textWidth = textHeight / 2;
78 wrapPositionIfNeeded(position, textWidth, textHeight + inflation);
79 atlasContext->strokeRect(FloatRect(FloatPoint(position.x() + 1, position.y() - textHeight + 1 + inflation), FloatSize(textWidth - 2, textHeight - 2 - inflation)), 1);
80
81 // Initialize the rect that would be copied when drawing this glyph from the atlas.
82 asciiToAtlasTable[0] = IntRect(IntPoint(position.x(), position.y() - textHeight), IntSize(textWidth, textHeight + inflation));
83
84 // Increment to the position where the next glyph will be placed.
85 position.setX(position.x() + textWidth);
86 }
87
88 // Then, draw the ASCII characters.
89 for (int i = 1; i < 128; ++i) {
90
91 if (i < 32) {
92 // Special characters will simply use the the default glyph.
93 asciiToAtlasTable[i] = asciiToAtlasTable[0];
94 continue;
95 }
96
97 UChar c = static_cast<UChar>(i);
98 String str;
99 str.append(c);
100 TextRun text(str);
101
102 int textWidth = round(font.width(text));
103 wrapPositionIfNeeded(position, textWidth, textHeight + inflation);
104 atlasContext->drawText(font, text, position);
105
106 // Initialize the rect that would be copied when drawing this glyph from the atlas.
107 asciiToAtlasTable[i] = IntRect(IntPoint(position.x(), position.y() - textHeight), IntSize(textWidth, textHeight + inflation));
108
109 // Increment to the position where the next glyph will be placed.
110 position.setX(position.x() + textWidth);
111 }
112}
113
114void CCFontAtlas::initialize()
115{
116 ASSERT(CCProxy::isMainThread());
117
118 // We expect this function to be called only once when the atlas did not exist yet. We should be aware if that's not true.
119 ASSERT(!m_atlas);
120
121 m_atlas = ImageBuffer::create(IntSize(ATLAS_SIZE, ATLAS_SIZE));
122 GraphicsContext* atlasContext = m_atlas->context();
123
124 // Clear the entire texture atlas to transparent before drawing fonts.
125 atlasContext->setFillColor(Color(0, 0, 0, 0), ColorSpaceDeviceRGB);
126 atlasContext->fillRect(FloatRect(0, 0, ATLAS_SIZE, ATLAS_SIZE));
127
128 // FIXME: monospace font does not work as expected.
129 FontDescription fontDescription;
130 fontDescription.setGenericFamily(FontDescription::MonospaceFamily);
131 fontDescription.setComputedSize(m_fontHeight);
132 generateAtlasForFont(atlasContext, fontDescription, Color(255, 0, 0), IntPoint(0, fontDescription.computedPixelSize()), m_asciiToRectTable);
133}
134
135void CCFontAtlas::drawText(GraphicsContext* targetContext, const String& text, const IntPoint& destPosition, const IntSize& targetSize) const
136{
137 ASSERT(CCProxy::isImplThread());
138 ASSERT(m_atlas);
139
140 Vector<String> lines;
141 text.split('\n', lines);
142
143 IntPoint position = destPosition;
144 for (size_t i = 0; i < lines.size(); ++i) {
145 drawOneLineOfTextInternal(targetContext, lines[i], position);
146 position.setY(position.y() + m_fontHeight);
147 if (position.y() > targetSize.height())
148 return;
149 }
150}
151
152void CCFontAtlas::drawOneLineOfTextInternal(GraphicsContext* targetContext, const String& textLine, const IntPoint& destPosition) const
153{
154 ASSERT(CCProxy::isImplThread());
155 ASSERT(m_atlas);
156
157 IntPoint position = destPosition;
158 for (unsigned i = 0; i < textLine.length(); ++i) {
159 // If the ASCII code is out of bounds, then index 0 is used, which is just a plain rectangle glyph.
160 int asciiIndex = (textLine[i] < 128) ? textLine[i] : 0;
161 IntRect glyphBounds = m_asciiToRectTable[asciiIndex];
162 targetContext->drawImageBuffer(m_atlas.get(), ColorSpaceDeviceRGB, position, glyphBounds);
163 position.setX(position.x() + glyphBounds.width());
164 }
165}
166
167void CCFontAtlas::drawDebugAtlas(GraphicsContext* targetContext, const IntPoint& destPosition) const
168{
169 ASSERT(CCProxy::isImplThread());
170 ASSERT(m_atlas);
171
172 targetContext->drawImageBuffer(m_atlas.get(), ColorSpaceDeviceRGB, destPosition, IntRect(IntPoint::zero(), IntSize(ATLAS_SIZE, ATLAS_SIZE)));
173}
174
175} // namespace WebCore
176
177#endif // USE(ACCELERATED_COMPOSITING)