2 * Copyright 2007-2014, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Stephan Aßmus <superstippi@gmx.de>
9 #ifndef GLYPH_LAYOUT_ENGINE_H
10 #define GLYPH_LAYOUT_ENGINE_H
12 #include "utf8_functions.h"
14 #include "FontCache.h"
15 #include "FontCacheEntry.h"
16 #include "FontManager.h"
17 #include "ServerFont.h"
23 class FontCacheReference
{
37 void SetTo(FontCacheEntry
* entry
, bool writeLocked
)
39 // NOTE: If the semantics are changed such
40 // that the reference to a previous entry
41 // is properly released, then don't forget
42 // to adapt existing which transfers
43 // responsibility of entries between
46 fWriteLocked
= writeLocked
;
51 if (fCacheEntry
== NULL
)
55 fCacheEntry
->WriteUnlock();
57 fCacheEntry
->ReadUnlock();
59 FontCache::Default()->Recycle(fCacheEntry
);
62 inline FontCacheEntry
* Entry() const
67 inline bool WriteLocked() const
73 FontCacheEntry
* fCacheEntry
;
78 class GlyphLayoutEngine
{
80 static bool IsWhiteSpace(uint32 glyphCode
);
82 static FontCacheEntry
* FontCacheEntryFor(const ServerFont
& font
,
84 const FontCacheEntry
* disallowedEntry
,
85 const char* utf8String
, int32 length
,
86 FontCacheReference
& cacheReference
,
89 template<class GlyphConsumer
>
90 static bool LayoutGlyphs(GlyphConsumer
& consumer
,
91 const ServerFont
& font
,
92 const char* utf8String
,
94 const escapement_delta
* delta
= NULL
,
95 uint8 spacing
= B_BITMAP_SPACING
,
96 const BPoint
* offsets
= NULL
,
97 FontCacheReference
* cacheReference
= NULL
);
100 static bool _WriteLockAndAcquireFallbackEntry(
101 FontCacheReference
& cacheReference
,
102 FontCacheEntry
* entry
,
103 const ServerFont
& font
, bool needsVector
,
104 const char* utf8String
, int32 length
,
105 FontCacheReference
& fallbackCacheReference
,
106 FontCacheEntry
*& fallbackEntry
);
109 virtual ~GlyphLayoutEngine();
114 GlyphLayoutEngine::IsWhiteSpace(uint32 charCode
)
117 case 0x0009: /* tab */
118 case 0x000b: /* vertical tab */
119 case 0x000c: /* form feed */
120 case 0x0020: /* space */
121 case 0x00a0: /* non breaking space */
122 case 0x000a: /* line feed */
123 case 0x000d: /* carriage return */
124 case 0x2028: /* line separator */
125 case 0x2029: /* paragraph separator */
133 inline FontCacheEntry
*
134 GlyphLayoutEngine::FontCacheEntryFor(const ServerFont
& font
, bool forceVector
,
135 const FontCacheEntry
* disallowedEntry
, const char* utf8String
, int32 length
,
136 FontCacheReference
& cacheReference
, bool needsWriteLock
)
138 ASSERT(cacheReference
.Entry() == NULL
);
140 FontCache
* cache
= FontCache::Default();
141 FontCacheEntry
* entry
= cache
->FontCacheEntryFor(font
, forceVector
);
145 if (entry
== disallowedEntry
) {
146 cache
->Recycle(entry
);
150 if (needsWriteLock
) {
151 if (!entry
->WriteLock()) {
152 cache
->Recycle(entry
);
156 if (!entry
->ReadLock()) {
157 cache
->Recycle(entry
);
162 // At this point, we have a valid FontCacheEntry and it is locked in the
163 // proper mode. We can setup the FontCacheReference so it takes care of
164 // the locking and recycling from now and return the entry.
165 cacheReference
.SetTo(entry
, needsWriteLock
);
170 template<class GlyphConsumer
>
172 GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer
& consumer
,
173 const ServerFont
& font
,
174 const char* utf8String
, int32 length
,
175 const escapement_delta
* delta
, uint8 spacing
,
176 const BPoint
* offsets
, FontCacheReference
* _cacheReference
)
178 // TODO: implement spacing modes
179 FontCacheEntry
* entry
= NULL
;
180 FontCacheReference cacheReference
;
181 FontCacheEntry
* fallbackEntry
= NULL
;
182 FontCacheReference fallbackCacheReference
;
183 if (_cacheReference
!= NULL
) {
184 entry
= _cacheReference
->Entry();
185 // When there is already a cacheReference, it means there was already
186 // an iteration over the glyphs. The use-case is for example to do
187 // a layout pass to get the string width for the bounding box, then a
188 // second layout pass to actually render the glyphs to the screen.
189 // This means that the fallback entry mechanism will not do any good
190 // for the second pass, since the fallback glyphs have been stored in
191 // the original entry.
195 entry
= FontCacheEntryFor(font
, consumer
.NeedsVector(), NULL
,
196 utf8String
, length
, cacheReference
, false);
200 } // else the entry was already used and is still locked
211 double advanceX
= 0.0;
212 double advanceY
= 0.0;
213 double size
= font
.Size();
215 uint32 lastCharCode
= 0; // Needed for kerning in B_STRING_SPACING mode
218 bool writeLocked
= false;
219 const char* start
= utf8String
;
220 while ((charCode
= UTF8ToCharCode(&utf8String
))) {
222 if (offsets
!= NULL
) {
223 // Use direct glyph locations instead of calculating them
224 // from the advance values
225 x
= offsets
[index
].x
;
226 y
= offsets
[index
].y
;
228 if (spacing
== B_STRING_SPACING
)
229 entry
->GetKerning(lastCharCode
, charCode
, &advanceX
, &advanceY
);
235 const GlyphCache
* glyph
= entry
->CachedGlyph(charCode
);
237 // The glyph has not been cached yet, switch to a write lock,
238 // acquire the fallback entry and create the glyph. Note that
239 // the write lock will persist (in the cacheReference) so that
240 // we only have to do this switch once for the whole string.
242 writeLocked
= _WriteLockAndAcquireFallbackEntry(cacheReference
,
243 entry
, font
, consumer
.NeedsVector(), utf8String
, length
,
244 fallbackCacheReference
, fallbackEntry
);
248 glyph
= entry
->CreateGlyph(charCode
, fallbackEntry
);
252 consumer
.ConsumeEmptyGlyph(index
++, charCode
, x
, y
);
256 // get next increment for pen position
257 if (spacing
== B_CHAR_SPACING
) {
258 advanceX
= glyph
->precise_advance_x
* size
;
259 advanceY
= glyph
->precise_advance_y
* size
;
261 advanceX
= glyph
->advance_x
;
262 advanceY
= glyph
->advance_y
;
265 // adjust for custom spacing
267 advanceX
+= IsWhiteSpace(charCode
)
268 ? delta
->space
: delta
->nonspace
;
271 if (!consumer
.ConsumeGlyph(index
++, charCode
, glyph
, entry
, x
, y
,
272 advanceX
, advanceY
)) {
279 lastCharCode
= charCode
;
280 if (utf8String
- start
+ 1 > length
)
286 consumer
.Finish(x
, y
);
288 if (_cacheReference
!= NULL
&& _cacheReference
->Entry() == NULL
) {
289 // The caller passed a FontCacheReference, but this is the first
290 // iteration -> switch the ownership from the stack allocated
291 // FontCacheReference to the one passed by the caller. The fallback
292 // FontCacheReference is not affected by this, since it is never used
293 // during a second iteration.
294 _cacheReference
->SetTo(entry
, cacheReference
.WriteLocked());
295 cacheReference
.SetTo(NULL
, false);
302 GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry(
303 FontCacheReference
& cacheReference
, FontCacheEntry
* entry
,
304 const ServerFont
& font
, bool forceVector
, const char* utf8String
,
305 int32 length
, FontCacheReference
& fallbackCacheReference
,
306 FontCacheEntry
*& fallbackEntry
)
308 // We need a fallback font, since potentially, we have to obtain missing
309 // glyphs from it. We need to obtain the fallback font while we have not
310 // locked anything, since locking the FontManager with the write-lock held
311 // can obvisouly lead to a deadlock.
313 bool writeLocked
= entry
->IsWriteLocked();
316 entry
->WriteUnlock();
318 cacheReference
.SetTo(NULL
, false);
322 // TODO: We always get the fallback glyphs from the Noto family, but of
323 // course the fallback font should a) contain the missing glyphs at all
324 // and b) be similar to the original font. So there should be a mapping
325 // of some kind to know the most suitable fallback font.
326 static const char* fallbacks
[] = {
335 // Try to get the glyph from the fallback fonts
336 while(fallbacks
[i
] != NULL
)
338 if (gFontManager
->Lock()) {
339 FontStyle
* fallbackStyle
= gFontManager
->GetStyleByIndex(
341 if (fallbackStyle
!= NULL
) {
342 ServerFont
fallbackFont(*fallbackStyle
, font
.Size());
343 gFontManager
->Unlock();
345 // Force the write-lock on the fallback entry, since we
346 // don't transfer or copy GlyphCache objects from one cache
347 // to the other, but create new glyphs which are stored in
348 // "entry" in any case, which requires the write cache for
349 // sure (used FontEngine of fallbackEntry).
350 fallbackEntry
= FontCacheEntryFor(fallbackFont
, forceVector
,
351 entry
, utf8String
, length
, fallbackCacheReference
, true);
353 if (fallbackEntry
!= NULL
)
356 gFontManager
->Unlock();
361 // NOTE: We don't care if fallbackEntry is still NULL, fetching
362 // alternate glyphs will simply not work.
364 if (!entry
->WriteLock()) {
365 FontCache::Default()->Recycle(entry
);
370 // Update the FontCacheReference, since the locking kind changed.
371 cacheReference
.SetTo(entry
, true);
377 #endif // GLYPH_LAYOUT_ENGINE_H