1/*
2 * Copyright (C) 2009 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
17package com.cooliris.media;
18
19import java.util.HashMap;
20import javax.microedition.khronos.opengles.GL11;
21import android.util.Log;
22
23import com.cooliris.app.Res;
24import com.cooliris.media.RenderView.Lists;
25
26public class BackgroundLayer extends Layer {
27    private static final String TAG = "AdaptiveBackground";
28    private final GridLayer mGridLayer;
29    private CrossFadingTexture mBackground;
30    private static final int MAX_ADAPTIVES_TO_KEEP_IN_MEMORY = 16;
31
32    private final HashMap<Texture, AdaptiveBackgroundTexture> mCacheAdaptiveTexture = new HashMap<Texture, AdaptiveBackgroundTexture>();
33    private int mCount;
34    private int mBackgroundBlitWidth;
35    private int mBackgroundOverlap;
36    private Texture mFallbackBackground = null;
37    private static final float Z_FAR_PLANE = 0.9999f;
38    private static final float PARALLAX = 0.5f;
39    private static final int ADAPTIVE_BACKGROUND_WIDTH = 256;
40    private static final int ADAPTIVE_BACKGROUND_HEIGHT = 128;
41
42    public BackgroundLayer(GridLayer layer) {
43        mGridLayer = layer;
44    }
45
46    @Override
47    public void generate(RenderView view, Lists lists) {
48        lists.blendedList.add(this);
49        lists.updateList.add(this);
50        lists.opaqueList.add(this);
51    }
52
53    @Override
54    public boolean update(RenderView view, float frameInterval) {
55        Texture fallback = mFallbackBackground;
56        if (fallback == null || !fallback.isLoaded())
57            return false;
58        CrossFadingTexture background = mBackground;
59        if (background == null) {
60            background = new CrossFadingTexture(fallback);
61            mBackground = background;
62        }
63        final boolean retVal = background.update(frameInterval);
64        int cameraPosition = (int) mGridLayer.getScrollPosition();
65        final int backgroundSpacing = mBackgroundBlitWidth - mBackgroundOverlap;
66        cameraPosition = (int) ((cameraPosition / backgroundSpacing) * backgroundSpacing);
67        final DisplayItem displayItem = mGridLayer.getRepresentativeDisplayItem();
68        if (displayItem != null) {
69            background.setTexture(getAdaptive(view, displayItem));
70        }
71        return retVal;
72    }
73
74    private Texture getAdaptive(RenderView view, DisplayItem item) {
75        if (item == null) {
76            return mFallbackBackground;
77        }
78        Texture itemThumbnail = item.getThumbnailImage(view.getContext(), null);
79        if (item == null || itemThumbnail == null || !itemThumbnail.isLoaded()) {
80            return mFallbackBackground;
81        }
82        HashMap<Texture, AdaptiveBackgroundTexture> adaptives = mCacheAdaptiveTexture;
83        AdaptiveBackgroundTexture retVal = adaptives.get(itemThumbnail);
84        if (retVal == null) {
85            retVal = new AdaptiveBackgroundTexture(itemThumbnail, ADAPTIVE_BACKGROUND_WIDTH, ADAPTIVE_BACKGROUND_HEIGHT);
86            if (mCount == MAX_ADAPTIVES_TO_KEEP_IN_MEMORY) {
87                mCount = 0;
88                adaptives.clear();
89                Log.i(TAG, "Clearing unused adaptive backgrounds.");
90            }
91            ++mCount;
92            adaptives.put(itemThumbnail, retVal);
93        }
94        return retVal;
95    }
96
97    @Override
98    public void renderOpaque(RenderView view, GL11 gl) {
99        gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
100        if (mFallbackBackground == null) {
101            mFallbackBackground = view.getResource(Res.drawable.default_background, false);
102            view.loadTexture(mFallbackBackground);
103        }
104    }
105
106    @Override
107    public void renderBlended(RenderView view, GL11 gl) {
108        CrossFadingTexture anchorTexture = mBackground;
109        if (mBackground == null || mFallbackBackground == null)
110            return;
111        gl.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
112        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
113        boolean bind = anchorTexture.bind(view, gl);
114        if (!bind) {
115            view.bind(mFallbackBackground);
116        } else {
117            Texture texture = anchorTexture.getTexture();
118            if (texture != null && texture.isLoaded()) {
119                mFallbackBackground = texture;
120            }
121        }
122
123        // We stitch this crossfading texture, and to cover all cases of overlap
124        // we need to perform 3 draws.
125        int cameraPosition = (int) (mGridLayer.getScrollPosition() * PARALLAX);
126        int backgroundSpacing = mBackgroundBlitWidth - mBackgroundOverlap;
127        int anchorEdge = -cameraPosition % (backgroundSpacing);
128        int rightEdge = anchorEdge + backgroundSpacing;
129
130        view.draw2D(rightEdge, 0, Z_FAR_PLANE, mBackgroundBlitWidth, mHeight);
131
132        view.draw2D(anchorEdge, 0, Z_FAR_PLANE, mBackgroundBlitWidth, mHeight);
133
134        int leftEdge = anchorEdge - backgroundSpacing;
135        view.draw2D(leftEdge, 0, Z_FAR_PLANE, mBackgroundBlitWidth, mHeight);
136
137        if (bind) {
138            anchorTexture.unbind(view, gl);
139        }
140
141        gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
142        gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
143    }
144
145    @Override
146    protected void onSizeChanged() {
147        mBackgroundBlitWidth = (int) (mWidth * 1.5f);
148        mBackgroundOverlap = (int) (mBackgroundBlitWidth * 0.25f);
149    }
150
151    public void clear() {
152        clearCache();
153        mBackground = null;
154        mFallbackBackground = null;
155    }
156
157    public void clearCache() {
158        mCacheAdaptiveTexture.clear();
159    }
160}
161