Home | History | Annotate | Download | only in emoji
      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