1f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka/*
2f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Copyright (C) 2010 The Android Open Source Project
3f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
4f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * you may not use this file except in compliance with the License.
6f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * You may obtain a copy of the License at
7f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
8f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
10f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Unless required by applicable law or agreed to in writing, software
11f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * See the License for the specific language governing permissions and
14f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * limitations under the License.
15f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka */
16f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
17f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapackage com.android.gallery3d.glrenderer;
18f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
19f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Bitmap;
20f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Bitmap.Config;
21f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.opengl.GLUtils;
222c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyalimport android.util.Pair;
23f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
2463142a8ddcb6a21b666d2a731fdf1c585afd13eeSunny Goyalimport com.android.gallery3d.common.Utils;
25105593bea0ff8b152e3bce39deaec263a34208f8Adam Cohen
26f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.util.HashMap;
27f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
28f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// UploadedTextures use a Bitmap for the content of the texture.
29f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka//
30f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// Subclasses should implement onGetBitmap() to provide the Bitmap and
31f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// implement onFreeBitmap(mBitmap) which will be called when the Bitmap
32f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// is not needed anymore.
33f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka//
34f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// isContentValid() is meaningful only when the isLoaded() returns true.
35f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// It means whether the content needs to be updated.
36f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka//
37f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// The user of this class should call recycle() when the texture is not
38f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// needed anymore.
39f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka//
40f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// By default an UploadedTexture is opaque (so it can be drawn faster without
41f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka// blending). The user or subclass can override it using setOpaque().
42f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapublic abstract class UploadedTexture extends BasicTexture {
43f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
44f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // To prevent keeping allocation the borders, we store those used borders here.
45f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // Since the length will be power of two, it won't use too much memory.
462c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal    private static HashMap<BorderKey, Bitmap> sBorderLines = new HashMap<BorderKey, Bitmap>();
47f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
482c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal    private static class BorderKey extends Pair<Config, Integer> {
492c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal        public BorderKey(Config config, boolean vertical, int length) {
502c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal            super(config, vertical ? length : -length);
512c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal        }
522c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal    }
53f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
542c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal    private boolean mContentValid = true;
55f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected Bitmap mBitmap;
56f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
57f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected UploadedTexture() {
58f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        super(null, 0, STATE_UNLOADED);
59f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
60f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
612c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal    private static Bitmap getBorderLine(boolean vertical, Config config, int length) {
622c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal        BorderKey key = new BorderKey(config, vertical, length);
63f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Bitmap bitmap = sBorderLines.get(key);
64f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (bitmap == null) {
65f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            bitmap = vertical
66f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    ? Bitmap.createBitmap(1, length, config)
67f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    : Bitmap.createBitmap(length, 1, config);
682c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal            sBorderLines.put(key, bitmap);
69f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
70f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return bitmap;
71f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
72f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
73f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private Bitmap getBitmap() {
74f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mBitmap == null) {
75f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mBitmap = onGetBitmap();
762c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal            int w = mBitmap.getWidth();
772c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal            int h = mBitmap.getHeight();
78f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mWidth == UNSPECIFIED) {
79f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                setSize(w, h);
80f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
81f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
82f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mBitmap;
83f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
84f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
85f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private void freeBitmap() {
8663142a8ddcb6a21b666d2a731fdf1c585afd13eeSunny Goyal        Utils.assertTrue(mBitmap != null);
87f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        onFreeBitmap(mBitmap);
88f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mBitmap = null;
89f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
90f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
91f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
92f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getWidth() {
93f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mWidth == UNSPECIFIED) getBitmap();
94f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mWidth;
95f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
96f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
97f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
98f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getHeight() {
99f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mWidth == UNSPECIFIED) getBitmap();
100f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mHeight;
101f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
102f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
103f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected abstract Bitmap onGetBitmap();
104f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
105f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected abstract void onFreeBitmap(Bitmap bitmap);
106f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
107f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void invalidateContent() {
108f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mBitmap != null) freeBitmap();
109f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mContentValid = false;
110f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mWidth = UNSPECIFIED;
111f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mHeight = UNSPECIFIED;
112f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
113f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
114f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    /**
115f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * Whether the content on GPU is valid.
116f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     */
117f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public boolean isContentValid() {
118f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return isLoaded() && mContentValid;
119f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
120f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
121f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    /**
122f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * Updates the content on GPU's memory.
123f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * @param canvas
124f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     */
125f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public void updateContent(GLCanvas canvas) {
126f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (!isLoaded()) {
127f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            uploadToCanvas(canvas);
128f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } else if (!mContentValid) {
129f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Bitmap bitmap = getBitmap();
130f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            int format = GLUtils.getInternalFormat(bitmap);
131f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            int type = GLUtils.getType(bitmap);
1322c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal            canvas.texSubImage2D(this, 0, 0, bitmap, format, type);
133f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            freeBitmap();
134f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mContentValid = true;
135f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
136f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
137f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
138f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private void uploadToCanvas(GLCanvas canvas) {
139f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Bitmap bitmap = getBitmap();
140f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (bitmap != null) {
141f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            try {
142f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int bWidth = bitmap.getWidth();
143f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int bHeight = bitmap.getHeight();
144f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int texWidth = getTextureWidth();
145f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int texHeight = getTextureHeight();
146f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
14763142a8ddcb6a21b666d2a731fdf1c585afd13eeSunny Goyal                Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
148f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
149f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // Upload the bitmap to a new texture.
150f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                mId = canvas.getGLId().generateTexture();
151f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                canvas.setTextureParameters(this);
152f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
153f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (bWidth == texWidth && bHeight == texHeight) {
154f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    canvas.initializeTexture(this, bitmap);
155f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                } else {
156f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    int format = GLUtils.getInternalFormat(bitmap);
157f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    int type = GLUtils.getType(bitmap);
158f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Config config = bitmap.getConfig();
159f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
160f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    canvas.initializeTextureSize(this, format, type);
1612c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal                    canvas.texSubImage2D(this, 0, 0, bitmap, format, type);
162f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
163f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    // Right border
1642c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal                    if (bWidth < texWidth) {
165f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Bitmap line = getBorderLine(true, config, texHeight);
1662c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal                        canvas.texSubImage2D(this, bWidth, 0, line, format, type);
167f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
168f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
169f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    // Bottom border
1702c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal                    if (bHeight < texHeight) {
171f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Bitmap line = getBorderLine(false, config, texWidth);
1722c7071f9393741acb281a0e67a6381e1c4905cbcSunny Goyal                        canvas.texSubImage2D(this, 0, bHeight, line, format, type);
173f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
174f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
175f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } finally {
176f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                freeBitmap();
177f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
178f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            // Update texture state.
179f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            setAssociatedCanvas(canvas);
180f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mState = STATE_LOADED;
181f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mContentValid = true;
182f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } else {
183f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mState = STATE_ERROR;
184f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            throw new RuntimeException("Texture load fail, no bitmap");
185f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
186f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
187f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
188f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
189f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected boolean onBind(GLCanvas canvas) {
190f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        updateContent(canvas);
191f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return isContentValid();
192f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
193f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
194f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
195f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public void recycle() {
196f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        super.recycle();
197f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mBitmap != null) freeBitmap();
198f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
199f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka}
200