• 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 ``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