1 /*
2 * Copyright 2009, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27
28 #include "EmojiFactory.h"
29 #include "EmojiFont.h"
30 #include "SkCanvas.h"
31 #include "SkImageDecoder.h"
32 #include "SkPaint.h"
33 #include "SkTSearch.h"
34 #include "SkUtils.h"
35
36 #include "gmoji_pua_table.h"
37
38 namespace android {
39
40 // lazily allocate the factory
get_emoji_factory()41 static EmojiFactory* get_emoji_factory() {
42 static EmojiFactory* gEmojiFactory;
43 if (NULL == gEmojiFactory) {
44 gEmojiFactory = EmojiFactory::GetAvailableImplementation();
45 // we may still be NULL, if there is no impl.
46 }
47 return gEmojiFactory;
48 }
49
50 #define UNINITIALIZED_ENCODE_SIZE 0 // our array is initialzed with 0s
51 #define NOT_AVAILABLE_ENCODE_SIZE -1 // never a legal length for data
52
53 struct EncodeDataRec {
54 SkBitmap* fBitmap;
55 const void* fData;
56 int fSize;
57 };
58
59 static EncodeDataRec gGmojiEncodeData[GMOJI_PUA_COUNT] = {};
60
61 /* Given a local index, return (initialized if needed) a rec containing the
62 encoded data and length. The bitmap field is initialized to 0, and is not
63 filled in by this routine per-se.
64 */
get_encoderec(int index)65 static EncodeDataRec* get_encoderec(int index) {
66 if ((unsigned)index >= GMOJI_PUA_COUNT) {
67 SkDebugf("bad index passed to EncodeDataRec& get_encode_data %d\n",
68 index);
69 return NULL;
70 }
71
72 // lazily fill in the data
73 EncodeDataRec* rec = &gGmojiEncodeData[index];
74
75 if (NOT_AVAILABLE_ENCODE_SIZE == rec->fSize) {
76 return NULL;
77 }
78 if (UNINITIALIZED_ENCODE_SIZE == rec->fSize) {
79 EmojiFactory* fact = get_emoji_factory();
80 if (NULL == fact) {
81 return NULL;
82 }
83
84 int32_t pua = GMOJI_PUA_MIN + gGmojiPUA[index];
85 rec->fData = fact->GetImageBinaryFromAndroidPua(pua, &rec->fSize);
86 if (NULL == rec->fData) {
87 // flag this entry is not available, so we won't ask again
88 rec->fSize = NOT_AVAILABLE_ENCODE_SIZE;
89 return NULL;
90 }
91 }
92 return rec;
93 }
94
95 /* Return the bitmap associated with the local index, or NULL if none is
96 available. Note that this will try to cache the bitmap the first time it
97 creates it.
98 */
get_bitmap(int index)99 static const SkBitmap* get_bitmap(int index) {
100 EncodeDataRec* rec = get_encoderec(index);
101 SkBitmap* bitmap = NULL;
102 if (rec) {
103 bitmap = rec->fBitmap;
104 if (NULL == bitmap) {
105 bitmap = new SkBitmap;
106 if (!SkImageDecoder::DecodeMemory(rec->fData, rec->fSize, bitmap)) {
107 delete bitmap;
108 // we failed, so mark us to not try again
109 rec->fSize = NOT_AVAILABLE_ENCODE_SIZE;
110 return NULL;
111 }
112 // cache the answer
113 rec->fBitmap = bitmap;
114 // todo: we never know if/when to let go of this cached bitmap
115 // tho, since the pixels are managed separately, and are purged,
116 // the "leak" may not be too important
117 }
118 }
119 return bitmap;
120 }
121
122 ///////////////////////////////////////////////////////////////////////////////
123
IsAvailable()124 bool EmojiFont::IsAvailable() {
125 return get_emoji_factory() != NULL;
126 }
127
UnicharToGlyph(int32_t unichar)128 uint16_t EmojiFont::UnicharToGlyph(int32_t unichar) {
129 // do a quick range check before calling the search routine
130 if (unichar >= GMOJI_PUA_MIN && unichar <= GMOJI_PUA_MAX) {
131 // our table is stored relative to GMOJI_PUA_MIN to save space (16bits)
132 uint16_t relative = unichar - GMOJI_PUA_MIN;
133 int index = SkTSearch<uint16_t>(gGmojiPUA, GMOJI_PUA_COUNT, relative,
134 sizeof(uint16_t));
135 // a negative value means it was not found
136 if (index >= 0) {
137 return index + kGlyphBase;
138 }
139 // fall through to return 0
140 }
141 // not a supported emoji pua
142 return 0;
143 }
144
GetAdvanceWidth(uint16_t glyphID,const SkPaint & paint)145 SkScalar EmojiFont::GetAdvanceWidth(uint16_t glyphID, const SkPaint& paint) {
146 if (glyphID < kGlyphBase) {
147 SkDebugf("-------- bad glyph passed to EmojiFont::GetAdvanceWidth %d\n",
148 glyphID);
149 return 0;
150 }
151
152 const SkBitmap* bitmap = get_bitmap(glyphID - kGlyphBase);
153 if (NULL == bitmap) {
154 return 0;
155 }
156
157 // assume that our advance width is always the pointsize
158 return paint.getTextSize();
159 }
160
161 /* This tells us to shift the emoji bounds down by 20% below the baseline,
162 to better align with the Kanji characters' placement in the line.
163 */
164 static const SkScalar gBaselinePercentDrop = SkFloatToScalar(0.2f);
165
Draw(SkCanvas * canvas,uint16_t glyphID,SkScalar x,SkScalar y,const SkPaint & paint)166 void EmojiFont::Draw(SkCanvas* canvas, uint16_t glyphID,
167 SkScalar x, SkScalar y, const SkPaint& paint) {
168 if (glyphID < kGlyphBase) {
169 SkDebugf("-------- bad glyph passed to EmojiFont::Draw %d\n", glyphID);
170 }
171
172 const SkBitmap* bitmap = get_bitmap(glyphID - kGlyphBase);
173 if (bitmap && !bitmap->empty()) {
174 SkRect dst;
175 SkScalar size = paint.getTextSize();
176 y += SkScalarMul(size, gBaselinePercentDrop);
177 dst.set(x, y - size, x + size, y);
178 canvas->drawBitmapRect(*bitmap, NULL, dst, &paint);
179 }
180 }
181
182 }
183