1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_TEXT_LAYOUT_CACHE_H
18#define ANDROID_TEXT_LAYOUT_CACHE_H
19
20#include "RtlProperties.h"
21
22#include <stddef.h>
23#include <utils/threads.h>
24#include <utils/String16.h>
25#include <utils/GenerationCache.h>
26#include <utils/KeyedVector.h>
27#include <utils/Compare.h>
28#include <utils/RefBase.h>
29#include <utils/Singleton.h>
30
31#include <SkPaint.h>
32#include <SkTemplates.h>
33#include <SkUtils.h>
34#include <SkAutoKern.h>
35#include <SkLanguage.h>
36
37#include <unicode/ubidi.h>
38#include <unicode/ushape.h>
39#include <unicode/unistr.h>
40
41#include "HarfbuzzSkia.h"
42#include "harfbuzz-shaper.h"
43
44#include <android_runtime/AndroidRuntime.h>
45
46#define UNICODE_NOT_A_CHAR              0xffff
47#define UNICODE_ZWSP                    0x200b
48#define UNICODE_FIRST_LOW_SURROGATE     0xdc00
49#define UNICODE_FIRST_HIGH_SURROGATE    0xd800
50#define UNICODE_FIRST_PRIVATE_USE       0xe000
51#define UNICODE_FIRST_RTL_CHAR          0x0590
52
53// Temporary buffer size
54#define CHAR_BUFFER_SIZE 80
55
56// Converts a number of mega-bytes into bytes
57#define MB(s) s * 1024 * 1024
58
59// Define the default cache size in Mb
60#define DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB 0.500f
61
62// Define the interval in number of cache hits between two statistics dump
63#define DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL 100
64
65namespace android {
66
67/**
68 * TextLayoutCacheKey is the Cache key
69 */
70class TextLayoutCacheKey {
71public:
72    TextLayoutCacheKey();
73
74    TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count,
75            size_t contextCount, int dirFlags);
76
77    TextLayoutCacheKey(const TextLayoutCacheKey& other);
78
79    /**
80     * Get the size of the Cache key.
81     */
82    size_t getSize() const;
83
84    static int compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs);
85
86    inline const UChar* getText() const { return textCopy.string(); }
87
88private:
89    String16 textCopy;
90    size_t start;
91    size_t count;
92    size_t contextCount;
93    int dirFlags;
94    SkTypeface* typeface;
95    SkScalar textSize;
96    SkScalar textSkewX;
97    SkScalar textScaleX;
98    uint32_t flags;
99    SkPaint::Hinting hinting;
100    SkPaint::FontVariant variant;
101    SkLanguage language;
102
103}; // TextLayoutCacheKey
104
105inline int strictly_order_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
106    return TextLayoutCacheKey::compare(lhs, rhs) < 0;
107}
108
109inline int compare_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
110    return TextLayoutCacheKey::compare(lhs, rhs);
111}
112
113/*
114 * TextLayoutValue is the Cache value
115 */
116class TextLayoutValue : public RefBase {
117public:
118    TextLayoutValue(size_t contextCount);
119
120    void setElapsedTime(uint32_t time);
121    uint32_t getElapsedTime();
122
123    inline const jfloat* getAdvances() const { return mAdvances.array(); }
124    inline size_t getAdvancesCount() const { return mAdvances.size(); }
125    inline jfloat getTotalAdvance() const { return mTotalAdvance; }
126    inline const jchar* getGlyphs() const { return mGlyphs.array(); }
127    inline size_t getGlyphsCount() const { return mGlyphs.size(); }
128    inline const jfloat* getPos() const { return mPos.array(); }
129    inline size_t getPosCount() const { return mPos.size(); }
130
131    /**
132     * Advances vector
133     */
134    Vector<jfloat> mAdvances;
135
136    /**
137     * Total number of advances
138     */
139    jfloat mTotalAdvance;
140
141    /**
142     * Glyphs vector
143     */
144    Vector<jchar> mGlyphs;
145
146    /**
147     * Pos vector (2 * i is x pos, 2 * i + 1 is y pos, same as drawPosText)
148     */
149    Vector<jfloat> mPos;
150
151    /**
152     * Get the size of the Cache entry
153     */
154    size_t getSize() const;
155
156private:
157    /**
158     * Time for computing the values (in milliseconds)
159     */
160    uint32_t mElapsedTime;
161
162}; // TextLayoutCacheValue
163
164/**
165 * The TextLayoutShaper is responsible for shaping (with the Harfbuzz library)
166 */
167class TextLayoutShaper {
168public:
169    TextLayoutShaper();
170    virtual ~TextLayoutShaper();
171
172    void computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
173            size_t start, size_t count, size_t contextCount, int dirFlags);
174
175    void purgeCaches();
176
177private:
178    /**
179     * Harfbuzz shaper item
180     */
181    HB_ShaperItem mShaperItem;
182
183    /**
184     * Harfbuzz font
185     */
186    HB_FontRec mFontRec;
187
188    /**
189     * Skia Paint used for shaping
190     */
191    SkPaint mShapingPaint;
192
193    /**
194     * Skia default typeface to be returned if we cannot resolve script
195     */
196    SkTypeface* mDefaultTypeface;
197
198    /**
199     * Cache of Harfbuzz faces
200     */
201    KeyedVector<SkFontID, HB_Face> mCachedHBFaces;
202
203    /**
204     * Cache of glyph array size
205     */
206    size_t mShaperItemGlyphArraySize;
207
208    /**
209     * Buffer for containing the ICU normalized form of a run
210     */
211    UnicodeString mNormalizedString;
212
213    /**
214     * Buffer for normalizing a piece of a run with ICU
215     */
216    UnicodeString mBuffer;
217
218    void init();
219    void unrefTypefaces();
220
221    SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
222        HB_Script script);
223
224    size_t shapeFontRun(const SkPaint* paint, bool isRTL);
225
226    void computeValues(const SkPaint* paint, const UChar* chars,
227            size_t start, size_t count, size_t contextCount, int dirFlags,
228            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
229            Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
230
231    void computeRunValues(const SkPaint* paint, const UChar* chars,
232            size_t count, bool isRTL,
233            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
234            Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
235
236    SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style);
237    HB_Face getCachedHBFace(SkTypeface* typeface);
238
239    bool doShaping(size_t size);
240    void createShaperItemGlyphArrays(size_t size);
241    void deleteShaperItemGlyphArrays();
242    bool isComplexScript(HB_Script script);
243
244}; // TextLayoutShaper
245
246/**
247 * Cache of text layout information.
248 */
249class TextLayoutCache : private OnEntryRemoved<TextLayoutCacheKey, sp<TextLayoutValue> >
250{
251public:
252    TextLayoutCache(TextLayoutShaper* shaper);
253
254    ~TextLayoutCache();
255
256    bool isInitialized() {
257        return mInitialized;
258    }
259
260    /**
261     * Used as a callback when an entry is removed from the cache
262     * Do not invoke directly
263     */
264    void operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc);
265
266    sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
267            jint count, jint contextCount, jint dirFlags);
268
269    /**
270     * Clear the cache
271     */
272    void purgeCaches();
273
274private:
275    TextLayoutShaper* mShaper;
276    Mutex mLock;
277    bool mInitialized;
278
279    GenerationCache<TextLayoutCacheKey, sp<TextLayoutValue> > mCache;
280
281    uint32_t mSize;
282    uint32_t mMaxSize;
283
284    uint32_t mCacheHitCount;
285    uint64_t mNanosecondsSaved;
286
287    uint64_t mCacheStartTime;
288
289    RtlDebugLevel mDebugLevel;
290    bool mDebugEnabled;
291
292    /*
293     * Class initialization
294     */
295    void init();
296
297    /**
298     * Dump Cache statistics
299     */
300    void dumpCacheStats();
301
302}; // TextLayoutCache
303
304/**
305 * The TextLayoutEngine is reponsible for computing TextLayoutValues
306 */
307class TextLayoutEngine : public Singleton<TextLayoutEngine> {
308public:
309    TextLayoutEngine();
310    virtual ~TextLayoutEngine();
311
312    /**
313     * Note: this method currently does a defensive copy of the text argument, in case
314     * there is concurrent mutation of it. The contract may change, and may in the
315     * future require the caller to guarantee that the contents will not change during
316     * the call. Be careful of this when doing optimization.
317     **/
318    sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
319            jint count, jint contextCount, jint dirFlags);
320
321    void purgeCaches();
322
323private:
324    TextLayoutCache* mTextLayoutCache;
325    TextLayoutShaper* mShaper;
326}; // TextLayoutEngine
327
328} // namespace android
329#endif /* ANDROID_TEXT_LAYOUT_CACHE_H */
330
331