Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "gfxDWriteShaper.h"
7 #include "gfxWindowsPlatform.h"
9 #include <dwrite.h>
11 #include "gfxDWriteTextAnalysis.h"
13 #include "nsCRT.h"
15 bool
16 gfxDWriteShaper::ShapeText(gfxContext *aContext,
17 const char16_t *aText,
18 uint32_t aOffset,
19 uint32_t aLength,
20 int32_t aScript,
21 gfxShapedText *aShapedText)
22 {
23 HRESULT hr;
24 // TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES
26 DWRITE_READING_DIRECTION readingDirection =
27 aShapedText->IsRightToLeft()
28 ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
29 : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
31 gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
33 gfxShapedText::CompressedGlyph g;
35 IDWriteTextAnalyzer *analyzer =
36 gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer();
37 if (!analyzer) {
38 return false;
39 }
41 /**
42 * There's an internal 16-bit limit on some things inside the analyzer,
43 * but we never attempt to shape a word longer than 32K characters
44 * in a single call, so we cannot exceed that limit.
45 */
46 UINT32 length = aLength;
47 char16ptr_t text = aText;
49 TextAnalysis analysis(text, length, nullptr, readingDirection);
50 TextAnalysis::Run *runHead;
51 hr = analysis.GenerateResults(analyzer, &runHead);
53 if (FAILED(hr)) {
54 NS_WARNING("Analyzer failed to generate results.");
55 return false;
56 }
58 int32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
60 UINT32 maxGlyphs = 0;
61 trymoreglyphs:
62 if ((UINT32_MAX - 3 * length / 2 + 16) < maxGlyphs) {
63 // This isn't going to work, we're going to cross the UINT32 upper
64 // limit. Give up.
65 NS_WARNING("Shaper needs to generate more than 2^32 glyphs?!");
66 return false;
67 }
68 maxGlyphs += 3 * length / 2 + 16;
70 AutoFallibleTArray<UINT16, 400> clusters;
71 AutoFallibleTArray<UINT16, 400> indices;
72 AutoFallibleTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
73 AutoFallibleTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
74 if (!clusters.SetLength(length) ||
75 !indices.SetLength(maxGlyphs) ||
76 !textProperties.SetLength(maxGlyphs) ||
77 !glyphProperties.SetLength(maxGlyphs)) {
78 NS_WARNING("Shaper failed to allocate memory.");
79 return false;
80 }
82 UINT32 actualGlyphs;
84 hr = analyzer->GetGlyphs(text, length,
85 font->GetFontFace(), FALSE,
86 readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
87 &runHead->mScript, nullptr, nullptr, nullptr, nullptr, 0,
88 maxGlyphs, clusters.Elements(), textProperties.Elements(),
89 indices.Elements(), glyphProperties.Elements(), &actualGlyphs);
91 if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
92 // We increase the amount of glyphs and try again.
93 goto trymoreglyphs;
94 }
95 if (FAILED(hr)) {
96 NS_WARNING("Analyzer failed to get glyphs.");
97 return false;
98 }
100 WORD gID = indices[0];
101 AutoFallibleTArray<FLOAT, 400> advances;
102 AutoFallibleTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
103 if (!advances.SetLength(actualGlyphs) ||
104 !glyphOffsets.SetLength(actualGlyphs)) {
105 NS_WARNING("Shaper failed to allocate memory.");
106 return false;
107 }
109 if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
110 hr = analyzer->GetGdiCompatibleGlyphPlacements(
111 text,
112 clusters.Elements(),
113 textProperties.Elements(),
114 length,
115 indices.Elements(),
116 glyphProperties.Elements(),
117 actualGlyphs,
118 font->GetFontFace(),
119 font->GetAdjustedSize(),
120 1.0,
121 nullptr,
122 FALSE,
123 FALSE,
124 FALSE,
125 &runHead->mScript,
126 nullptr,
127 nullptr,
128 nullptr,
129 0,
130 advances.Elements(),
131 glyphOffsets.Elements());
132 } else {
133 hr = analyzer->GetGlyphPlacements(text,
134 clusters.Elements(),
135 textProperties.Elements(),
136 length,
137 indices.Elements(),
138 glyphProperties.Elements(),
139 actualGlyphs,
140 font->GetFontFace(),
141 font->GetAdjustedSize(),
142 FALSE,
143 FALSE,
144 &runHead->mScript,
145 nullptr,
146 nullptr,
147 nullptr,
148 0,
149 advances.Elements(),
150 glyphOffsets.Elements());
151 }
152 if (FAILED(hr)) {
153 NS_WARNING("Analyzer failed to get glyph placements.");
154 return false;
155 }
157 nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
158 gfxShapedText::CompressedGlyph *charGlyphs =
159 aShapedText->GetCharacterGlyphs();
161 for (unsigned int c = 0; c < length; c++) {
162 uint32_t k = clusters[c];
163 uint32_t absC = aOffset + c;
165 if (c > 0 && k == clusters[c - 1]) {
166 // This is a cluster continuation. No glyph here.
167 gfxShapedText::CompressedGlyph &g = charGlyphs[absC];
168 NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
169 g.SetComplex(g.IsClusterStart(), false, 0);
170 continue;
171 }
173 // Count glyphs for this character
174 uint32_t glyphCount = actualGlyphs - k;
175 uint32_t nextClusterOffset;
176 for (nextClusterOffset = c + 1;
177 nextClusterOffset < length; ++nextClusterOffset) {
178 if (clusters[nextClusterOffset] > k) {
179 glyphCount = clusters[nextClusterOffset] - k;
180 break;
181 }
182 }
183 int32_t advance = (int32_t)(advances[k] * appUnitsPerDevPixel);
184 if (glyphCount == 1 && advance >= 0 &&
185 glyphOffsets[k].advanceOffset == 0 &&
186 glyphOffsets[k].ascenderOffset == 0 &&
187 charGlyphs[absC].IsClusterStart() &&
188 gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
189 gfxShapedText::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
190 charGlyphs[absC].SetSimpleGlyph(advance, indices[k]);
191 } else {
192 if (detailedGlyphs.Length() < glyphCount) {
193 if (!detailedGlyphs.AppendElements(
194 glyphCount - detailedGlyphs.Length())) {
195 continue;
196 }
197 }
198 float totalAdvance = 0;
199 for (unsigned int z = 0; z < glyphCount; z++) {
200 detailedGlyphs[z].mGlyphID = indices[k + z];
201 detailedGlyphs[z].mAdvance =
202 (int32_t)(advances[k + z]
203 * appUnitsPerDevPixel);
204 if (readingDirection ==
205 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
206 detailedGlyphs[z].mXOffset =
207 (totalAdvance +
208 glyphOffsets[k + z].advanceOffset)
209 * appUnitsPerDevPixel;
210 } else {
211 detailedGlyphs[z].mXOffset =
212 glyphOffsets[k + z].advanceOffset *
213 appUnitsPerDevPixel;
214 }
215 detailedGlyphs[z].mYOffset =
216 -glyphOffsets[k + z].ascenderOffset *
217 appUnitsPerDevPixel;
218 totalAdvance += advances[k + z];
219 }
220 aShapedText->SetGlyphs(
221 absC,
222 g.SetComplex(charGlyphs[absC].IsClusterStart(),
223 true,
224 glyphCount),
225 detailedGlyphs.Elements());
226 }
227 }
229 return true;
230 }