blob: 0d4caa86246a8685efd0256600b87840ba4f2098 [file] [log] [blame]
Yuli Huang6a12ad72011-09-12 22:25:30 +08001/*
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
17package com.android.gallery3d.photoeditor;
18
19import android.content.Context;
20import android.graphics.RectF;
21import android.opengl.GLSurfaceView;
22import android.util.AttributeSet;
23
24import java.util.Vector;
25
26import javax.microedition.khronos.egl.EGLConfig;
27import javax.microedition.khronos.opengles.GL10;
28
29/**
30 * Renders and displays photo in the surface view.
31 */
32public 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 Huange0e82f12011-10-10 00:55:26 +080079 public void setPhoto(Photo photo, boolean clearTransform) {
80 renderer.setPhoto(photo, clearTransform);
Yuli Huang6a12ad72011-09-12 22:25:30 +080081 }
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 Huang7b62f482011-10-20 18:29:05 +080091 * 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 Huang6a12ad72011-09-12 22:25:30 +080098 * 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 Huange0e82f12011-10-10 00:55:26 +0800108 float rotatedDegrees;
Yuli Huang7b62f482011-10-20 18:29:05 +0800109 float flippedHorizontalDegrees;
110 float flippedVerticalDegrees;
Yuli Huang6a12ad72011-09-12 22:25:30 +0800111
Yuli Huange0e82f12011-10-10 00:55:26 +0800112 void setPhoto(Photo photo, boolean clearTransform) {
Yuli Huang6a12ad72011-09-12 22:25:30 +0800113 int width = (photo != null) ? photo.width() : 0;
114 int height = (photo != null) ? photo.height() : 0;
Yuli Huange0e82f12011-10-10 00:55:26 +0800115 boolean changed;
Yuli Huang6a12ad72011-09-12 22:25:30 +0800116 synchronized (photoBounds) {
Yuli Huange0e82f12011-10-10 00:55:26 +0800117 changed = (photoBounds.width() != width) || (photoBounds.height() != height);
118 if (changed) {
119 photoBounds.set(0, 0, width, height);
120 }
Yuli Huang6a12ad72011-09-12 22:25:30 +0800121 }
122 this.photo = photo;
Yuli Huange0e82f12011-10-10 00:55:26 +0800123 updateSurface(clearTransform, changed);
Yuli Huang6a12ad72011-09-12 22:25:30 +0800124 }
125
Yuli Huange0e82f12011-10-10 00:55:26 +0800126 void updateSurface(boolean clearTransform, boolean sizeChanged) {
Yuli Huang7b62f482011-10-20 18:29:05 +0800127 boolean flipped = (flippedHorizontalDegrees != 0) || (flippedVerticalDegrees != 0);
128 boolean transformed = (rotatedDegrees != 0) || flipped;
Yuli Huange0e82f12011-10-10 00:55:26 +0800129 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 Huang7b62f482011-10-20 18:29:05 +0800135 flippedHorizontalDegrees = 0;
136 flippedVerticalDegrees = 0;
Yuli Huange0e82f12011-10-10 00:55:26 +0800137 }
138 } else {
139 // Restore existing transformations for orientation changes or awaking from sleep.
140 if (rotatedDegrees != 0) {
141 rotatePhoto(rotatedDegrees);
Yuli Huang7b62f482011-10-20 18:29:05 +0800142 } else if (flipped) {
143 flipPhoto(flippedHorizontalDegrees, flippedVerticalDegrees);
Yuli Huange0e82f12011-10-10 00:55:26 +0800144 }
Yuli Huang6a12ad72011-09-12 22:25:30 +0800145 }
146 }
147
148 void rotatePhoto(float degrees) {
149 if (photo != null) {
150 RendererUtils.setRenderToRotate(renderContext, photo.width(), photo.height(),
151 viewWidth, viewHeight, degrees);
Yuli Huange0e82f12011-10-10 00:55:26 +0800152 rotatedDegrees = degrees;
Yuli Huang6a12ad72011-09-12 22:25:30 +0800153 }
154 }
155
Yuli Huang7b62f482011-10-20 18:29:05 +0800156 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 Huang6a12ad72011-09-12 22:25:30 +0800165 @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 Huangeb83d832011-10-11 21:23:22 +0800179 RendererUtils.renderBackground();
Yuli Huange0e82f12011-10-10 00:55:26 +0800180 if (photo != null) {
181 RendererUtils.renderTexture(renderContext, photo.texture(), viewWidth, viewHeight);
182 }
Yuli Huang6a12ad72011-09-12 22:25:30 +0800183 }
184
185 @Override
186 public void onSurfaceChanged(GL10 gl, int width, int height) {
187 viewWidth = width;
188 viewHeight = height;
Yuli Huange0e82f12011-10-10 00:55:26 +0800189 updateSurface(false, true);
Yuli Huang6a12ad72011-09-12 22:25:30 +0800190 }
191
192 @Override
193 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
194 renderContext = RendererUtils.createProgram();
195 }
196 }
197}