11e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy/*
21e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * Copyright (C) 2010 The Android Open Source Project
31e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy *
41e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * Licensed under the Apache License, Version 2.0 (the "License");
51e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * you may not use this file except in compliance with the License.
61e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * You may obtain a copy of the License at
71e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy *
81e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy *      http://www.apache.org/licenses/LICENSE-2.0
91e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy *
101e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * Unless required by applicable law or agreed to in writing, software
111e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * distributed under the License is distributed on an "AS IS" BASIS,
121e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * See the License for the specific language governing permissions and
141e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy * limitations under the License.
151e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy */
161e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
17059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy#include <utils/JenkinsHash.h>
18059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
198aa195d7081b889f3a7b1f426cbd8556377aae5eRomain Guy#include "Caches.h"
20c9855a53edfac818dc68714557185977556f849dRomain Guy#include "Debug.h"
212dc236b2bae13b9a0ed9b3f7320502aecd7983b3Tom Hudson#include "FontRenderer.h"
221e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy#include "TextDropShadowCache.h"
23b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy#include "Properties.h"
241e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
251e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guynamespace android {
261e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guynamespace uirenderer {
271e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
281e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
29059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy// Cache support
30059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy///////////////////////////////////////////////////////////////////////////////
31059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
32059e12ccd20f5c249724a8362d6bac325334ea76Romain Guyhash_t ShadowText::hash() const {
33a1717271caac5e8ea3808c331d4141ac01a42134Chris Craik    uint32_t hash = JenkinsHashMix(0, glyphCount);
34059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    hash = JenkinsHashMix(hash, android::hash_type(radius));
35059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    hash = JenkinsHashMix(hash, android::hash_type(textSize));
36059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    hash = JenkinsHashMix(hash, android::hash_type(typeface));
37059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    hash = JenkinsHashMix(hash, flags);
38059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
39059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    hash = JenkinsHashMix(hash, android::hash_type(scaleX));
40e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris Craik    if (glyphs) {
416698749dd4d4d6513b26aa9071af290b956b68a7Dan Albert        hash = JenkinsHashMixShorts(
42e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris Craik            hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount);
4369fcbccf476ffc55af4da4dfedc72c3de8f91ce8Romain Guy    }
4469fcbccf476ffc55af4da4dfedc72c3de8f91ce8Romain Guy    if (positions) {
45a1717271caac5e8ea3808c331d4141ac01a42134Chris Craik        for (uint32_t i = 0; i < glyphCount * 2; i++) {
4669fcbccf476ffc55af4da4dfedc72c3de8f91ce8Romain Guy            hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
4769fcbccf476ffc55af4da4dfedc72c3de8f91ce8Romain Guy        }
48059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    }
49059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    return JenkinsHashWhiten(hash);
50059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy}
51059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
52059e12ccd20f5c249724a8362d6bac325334ea76Romain Guyint ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
53a1717271caac5e8ea3808c331d4141ac01a42134Chris Craik    int deltaInt = int(lhs.glyphCount) - int(rhs.glyphCount);
54059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (deltaInt != 0) return deltaInt;
55059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
56059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    deltaInt = lhs.flags - rhs.flags;
57059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (deltaInt != 0) return deltaInt;
58059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
59059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.radius < rhs.radius) return -1;
60059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.radius > rhs.radius) return +1;
61059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
62059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.typeface < rhs.typeface) return -1;
63059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.typeface > rhs.typeface) return +1;
64059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
65059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.textSize < rhs.textSize) return -1;
66059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.textSize > rhs.textSize) return +1;
67059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
68059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.italicStyle < rhs.italicStyle) return -1;
69059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.italicStyle > rhs.italicStyle) return +1;
70059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
71059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.scaleX < rhs.scaleX) return -1;
72059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.scaleX > rhs.scaleX) return +1;
73059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
74e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris Craik    if (lhs.glyphs != rhs.glyphs) {
75e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris Craik        if (!lhs.glyphs) return -1;
76e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris Craik        if (!rhs.glyphs) return +1;
77059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
78e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris Craik        deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t));
79059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy        if (deltaInt != 0) return deltaInt;
80059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    }
81059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
82059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    if (lhs.positions != rhs.positions) {
83059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy        if (!lhs.positions) return -1;
84059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy        if (!rhs.positions) return +1;
85059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
86086847797142a25e9e21611e9864c53abfca174fChris Craik        return memcmp(lhs.positions, rhs.positions, lhs.glyphCount * sizeof(float) * 2);
87059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    }
88059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
89059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy    return 0;
90059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy}
91059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy
92059e12ccd20f5c249724a8362d6bac325334ea76Romain Guy///////////////////////////////////////////////////////////////////////////////
931e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy// Constructors/destructor
941e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
951e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
9648a8f431fa52ae2ee25ffba9d20676f03bb710ffChris CraikTextDropShadowCache::TextDropShadowCache()
9748a8f431fa52ae2ee25ffba9d20676f03bb710ffChris Craik        : TextDropShadowCache(Properties::textDropShadowCacheSize) {}
98fb8b763f762ae21923c58d64caa729b012f40e05Romain Guy
9948a8f431fa52ae2ee25ffba9d20676f03bb710ffChris CraikTextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize)
10048a8f431fa52ae2ee25ffba9d20676f03bb710ffChris Craik        : mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity)
10148a8f431fa52ae2ee25ffba9d20676f03bb710ffChris Craik        , mSize(0)
10248a8f431fa52ae2ee25ffba9d20676f03bb710ffChris Craik        , mMaxSize(maxByteSize) {
10348a8f431fa52ae2ee25ffba9d20676f03bb710ffChris Craik    mCache.setOnEntryRemovedListener(this);
10448a8f431fa52ae2ee25ffba9d20676f03bb710ffChris Craik    mDebugEnabled = Properties::debugLevel & kDebugMoreCaches;
1051e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
1061e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1071e45aae5de003657e5d18f74d34998f5de5db5b7Romain GuyTextDropShadowCache::~TextDropShadowCache() {
1081e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    mCache.clear();
1091e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
1101e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1111e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
1121e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy// Size management
1131e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
1141e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1151e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guyuint32_t TextDropShadowCache::getSize() {
1161e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    return mSize;
1171e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
1181e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1191e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guyuint32_t TextDropShadowCache::getMaxSize() {
1201e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    return mMaxSize;
1211e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
1221e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1231e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
1241e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy// Callbacks
1251e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
1261e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
127e63f7c622a2086aefa80983c6f41b74fb166bb42Chris Craikvoid TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) {
1281e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (texture) {
129c3127a78b996a540cd002e5a87861e8a2adeb336John Reck        mSize -= texture->objectSize();
13025dc3a7dbac2f90f5144035e9c8ed99c09cc3132Romain Guy
13125dc3a7dbac2f90f5144035e9c8ed99c09cc3132Romain Guy        if (mDebugEnabled) {
1325baa3a62a97544669fba6d65a11c07f252e654ddSteve Block            ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
13325dc3a7dbac2f90f5144035e9c8ed99c09cc3132Romain Guy        }
134f70a7e34953438dd70573dbb6fd3dd34a1bce831Romain Guy
135be1b127c7bec252e0c6ab0e06ed6babed07d496fRomain Guy        texture->deleteTexture();
1361e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        delete texture;
1371e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
1381e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
1391e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1401e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
1411e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy// Caching
1421e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy///////////////////////////////////////////////////////////////////////////////
1431e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1441e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guyvoid TextDropShadowCache::clear() {
1451e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    mCache.clear();
1461e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
1471e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
148e8c3c813b0e3ac98304b17a751ce6e436e252bd9Chris CraikShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
149a1717271caac5e8ea3808c331d4141ac01a42134Chris Craik        float radius, const float* positions) {
150086847797142a25e9e21611e9864c53abfca174fChris Craik    ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
1511e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    ShadowTexture* texture = mCache.get(entry);
1521e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1531e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!texture) {
1548b4072d3fb9bb49d774d97689a065204beca1752Raph Levien        SkPaint paintCopy(*paint);
1558b4072d3fb9bb49d774d97689a065204beca1752Raph Levien        paintCopy.setTextAlign(SkPaint::kLeft_Align);
156a1717271caac5e8ea3808c331d4141ac01a42134Chris Craik        FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs,
157a1717271caac5e8ea3808c331d4141ac01a42134Chris Craik                radius, positions);
1581e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
159cf51a4199835e9604aa4c8b3854306f8fbabbf33Romain Guy        if (!shadow.image) {
160d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik            return nullptr;
161cf51a4199835e9604aa4c8b3854306f8fbabbf33Romain Guy        }
162cf51a4199835e9604aa4c8b3854306f8fbabbf33Romain Guy
1638aa195d7081b889f3a7b1f426cbd8556377aae5eRomain Guy        Caches& caches = Caches::getInstance();
1648aa195d7081b889f3a7b1f426cbd8556377aae5eRomain Guy
1658aa195d7081b889f3a7b1f426cbd8556377aae5eRomain Guy        texture = new ShadowTexture(caches);
1661e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        texture->left = shadow.penX;
1671e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        texture->top = shadow.penY;
1681e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        texture->generation = 0;
1691e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        texture->blend = true;
1701e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1711e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint32_t size = shadow.width * shadow.height;
17225dc3a7dbac2f90f5144035e9c8ed99c09cc3132Romain Guy
1731e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        // Don't even try to cache a bitmap that's bigger than the cache
1741e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        if (size < mMaxSize) {
1751e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            while (mSize + size > mMaxSize) {
176c3127a78b996a540cd002e5a87861e8a2adeb336John Reck                LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
177c3127a78b996a540cd002e5a87861e8a2adeb336John Reck                        "Failed to remove oldest from cache. mSize = %"
178c3127a78b996a540cd002e5a87861e8a2adeb336John Reck                        PRIu32 ", mCache.size() = %zu", mSize, mCache.size());
1791e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            }
1801e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        }
1811e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1821e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        // Textures are Alpha8
18338e0c32852e3b9d8ca4a9d3791577f52536419cbJohn Reck        texture->upload(GL_ALPHA, shadow.width, shadow.height,
1841e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
18539d252a6632d057d5077f7eaf1b8ed7a142f3397Romain Guy        texture->setFilter(GL_LINEAR);
18639d252a6632d057d5077f7eaf1b8ed7a142f3397Romain Guy        texture->setWrap(GL_CLAMP_TO_EDGE);
1871e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1881e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        if (size < mMaxSize) {
18925dc3a7dbac2f90f5144035e9c8ed99c09cc3132Romain Guy            if (mDebugEnabled) {
1905baa3a62a97544669fba6d65a11c07f252e654ddSteve Block                ALOGD("Shadow texture created, size = %d", texture->bitmapSize);
19125dc3a7dbac2f90f5144035e9c8ed99c09cc3132Romain Guy            }
192321dce646dc3c2ecfbd72a693d8d9294a6119736Romain Guy
193321dce646dc3c2ecfbd72a693d8d9294a6119736Romain Guy            entry.copyTextLocally();
194321dce646dc3c2ecfbd72a693d8d9294a6119736Romain Guy
195c3127a78b996a540cd002e5a87861e8a2adeb336John Reck            mSize += texture->objectSize();
1961e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            mCache.put(entry, texture);
1971e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        } else {
1981e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            texture->cleanup = true;
1991e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        }
2001e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
2011e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        // Cleanup shadow
20215641a6181cdaac2aadf07585de66483b4b7ae6bBen Cheng        free(shadow.image);
2031e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
2041e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
2051e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    return texture;
2061e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}
2071e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
2081e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}; // namespace uirenderer
2091e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy}; // namespace android
210