• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PhotoView(Context context, AttributeSet attrs)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 
getPhotoBounds()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      */
queue(Runnable r)57     public void queue(Runnable r) {
58         renderer.queue.add(r);
59         requestRender();
60     }
61 
62     /**
63      * Removes the specified queued runnable.
64      */
remove(Runnable runnable)65     public void remove(Runnable runnable) {
66         renderer.queue.remove(runnable);
67     }
68 
69     /**
70      * Flushes all queued runnables to cancel their execution.
71      */
flush()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      */
setPhoto(Photo photo, boolean clearTransform)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      */
rotatePhoto(float degrees)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      */
flipPhoto(float horizontalDegrees, float verticalDegrees)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 
setPhoto(Photo photo, boolean clearTransform)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 
updateSurface(boolean clearTransform, boolean sizeChanged)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 
rotatePhoto(float degrees)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 
flipPhoto(float horizontalDegrees, float verticalDegrees)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
onDrawFrame(GL10 gl)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
onSurfaceChanged(GL10 gl, int width, int height)186         public void onSurfaceChanged(GL10 gl, int width, int height) {
187             viewWidth = width;
188             viewHeight = height;
189             updateSurface(false, true);
190         }
191 
192         @Override
onSurfaceCreated(GL10 gl, EGLConfig config)193         public void onSurfaceCreated(GL10 gl, EGLConfig config) {
194             renderContext = RendererUtils.createProgram();
195         }
196     }
197 }
198