1 /*
2  * Copyright 2024 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 androidx.graphics.shapes
18 
19 import android.graphics.Bitmap
20 import android.graphics.Canvas
21 import android.graphics.Color
22 import android.graphics.Paint
23 import android.graphics.Path
24 import androidx.core.graphics.scaleMatrix
25 import androidx.test.filters.SmallTest
26 import org.junit.Test
27 
28 @SmallTest
29 class MorphTest {
30 
31     val RADIUS = 50
32     val SCALE = RADIUS.toFloat()
33 
34     val poly1 = RoundedPolygon(3, centerX = .5f, centerY = .5f)
35     val poly2 = RoundedPolygon(4, centerX = .5f, centerY = .5f)
36     val morph11 = Morph(poly1, poly1)
37     val morph12 = Morph(poly1, poly2)
38 
39     /**
40      * Simple test to verify that a Morph with the same start and end shape has curves equivalent to
41      * those in that shape.
42      */
43     @Test
cubicsTestnull44     fun cubicsTest() {
45         val p1Cubics = poly1.cubics
46         val cubics11 = morph11.asCubics(0f)
47         assert(cubics11.size > 0)
48         // The structure of a morph and its component shapes may not match exactly, because morph
49         // calculations may optimize some of the zero-length curves out. But in general, every
50         // curve in the morph *should* exist somewhere in the shape it is based on, so we
51         // do an exhaustive search for such existence. Note that this assertion only works because
52         // we constructed the Morph from/to the same shape. A Morph between different shapes
53         // may not have the curves replicated exactly.
54         for (morphCubic in cubics11) {
55             var matched = false
56             for (p1Cubic in p1Cubics) {
57                 if (cubicsEqualish(morphCubic, p1Cubic)) {
58                     matched = true
59                     continue
60                 }
61             }
62             assert(matched)
63         }
64     }
65 
66     /**
67      * This test checks to see whether a morph between two different polygons is correct at the
68      * start (progress 0) and end (progress 1). The actual cubics of the morph vs the polygons it
69      * was constructed from may differ, due to the way the morph is constructed, but the rendering
70      * result should be the same.
71      */
72     @Test
morphDrawingTestnull73     fun morphDrawingTest() {
74         // Shapes are in canonical size of 2x2 around center (.5, .5). Translate/scale to
75         // get a larger path
76         val m = scaleMatrix(SCALE, SCALE)
77         m.postTranslate(SCALE / 2f, SCALE / 2F)
78         val poly1Path = poly1.toPath()
79         poly1Path.transform(m)
80         val poly2Path = poly2.toPath()
81         poly2Path.transform(m)
82         val morph120Path = morph12.toPath(0f)
83         morph120Path.transform(m)
84         val morph121Path = morph12.toPath(1f)
85         morph121Path.transform(m)
86 
87         val polyBitmap = Bitmap.createBitmap(RADIUS * 2, RADIUS * 2, Bitmap.Config.ARGB_8888)
88         val morphBitmap = Bitmap.createBitmap(RADIUS * 2, RADIUS * 2, Bitmap.Config.ARGB_8888)
89         val polyCanvas = Canvas(polyBitmap)
90         val morphCanvas = Canvas(morphBitmap)
91 
92         // Check that the morph at progress 0 is equivalent to poly1
93         drawTestPath(polyCanvas, poly1Path)
94         drawTestPath(morphCanvas, morph120Path)
95         assertBitmapsEqual(polyBitmap, morphBitmap)
96 
97         // Check that the morph at progress 1 is equivalent to poly2
98         drawTestPath(polyCanvas, poly2Path)
99         drawTestPath(morphCanvas, morph121Path)
100         assertBitmapsEqual(polyBitmap, morphBitmap)
101     }
102 
103     /** Utility function - Fill the canvas with black and draw the path in white. */
drawTestPathnull104     private fun drawTestPath(canvas: Canvas, path: Path) {
105         canvas.drawColor(Color.BLACK)
106         val paint = Paint()
107         paint.isAntiAlias = false
108         paint.style = Paint.Style.FILL
109         paint.color = Color.WHITE
110         canvas.drawPath(path, paint)
111     }
112 }
113