CacheTexture.h revision 574cf6070d34e66dfd6f2006937986eddd1f09e7
1/* 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#ifndef ANDROID_HWUI_CACHE_TEXTURE_H 18#define ANDROID_HWUI_CACHE_TEXTURE_H 19 20#include <GLES2/gl2.h> 21 22#include <SkScalerContext.h> 23 24#include <utils/Log.h> 25 26#include "FontUtil.h" 27 28namespace android { 29namespace uirenderer { 30 31/** 32 * CacheBlock is a node in a linked list of current free space areas in a CacheTexture. 33 * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right. 34 * When we add a glyph to the cache, we see if it fits within one of the existing columns that 35 * have already been started (this is the case if the glyph fits vertically as well as 36 * horizontally, and if its width is sufficiently close to the column width to avoid 37 * sub-optimal packing of small glyphs into wide columns). If there is no column in which the 38 * glyph fits, we check the final node, which is the remaining space in the cache, creating 39 * a new column as appropriate. 40 * 41 * As columns fill up, we remove their CacheBlock from the list to avoid having to check 42 * small blocks in the future. 43 */ 44struct CacheBlock { 45 uint16_t mX; 46 uint16_t mY; 47 uint16_t mWidth; 48 uint16_t mHeight; 49 CacheBlock* mNext; 50 CacheBlock* mPrev; 51 52 CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false): 53 mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) { 54 } 55 56 static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock); 57 58 static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove); 59 60 void output() { 61 CacheBlock* currBlock = this; 62 while (currBlock) { 63 ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d", 64 currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight); 65 currBlock = currBlock->mNext; 66 } 67 } 68}; 69 70class CacheTexture { 71public: 72 CacheTexture(uint16_t width, uint16_t height) : 73 mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height), 74 mLinearFiltering(false), mDirty(false), mNumGlyphs(0) { 75 mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, 76 mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true); 77 } 78 79 ~CacheTexture() { 80 releaseTexture(); 81 reset(); 82 } 83 84 void reset() { 85 // Delete existing cache blocks 86 while (mCacheBlocks != NULL) { 87 CacheBlock* tmpBlock = mCacheBlocks; 88 mCacheBlocks = mCacheBlocks->mNext; 89 delete tmpBlock; 90 } 91 mNumGlyphs = 0; 92 } 93 94 void init() { 95 // reset, then create a new remainder space to start again 96 reset(); 97 mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, 98 mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true); 99 } 100 101 void releaseTexture() { 102 if (mTexture) { 103 delete[] mTexture; 104 mTexture = NULL; 105 } 106 if (mTextureId) { 107 glDeleteTextures(1, &mTextureId); 108 mTextureId = 0; 109 } 110 mDirty = false; 111 } 112 113 /** 114 * This method assumes that the proper texture unit is active. 115 */ 116 void allocateTexture() { 117 if (!mTexture) { 118 mTexture = new uint8_t[mWidth * mHeight]; 119 } 120 121 if (!mTextureId) { 122 glGenTextures(1, &mTextureId); 123 124 glBindTexture(GL_TEXTURE_2D, mTextureId); 125 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 126 // Initialize texture dimensions 127 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0, 128 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 129 130 const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST; 131 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 133 134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 136 } 137 } 138 139 bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY); 140 141 inline uint16_t getWidth() const { 142 return mWidth; 143 } 144 145 inline uint16_t getHeight() const { 146 return mHeight; 147 } 148 149 inline uint8_t* getTexture() const { 150 return mTexture; 151 } 152 153 GLuint getTextureId() { 154 allocateTexture(); 155 return mTextureId; 156 } 157 158 inline bool isDirty() const { 159 return mDirty; 160 } 161 162 inline void setDirty(bool dirty) { 163 mDirty = dirty; 164 } 165 166 inline bool getLinearFiltering() const { 167 return mLinearFiltering; 168 } 169 170 /** 171 * This method assumes that the proper texture unit is active. 172 */ 173 void setLinearFiltering(bool linearFiltering, bool bind = true) { 174 if (linearFiltering != mLinearFiltering) { 175 mLinearFiltering = linearFiltering; 176 177 const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST; 178 if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId()); 179 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 180 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 181 } 182 } 183 184 inline uint16_t getGlyphCount() const { 185 return mNumGlyphs; 186 } 187 188private: 189 uint8_t* mTexture; 190 GLuint mTextureId; 191 uint16_t mWidth; 192 uint16_t mHeight; 193 bool mLinearFiltering; 194 bool mDirty; 195 uint16_t mNumGlyphs; 196 CacheBlock* mCacheBlocks; 197}; 198 199}; // namespace uirenderer 200}; // namespace android 201 202#endif // ANDROID_HWUI_CACHE_TEXTURE_H 203