The Mystery of the Disappearing Holes — a gripping tale of using SVG in Android
“Dude, where is the hole?” the designer asked me. She was checking our Android app.
“What hole?” I said.
“There should be a hole at the top of this icon.” She poked her finger at an icon in the app.
“You probably forgot to put the hole on the icon,” I said.
She showed me the icon in her Macbook’s local folder, and there was a hole at the top of the icon.
“Okay. Then perhaps you sent me a wrong icon,” I said, without giving up.
She opened her sent emails and showed the email she had sent, and that icon had the hole, too. She looked at me, with one of her eyebrows raised, and a smirk in her face.
“I will look into this issue,” I mumbled, and left the place confused.
I had no idea how the hole got disappeared only in the app. A new mystery to solve, I thought to myself.
Chapter 1: Finding the Traces
I opened that SVG icon using Text Editor and found a lot of numbers with a few alphabets thrown here and there. What the #%@& is this, I thought and closed the editor. I read the VectorDrawable documentation again and then read a few articles about how to use it.
Then I thought Android Studio would have messed up the icon while importing. Damn you, Android Studio. I used an online tool to convert the SVG to VectorDrawable. There I got a nice warning: “found attribute ‘fill-rule:evenodd’ which is supported only on Android API 24 and higher”.
I remembered reading something about this “evenodd” in the documentation. Then I googled it and found an issue had been filed in AOSP, that VectorDrawable wasn’t supporting evenodd, and the holes were getting disappeared from the icons. Ah, the same issue, I thought. But there wasn’t any direct solution mentioned there, it simply said to use “nonzero” instead of “evenodd”, and I hadn’t the slightest idea about either of those.
Chapter 2: Technical Details
After a bit of research on Raster graphics, Vector graphics, SVGs, the “evenodd” vs “nonzero” fill-rule property, I made a note about those properties:
The fill-rule property:
This property indicates what algorithm to use to determine what parts of the canvas are included inside more complex shapes. i.e. what algorithm to use to determine whether to fill the shape with the color or not.
To find whether a point in the canvas will be filled or not, draw an imaginary line from that point through the entire shape in any direction. That imaginary line would intersect the path segments of the shape. Count the number of times that imaginary line intersects with the path segments. If the number is even, the point is outside and so that area should not be filled. If the number is odd, the point is inside the shape and so that area is filled with color.
Here again, draw an imaginary line from the point through the shape in any direction. Start with a count of “zero”. Add one to the count each time a path segment crosses the imaginary line from left to right (clockwise), subtract one from the count, each time a path segment crosses from right to left (counter clockwise). So whether a path is drawn clockwise or counter clockwise will have an impact in the final count. Now, if the total count is zero, then the point is outside the shape and it is not filled. If we are left with a count other than zero, the point is inside shape and is filled with color.
Chapter 3: Finding the Culprit
Now I sensed that this “fill-rule” property might be the reason for the disappearing hole.
After reading some more articles about it, I confirmed the issue. The icon generated from Sketch uses the fill-rule “evenodd” property. But Android uses the property “nonzero” by default.
So I thought it should be easy just to change that default property in Android studio to use “evenodd” instead of “nonzero”. But Android did not support the fill-rule “evenodd” prior to API 24 (Nougat). Now the icon must be generated using “nonzero” fill-rule instead of “evenodd”.
Chapter 4: Solving the Mystery
I opened the SVG icon in Sketch and inspected the hole at the top of the icon. As expected it uses fill-rule:evenodd property. Now I had to change the fill-rule to use “nonzero” property. How?
Select the path. On the right side, there is a settings icon at the “Fills” property. Click it and choose “non-zero”.
Now the hole disappeared from the Sketch itself. Crap.
Then I remembered that in “nonzero” property, the direction of the path determines whether to fill the shape or not. So I knew I had to reverse the path.
From the main menu, choose Layer → Paths → Reverse Order.
I got the hole back at the top of the icon in Sketch, and I got the hole in the app, too.
TL;DR: SVG icons should use fill-rule:nonzero instead of Sketch default fill-rule:evenodd property to support Android VectorDrawable (as of now)
Sources and Further Readings: