| /* |
| * Copyright 2009, The Android Open Source Project |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "EmojiFactory.h" |
| #include "EmojiFont.h" |
| #include "SkBitmap.h" |
| #include "SkCanvas.h" |
| #include "SkImageDecoder.h" |
| #include "SkPaint.h" |
| #include "SkTSearch.h" |
| #include "SkUtils.h" |
| |
| #include "gmoji_pua_table.h" |
| |
| #include <string.h> |
| |
| namespace android { |
| |
| // lazily allocate the factory |
| static EmojiFactory* get_emoji_factory() { |
| static EmojiFactory* gEmojiFactory; |
| if (NULL == gEmojiFactory) { |
| gEmojiFactory = EmojiFactory::GetAvailableImplementation(); |
| // we may still be NULL, if there is no impl. |
| } |
| return gEmojiFactory; |
| } |
| |
| #define UNINITIALIZED_ENCODE_SIZE 0 // our array is initialzed with 0s |
| #define NOT_AVAILABLE_ENCODE_SIZE -1 // never a legal length for data |
| |
| struct EncodeDataRec { |
| SkBitmap* fBitmap; |
| const void* fData; |
| int fSize; |
| }; |
| |
| static EncodeDataRec gGmojiEncodeData[GMOJI_PUA_COUNT] = {}; |
| |
| /* Given a local index, return (initialized if needed) a rec containing the |
| encoded data and length. The bitmap field is initialized to 0, and is not |
| filled in by this routine per-se. |
| */ |
| static EncodeDataRec* get_encoderec(int index) { |
| if ((unsigned)index >= GMOJI_PUA_COUNT) { |
| SkDebugf("bad index passed to EncodeDataRec& get_encode_data %d\n", |
| index); |
| return NULL; |
| } |
| |
| // lazily fill in the data |
| EncodeDataRec* rec = &gGmojiEncodeData[index]; |
| |
| if (NOT_AVAILABLE_ENCODE_SIZE == rec->fSize) { |
| return NULL; |
| } |
| if (UNINITIALIZED_ENCODE_SIZE == rec->fSize) { |
| EmojiFactory* fact = get_emoji_factory(); |
| if (NULL == fact) { |
| return NULL; |
| } |
| |
| int32_t pua = GMOJI_PUA_MIN + gGmojiPUA[index]; |
| rec->fData = fact->GetImageBinaryFromAndroidPua(pua, &rec->fSize); |
| if (NULL == rec->fData) { |
| // flag this entry is not available, so we won't ask again |
| rec->fSize = NOT_AVAILABLE_ENCODE_SIZE; |
| return NULL; |
| } |
| } |
| return rec; |
| } |
| |
| /* Return the bitmap associated with the local index, or NULL if none is |
| available. Note that this will try to cache the bitmap the first time it |
| creates it. |
| */ |
| static const SkBitmap* get_bitmap(int index) { |
| EncodeDataRec* rec = get_encoderec(index); |
| SkBitmap* bitmap = NULL; |
| if (rec) { |
| bitmap = rec->fBitmap; |
| if (NULL == bitmap) { |
| bitmap = new SkBitmap; |
| if (!SkImageDecoder::DecodeMemory(rec->fData, rec->fSize, bitmap)) { |
| delete bitmap; |
| // we failed, so mark us to not try again |
| rec->fSize = NOT_AVAILABLE_ENCODE_SIZE; |
| return NULL; |
| } |
| // cache the answer |
| rec->fBitmap = bitmap; |
| // todo: we never know if/when to let go of this cached bitmap |
| // tho, since the pixels are managed separately, and are purged, |
| // the "leak" may not be too important |
| } |
| } |
| return bitmap; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| bool EmojiFont::IsAvailable() { |
| return get_emoji_factory() != NULL; |
| } |
| |
| const char *EmojiFont::GetShiftJisConverterName() { |
| EmojiFactory* fact = get_emoji_factory(); |
| if (NULL != fact) { |
| if (strcmp(fact->Name(), "kddi") == 0) { |
| return "kddi-emoji"; |
| } else if (strcmp(fact->Name(), "softbank") == 0) { |
| return "softbank-emoji"; |
| } |
| } |
| |
| // Until Eclair, we have used DoCoMo's Shift_JIS table. |
| return "docomo-emoji"; |
| } |
| |
| uint16_t EmojiFont::UnicharToGlyph(int32_t unichar) { |
| // do a quick range check before calling the search routine |
| if (unichar >= GMOJI_PUA_MIN && unichar <= GMOJI_PUA_MAX) { |
| // our table is stored relative to GMOJI_PUA_MIN to save space (16bits) |
| uint16_t relative = unichar - GMOJI_PUA_MIN; |
| int index = SkTSearch<uint16_t>(gGmojiPUA, GMOJI_PUA_COUNT, relative, |
| sizeof(uint16_t)); |
| // a negative value means it was not found |
| if (index >= 0) { |
| return index + kGlyphBase; |
| } |
| // fall through to return 0 |
| } |
| // not a supported emoji pua |
| return 0; |
| } |
| |
| SkScalar EmojiFont::GetAdvanceWidth(uint16_t glyphID, const SkPaint& paint) { |
| if (glyphID < kGlyphBase) { |
| SkDebugf("-------- bad glyph passed to EmojiFont::GetAdvanceWidth %d\n", |
| glyphID); |
| return 0; |
| } |
| |
| const SkBitmap* bitmap = get_bitmap(glyphID - kGlyphBase); |
| if (NULL == bitmap) { |
| return 0; |
| } |
| |
| // assume that our advance width is always the pointsize |
| return paint.getTextSize(); |
| } |
| |
| /* This tells us to shift the emoji bounds down by 20% below the baseline, |
| to better align with the Kanji characters' placement in the line. |
| */ |
| static const SkScalar gBaselinePercentDrop = SkFloatToScalar(0.2f); |
| |
| void EmojiFont::Draw(SkCanvas* canvas, uint16_t glyphID, |
| SkScalar x, SkScalar y, const SkPaint& paint) { |
| if (glyphID < kGlyphBase) { |
| SkDebugf("-------- bad glyph passed to EmojiFont::Draw %d\n", glyphID); |
| } |
| |
| const SkBitmap* bitmap = get_bitmap(glyphID - kGlyphBase); |
| if (bitmap && !bitmap->empty()) { |
| SkRect dst; |
| SkScalar size = paint.getTextSize(); |
| y += SkScalarMul(size, gBaselinePercentDrop); |
| dst.set(x, y - size, x + size, y); |
| canvas->drawBitmapRect(*bitmap, NULL, dst, &paint); |
| } |
| } |
| |
| } |