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