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