vfs: check userland buffers before reading them.
[haiku.git] / src / servers / app / font / GlyphLayoutEngine.h
blobfd81dd03d66060f0e6edf7f1c1090e468a307fc3
1 /*
2 * Copyright 2007-2014, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
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"
19 #include <Debug.h>
21 #include <ctype.h>
23 class FontCacheReference {
24 public:
25 FontCacheReference()
27 fCacheEntry(NULL),
28 fWriteLocked(false)
32 ~FontCacheReference()
34 Unset();
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
44 // references!
45 fCacheEntry = entry;
46 fWriteLocked = writeLocked;
49 void Unset()
51 if (fCacheEntry == NULL)
52 return;
54 if (fWriteLocked)
55 fCacheEntry->WriteUnlock();
56 else
57 fCacheEntry->ReadUnlock();
59 FontCache::Default()->Recycle(fCacheEntry);
62 inline FontCacheEntry* Entry() const
64 return fCacheEntry;
67 inline bool WriteLocked() const
69 return fWriteLocked;
72 private:
73 FontCacheEntry* fCacheEntry;
74 bool fWriteLocked;
78 class GlyphLayoutEngine {
79 public:
80 static bool IsWhiteSpace(uint32 glyphCode);
82 static FontCacheEntry* FontCacheEntryFor(const ServerFont& font,
83 bool forceVector,
84 const FontCacheEntry* disallowedEntry,
85 const char* utf8String, int32 length,
86 FontCacheReference& cacheReference,
87 bool needsWriteLock);
89 template<class GlyphConsumer>
90 static bool LayoutGlyphs(GlyphConsumer& consumer,
91 const ServerFont& font,
92 const char* utf8String,
93 int32 length,
94 const escapement_delta* delta = NULL,
95 uint8 spacing = B_BITMAP_SPACING,
96 const BPoint* offsets = NULL,
97 FontCacheReference* cacheReference = NULL);
99 private:
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);
108 GlyphLayoutEngine();
109 virtual ~GlyphLayoutEngine();
113 inline bool
114 GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
116 switch (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 */
126 return true;
129 return false;
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);
142 if (entry == NULL)
143 return NULL;
145 if (entry == disallowedEntry) {
146 cache->Recycle(entry);
147 return NULL;
150 if (needsWriteLock) {
151 if (!entry->WriteLock()) {
152 cache->Recycle(entry);
153 return NULL;
155 } else {
156 if (!entry->ReadLock()) {
157 cache->Recycle(entry);
158 return NULL;
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);
166 return entry;
170 template<class GlyphConsumer>
171 inline bool
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.
194 if (entry == NULL) {
195 entry = FontCacheEntryFor(font, consumer.NeedsVector(), NULL,
196 utf8String, length, cacheReference, false);
198 if (entry == NULL)
199 return false;
200 } // else the entry was already used and is still locked
202 consumer.Start();
204 double x = 0.0;
205 double y = 0.0;
206 if (offsets) {
207 x = offsets[0].x;
208 y = offsets[0].y;
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
216 uint32 charCode;
217 int32 index = 0;
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;
227 } else {
228 if (spacing == B_STRING_SPACING)
229 entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY);
231 x += advanceX;
232 y += advanceY;
235 const GlyphCache* glyph = entry->CachedGlyph(charCode);
236 if (glyph == NULL) {
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.
241 if (!writeLocked) {
242 writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference,
243 entry, font, consumer.NeedsVector(), utf8String, length,
244 fallbackCacheReference, fallbackEntry);
247 if (writeLocked)
248 glyph = entry->CreateGlyph(charCode, fallbackEntry);
251 if (glyph == NULL) {
252 consumer.ConsumeEmptyGlyph(index++, charCode, x, y);
253 advanceX = 0;
254 advanceY = 0;
255 } else {
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;
260 } else {
261 advanceX = glyph->advance_x;
262 advanceY = glyph->advance_y;
265 // adjust for custom spacing
266 if (delta != NULL) {
267 advanceX += IsWhiteSpace(charCode)
268 ? delta->space : delta->nonspace;
271 if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y,
272 advanceX, advanceY)) {
273 advanceX = 0.0;
274 advanceY = 0.0;
275 break;
279 lastCharCode = charCode;
280 if (utf8String - start + 1 > length)
281 break;
284 x += advanceX;
285 y += advanceY;
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);
297 return true;
301 inline bool
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();
315 if (writeLocked) {
316 entry->WriteUnlock();
317 } else {
318 cacheReference.SetTo(NULL, false);
319 entry->ReadUnlock();
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[] = {
327 "Noto Sans",
328 "Noto Sans CJK JP",
329 "Noto Sans Symbols",
330 NULL
333 int i = 0;
335 // Try to get the glyph from the fallback fonts
336 while(fallbacks[i] != NULL)
338 if (gFontManager->Lock()) {
339 FontStyle* fallbackStyle = gFontManager->GetStyleByIndex(
340 fallbacks[i], 0);
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)
354 break;
355 } else
356 gFontManager->Unlock();
359 i++;
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);
366 return false;
369 if (!writeLocked) {
370 // Update the FontCacheReference, since the locking kind changed.
371 cacheReference.SetTo(entry, true);
373 return true;
377 #endif // GLYPH_LAYOUT_ENGINE_H