@@ -4,6 +4,9 @@ import io.github.composegears.valkyrie.sdk.ir.core.IrColor
44import io.github.composegears.valkyrie.sdk.ir.core.IrFill
55import io.github.composegears.valkyrie.sdk.ir.core.IrImageVector
66import io.github.composegears.valkyrie.sdk.ir.core.IrPathFillType
7+ import io.github.composegears.valkyrie.sdk.ir.core.IrPathNode.Close
8+ import io.github.composegears.valkyrie.sdk.ir.core.IrPathNode.LineTo
9+ import io.github.composegears.valkyrie.sdk.ir.core.IrPathNode.MoveTo
710import io.github.composegears.valkyrie.sdk.ir.core.IrStroke
811import io.github.composegears.valkyrie.sdk.ir.core.IrStrokeLineCap
912import io.github.composegears.valkyrie.sdk.ir.core.IrStrokeLineJoin
@@ -167,6 +170,292 @@ class XmlToImageVectorParserTest {
167170 }
168171 }
169172
173+ @Test
174+ fun parse_path_with_linear_gradient () {
175+ val vector = vectorWithGradient(
176+ """
177+ <path android:pathData="M0,0 L10,0 L10,10 L0,10 Z">
178+ <aapt:attr name="android:fillColor">
179+ <gradient
180+ android:type="linear"
181+ android:startX="0"
182+ android:startY="0"
183+ android:endX="10"
184+ android:endY="10"
185+ android:startColor="#FF0000"
186+ android:endColor="#0000FF"/>
187+ </aapt:attr>
188+ </path>
189+ """ .trimIndent(),
190+ )
191+
192+ assertEquals(
193+ expected = imageVector(
194+ nodes = listOf (
195+ IrVectorNode .IrPath (
196+ pathFillType = IrPathFillType .NonZero ,
197+ fill = IrFill .LinearGradient (
198+ startX = 0f ,
199+ startY = 0f ,
200+ endX = 10f ,
201+ endY = 10f ,
202+ colorStops = mutableListOf (
203+ IrFill .ColorStop (0f , IrColor (0xFFFF0000 )),
204+ IrFill .ColorStop (1f , IrColor (0xFF0000FF )),
205+ ),
206+ ),
207+ fillAlpha = 1f ,
208+ paths = listOf (MoveTo (x= 0f , y= 0f ), LineTo (x= 10f , y= 0f ), LineTo (x= 10f , y= 10f ), LineTo (x= 0f , y= 10f ), Close ),
209+ ),
210+ ),
211+ ),
212+ actual = XmlToImageVectorParser .parse(vector),
213+ )
214+ }
215+
216+ @Test
217+ fun parse_path_with_radial_gradient () {
218+ val vector = vectorWithGradient(
219+ """
220+ <path android:pathData="M0,0 L10,0 L10,10 L0,10 Z">
221+ <aapt:attr name="android:fillColor">
222+ <gradient
223+ android:type="radial"
224+ android:centerX="5"
225+ android:centerY="5"
226+ android:gradientRadius="7.5"
227+ android:startColor="#00FF00"
228+ android:endColor="#FFFF00"/>
229+ </aapt:attr>
230+ </path>
231+ """ .trimIndent(),
232+ )
233+
234+ assertEquals(
235+ expected = imageVector(
236+ nodes = listOf (
237+ IrVectorNode .IrPath (
238+ pathFillType = IrPathFillType .NonZero ,
239+ fill = IrFill .RadialGradient (
240+ centerX = 5f ,
241+ centerY = 5f ,
242+ radius = 7.5f ,
243+ colorStops = mutableListOf (
244+ IrFill .ColorStop (0f , IrColor (0xFF00FF00 )),
245+ IrFill .ColorStop (1f , IrColor (0xFFFFFF00 )),
246+ ),
247+ ),
248+ fillAlpha = 1f ,
249+ paths = listOf (
250+ MoveTo (x = 0f , y = 0f ),
251+ LineTo (x = 10f , y = 0f ),
252+ LineTo (x = 10f , y = 10f ),
253+ LineTo (x = 0f , y = 10f ),
254+ Close
255+ ),
256+ ),
257+ ),
258+ ),
259+ actual = XmlToImageVectorParser .parse(vector),
260+ )
261+ }
262+
263+ @Test
264+ fun parse_path_with_linear_gradient_and_color_stops () {
265+ val vector = vectorWithGradient(
266+ """
267+ <path android:pathData="M0,0 L10,0 L10,10 L0,10 Z">
268+ <aapt:attr name="android:fillColor">
269+ <gradient
270+ android:type="linear"
271+ android:startX="0"
272+ android:startY="0"
273+ android:endX="10"
274+ android:endY="0">
275+ <item android:color="#FF0000" android:offset="0"/>
276+ <item android:color="#00FF00" android:offset="0.5"/>
277+ <item android:color="#0000FF" android:offset="1"/>
278+ </gradient>
279+ </aapt:attr>
280+ </path>
281+ """ .trimIndent(),
282+ )
283+
284+ assertEquals(
285+ expected = imageVector(
286+ nodes = listOf (
287+ IrVectorNode .IrPath (
288+ pathFillType = IrPathFillType .NonZero ,
289+ fill = IrFill .LinearGradient (
290+ startX = 0f ,
291+ startY = 0f ,
292+ endX = 10f ,
293+ endY = 0f ,
294+ colorStops = mutableListOf (
295+ IrFill .ColorStop (0f , IrColor (0xFFFF0000 )),
296+ IrFill .ColorStop (0.5f , IrColor (0xFF00FF00 )),
297+ IrFill .ColorStop (1f , IrColor (0xFF0000FF )),
298+ ),
299+ ),
300+ fillAlpha = 1f ,
301+ paths = listOf (MoveTo (x= 0f , y= 0f ), LineTo (x= 10f , y= 0f ), LineTo (x= 10f , y= 10f ), LineTo (x= 0f , y= 10f ), Close ),
302+ ),
303+ ),
304+ ),
305+ actual = XmlToImageVectorParser .parse(vector),
306+ )
307+ }
308+
309+ @Test
310+ fun parse_path_with_radial_gradient_and_color_stops () {
311+ val vector = vectorWithGradient(
312+ """
313+ <path android:pathData="M0,0 L10,0 L10,10 L0,10 Z">
314+ <aapt:attr name="android:fillColor">
315+ <gradient
316+ android:type="radial"
317+ android:centerX="5"
318+ android:centerY="5"
319+ android:gradientRadius="10">
320+ <item android:color="#FFFFFF" android:offset="0"/>
321+ <item android:color="#888888" android:offset="0.7"/>
322+ <item android:color="#000000" android:offset="1"/>
323+ </gradient>
324+ </aapt:attr>
325+ </path>
326+ """ .trimIndent(),
327+ )
328+
329+ assertEquals(
330+ expected = imageVector(
331+ nodes = listOf (
332+ IrVectorNode .IrPath (
333+ pathFillType = IrPathFillType .NonZero ,
334+ fill = IrFill .RadialGradient (
335+ centerX = 5f ,
336+ centerY = 5f ,
337+ radius = 10f ,
338+ colorStops = mutableListOf (
339+ IrFill .ColorStop (0f , IrColor (0xFFFFFFFF )),
340+ IrFill .ColorStop (0.7f , IrColor (0xFF888888 )),
341+ IrFill .ColorStop (1f , IrColor (0xFF000000 )),
342+ ),
343+ ),
344+ fillAlpha = 1f ,
345+ paths = listOf (MoveTo (x= 0f , y= 0f ), LineTo (x= 10f , y= 0f ), LineTo (x= 10f , y= 10f ), LineTo (x= 0f , y= 10f ), Close ),
346+ ),
347+ ),
348+ ),
349+ actual = XmlToImageVectorParser .parse(vector),
350+ )
351+ }
352+
353+ @Test
354+ fun parse_multiple_paths_with_gradients () {
355+ val vector = vectorWithGradient(
356+ """
357+ <path android:pathData="M0,0 L5,0 L5,5 L0,5 Z">
358+ <aapt:attr name="android:fillColor">
359+ <gradient
360+ android:type="linear"
361+ android:startX="0"
362+ android:startY="0"
363+ android:endX="5"
364+ android:endY="0"
365+ android:startColor="#FF0000"
366+ android:endColor="#00FF00"/>
367+ </aapt:attr>
368+ </path>
369+ <path android:pathData="M6,0 L10,0 L10,5 L6,5 Z">
370+ <aapt:attr name="android:fillColor">
371+ <gradient
372+ android:type="radial"
373+ android:centerX="8"
374+ android:centerY="2.5"
375+ android:gradientRadius="2.5"
376+ android:startColor="#0000FF"
377+ android:endColor="#FFFF00"/>
378+ </aapt:attr>
379+ </path>
380+ """ .trimIndent(),
381+ )
382+ val result = XmlToImageVectorParser .parse(vector)
383+
384+ assertEquals(2 , result.nodes.size)
385+ val firstPath = result.nodes[0 ] as IrVectorNode .IrPath
386+ val secondPath = result.nodes[1 ] as IrVectorNode .IrPath
387+
388+ assertEquals(
389+ expected = IrFill .LinearGradient (
390+ startX = 0f ,
391+ startY = 0f ,
392+ endX = 5f ,
393+ endY = 0f ,
394+ colorStops = mutableListOf (
395+ IrFill .ColorStop (0f , IrColor (0xFFFF0000 )),
396+ IrFill .ColorStop (1f , IrColor (0xFF00FF00 )),
397+ ),
398+ ),
399+ actual = firstPath.fill,
400+ )
401+
402+ assertEquals(
403+ expected = IrFill .RadialGradient (
404+ centerX = 8f ,
405+ centerY = 2.5f ,
406+ radius = 2.5f ,
407+ colorStops = mutableListOf (
408+ IrFill .ColorStop (0f , IrColor (0xFF0000FF )),
409+ IrFill .ColorStop (1f , IrColor (0xFFFFFF00 )),
410+ ),
411+ ),
412+ actual = secondPath.fill,
413+ )
414+ }
415+
416+ @Test
417+ fun parse_path_with_gradient_in_group () {
418+ val vector = vectorWithGradient(
419+ """
420+ <group android:name="gradientGroup">
421+ <path android:pathData="M0,0 L10,0 L10,10 L0,10 Z">
422+ <aapt:attr name="android:fillColor">
423+ <gradient
424+ android:type="linear"
425+ android:startX="0"
426+ android:startY="0"
427+ android:endX="10"
428+ android:endY="10"
429+ android:startColor="#FFFFFF"
430+ android:endColor="#000000"/>
431+ </aapt:attr>
432+ </path>
433+ </group>
434+ """ .trimIndent(),
435+ )
436+ val result = XmlToImageVectorParser .parse(vector)
437+
438+ assertEquals(1 , result.nodes.size)
439+ val group = result.nodes[0 ] as IrVectorNode .IrGroup
440+ assertEquals(" gradientGroup" , group.name)
441+ assertEquals(1 , group.nodes.size)
442+
443+ val path = group.nodes[0 ] as IrVectorNode .IrPath
444+ assertEquals(
445+ expected = IrFill .LinearGradient (
446+ startX = 0f ,
447+ startY = 0f ,
448+ endX = 10f ,
449+ endY = 10f ,
450+ colorStops = mutableListOf (
451+ IrFill .ColorStop (0f , IrColor (0xFFFFFFFF )),
452+ IrFill .ColorStop (1f , IrColor (0xFF000000 )),
453+ ),
454+ ),
455+ actual = path.fill,
456+ )
457+ }
458+
170459 private fun vector (
171460 content : String ,
172461 width : String = "24dp",
@@ -185,6 +474,25 @@ class XmlToImageVectorParserTest {
185474 appendLine(" </vector>" )
186475 }
187476
477+ private fun vectorWithGradient (
478+ content : String ,
479+ width : String = "24dp",
480+ height : String = "24dp",
481+ ): String = buildString {
482+ appendLine(
483+ """
484+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
485+ xmlns:aapt="http://schemas.android.com/aapt"
486+ android:width="$width "
487+ android:height="$height "
488+ android:viewportWidth="24"
489+ android:viewportHeight="24">
490+ """ .trimIndent(),
491+ )
492+ appendLine(content)
493+ appendLine(" </vector>" )
494+ }
495+
188496 private fun imageVector (nodes : List <IrVectorNode > = emptyList()): IrImageVector = IrImageVector (
189497 defaultWidth = 24f ,
190498 defaultHeight = 24f ,
0 commit comments