I'm drawing a progress bar with a foreground track...
# compose
c
I'm drawing a progress bar with a foreground track and background track, but it looks grainy on the edges. Anti aliasing? My code is fairly simple to start off with. What's the best solution in this scenario?
Copy code
drawArc(
  brush = brushBackground,
  startAngle = 90f,
  sweepAngle = PROGRESS_FULL_DEGREES ...
)

drawArc(
  brush = brush,
  startAngle = 270f,
  sweepAngle = progressDegrees ...
)
I've tried wrapping the draw arcs with a
drawIntoCanvas
and blendMode BloendMode.SrcOver, but it still had some aliasing going on. As well as tried a few different combos of "withSaveLayer" and graphicsLayer
r
SrcOver is the default
Can you show screenshots?
e
what if you tried drawing the arcs without overlap?
Copy code
drawArc(
  brush = brushBackground,
  startAngle = progressDegrees - 90f,
  sweepAngle = PROGRESS_FULL_DEGREES - progressDegrees
)

drawArc(
  brush = brush,
  startAngle = -90f,
  sweepAngle = progressDegrees
)
r
Note that you don't need to draw two arcs either. You can draw a single one with a sweep gradient (if they have the same stroke width)
πŸ‘€ 1
c
this is what I've got currently. So basically a count down timer. with text in the middle. with a progress track (gray) and the main progress bar indicator (yellow)
e
if they have different stroke width then you would want to draw them overlapping anyhow πŸ™‚
c
full Code
Copy code
val drawStyle = remember { Stroke(width = 6.dp.value, cap = StrokeCap.Round) }
    val brush = remember {
        SolidColor(progressColor)
    }

    val brushBackground = remember { SolidColor(backgroundColor) }
    val animateCurrentProgress = animateFloatAsState(
        targetValue = progress,
        animationSpec = tween(durationMillis = 100, easing = LinearEasing)
    )

    val progressDegrees = animateCurrentProgress.value * PROGRESS_FULL_DEGREES

    Box(
        modifier = modifier
            .size(150.dp)
            .padding(16.dp)
            .drawBehind {
                val strokeWidth = drawStyle.width
                val radius = size.minDimension / 2
                val arcBounds = size.toRect().deflate(strokeWidth / 2)

                drawArc(
                    brush = brushBackground,
                    startAngle = 90f,
                    sweepAngle = PROGRESS_FULL_DEGREES,
                    useCenter = false,
                    topLeft = arcBounds.topLeft,
                    size = arcBounds.size,
                    style = drawStyle
                )

                drawArc(
                    brush = brush,
                    startAngle = -90f,
                    sweepAngle = -progressDegrees,
                    useCenter = false,
                    topLeft = arcBounds.topLeft,
                    size = arcBounds.size,
                    style = drawStyle
                )

                // Draw thumb at the end of arc
                val angleRad = Math.toRadians((-90f - progressDegrees).toDouble())
                val center = size.center
                val thumbX = center.x + radius * kotlin.math.cos(angleRad).toFloat()
                val thumbY = center.y + radius * kotlin.math.sin(angleRad).toFloat()

                drawCircle(
                    color = progressColor,
                    radius = strokeWidth * 2,
                    center = androidx.compose.ui.geometry.Offset(thumbX, thumbY)
                )
            }
    ) {
        Box(modifier = Modifier.align(Alignment.Center)) {
            content()
        }
    }
r
Yeah ok it's not an AA problem really. It's because you overlap the two arcs like @ephemient said. So either draw two connected arcs or use a single one with a sweep gradient
πŸ‘€ 1
c
if this isn't AA, what is this problem called?
e
two separate arcs with the circle end caps hiding the join would work, or as a single sweep gradient plus a couple additional circles on the ends to keep the same look as what you have now
c
Okay. let me try the two separate arc approach (since that's what I have) and then if that works I can try the "simpler" version with a single sweep gradient.
r
@Colton Idle well it's AA working as it should. But since you overlap the two arcs the AA pixels blend into each other. The top one doesn't replace the bottom one
c
okay. that makes sense. the solution from @ephemient is to use two separate arcs. but aren't I using two separate arcs? lol (sorry still noob at drawing)
e
two non-overlapping arcs
☝️ 1
if the end caps overlap it's fine, you won't really see it
c
but don't I want them to be overlapping? like one on top of the other is what my design basically shows
r
It will look like they are overlapping πŸ™‚
πŸ‘€ 1
Graphics programming is all about cheating πŸ˜›
c
yeah. i guess i dont understand the cheat here πŸ™ˆ
e
as a linear progress bar rather than a circular one just for the sake of illustration,
Copy code
+-------------+     +----+     +****+--------+
|             |  +  |    |  =  |    |        |
+-------------+     +----+     +****+--------+
Copy code
+--------+     +----+     +----+--------+
     |        |  +  |    |  =  |    |        |
     +--------+     +----+     +----+--------+
πŸ‘€ 1
c
ah. okay. took me a minute. so only ever draw one color in a pixel. so eerily close to romains solution of a single with a sweep gradient.
r
yeah it's the same solution either way
c
so my draw arc code itself isn't changing, its the math inside of each draw arc. okay cool. makes sense phew!
r
except with the two arcs you can overlap the slightly for the rounded endcap
c
gotcha. so with a single sweep gradient i wouldn't actually overlap at all for that single "thumb" piece/single endcap circle thingy
e
if you want the circle you'll have to add it
r
you would draw a circle as a second step
c
cool. thats what im doing now. i have a separate drawCircle call
hopefully when the yellow thumb overlaps the gray/yellow track it won't look bad either.
e
StrokeCap.Round is easy for the small end, but the thumb is a different size so has to be a different circle
☝️ 1
r
you don't need a draw circle if you use arcs, you can use a stroke cap instead
c
yeah. i basically want no stroke cap on one end. and just the thumb that likes 2x the stroke width on the other end. so. i'll keep it a separate drawCircle call and hopefully it doesn't look jagged.
e
so only ever draw one color in a pixel
btw I wouldn't say that. it's fine to draw a pixel more than once, but for any shapes that aren't perfectly pixel-aligned, try to either keep at least 1px apart or have at least 1px of overlap, to avoid the edges blending unexpectedly
πŸ‘ 1
c
cool. FWIW I had to ship the project with this already but I plan on coming around to it again on a weekend to see what i can come up with. I'm assuming there might also be some open source projects that do this that i can look at. seems like the "timer" thing is a pretty popular pattern from what ive seen