summaryrefslogtreecommitdiffstats
path: root/WallpaperPicker/src/com/android/photos/views/TiledImageView.java
diff options
context:
space:
mode:
Diffstat (limited to 'WallpaperPicker/src/com/android/photos/views/TiledImageView.java')
-rw-r--r--WallpaperPicker/src/com/android/photos/views/TiledImageView.java386
1 files changed, 386 insertions, 0 deletions
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;
+ }
+ }
+}