blob: 5fb2636dbdff7f8beb2fcc265a9006226a90abcc [file] [log] [blame]
Romain Guy9f5dab32012-09-04 12:55:44 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <utils/Log.h>
18
19#include "Debug.h"
20#include "CacheTexture.h"
21
22namespace android {
23namespace uirenderer {
24
25///////////////////////////////////////////////////////////////////////////////
26// CacheBlock
27///////////////////////////////////////////////////////////////////////////////
28
29/**
30 * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
31 * order, except for the final block (the remainder space at the right, since we fill from the
32 * left).
33 */
34CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
35#if DEBUG_FONT_RENDERER
36 ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
37 newBlock, newBlock->mX, newBlock->mY,
38 newBlock->mWidth, newBlock->mHeight);
39#endif
40 CacheBlock *currBlock = head;
41 CacheBlock *prevBlock = NULL;
42 while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
43 if (newBlock->mWidth < currBlock->mWidth) {
44 newBlock->mNext = currBlock;
45 newBlock->mPrev = prevBlock;
46 currBlock->mPrev = newBlock;
47 if (prevBlock) {
48 prevBlock->mNext = newBlock;
49 return head;
50 } else {
51 return newBlock;
52 }
53 }
54 prevBlock = currBlock;
55 currBlock = currBlock->mNext;
56 }
57 // new block larger than all others - insert at end (but before the remainder space, if there)
58 newBlock->mNext = currBlock;
59 newBlock->mPrev = prevBlock;
60 if (currBlock) {
61 currBlock->mPrev = newBlock;
62 }
63 if (prevBlock) {
64 prevBlock->mNext = newBlock;
65 return head;
66 } else {
67 return newBlock;
68 }
69}
70
71CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
72#if DEBUG_FONT_RENDERER
73 ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
74 blockToRemove, blockToRemove->mX, blockToRemove->mY,
75 blockToRemove->mWidth, blockToRemove->mHeight);
76#endif
77 CacheBlock* newHead = head;
78 CacheBlock* nextBlock = blockToRemove->mNext;
79 CacheBlock* prevBlock = blockToRemove->mPrev;
80 if (prevBlock) {
81 prevBlock->mNext = nextBlock;
82 } else {
83 newHead = nextBlock;
84 }
85 if (nextBlock) {
86 nextBlock->mPrev = prevBlock;
87 }
88 delete blockToRemove;
89 return newHead;
90}
91
92///////////////////////////////////////////////////////////////////////////////
93// CacheTexture
94///////////////////////////////////////////////////////////////////////////////
95
96bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
97 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
98 return false;
99 }
100
101 uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
102 uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
103 // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
104 // This columns for glyphs that are close but not necessarily exactly the same size. It trades
105 // off the loss of a few pixels for some glyphs against the ability to store more glyphs
106 // of varying sizes in one block.
107 uint16_t roundedUpW =
108 (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
109 CacheBlock *cacheBlock = mCacheBlocks;
110 while (cacheBlock) {
111 // Store glyph in this block iff: it fits the block's remaining space and:
112 // it's the remainder space (mY == 0) or there's only enough height for this one glyph
113 // or it's within ROUNDING_SIZE of the block width
114 if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
115 (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
116 (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
117 if (cacheBlock->mHeight - glyphH < glyphH) {
118 // Only enough space for this glyph - don't bother rounding up the width
119 roundedUpW = glyphW;
120 }
121 *retOriginX = cacheBlock->mX;
122 *retOriginY = cacheBlock->mY;
123 // If this is the remainder space, create a new cache block for this column. Otherwise,
124 // adjust the info about this column.
125 if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
126 uint16_t oldX = cacheBlock->mX;
127 // Adjust remainder space dimensions
128 cacheBlock->mWidth -= roundedUpW;
129 cacheBlock->mX += roundedUpW;
130 if (mHeight - glyphH >= glyphH) {
131 // There's enough height left over to create a new CacheBlock
132 CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
133 roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
134#if DEBUG_FONT_RENDERER
135 ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
136 newBlock, newBlock->mX, newBlock->mY,
137 newBlock->mWidth, newBlock->mHeight);
138#endif
139 mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
140 }
141 } else {
142 // Insert into current column and adjust column dimensions
143 cacheBlock->mY += glyphH;
144 cacheBlock->mHeight -= glyphH;
145#if DEBUG_FONT_RENDERER
146 ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
147 cacheBlock, cacheBlock->mX, cacheBlock->mY,
148 cacheBlock->mWidth, cacheBlock->mHeight);
149#endif
150 }
151 if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
152 // If remaining space in this block is too small to be useful, remove it
153 mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
154 }
155 mDirty = true;
156#if DEBUG_FONT_RENDERER
157 ALOGD("fitBitmap: current block list:");
158 mCacheBlocks->output();
159#endif
160 ++mNumGlyphs;
161 return true;
162 }
163 cacheBlock = cacheBlock->mNext;
164 }
165#if DEBUG_FONT_RENDERER
166 ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
167#endif
168 return false;
169}
170
171}; // namespace uirenderer
172}; // namespace android