From aa05711971753bb31cc01f46d4ab10fcf9f9af3b Mon Sep 17 00:00:00 2001 From: Michael Jurka Date: Thu, 12 Dec 2013 15:04:25 +0100 Subject: Create separate project for Wallpaper Picker Change-Id: Id9e855780b9fb68c63eb6e9f6c19bcbce28a6fd5 --- .../com/android/photos/views/TiledImageView.java | 386 +++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 WallpaperPicker/src/com/android/photos/views/TiledImageView.java (limited to 'WallpaperPicker/src/com/android/photos/views/TiledImageView.java') diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java new file mode 100644 index 000000000..af4199c91 --- /dev/null +++ b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2013 The Android Open Source 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 com.android.photos.views; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.RectF; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Build; +import android.util.AttributeSet; +import android.view.Choreographer; +import android.view.Choreographer.FrameCallback; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.gallery3d.glrenderer.BasicTexture; +import com.android.gallery3d.glrenderer.GLES20Canvas; +import com.android.photos.views.TiledImageRenderer.TileSource; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * Shows an image using {@link TiledImageRenderer} using either {@link GLSurfaceView} + * or {@link BlockingGLTextureView}. + */ +public class TiledImageView extends FrameLayout { + + private static final boolean USE_TEXTURE_VIEW = false; + private static final boolean IS_SUPPORTED = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + private static final boolean USE_CHOREOGRAPHER = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + + private BlockingGLTextureView mTextureView; + private GLSurfaceView mGLSurfaceView; + private boolean mInvalPending = false; + private FrameCallback mFrameCallback; + + protected static class ImageRendererWrapper { + // Guarded by locks + public float scale; + public int centerX, centerY; + public int rotation; + public TileSource source; + Runnable isReadyCallback; + + // GL thread only + TiledImageRenderer image; + } + + private float[] mValues = new float[9]; + + // ------------------------- + // Guarded by mLock + // ------------------------- + protected Object mLock = new Object(); + protected ImageRendererWrapper mRenderer; + + public static boolean isTilingSupported() { + return IS_SUPPORTED; + } + + public TiledImageView(Context context) { + this(context, null); + } + + public TiledImageView(Context context, AttributeSet attrs) { + super(context, attrs); + if (!IS_SUPPORTED) { + return; + } + + mRenderer = new ImageRendererWrapper(); + mRenderer.image = new TiledImageRenderer(this); + View view; + if (USE_TEXTURE_VIEW) { + mTextureView = new BlockingGLTextureView(context); + mTextureView.setRenderer(new TileRenderer()); + view = mTextureView; + } else { + mGLSurfaceView = new GLSurfaceView(context); + mGLSurfaceView.setEGLContextClientVersion(2); + mGLSurfaceView.setRenderer(new TileRenderer()); + mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + view = mGLSurfaceView; + } + addView(view, new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + //setTileSource(new ColoredTiles()); + } + + public void destroy() { + if (!IS_SUPPORTED) { + return; + } + if (USE_TEXTURE_VIEW) { + mTextureView.destroy(); + } else { + mGLSurfaceView.queueEvent(mFreeTextures); + } + } + + private Runnable mFreeTextures = new Runnable() { + + @Override + public void run() { + mRenderer.image.freeTextures(); + } + }; + + public void onPause() { + if (!IS_SUPPORTED) { + return; + } + if (!USE_TEXTURE_VIEW) { + mGLSurfaceView.onPause(); + } + } + + public void onResume() { + if (!IS_SUPPORTED) { + return; + } + if (!USE_TEXTURE_VIEW) { + mGLSurfaceView.onResume(); + } + } + + public void setTileSource(TileSource source, Runnable isReadyCallback) { + if (!IS_SUPPORTED) { + return; + } + synchronized (mLock) { + mRenderer.source = source; + mRenderer.isReadyCallback = isReadyCallback; + mRenderer.centerX = source != null ? source.getImageWidth() / 2 : 0; + mRenderer.centerY = source != null ? source.getImageHeight() / 2 : 0; + mRenderer.rotation = source != null ? source.getRotation() : 0; + mRenderer.scale = 0; + updateScaleIfNecessaryLocked(mRenderer); + } + invalidate(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (!IS_SUPPORTED) { + return; + } + synchronized (mLock) { + updateScaleIfNecessaryLocked(mRenderer); + } + } + + private void updateScaleIfNecessaryLocked(ImageRendererWrapper renderer) { + if (renderer == null || renderer.source == null + || renderer.scale > 0 || getWidth() == 0) { + return; + } + renderer.scale = Math.min( + (float) getWidth() / (float) renderer.source.getImageWidth(), + (float) getHeight() / (float) renderer.source.getImageHeight()); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!IS_SUPPORTED) { + return; + } + if (USE_TEXTURE_VIEW) { + mTextureView.render(); + } + super.dispatchDraw(canvas); + } + + @SuppressLint("NewApi") + @Override + public void setTranslationX(float translationX) { + if (!IS_SUPPORTED) { + return; + } + super.setTranslationX(translationX); + } + + @Override + public void invalidate() { + if (!IS_SUPPORTED) { + return; + } + if (USE_TEXTURE_VIEW) { + super.invalidate(); + mTextureView.invalidate(); + } else { + if (USE_CHOREOGRAPHER) { + invalOnVsync(); + } else { + mGLSurfaceView.requestRender(); + } + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private void invalOnVsync() { + if (!mInvalPending) { + mInvalPending = true; + if (mFrameCallback == null) { + mFrameCallback = new FrameCallback() { + @Override + public void doFrame(long frameTimeNanos) { + mInvalPending = false; + mGLSurfaceView.requestRender(); + } + }; + } + Choreographer.getInstance().postFrameCallback(mFrameCallback); + } + } + + private RectF mTempRectF = new RectF(); + public void positionFromMatrix(Matrix matrix) { + if (!IS_SUPPORTED) { + return; + } + if (mRenderer.source != null) { + final int rotation = mRenderer.source.getRotation(); + final boolean swap = !(rotation % 180 == 0); + final int width = swap ? mRenderer.source.getImageHeight() + : mRenderer.source.getImageWidth(); + final int height = swap ? mRenderer.source.getImageWidth() + : mRenderer.source.getImageHeight(); + mTempRectF.set(0, 0, width, height); + matrix.mapRect(mTempRectF); + matrix.getValues(mValues); + int cx = width / 2; + int cy = height / 2; + float scale = mValues[Matrix.MSCALE_X]; + int xoffset = Math.round((getWidth() - mTempRectF.width()) / 2 / scale); + int yoffset = Math.round((getHeight() - mTempRectF.height()) / 2 / scale); + if (rotation == 90 || rotation == 180) { + cx += (mTempRectF.left / scale) - xoffset; + } else { + cx -= (mTempRectF.left / scale) - xoffset; + } + if (rotation == 180 || rotation == 270) { + cy += (mTempRectF.top / scale) - yoffset; + } else { + cy -= (mTempRectF.top / scale) - yoffset; + } + mRenderer.scale = scale; + mRenderer.centerX = swap ? cy : cx; + mRenderer.centerY = swap ? cx : cy; + invalidate(); + } + } + + private class TileRenderer implements Renderer { + + private GLES20Canvas mCanvas; + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + mCanvas = new GLES20Canvas(); + BasicTexture.invalidateAllTextures(); + mRenderer.image.setModel(mRenderer.source, mRenderer.rotation); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + mCanvas.setSize(width, height); + mRenderer.image.setViewSize(width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + mCanvas.clearBuffer(); + Runnable readyCallback; + synchronized (mLock) { + readyCallback = mRenderer.isReadyCallback; + mRenderer.image.setModel(mRenderer.source, mRenderer.rotation); + mRenderer.image.setPosition(mRenderer.centerX, mRenderer.centerY, + mRenderer.scale); + } + boolean complete = mRenderer.image.draw(mCanvas); + if (complete && readyCallback != null) { + synchronized (mLock) { + // Make sure we don't trample on a newly set callback/source + // if it changed while we were rendering + if (mRenderer.isReadyCallback == readyCallback) { + mRenderer.isReadyCallback = null; + } + } + if (readyCallback != null) { + post(readyCallback); + } + } + } + + } + + @SuppressWarnings("unused") + private static class ColoredTiles implements TileSource { + private static final int[] COLORS = new int[] { + Color.RED, + Color.BLUE, + Color.YELLOW, + Color.GREEN, + Color.CYAN, + Color.MAGENTA, + Color.WHITE, + }; + + private Paint mPaint = new Paint(); + private Canvas mCanvas = new Canvas(); + + @Override + public int getTileSize() { + return 256; + } + + @Override + public int getImageWidth() { + return 16384; + } + + @Override + public int getImageHeight() { + return 8192; + } + + @Override + public int getRotation() { + return 0; + } + + @Override + public Bitmap getTile(int level, int x, int y, Bitmap bitmap) { + int tileSize = getTileSize(); + if (bitmap == null) { + bitmap = Bitmap.createBitmap(tileSize, tileSize, + Bitmap.Config.ARGB_8888); + } + mCanvas.setBitmap(bitmap); + mCanvas.drawColor(COLORS[level]); + mPaint.setColor(Color.BLACK); + mPaint.setTextSize(20); + mPaint.setTextAlign(Align.CENTER); + mCanvas.drawText(x + "x" + y, 128, 128, mPaint); + tileSize <<= level; + x /= tileSize; + y /= tileSize; + mCanvas.drawText(x + "x" + y + " @ " + level, 128, 30, mPaint); + mCanvas.setBitmap(null); + return bitmap; + } + + @Override + public BasicTexture getPreview() { + return null; + } + } +} -- cgit v1.2.3