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
41namespace android {
42
43// lazily allocate the factory
44static 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
56struct EncodeDataRec {
57    SkBitmap*   fBitmap;
58    const void* fData;
59    int         fSize;
60};
61
62static 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 */
68static 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 */
102static 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
127bool EmojiFont::IsAvailable() {
128    return get_emoji_factory() != NULL;
129}
130
131const 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
145uint16_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
162SkScalar 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 */
181static const SkScalar gBaselinePercentDrop = SkFloatToScalar(0.2f);
182
183void 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