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 <GLES3/gl3.h>
21
22#include <SkScalerContext.h>
23
24#include <utils/Log.h>
25
26#include "FontUtil.h"
27#include "../PixelBuffer.h"
28#include "../Rect.h"
29#include "../Vertex.h"
30
31namespace android {
32namespace uirenderer {
33
34class Caches;
35
36/**
37 * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
38 * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
39 * When we add a glyph to the cache, we see if it fits within one of the existing columns that
40 * have already been started (this is the case if the glyph fits vertically as well as
41 * horizontally, and if its width is sufficiently close to the column width to avoid
42 * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
43 * glyph fits, we check the final node, which is the remaining space in the cache, creating
44 * a new column as appropriate.
45 *
46 * As columns fill up, we remove their CacheBlock from the list to avoid having to check
47 * small blocks in the future.
48 */
49struct CacheBlock {
50    uint16_t mX;
51    uint16_t mY;
52    uint16_t mWidth;
53    uint16_t mHeight;
54    CacheBlock* mNext;
55    CacheBlock* mPrev;
56
57    CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height):
58            mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
59    }
60
61    static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
62    static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
63
64    void output() {
65        CacheBlock* currBlock = this;
66        while (currBlock) {
67            ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
68                    currBlock, currBlock->mX, currBlock->mY,
69                    currBlock->mWidth, currBlock->mHeight);
70            currBlock = currBlock->mNext;
71        }
72    }
73};
74
75class CacheTexture {
76public:
77    CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount);
78    ~CacheTexture();
79
80    void reset();
81    void init();
82
83    void releaseMesh();
84    void releaseTexture();
85
86    void allocateTexture();
87    void allocateMesh();
88
89    // Returns true if glPixelStorei(GL_UNPACK_ROW_LENGTH) must be reset
90    // This method will also call setDirty(false)
91    bool upload();
92
93    bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
94
95    inline uint16_t getWidth() const {
96        return mWidth;
97    }
98
99    inline uint16_t getHeight() const {
100        return mHeight;
101    }
102
103    inline GLenum getFormat() const {
104        return mFormat;
105    }
106
107    inline uint32_t getOffset(uint16_t x, uint16_t y) const {
108        return (y * mWidth + x) * PixelBuffer::formatSize(mFormat);
109    }
110
111    inline const Rect* getDirtyRect() const {
112        return &mDirtyRect;
113    }
114
115    inline PixelBuffer* getPixelBuffer() const {
116        return mTexture;
117    }
118
119    GLuint getTextureId() {
120        allocateTexture();
121        return mTextureId;
122    }
123
124    inline bool isDirty() const {
125        return mDirty;
126    }
127
128    inline bool getLinearFiltering() const {
129        return mLinearFiltering;
130    }
131
132    /**
133     * This method assumes that the proper texture unit is active.
134     */
135    void setLinearFiltering(bool linearFiltering, bool bind = true);
136
137    inline uint16_t getGlyphCount() const {
138        return mNumGlyphs;
139    }
140
141    TextureVertex* mesh() const {
142        return mMesh;
143    }
144
145    uint32_t meshElementCount() const {
146        return mCurrentQuad * 6;
147    }
148
149    uint16_t* indices() const {
150        return (uint16_t*) 0;
151    }
152
153    void resetMesh() {
154        mCurrentQuad = 0;
155    }
156
157    inline void addQuad(float x1, float y1, float u1, float v1,
158            float x2, float y2, float u2, float v2,
159            float x3, float y3, float u3, float v3,
160            float x4, float y4, float u4, float v4) {
161        TextureVertex* mesh = mMesh + mCurrentQuad * 4;
162        TextureVertex::set(mesh++, x2, y2, u2, v2);
163        TextureVertex::set(mesh++, x3, y3, u3, v3);
164        TextureVertex::set(mesh++, x1, y1, u1, v1);
165        TextureVertex::set(mesh++, x4, y4, u4, v4);
166        mCurrentQuad++;
167    }
168
169    bool canDraw() const {
170        return mCurrentQuad > 0;
171    }
172
173    bool endOfMesh() const {
174        return mCurrentQuad == mMaxQuadCount;
175    }
176
177private:
178    void setDirty(bool dirty);
179
180    PixelBuffer* mTexture;
181    GLuint mTextureId;
182    uint16_t mWidth;
183    uint16_t mHeight;
184    GLenum mFormat;
185    bool mLinearFiltering;
186    bool mDirty;
187    uint16_t mNumGlyphs;
188    TextureVertex* mMesh;
189    uint32_t mCurrentQuad;
190    uint32_t mMaxQuadCount;
191    Caches& mCaches;
192    CacheBlock* mCacheBlocks;
193    bool mHasUnpackRowLength;
194    Rect mDirtyRect;
195};
196
197}; // namespace uirenderer
198}; // namespace android
199
200#endif // ANDROID_HWUI_CACHE_TEXTURE_H
201