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 "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