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/LruCache.h>
26#include <utils/KeyedVector.h>
27#include <utils/RefBase.h>
28#include <utils/Singleton.h>
29
30#include <SkAutoKern.h>
31#include <SkLanguage.h>
32#include <SkPaint.h>
33#include <SkTemplates.h>
34#include <SkTypeface.h>
35#include <SkUtils.h>
36
37#include <unicode/ubidi.h>
38#include <unicode/unistr.h>
39
40#include <hb.h>
41
42#include <android_runtime/AndroidRuntime.h>
43
44#define UNICODE_NOT_A_CHAR              0xffff
45#define UNICODE_ZWSP                    0x200b
46#define UNICODE_FIRST_LOW_SURROGATE     0xdc00
47#define UNICODE_FIRST_HIGH_SURROGATE    0xd800
48#define UNICODE_FIRST_PRIVATE_USE       0xe000
49#define UNICODE_FIRST_RTL_CHAR          0x0590
50
51// Temporary buffer size
52#define CHAR_BUFFER_SIZE 80
53
54// Converts a number of mega-bytes into bytes
55#define MB(s) s * 1024 * 1024
56
57// Define the default cache size in Mb
58#define DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB 0.500f
59
60// Define the interval in number of cache hits between two statistics dump
61#define DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL 100
62
63namespace android {
64
65/**
66 * TextLayoutCacheKey is the Cache key
67 */
68class TextLayoutCacheKey {
69public:
70    TextLayoutCacheKey();
71
72    TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count,
73            size_t contextCount, int dirFlags);
74
75    TextLayoutCacheKey(const TextLayoutCacheKey& other);
76
77    /**
78     * Get the size of the Cache key.
79     */
80    size_t getSize() const;
81
82    static int compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs);
83
84    inline const UChar* getText() const { return textCopy.string(); }
85
86    bool operator==(const TextLayoutCacheKey& other) const {
87        return compare(*this, other) == 0;
88    }
89
90    bool operator!=(const TextLayoutCacheKey& other) const {
91        return compare(*this, other) != 0;
92    }
93
94    hash_t hash() const;
95private:
96    String16 textCopy;
97    size_t start;
98    size_t count;
99    size_t contextCount;
100    int dirFlags;
101    SkTypeface* typeface;
102    SkScalar textSize;
103    SkScalar textSkewX;
104    SkScalar textScaleX;
105    uint32_t flags;
106    SkPaint::Hinting hinting;
107    SkPaint::FontVariant variant;
108    SkLanguage language;
109
110}; // TextLayoutCacheKey
111
112inline int strictly_order_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
113    return TextLayoutCacheKey::compare(lhs, rhs) < 0;
114}
115
116inline int compare_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
117    return TextLayoutCacheKey::compare(lhs, rhs);
118}
119
120inline hash_t hash_type(const TextLayoutCacheKey& key) {
121    return key.hash();
122}
123
124/*
125 * TextLayoutValue is the Cache value
126 */
127class TextLayoutValue : public RefBase {
128public:
129    TextLayoutValue(size_t contextCount);
130
131    void setElapsedTime(uint32_t time);
132    uint32_t getElapsedTime();
133
134    inline const jfloat* getAdvances() const { return mAdvances.array(); }
135    inline size_t getAdvancesCount() const { return mAdvances.size(); }
136    inline jfloat getTotalAdvance() const { return mTotalAdvance; }
137    inline const jchar* getGlyphs() const { return mGlyphs.array(); }
138    inline size_t getGlyphsCount() const { return mGlyphs.size(); }
139    inline const jfloat* getPos() const { return mPos.array(); }
140    inline size_t getPosCount() const { return mPos.size(); }
141
142    /**
143     * Advances vector
144     */
145    Vector<jfloat> mAdvances;
146
147    /**
148     * Total number of advances
149     */
150    jfloat mTotalAdvance;
151
152    /**
153     * Glyphs vector
154     */
155    Vector<jchar> mGlyphs;
156
157    /**
158     * Pos vector (2 * i is x pos, 2 * i + 1 is y pos, same as drawPosText)
159     */
160    Vector<jfloat> mPos;
161
162    /**
163     * Get the size of the Cache entry
164     */
165    size_t getSize() const;
166
167private:
168    /**
169     * Time for computing the values (in milliseconds)
170     */
171    uint32_t mElapsedTime;
172
173}; // TextLayoutCacheValue
174
175/**
176 * The TextLayoutShaper is responsible for shaping (with the Harfbuzz library)
177 */
178class TextLayoutShaper {
179public:
180    TextLayoutShaper();
181    virtual ~TextLayoutShaper();
182
183    void computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
184            size_t start, size_t count, size_t contextCount, int dirFlags);
185
186    void purgeCaches();
187
188private:
189    /**
190     * Harfbuzz buffer for shaping
191     */
192    hb_buffer_t* mBuffer;
193
194    /**
195     * Skia Paint used for shaping
196     */
197    SkPaint mShapingPaint;
198
199    /**
200     * Cache of Harfbuzz faces
201     */
202    KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces;
203
204    SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
205        hb_script_t script);
206
207    size_t shapeFontRun(const SkPaint* paint);
208
209    void computeValues(const SkPaint* paint, const UChar* chars,
210            size_t start, size_t count, size_t contextCount, int dirFlags,
211            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
212            Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
213
214    void computeRunValues(const SkPaint* paint, const UChar* chars,
215            size_t start, size_t count, size_t contextCount, bool isRTL,
216            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
217            Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
218
219    SkTypeface* setCachedTypeface(SkTypeface** typeface, hb_script_t script, SkTypeface::Style style);
220    hb_face_t* referenceCachedHBFace(SkTypeface* typeface);
221
222    bool isComplexScript(hb_script_t script);
223}; // TextLayoutShaper
224
225/**
226 * Cache of text layout information.
227 */
228class TextLayoutCache : private OnEntryRemoved<TextLayoutCacheKey, sp<TextLayoutValue> >
229{
230public:
231    TextLayoutCache(TextLayoutShaper* shaper);
232
233    ~TextLayoutCache();
234
235    bool isInitialized() {
236        return mInitialized;
237    }
238
239    /**
240     * Used as a callback when an entry is removed from the cache
241     * Do not invoke directly
242     */
243    void operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc);
244
245    sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
246            jint count, jint contextCount, jint dirFlags);
247
248    /**
249     * Clear the cache
250     */
251    void purgeCaches();
252
253private:
254    TextLayoutShaper* mShaper;
255    Mutex mLock;
256    bool mInitialized;
257
258    LruCache<TextLayoutCacheKey, sp<TextLayoutValue> > mCache;
259
260    uint32_t mSize;
261    uint32_t mMaxSize;
262
263    uint32_t mCacheHitCount;
264    uint64_t mNanosecondsSaved;
265
266    uint64_t mCacheStartTime;
267
268    RtlDebugLevel mDebugLevel;
269    bool mDebugEnabled;
270
271    /*
272     * Class initialization
273     */
274    void init();
275
276    /**
277     * Dump Cache statistics
278     */
279    void dumpCacheStats();
280
281}; // TextLayoutCache
282
283/**
284 * The TextLayoutEngine is reponsible for computing TextLayoutValues
285 */
286class TextLayoutEngine : public Singleton<TextLayoutEngine> {
287public:
288    TextLayoutEngine();
289    virtual ~TextLayoutEngine();
290
291    /**
292     * Note: this method currently does a defensive copy of the text argument, in case
293     * there is concurrent mutation of it. The contract may change, and may in the
294     * future require the caller to guarantee that the contents will not change during
295     * the call. Be careful of this when doing optimization.
296     **/
297    sp<TextLayoutValue> getValue(const SkPaint* paint, const jchar* text, jint start,
298            jint count, jint contextCount, jint dirFlags);
299
300    void purgeCaches();
301
302private:
303    TextLayoutCache* mTextLayoutCache;
304    TextLayoutShaper* mShaper;
305}; // TextLayoutEngine
306
307} // namespace android
308#endif /* ANDROID_TEXT_LAYOUT_CACHE_H */
309
310