diff options
author | jruesga <jorge@ruesga.com> | 2013-07-01 02:42:54 +0200 |
---|---|---|
committer | jruesga <jorge@ruesga.com> | 2013-07-01 02:42:54 +0200 |
commit | f29d68d929061ccbb982019c5020be721bbef862 (patch) | |
tree | 24ce41fb0dd7f942ca9dbe956fe93fe836da66c2 /src/org/cyanogenmod/wallpapers/photophase/TextureManager.java | |
download | android_packages_wallpapers_PhotoPhase-f29d68d929061ccbb982019c5020be721bbef862.tar.gz android_packages_wallpapers_PhotoPhase-f29d68d929061ccbb982019c5020be721bbef862.tar.bz2 android_packages_wallpapers_PhotoPhase-f29d68d929061ccbb982019c5020be721bbef862.zip |
Initial commit
Signed-off-by: jruesga <jorge@ruesga.com>
Diffstat (limited to 'src/org/cyanogenmod/wallpapers/photophase/TextureManager.java')
-rw-r--r-- | src/org/cyanogenmod/wallpapers/photophase/TextureManager.java | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java new file mode 100644 index 0000000..bd42af9 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.cyanogenmod.wallpapers.photophase; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.opengl.GLES20; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.FixedQueue.EmptyQueueException; +import org.cyanogenmod.wallpapers.photophase.GLESUtil.GLESTextureInfo; +import org.cyanogenmod.wallpapers.photophase.MediaPictureDiscoverer.OnMediaPictureDiscoveredListener; +import org.cyanogenmod.wallpapers.photophase.effects.Effects; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A class that manages the acquisition of new textures. + */ +public class TextureManager implements OnMediaPictureDiscoveredListener { + + private static final String TAG = "TextureManager"; + + private static final int QUEUE_SIZE = 1; + + static final List<Bitmap> sRecycledBitmaps = new ArrayList<Bitmap>(); + + final Context mContext; + final Object mSync; + final List<TextureRequestor> mPendingRequests; + final FixedQueue<GLESTextureInfo> mQueue = new FixedQueue<GLESTextureInfo>(QUEUE_SIZE); + BackgroundPictureLoaderThread mBackgroundTask; + private final MediaPictureDiscoverer mPictureDiscoverer; + + + + /*package*/ Rect mDimensions; + + final GLESSurfaceDispatcher mDispatcher; + + /** + * A private runnable that will run in the GLThread + */ + /*package*/ class PictureDispatcher implements Runnable { + File mImage; + GLESTextureInfo ti; + final Object mWait = new Object(); + + /** + * {@inheritDoc} + */ + @Override + public void run() { + try { + // If we have bitmap to reused then pick up from the recycled list + if (sRecycledBitmaps.size() > 0) { + // Bind to the GLES context + ti = GLESUtil.loadTexture(sRecycledBitmaps.remove(0)); + } else { + // Load and bind to the GLES context + ti = GLESUtil.loadTexture(mImage, mDimensions, Effects.getNextEffect(), false); + } + + synchronized (mSync) { + // Notify the new images to all pending frames + if (mPendingRequests.size() > 0) { + // Invalid textures are also reported, so requestor can handle it + mPendingRequests.remove(0).setTextureHandle(ti); + } else { + // Add to the queue (only valid textures) + if (ti.handle > 0) { + mQueue.insert(ti); + } + } + } + + } catch (Exception e) { + Log.e(TAG, "Something was wrong loading the texture: " + mImage.getAbsolutePath(), e); + + } finally { + // Notify that we have a new image + synchronized (mWait) { + mWait.notify(); + } + } + } + } + + /** + * Constructor of <code>TextureManager</code> + * + * @param ctx The current context + * @param dispatcher The GLES dispatcher + * @param requestors The number of requestors + * @param dimensions The desired dimensions for the decoded bitmaps + */ + public TextureManager(final Context ctx, GLESSurfaceDispatcher dispatcher, int requestors, Rect dimensions) { + super(); + mContext = ctx; + mDispatcher = dispatcher; + mDimensions = dimensions; + mSync = new Object(); + mPendingRequests = new ArrayList<TextureRequestor>(requestors); + mPictureDiscoverer = new MediaPictureDiscoverer(mContext, this); + + // Run the media discovery thread + mBackgroundTask = new BackgroundPictureLoaderThread(); + mBackgroundTask.mTaskPaused = false; + reloadMedia(); + } + + /** + * Method that allow to change the preferred dimensions of the bitmaps loaded + * + * @param dimensions The new dimensions + */ + public void setDimensions(Rect dimensions) { + mDimensions = dimensions; + } + + /** + * Method that returns if the texture manager is paused + * + * @return boolean whether the texture manager is paused + */ + public boolean isPaused() { + return mBackgroundTask != null && mBackgroundTask.mTaskPaused; + } + + /** + * Method that pauses the internal threads + * + * @param pause If the thread is paused (true) or resumed (false) + */ + public synchronized void setPause(boolean pause) { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mTaskPaused = pause; + if (!mBackgroundTask.mTaskPaused) { + mBackgroundTask.mLoadSync.notify(); + } + } + } + + /** + * Method that reload the references of media pictures + */ + void reloadMedia() { + Log.d(TAG, "Reload media picture data"); + // Discover new media + mPictureDiscoverer.discover(Preferences.Media.getSelectedAlbums()); + } + + /** + * Method that returns a bitmap to be reused + * + * @param bitmap The bitmap to release + */ + @SuppressWarnings("static-method") + public void releaseBitmap(Bitmap bitmap) { + if (bitmap != null) { + sRecycledBitmaps.add(0, bitmap); + } + } + + /** + * Method that request a new picture for the {@link TextureRequestor} + * + * @param requestor The requestor of the texture + */ + public void request(TextureRequestor requestor) { + synchronized (mSync) { + try { + requestor.setTextureHandle(mQueue.remove()); + } catch (EmptyQueueException eqex) { + // Add to queue of pending request to be notified when + // we have a new bitmap in the queue + mPendingRequests.add(requestor); + } + } + + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); + } + } + + /** + * Method that removes all the textures from the queue + * + * @param reload Forces a reload of the queue + */ + public void emptyTextureQueue(boolean reload) { + synchronized (mSync) { + try { + List<GLESTextureInfo> all = mQueue.removeAll(); + for (GLESTextureInfo info : all) { + if (GLES20.glIsTexture(info.handle)) { + int[] textures = new int[] {info.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + // Return the bitmap + sRecycledBitmaps.add(info.bitmap); + } + } catch (EmptyQueueException eqex) { + // Ignore + } + + // Reload the queue + if (reload) { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); + } + } + } + } + + /** + * Method that cancels a request did it previously. + * + * @param requestor The requestor of the texture + */ + public void cancelRequest(TextureRequestor requestor) { + synchronized (mSync) { + if (mPendingRequests.contains(requestor)) { + mPendingRequests.remove(requestor); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onMediaDiscovered(MediaPictureDiscoverer mpc, File[] images) { + // Now we have the paths of the images to use. Start a image loader + // thread to load pictures in background + mBackgroundTask.setAvailableImages(images); + if (!mBackgroundTask.mRun) { + mBackgroundTask.start(); + } else { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); + } + } + int found = images == null ? 0 : images.length; + Log.d(TAG, "Media picture data reloaded: " + found + " images found."); + } + + /** + * Method that destroy the references of this class + */ + public void recycle() { + // Destroy the media discovery task + mPictureDiscoverer.recycle(); + + // Destroy the background task + if (mBackgroundTask != null) { + mBackgroundTask.mRun = false; + try { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.interrupt(); + } + } catch (Exception e) { + // Ignore + } + } + mBackgroundTask = null; + + // Recycle the textures of the queue + emptyTextureQueue(false); + } + + /** + * An internal thread to load pictures in background + */ + private class BackgroundPictureLoaderThread extends Thread { + + final Object mLoadSync = new Object(); + boolean mRun; + boolean mTaskPaused; + + private final List<File> mNewImages; + private final List<File> mUsedImages; + + /** + * Constructor of <code>BackgroundPictureLoaderThread</code>. + */ + public BackgroundPictureLoaderThread() { + super(); + mNewImages = new ArrayList<File>(); + mUsedImages = new ArrayList<File>(); + } + + /** + * Method that sets the current available images. + * + * @param images The current images + */ + public void setAvailableImages(File[] images) { + synchronized (mLoadSync) { + mNewImages.addAll(Arrays.asList(images)); + mUsedImages.clear(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void run() { + mRun = true; + while (mRun) { + // Check if we need to load more images + while (!mTaskPaused && TextureManager.this.mQueue.items() < TextureManager.this.mQueue.size()) { + File image = null; + synchronized (mLoadSync) { + // Swap arrays if needed + if (mNewImages.size() == 0) { + mNewImages.addAll(mUsedImages); + mUsedImages.clear(); + } + if (mNewImages.size() == 0) { + reloadMedia(); + break; + } + + // Extract a random image + int low = 0; + int hight = mNewImages.size()-1; + int index = low + (int)(Math.random() * ((hight - low) + 1)); + image = mNewImages.remove(index); + } + + // Run commands in the GLThread + if (!mRun) break; + PictureDispatcher pd = new PictureDispatcher(); + pd.mImage = image; + mDispatcher.dispatch(pd); + + // Wait until the texture is loaded + try { + synchronized (pd.mWait) { + pd.mWait.wait(); + } + } catch (Exception e) { + // Ignore + } + + // Add to used images + mUsedImages.add(image); + } + + // Wait for new request + synchronized (mLoadSync) { + try { + mLoadSync.wait(); + } catch (Exception e) { + // Ignore + } + } + } + } + + } +} |