/* * 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 ``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 APPLE COMPUTER, INC. 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 "SkCanvas.h" #include "SkImageDecoder.h" #include "SkPaint.h" #include "SkTSearch.h" #include "SkUtils.h" #include "gmoji_pua_table.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; } 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); } } }