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 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 */ 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 */ 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 127 bool EmojiFont::IsAvailable() { 128 return get_emoji_factory() != NULL; 129 } 130 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 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 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 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