1 /*
2  * Copyright 2020 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.compose.ui.graphics.drawscope
18 
19 import androidx.compose.ui.geometry.Offset
20 import androidx.compose.ui.geometry.Size
21 import androidx.compose.ui.graphics.ClipOp
22 import androidx.compose.ui.graphics.Matrix
23 import androidx.compose.ui.graphics.Path
24 import androidx.compose.ui.graphics.degrees
25 import androidx.compose.ui.graphics.internal.JvmDefaultWithCompatibility
26 
27 /**
28  * Convenience method modifies the [DrawTransform] bounds to inset both left and right bounds by
29  * [horizontal] as well as the top and bottom by [vertical]. After this method is invoked, the
30  * coordinate space is returned to the state before the inset was applied
31  *
32  * @param horizontal number of pixels to inset both left and right bounds. Zero by default.
33  * @param vertical number of pixels to inset both top and bottom bounds. Zero by default.
34  */
35 @Suppress("NOTHING_TO_INLINE")
insetnull36 inline fun DrawTransform.inset(horizontal: Float = 0.0f, vertical: Float = 0.0f) =
37     inset(horizontal, vertical, horizontal, vertical)
38 
39 /**
40  * Convenience method modifies the [DrawScope] bounds to inset both left, top, right and bottom
41  * bounds by [inset]. After this method is invoked, the coordinate space is returned to the state
42  * before this inset was applied.
43  *
44  * @param inset number of pixels to inset left, top, right, and bottom bounds.
45  */
46 @Suppress("NOTHING_TO_INLINE") inline fun DrawTransform.inset(inset: Float) = inset(inset, inset)
47 
48 /**
49  * Add a rotation (in radians clockwise) to the current transform at the given pivot point. The
50  * pivot coordinate remains unchanged by the rotation transformation
51  *
52  * @param radians to rotate clockwise
53  * @param pivot The coordinate for the pivot point, defaults to the center of the coordinate space
54  */
55 @Suppress("NOTHING_TO_INLINE")
56 inline fun DrawTransform.rotateRad(radians: Float, pivot: Offset = center) =
57     rotate(degrees(radians), pivot)
58 
59 /**
60  * Add an axis-aligned scale to the current transform, scaling uniformly in both directions by the
61  * provided scale factor at the pivot coordinate. The pivot coordinate remains unchanged by the
62  * scale transformation.
63  *
64  * @param scale The amount to scale
65  * @param pivot The coordinate for the pivot point, defaults to the center of the coordinate space
66  */
67 @Suppress("NOTHING_TO_INLINE")
68 inline fun DrawTransform.scale(scale: Float, pivot: Offset = center) = scale(scale, scale, pivot)
69 
70 /** Defines transformations that can be applied to a drawing environment */
71 @DrawScopeMarker
72 @JvmDefaultWithCompatibility
73 interface DrawTransform {
74 
75     /** Get the current size of the CanvasTransform */
76     val size: Size
77 
78     /** Convenience method to obtain the current position of the current transformation */
79     val center: Offset
80         get() = Offset(size.width / 2, size.height / 2)
81 
82     /**
83      * Simultaneously translate the coordinate space by [left] and [top] as well as modify the
84      * dimensions of the current painting area. This provides a callback to issue more drawing
85      * instructions within the modified coordinate space. This method modifies the width to be
86      * equivalent to width - (left + right) as well as height to height - (top + bottom)
87      *
88      * @param left number of pixels to inset the left drawing bound
89      * @param top number of pixels to inset the top drawing bound
90      * @param right number of pixels to inset the right drawing bound
91      * @param bottom number of pixels to inset the bottom drawing bound
92      */
93     fun inset(left: Float, top: Float, right: Float, bottom: Float)
94 
95     /**
96      * Reduces the clip region to the intersection of the current clip and the given rectangle
97      * indicated by the given left, top, right and bottom bounds. After this method is invoked, this
98      * clip is no longer applied.
99      *
100      * Use [ClipOp.Difference] to subtract the provided rectangle from the current clip.
101      *
102      * @param left Left bound of the rectangle to clip
103      * @param top Top bound of the rectangle to clip
104      * @param right Right bound ofthe rectangle to clip
105      * @param bottom Bottom bound of the rectangle to clip
106      * @param clipOp Clipping operation to perform on the given bounds
107      */
108     fun clipRect(
109         left: Float = 0.0f,
110         top: Float = 0.0f,
111         right: Float = size.width,
112         bottom: Float = size.height,
113         clipOp: ClipOp = ClipOp.Intersect
114     )
115 
116     /**
117      * Reduces the clip region to the intersection of the current clip and the given rounded
118      * rectangle. After this method is invoked, this clip is no longer applied
119      *
120      * @param path Shape to clip drawing content within
121      * @param clipOp Clipping operation to conduct on the given bounds, defaults to
122      *   [ClipOp.Intersect]
123      */
124     fun clipPath(path: Path, clipOp: ClipOp = ClipOp.Intersect)
125 
126     /**
127      * Translate the coordinate space by the given delta in pixels in both the x and y coordinates
128      * respectively
129      *
130      * @param left Pixels to translate the coordinate space in the x-axis
131      * @param top Pixels to translate the coordinate space in the y-axis
132      */
133     fun translate(left: Float = 0.0f, top: Float = 0.0f)
134 
135     /**
136      * Add a rotation (in degrees clockwise) to the current transform at the given pivot point. The
137      * pivot coordinate remains unchanged by the rotation transformation.
138      *
139      * @param degrees to rotate clockwise
140      * @param pivot The coordinates for the pivot point, defaults to the center of the coordinate
141      *   space
142      */
143     fun rotate(degrees: Float, pivot: Offset = center)
144 
145     /**
146      * Add an axis-aligned scale to the current transform, scaling by the first argument in the
147      * horizontal direction and the second in the vertical direction at the given pivot coordinate.
148      * The pivot coordinate remains unchanged by the scale transformation.
149      *
150      * @param scaleX The amount to scale in X
151      * @param scaleY The amount to scale in Y
152      * @param pivot The coordinate for the pivot point, defaults to the center of the coordinate
153      *   space
154      */
155     fun scale(scaleX: Float, scaleY: Float, pivot: Offset = center)
156 
157     /**
158      * Transform the drawing environment by the given matrix
159      *
160      * @param matrix transformation matrix used to transform the drawing environment
161      */
162     fun transform(matrix: Matrix)
163 }
164