• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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