96749c1321
This reverts commit 39de459a86.
387 lines
12 KiB
Java
387 lines
12 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|