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
17package com.android.gallery3d.ui;
18
19import android.test.suitebuilder.annotation.SmallTest;
20import android.util.Log;
21
22import junit.framework.TestCase;
23
24import java.util.Arrays;
25
26import javax.microedition.khronos.opengles.GL10;
27import javax.microedition.khronos.opengles.GL11;
28
29@SmallTest
30public class GLCanvasTest extends TestCase {
31    private static final String TAG = "GLCanvasTest";
32
33    private static GLPaint newColorPaint(int color) {
34        GLPaint paint = new GLPaint();
35        paint.setColor(color);
36        return paint;
37    }
38
39    @SmallTest
40    public void testSetSize() {
41        GL11 glStub = new GLStub();
42        GLCanvas canvas = new GLCanvasImpl(glStub);
43        canvas.setSize(100, 200);
44        canvas.setSize(1000, 100);
45        try {
46            canvas.setSize(-1, 100);
47            fail();
48        } catch (Throwable ex) {
49            // expected.
50        }
51    }
52
53    @SmallTest
54    public void testClearBuffer() {
55        new ClearBufferTest().run();
56    }
57
58    private static class ClearBufferTest extends GLMock {
59        void run() {
60            GLCanvas canvas = new GLCanvasImpl(this);
61            assertEquals(0, mGLClearCalled);
62            canvas.clearBuffer();
63            assertEquals(GL10.GL_COLOR_BUFFER_BIT, mGLClearMask);
64            assertEquals(1, mGLClearCalled);
65        }
66    }
67
68    @SmallTest
69    public void testSetColor() {
70        new SetColorTest().run();
71    }
72
73    // This test assumes we use pre-multipled alpha blending and should
74    // set the blending function and color correctly.
75    private static class SetColorTest extends GLMock {
76        void run() {
77            int[] testColors = new int[] {
78                0, 0xFFFFFFFF, 0xFF000000, 0x00FFFFFF, 0x80FF8001,
79                0x7F010101, 0xFEFEFDFC, 0x017F8081, 0x027F8081, 0x2ADE4C4D
80            };
81
82            GLCanvas canvas = new GLCanvasImpl(this);
83            canvas.setSize(400, 300);
84            // Test one color to make sure blend function is set.
85            assertEquals(0, mGLColorCalled);
86            canvas.drawLine(0, 0, 1, 1, newColorPaint(0x7F804020));
87            assertEquals(1, mGLColorCalled);
88            assertEquals(0x7F402010, mGLColor);
89            assertPremultipliedBlending(this);
90
91            // Test other colors to make sure premultiplication is right
92            for (int c : testColors) {
93                float a = (c >>> 24) / 255f;
94                float r = ((c >> 16) & 0xff) / 255f;
95                float g = ((c >> 8) & 0xff) / 255f;
96                float b = (c & 0xff) / 255f;
97                int pre = makeColor4f(a * r, a * g, a * b, a);
98
99                mGLColorCalled = 0;
100                canvas.drawLine(0, 0, 1, 1, newColorPaint(c));
101                assertEquals(1, mGLColorCalled);
102                assertEquals(pre, mGLColor);
103            }
104        }
105    }
106
107    @SmallTest
108    public void testSetGetMultiplyAlpha() {
109        GL11 glStub = new GLStub();
110        GLCanvas canvas = new GLCanvasImpl(glStub);
111
112        canvas.setAlpha(1f);
113        assertEquals(1f, canvas.getAlpha());
114
115        canvas.setAlpha(0f);
116        assertEquals(0f, canvas.getAlpha());
117
118        canvas.setAlpha(0.5f);
119        assertEquals(0.5f, canvas.getAlpha());
120
121        canvas.multiplyAlpha(0.5f);
122        assertEquals(0.25f, canvas.getAlpha());
123
124        canvas.multiplyAlpha(0f);
125        assertEquals(0f, canvas.getAlpha());
126
127        try {
128            canvas.setAlpha(-0.01f);
129            fail();
130        } catch (Throwable ex) {
131            // expected.
132        }
133
134        try {
135            canvas.setAlpha(1.01f);
136            fail();
137        } catch (Throwable ex) {
138            // expected.
139        }
140    }
141
142    @SmallTest
143    public void testAlpha() {
144        new AlphaTest().run();
145    }
146
147    private static class AlphaTest extends GLMock {
148        void run() {
149            GLCanvas canvas = new GLCanvasImpl(this);
150            canvas.setSize(400, 300);
151
152            assertEquals(0, mGLColorCalled);
153            canvas.setAlpha(0.48f);
154            canvas.drawLine(0, 0, 1, 1, newColorPaint(0xFF804020));
155            assertPremultipliedBlending(this);
156            assertEquals(1, mGLColorCalled);
157            assertEquals(0x7A3D1F0F, mGLColor);
158        }
159    }
160
161    @SmallTest
162    public void testDrawLine() {
163        new DrawLineTest().run();
164    }
165
166    // This test assumes the drawLine() function use glDrawArrays() with
167    // GL_LINE_STRIP mode to draw the line and the input coordinates are used
168    // directly.
169    private static class DrawLineTest extends GLMock {
170        private int mDrawArrayCalled = 0;
171        private final int[] mResult = new int[4];
172
173        @Override
174        public void glDrawArrays(int mode, int first, int count) {
175            assertNotNull(mGLVertexPointer);
176            assertEquals(GL10.GL_LINE_STRIP, mode);
177            assertEquals(2, count);
178            mGLVertexPointer.bindByteBuffer();
179
180            double[] coord = new double[4];
181            mGLVertexPointer.getArrayElement(first, coord);
182            mResult[0] = (int) coord[0];
183            mResult[1] = (int) coord[1];
184            mGLVertexPointer.getArrayElement(first + 1, coord);
185            mResult[2] = (int) coord[0];
186            mResult[3] = (int) coord[1];
187            mDrawArrayCalled++;
188        }
189
190        void run() {
191            GLCanvas canvas = new GLCanvasImpl(this);
192            canvas.setSize(400, 300);
193            canvas.drawLine(2, 7, 1, 8, newColorPaint(0) /* color */);
194            assertTrue(mGLVertexArrayEnabled);
195            assertEquals(1, mDrawArrayCalled);
196
197            Log.v(TAG, "result = " + Arrays.toString(mResult));
198            int[] answer = new int[] {2, 7, 1, 8};
199            for (int i = 0; i < answer.length; i++) {
200                assertEquals(answer[i], mResult[i]);
201            }
202        }
203    }
204
205    @SmallTest
206    public void testFillRect() {
207        new FillRectTest().run();
208    }
209
210    // This test assumes the drawLine() function use glDrawArrays() with
211    // GL_TRIANGLE_STRIP mode to draw the line and the input coordinates
212    // are used directly.
213    private static class FillRectTest extends GLMock {
214        private int mDrawArrayCalled = 0;
215        private final int[] mResult = new int[8];
216
217        @Override
218        public void glDrawArrays(int mode, int first, int count) {
219            assertNotNull(mGLVertexPointer);
220            assertEquals(GL10.GL_TRIANGLE_STRIP, mode);
221            assertEquals(4, count);
222            mGLVertexPointer.bindByteBuffer();
223
224            double[] coord = new double[4];
225            for (int i = 0; i < 4; i++) {
226                mGLVertexPointer.getArrayElement(first + i, coord);
227                mResult[i * 2 + 0] = (int) coord[0];
228                mResult[i * 2 + 1] = (int) coord[1];
229            }
230
231            mDrawArrayCalled++;
232        }
233
234        void run() {
235            GLCanvas canvas = new GLCanvasImpl(this);
236            canvas.setSize(400, 300);
237            canvas.fillRect(2, 7, 1, 8, 0 /* color */);
238            assertTrue(mGLVertexArrayEnabled);
239            assertEquals(1, mDrawArrayCalled);
240            Log.v(TAG, "result = " + Arrays.toString(mResult));
241
242            // These are the four vertics that should be used.
243            int[] answer = new int[] {
244                2, 7,
245                3, 7,
246                3, 15,
247                2, 15};
248            int count[] = new int[4];
249
250            // Count the number of appearances for each vertex.
251            for (int i = 0; i < 4; i++) {
252                for (int j = 0; j < 4; j++) {
253                    if (answer[i * 2] == mResult[j * 2] &&
254                        answer[i * 2 + 1] == mResult[j * 2 + 1]) {
255                        count[i]++;
256                    }
257                }
258            }
259
260            // Each vertex should appear exactly once.
261            for (int i = 0; i < 4; i++) {
262                assertEquals(1, count[i]);
263            }
264        }
265    }
266
267    @SmallTest
268    public void testTransform() {
269        new TransformTest().run();
270    }
271
272    // This test assumes glLoadMatrixf is used to load the model view matrix,
273    // and glOrthof is used to load the projection matrix.
274    //
275    // The projection matrix is set to an orthogonal projection which is the
276    // inverse of viewport transform. So the model view matrix maps input
277    // directly to screen coordinates (default no scaling, and the y-axis is
278    // reversed).
279    //
280    // The matrix here are all listed in column major order.
281    //
282    private static class TransformTest extends GLMock {
283        private final float[] mModelViewMatrixUsed = new float[16];
284        private final float[] mProjectionMatrixUsed = new float[16];
285
286        @Override
287        public void glDrawArrays(int mode, int first, int count) {
288            copy(mModelViewMatrixUsed, mGLModelViewMatrix);
289            copy(mProjectionMatrixUsed, mGLProjectionMatrix);
290        }
291
292        private void copy(float[] dest, float[] src) {
293            System.arraycopy(src, 0, dest, 0, 16);
294        }
295
296        void run() {
297            GLCanvas canvas = new GLCanvasImpl(this);
298            canvas.setSize(40, 50);
299            int color = 0;
300
301            // Initial matrix
302            canvas.drawLine(0, 0, 1, 1, newColorPaint(color));
303            assertMatrixEq(new float[] {
304                    1,  0, 0, 0,
305                    0, -1, 0, 0,
306                    0,  0, 1, 0,
307                    0, 50, 0, 1
308                    }, mModelViewMatrixUsed);
309
310            assertMatrixEq(new float[] {
311                    2f / 40,       0,  0, 0,
312                          0, 2f / 50,  0, 0,
313                          0,       0, -1, 0,
314                         -1,      -1,  0, 1
315                    }, mProjectionMatrixUsed);
316
317            // Translation
318            canvas.translate(3, 4, 5);
319            canvas.drawLine(0, 0, 1, 1, newColorPaint(color));
320            assertMatrixEq(new float[] {
321                    1,  0, 0, 0,
322                    0, -1, 0, 0,
323                    0,  0, 1, 0,
324                    3, 46, 5, 1
325                    }, mModelViewMatrixUsed);
326            canvas.save();
327
328            // Scaling
329            canvas.scale(0.7f, 0.6f, 0.5f);
330            canvas.drawLine(0, 0, 1, 1, newColorPaint(color));
331            assertMatrixEq(new float[] {
332                    0.7f,     0,    0, 0,
333                    0,    -0.6f,    0, 0,
334                    0,        0, 0.5f, 0,
335                    3,       46,    5, 1
336                    }, mModelViewMatrixUsed);
337
338            // Rotation
339            canvas.rotate(90, 0, 0, 1);
340            canvas.drawLine(0, 0, 1, 1, newColorPaint(color));
341            assertMatrixEq(new float[] {
342                        0, -0.6f,    0, 0,
343                    -0.7f,     0,    0, 0,
344                        0,     0, 0.5f, 0,
345                        3,    46,    5, 1
346                    }, mModelViewMatrixUsed);
347            canvas.restore();
348
349            // After restoring to the point just after translation,
350            // do rotation again.
351            canvas.rotate(180, 1, 0, 0);
352            canvas.drawLine(0, 0, 1, 1, newColorPaint(color));
353            assertMatrixEq(new float[] {
354                    1,  0,  0, 0,
355                    0,  1,  0, 0,
356                    0,  0, -1, 0,
357                    3, 46,  5, 1
358                    }, mModelViewMatrixUsed);
359        }
360    }
361
362    @SmallTest
363    public void testGetGLInstance() {
364        GL11 glStub = new GLStub();
365        GLCanvas canvas = new GLCanvasImpl(glStub);
366        assertSame(glStub, canvas.getGLInstance());
367    }
368
369    private static void assertPremultipliedBlending(GLMock mock) {
370        assertTrue(mock.mGLBlendFuncCalled > 0);
371        assertTrue(mock.mGLBlendEnabled);
372        assertEquals(GL11.GL_ONE, mock.mGLBlendFuncSFactor);
373        assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, mock.mGLBlendFuncDFactor);
374    }
375
376    private static void assertMatrixEq(float[] expected, float[] actual) {
377        try {
378            for (int i = 0; i < 16; i++) {
379                assertFloatEq(expected[i], actual[i]);
380            }
381        } catch (Throwable t) {
382            Log.v(TAG, "expected = " + Arrays.toString(expected) +
383                    ", actual = " + Arrays.toString(actual));
384            fail();
385        }
386    }
387
388    public static void assertFloatEq(float expected, float actual) {
389        if (Math.abs(actual - expected) > 1e-6) {
390            Log.v(TAG, "expected: " + expected + ", actual: " + actual);
391            fail();
392        }
393    }
394}
395