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 */ 79 public void setPhoto(Photo photo, boolean clearTransform) { 80 renderer.setPhoto(photo, clearTransform); 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 /** 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 /** 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; 108 float rotatedDegrees; 109 float flippedHorizontalDegrees; 110 float flippedVerticalDegrees; 111 112 void setPhoto(Photo photo, boolean clearTransform) { 113 int width = (photo != null) ? photo.width() : 0; 114 int height = (photo != null) ? photo.height() : 0; 115 boolean changed; 116 synchronized (photoBounds) { 117 changed = (photoBounds.width() != width) || (photoBounds.height() != height); 118 if (changed) { 119 photoBounds.set(0, 0, width, height); 120 } 121 } 122 this.photo = photo; 123 updateSurface(clearTransform, changed); 124 } 125 126 void updateSurface(boolean clearTransform, boolean sizeChanged) { 127 boolean flipped = (flippedHorizontalDegrees != 0) || (flippedVerticalDegrees != 0); 128 boolean transformed = (rotatedDegrees != 0) || flipped; 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; 135 flippedHorizontalDegrees = 0; 136 flippedVerticalDegrees = 0; 137 } 138 } else { 139 // Restore existing transformations for orientation changes or awaking from sleep. 140 if (rotatedDegrees != 0) { 141 rotatePhoto(rotatedDegrees); 142 } else if (flipped) { 143 flipPhoto(flippedHorizontalDegrees, flippedVerticalDegrees); 144 } 145 } 146 } 147 148 void rotatePhoto(float degrees) { 149 if (photo != null) { 150 RendererUtils.setRenderToRotate(renderContext, photo.width(), photo.height(), 151 viewWidth, viewHeight, degrees); 152 rotatedDegrees = degrees; 153 } 154 } 155 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 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 } 179 RendererUtils.renderBackground(); 180 if (photo != null) { 181 RendererUtils.renderTexture(renderContext, photo.texture(), viewWidth, viewHeight); 182 } 183 } 184 185 @Override 186 public void onSurfaceChanged(GL10 gl, int width, int height) { 187 viewWidth = width; 188 viewHeight = height; 189 updateSurface(false, true); 190 } 191 192 @Override 193 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 194 renderContext = RendererUtils.createProgram(); 195 } 196 } 197 } 198