White background

Please complete the following information:

Library Version [v2.2.2]
implementation 'com.github.skydoves:landscapist-glide:2.2.2'
implementation 'com.github.skydoves:landscapist-coil:2.2.2'
implementation 'com.github.skydoves:landscapist-placeholder:2.2.2'

Affected Device(s) [Not a device specific]

Describe the Bug:
I've tried GlideImage and CoilImage. Both show white background when image loading. It appears before loading placeholder:
error placeholder -> white background -> loading placeholder -> loaded image
There is no white background when using coil or glide compose instead
Sample code
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ImageTestTheme {
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    MainScreen()
                }
            }
        }
    }
}

@Composable
private fun MainScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        var imageRes by remember { mutableStateOf("") }
        key(imageRes) {
            CoilImage(
                modifier = Modifier
                    .height(300.dp)
                    .fillMaxWidth(),
                imageModel = remember { { imageRes } },
                imageOptions = remember {
                    ImageOptions(
                        contentScale = ContentScale.Crop,
                    )
                },
                previewPlaceholder = R.drawable.ic_empty_image,
                component = rememberImageComponent {
                    +ShimmerPlugin(
                        baseColor = Color.DarkGray,
                        highlightColor = Color.LightGray
                    )
                    +PlaceholderPlugin.Failure(painterResource(id = R.drawable.ic_broken_image))
                },
            )
        }
        Button(
            onClick = remember { { imageRes = Generator.generate() } } ) {
            Text(text = "Update")
        }
    }
}

object Generator {
    private val list = listOf(
        "https://proprikol.ru/wp-content/uploads/2019/08/kartinki-nyashnye-kotiki-16.jpg",
        "https://www.wallpaperflare.com/static/688/210/437/landscape-nature-sunset-river-wallpaper.jpg",
        "https://i.pinimg.com/736x/d1/db/93/d1db93fed23ae10972bfac57406142a9.jpg"
    )

    fun generate(): String = list.random()
}

Video sample (slowing)
https://github.com/skydoves/landscapist/assets/58473570/7dd552a1-bccd-4b4d-abb2-6183585ec2f8
Expected Behavior:
error placeholder -> loading placeholder -> loaded image

@taasonei I think this result is an expected result because Landscapist doesn't compose everything from the first, it observes the state and compose each different composables depending on its state.
Let me think if there's a great way to make the transition smooth with fade animation or something.

@taasonei I'm wondering what if you use the Crossfade over GlideImage like the sample below?
Crossfade(targetState = imageRes, label = "") { image ->
        CoilImage(
        )
..

@skydoves looks like there is no difference when I add Crossfade, white background's still visible

any update on this issue?

Hey guys, @taasonei @aznj I wonder if this issue occurs only when the error placeholder is displayed first.

A similar thing is happening for me. It's a gray box that appears every time before the loading preview appears. There's no error placeholder being shown. It's especially jarring while scrolling in a grid with multiple items because you see a lot of gray boxes really quickly and then the loading state/image.
I think this could be the same issue but our apps have different default color schemes? Not sure though

Hey @JayyyR, sorry for the delayed response. Do you use a shimmering effect or what placeholder do you use?
If would be really helpful to debug if you could share the imageComponent implementation on your project. Thanks!

I use a shimmer effect with landscapist-coil and I see this problem.
The first time that this screen is opened, there's the regular shimmer (for a few seconds) because the image is being loaded from the network.
Next time I open this screen, I see is a brief shimmer appears in the UI before disappearing. It looks like the shimmer is being applied for a very brief duration (perhaps less than 100ms), but in reality the shimmer was not actually needed (because the image was fetched from cache).
Here's the code I use (landscapist 2.2.13):
if (classPreviewState.thumbnailUrl != null) {
    CoilImage(
        imageModel = { classPreviewState.thumbnailUrl },
        imageOptions = ImageOptions(
            contentScale = ContentScale.Crop,
            alignment = Alignment.Center
        ),
        component = rememberImageComponent {
            +ShimmerPlugin(
                baseColor = colorResource(id = R.color.background_blue_light),
                highlightColor = colorResource(id = R.color.highlight_purple)
            )
        },
        modifier = Modifier.fillMaxSize(),
    )
}

@curioustechizen that sounds very fair. Thank you for the details! Let me fix this in the next stable release.

Hey @taasonei @curioustechizen @aznj @JayyyR, sorry for the delayed response.
Here's the solution that I've found the way that you can switch images without displaying the background using AnimatedContent.
  var palette by rememberPaletteState(null)
  var imageRes by remember { mutableStateOf("") }

  AnimatedContent(
    targetState = imageRes, label = "",
    transitionSpec = {
      (fadeIn(animationSpec = tween(500, delayMillis = 0)))
        .togetherWith(fadeOut(animationSpec = tween(500, delayMillis = 0)))
    }
  ) { res ->
    GlideImage(
      imageModel = { res },
      modifier = Modifier.aspectRatio(0.8f).background(Color.Red),
      component = rememberImageComponent {
        +ShimmerPlugin(
          Shimmer.Resonate(
            baseColor = if (isSystemInDarkTheme()) {
              Color.Yellow
            } else {
              Color.White
            },
            highlightColor = Color.Green,
          ),
        )
        +PlaceholderPlugin.Failure(painterResource(id = R.drawable.ic_android))
        +PalettePlugin { palette = it }
      },
      previewPlaceholder = painterResource(id = R.drawable.poster),
    )
  }

  Button(
    onClick = remember { { imageRes = Generator.generate() } } ) {
    Text(text = "Update")
  }

This is the result:

I hope this solution can resolve your issues!


WIP: Kubelet configure cbr0 instead of configure-vm.sh

We need this to get #6949 to work, now that the master assigns CIDRs to nodes.
Currently, we have:
Kubelet:

Configure-vm.sh spins until node-ip-range entry can be read from metadata.
Salt creates cbr0 w/ node-ip-range.
Kubelet starts up, asks for itself from apiserver.

NodeController:

Query GCE instances to get list of (new) nodes.
For each node:

Choose an unused CIDR for that node.
Add node to registry
Create a route for that CIDR to the node.
Set the node-ip-range value in that node's metadata to the CIDR.

If we want kubelet to reach out to the master to register (#6949), we have a bootstrapping problem:

NodeController is not relying on cloudProvider for a list of nodes, so starts with no known nodes.
Kubelet never starts because configure-vm.sh is waiting for NodeController to write node-ip-range to metadata.

What this PR would do is:
Kubelet:

Salt creates cbr0 w/ some garbage CIDR range.
Kubelet starts up, asks for itself from apiserver.
If the NodeSpec has a PodCIDR set that does not match the existing cbr0, reconfigure cbr0 and restart docker.

NodeController:

Query GCE instances to get list of (new) nodes.
For each node:

Choose an unused CIDR for that node.
Add node to registry
Create a route for that CIDR to the node.

Kubelet starts up, posts registration to apiserver.
Kubelet asks for itself from apiserver.
If the NodeSpec has a PodCIDR set that does not match the existing cbr0, reconfigure cbr0 and restart docker.

NodeController:

When a node posts its registration:

Choose an unused CIDR for that node.
Add node to registry
Create a route for that CIDR to the node.

cc @roberthbailey

All of the shelling out is pretty gross, and I should probably abstract out the restart call to docker.
I can also put this functionality behind the same "allocate-cidrs" flags as the NodeController.

cc/ @justinsb on aws side and @derekwaynecarr on vagrant side.

So we'd need to do the same thing for AWS & for vagrant?  I really like where I think this is going.  I was just today thinking a good first step towards reducing our bash/salt tangle would be to have the master able to create the minions, but assigning a CIDR to the minion was going to be particularly tricky.
So I'd love to support this for AWS.  What's the right process for doing that?  Should we try to prepare patches in advance of this landing?  Or should we let this land and then (ideally) someone assigns us each a bug to do the same?  I think that this won't actively break AWS or vagrant, it just won't work as nicely as GCE, so we don't need to do the complex coordination of trying to land the patch simultaneously on all three platforms.

I think your right about simultaneous patches not being necessary.
If this hides behind a flag, we can have AWS & Vagrant flip that flag when the support is there. Until then they'd just stay with statically configured node networks.

@justinsb I'd suggest letting this land for GCE, make sure it sticks for a day or two (e.g. we don't find weird bugs and/or have to roll it back) and then follow suit on AWS. For AWS/Vagrant it should just require a bit of salt modification to pick up the code changes once they are in.

I added a "--configure-cbr0" flag to the kubeletServer. The gce cluster scripts set that flag to the same value as the --allocate-node-cidrs flag passed to the NodeController.

Addressed comments, took off the WIP tag.

LGTM