Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 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 | |
| 17 | package com.android.gallery3d.photoeditor; |
| 18 | |
| 19 | import android.content.Context; |
| 20 | import android.graphics.RectF; |
| 21 | import android.opengl.GLSurfaceView; |
| 22 | import android.util.AttributeSet; |
| 23 | |
| 24 | import java.util.Vector; |
| 25 | |
| 26 | import javax.microedition.khronos.egl.EGLConfig; |
| 27 | import javax.microedition.khronos.opengles.GL10; |
| 28 | |
| 29 | /** |
| 30 | * Renders and displays photo in the surface view. |
| 31 | */ |
| 32 | public class PhotoView extends GLSurfaceView { |
| 33 | |
| 34 | private final PhotoRenderer renderer; |
| 35 | |
| 36 | public PhotoView(Context context, AttributeSet attrs) { |
| 37 | super(context, attrs); |
| 38 | |
| 39 | renderer = new PhotoRenderer(); |
| 40 | setEGLContextClientVersion(2); |
| 41 | setRenderer(renderer); |
| 42 | setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); |
| 43 | } |
| 44 | |
| 45 | public RectF getPhotoBounds() { |
| 46 | RectF photoBounds; |
| 47 | synchronized (renderer.photoBounds) { |
| 48 | photoBounds = new RectF(renderer.photoBounds); |
| 49 | } |
| 50 | return photoBounds; |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * Queues a runnable and renders a frame after execution. Queued runnables could be later |
| 55 | * removed by remove() or flush(). |
| 56 | */ |
| 57 | public void queue(Runnable r) { |
| 58 | renderer.queue.add(r); |
| 59 | requestRender(); |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Removes the specified queued runnable. |
| 64 | */ |
| 65 | public void remove(Runnable runnable) { |
| 66 | renderer.queue.remove(runnable); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Flushes all queued runnables to cancel their execution. |
| 71 | */ |
| 72 | public void flush() { |
| 73 | renderer.queue.clear(); |
| 74 | } |
| 75 | |
| 76 | /** |
| 77 | * Sets photo for display; this method must be queued for GL thread. |
| 78 | */ |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 79 | public void setPhoto(Photo photo, boolean clearTransform) { |
| 80 | renderer.setPhoto(photo, clearTransform); |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Rotates displayed photo; this method must be queued for GL thread. |
| 85 | */ |
| 86 | public void rotatePhoto(float degrees) { |
| 87 | renderer.rotatePhoto(degrees); |
| 88 | } |
| 89 | |
| 90 | /** |
Yuli Huang | 7b62f48 | 2011-10-20 18:29:05 +0800 | [diff] [blame] | 91 | * Flips displayed photo; this method must be queued for GL thread. |
| 92 | */ |
| 93 | public void flipPhoto(float horizontalDegrees, float verticalDegrees) { |
| 94 | renderer.flipPhoto(horizontalDegrees, verticalDegrees); |
| 95 | } |
| 96 | |
| 97 | /** |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 98 | * Renderer that renders the GL surface-view and only be called from the GL thread. |
| 99 | */ |
| 100 | private class PhotoRenderer implements GLSurfaceView.Renderer { |
| 101 | |
| 102 | final Vector<Runnable> queue = new Vector<Runnable>(); |
| 103 | final RectF photoBounds = new RectF(); |
| 104 | RendererUtils.RenderContext renderContext; |
| 105 | Photo photo; |
| 106 | int viewWidth; |
| 107 | int viewHeight; |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 108 | float rotatedDegrees; |
Yuli Huang | 7b62f48 | 2011-10-20 18:29:05 +0800 | [diff] [blame] | 109 | float flippedHorizontalDegrees; |
| 110 | float flippedVerticalDegrees; |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 111 | |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 112 | void setPhoto(Photo photo, boolean clearTransform) { |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 113 | int width = (photo != null) ? photo.width() : 0; |
| 114 | int height = (photo != null) ? photo.height() : 0; |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 115 | boolean changed; |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 116 | synchronized (photoBounds) { |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 117 | changed = (photoBounds.width() != width) || (photoBounds.height() != height); |
| 118 | if (changed) { |
| 119 | photoBounds.set(0, 0, width, height); |
| 120 | } |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 121 | } |
| 122 | this.photo = photo; |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 123 | updateSurface(clearTransform, changed); |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 124 | } |
| 125 | |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 126 | void updateSurface(boolean clearTransform, boolean sizeChanged) { |
Yuli Huang | 7b62f48 | 2011-10-20 18:29:05 +0800 | [diff] [blame] | 127 | boolean flipped = (flippedHorizontalDegrees != 0) || (flippedVerticalDegrees != 0); |
| 128 | boolean transformed = (rotatedDegrees != 0) || flipped; |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 129 | if ((clearTransform && transformed) || (sizeChanged && !transformed)) { |
| 130 | // Fit photo when clearing existing transforms or changing surface/photo sizes. |
| 131 | if (photo != null) { |
| 132 | RendererUtils.setRenderToFit(renderContext, photo.width(), photo.height(), |
| 133 | viewWidth, viewHeight); |
| 134 | rotatedDegrees = 0; |
Yuli Huang | 7b62f48 | 2011-10-20 18:29:05 +0800 | [diff] [blame] | 135 | flippedHorizontalDegrees = 0; |
| 136 | flippedVerticalDegrees = 0; |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 137 | } |
| 138 | } else { |
| 139 | // Restore existing transformations for orientation changes or awaking from sleep. |
| 140 | if (rotatedDegrees != 0) { |
| 141 | rotatePhoto(rotatedDegrees); |
Yuli Huang | 7b62f48 | 2011-10-20 18:29:05 +0800 | [diff] [blame] | 142 | } else if (flipped) { |
| 143 | flipPhoto(flippedHorizontalDegrees, flippedVerticalDegrees); |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 144 | } |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 145 | } |
| 146 | } |
| 147 | |
| 148 | void rotatePhoto(float degrees) { |
| 149 | if (photo != null) { |
| 150 | RendererUtils.setRenderToRotate(renderContext, photo.width(), photo.height(), |
| 151 | viewWidth, viewHeight, degrees); |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 152 | rotatedDegrees = degrees; |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 153 | } |
| 154 | } |
| 155 | |
Yuli Huang | 7b62f48 | 2011-10-20 18:29:05 +0800 | [diff] [blame] | 156 | void flipPhoto(float horizontalDegrees, float verticalDegrees) { |
| 157 | if (photo != null) { |
| 158 | RendererUtils.setRenderToFlip(renderContext, photo.width(), photo.height(), |
| 159 | viewWidth, viewHeight, horizontalDegrees, verticalDegrees); |
| 160 | flippedHorizontalDegrees = horizontalDegrees; |
| 161 | flippedVerticalDegrees = verticalDegrees; |
| 162 | } |
| 163 | } |
| 164 | |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 165 | @Override |
| 166 | public void onDrawFrame(GL10 gl) { |
| 167 | Runnable r = null; |
| 168 | synchronized (queue) { |
| 169 | if (!queue.isEmpty()) { |
| 170 | r = queue.remove(0); |
| 171 | } |
| 172 | } |
| 173 | if (r != null) { |
| 174 | r.run(); |
| 175 | } |
| 176 | if (!queue.isEmpty()) { |
| 177 | requestRender(); |
| 178 | } |
Yuli Huang | eb83d83 | 2011-10-11 21:23:22 +0800 | [diff] [blame] | 179 | RendererUtils.renderBackground(); |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 180 | if (photo != null) { |
| 181 | RendererUtils.renderTexture(renderContext, photo.texture(), viewWidth, viewHeight); |
| 182 | } |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | @Override |
| 186 | public void onSurfaceChanged(GL10 gl, int width, int height) { |
| 187 | viewWidth = width; |
| 188 | viewHeight = height; |
Yuli Huang | e0e82f1 | 2011-10-10 00:55:26 +0800 | [diff] [blame] | 189 | updateSurface(false, true); |
Yuli Huang | 6a12ad7 | 2011-09-12 22:25:30 +0800 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | @Override |
| 193 | public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
| 194 | renderContext = RendererUtils.createProgram(); |
| 195 | } |
| 196 | } |
| 197 | } |