Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crossFade messes up aspect ratio for thumbnail/placeholder #363

Open
TWiStErRob opened this issue Mar 6, 2015 · 36 comments
Open

crossFade messes up aspect ratio for thumbnail/placeholder #363

TWiStErRob opened this issue Mar 6, 2015 · 36 comments

Comments

@TWiStErRob
Copy link
Collaborator

Glide Version/Integration library (if any): 3.5.2
Device/Android Version: S4/4.4
Issue details/Repro steps: Load a different sized thumbnail and image, or placeholder and image and note the result:
demo

Glide load line:

class Delay extends UnitTransformation {
    private final int sleepTime;
    public Delay(int sleepTime) { this.sleepTime = sleepTime; }
    @Override public Resource transform(Resource resource, int outWidth, int outHeight) {
        try { Thread.sleep(sleepTime); } catch (InterruptedException ex) {}
        return super.transform(resource, outWidth, outHeight);
    }
}

@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final ImageView image = new ImageView(this);
    setContentView(image);

    // Saved as R.drawable.placeholder
    String placeholder = "http://placehold.it/300x500/eedddd.png&text=PLACEHOLDER";
    // Square image
    String thumb = "http://placehold.it/300x300/eeeedd.png&text=THUMB";
    // Wide image
    final String full = "http://placehold.it/300x100/eedddd.png&text=FULL";
    // Prepared request with unimportant clutter
    final DrawableRequestBuilder<String> req = Glide
            .with(this)
            .fromString()
            .diskCacheStrategy(DiskCacheStrategy.SOURCE) // disable network delay for demo
            .skipMemoryCache(true) // make sure transform runs for demo
            .crossFade(2000) // default, just stretch time for noticability
    ;

    req.clone()
        .thumbnail(req.clone()
                    .transform(new Delay(500)) // wait a little
                    .load(thumb)
        )
        .transform(new Delay(1000)) // wait before going from thumbnail to image
        //.dontAnimate() // solves the problem
        .load(full)
        .into(image);

    image.setOnClickListener(new OnClickListener() {
        @Override public void onClick(View v) {
            req.clone()
                .load(full)
                .placeholder(R.drawable.placeholder)
                .transform(new Delay(1000))
                //.animate(R.anim.abc_fade_in) // also solves the problem
                .into(image);
        }
    });

By removing all the delaying parts, the end result is better, but the now the thumbnail is messed up:
demo3

@sjudd
Copy link
Collaborator

sjudd commented Mar 6, 2015

Can you try overriding the Target and avoiding the SquaringDrawable?

@TWiStErRob
Copy link
Collaborator Author

Based on ImageViewFactory and the "dirty hack" in GlideDrawableImageViewTarget.onResourceReady I changed into(image) to .into((Target<GlideDrawable>)(Target)new DrawableImageViewTarget(image)), and confirmed in debug that the target receives a GlideBitmapDrawable, but after that I wanted to check if it was SquaringDrawable before: however the drawableRatio for thumb is 1, the view size is the whole activity, so that ratio is ~16:9 so the viewRatio doesn't fall into SQUARE_RATIO_MARGIN; so it seems there's no SquaringDrawable involved.

Is this what you meant?

@sjudd
Copy link
Collaborator

sjudd commented Mar 7, 2015

Yup that makes sense.

Unfortunately if SquaringDrawable isn't involved, then this is an issue with TransitionDrawable which is owned by the framework. I'd love to write our own cross fading Drawable because TransitionDrawable seems to have a number of issues, and this just reinforces that thought. However I can't say it's super likely to happen any time soon if left to just me...

@Tolriq
Copy link
Contributor

Tolriq commented Apr 15, 2015

Just faced this with a simple request with a place holder and crossfade.

Changing crossfade to animation solved this, so really sounds like a TransitionDrawable problem.

But maybe there should be some kind of notice somewhere because it took me some time to find this issue :(

@Tolriq
Copy link
Contributor

Tolriq commented Apr 15, 2015

@sjudd : Update :( It seems there's a crossfade by default applied so in fact this "problem" is quite important.

@sjudd
Copy link
Collaborator

sjudd commented Jun 13, 2015

Yeah it is applied by default... Again a pull request would be welcome here if anyone wants to try to take on writing a better TransitionDrawable specifically for cross fades.

@nathanielwolf
Copy link

I'm surprised there is no resolution for this issue yet. We've had to disable all crossfades because of this since we use a square placeholder and load images with various aspect ratios. We are using a fade in of the image, but this is not as good as a crossfade since the placeholder just disappears . Anyone else have a better work around?

@TWiStErRob
Copy link
Collaborator Author

Long shot: since you have square placeholder I assume the space for the images isn't much wider, how about creating Drawable with the space's aspect (pad the square) and/or add a transformation for the main image that adds tranparent padding to the loaded Bitmap. This would result in the layers of TransitionDrawable having the same intrinsic size aspect, and hence working as intended.

Alternatively, but in a similar fashion: placeholder with same aspect as available space and centerCrop so the loaded Bitmap has exactly the space's size (and aspect).

Sadly both of these are hacky workarounds, if you feel like it, you could fix Android's TransitionDrawable to handle different aspects.

@kevinvanmierlo
Copy link

@sjudd @TWiStErRob
I know the code for a new TransitionDrawable which keeps the aspect ratio. I just don't know where to implement it. Can someone else help me where to implement this or file a pull request themselves using this code:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.SystemClock;
import android.widget.ImageView;

public final class GlideTransitionDrawable extends BitmapDrawable
{
    // Only accessed from main thread.
    private static final float FADE_DURATION = 800f; //ms

    /**
     * Create or update the drawable on the target {@link ImageView} to display the supplied bitmap
     * image.
     */
    public static void setBitmap(ImageView target, Context context, Bitmap bitmap)
    {
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof AnimationDrawable)
        {
            ((AnimationDrawable) placeholder).stop();
        }
        GlideTransitionDrawable drawable = new GlideTransitionDrawable(context, bitmap, placeholder);
        target.setImageDrawable(drawable);
    }

    /**
     * Create or update the drawable on the target {@link ImageView} to display the supplied
     * placeholder image.
     */
    public static void setPlaceholder(ImageView target, Drawable placeholderDrawable)
    {
        target.setImageDrawable(placeholderDrawable);
        if (target.getDrawable() instanceof AnimationDrawable)
        {
            ((AnimationDrawable) target.getDrawable()).start();
        }
    }

    Drawable placeholder;

    long startTimeMillis;
    boolean animating;
    int alpha = 0xFF;

    GlideTransitionDrawable(Context context, Bitmap bitmap, Drawable placeholder)
    {
        super(context.getResources(), bitmap);

        this.placeholder = placeholder;
        animating = true;
        startTimeMillis = SystemClock.uptimeMillis();
    }

    @Override
    public void draw(Canvas canvas)
    {
        if (!animating)
        {
            super.draw(canvas);
        } else
        {
            float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION;
            if (normalized >= 1f)
            {
                animating = false;
                placeholder = null;
                super.draw(canvas);
            } else
            {
                if (placeholder != null)
                {
                    placeholder.draw(canvas);
                }

                int partialAlpha = (int) (alpha * normalized);
                super.setAlpha(partialAlpha);
                super.draw(canvas);
                super.setAlpha(alpha);
                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
                {
                    invalidateSelf();
                }
            }
        }
    }

    @Override
    public void setAlpha(int alpha)
    {
        this.alpha = alpha;
        if (placeholder != null)
        {
            placeholder.setAlpha(alpha);
        }
        super.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf)
    {
        if (placeholder != null)
        {
            placeholder.setColorFilter(cf);
        }
        super.setColorFilter(cf);
    }

    @Override
    protected void onBoundsChange(Rect bounds)
    {
        if (placeholder != null)
        {
            placeholder.setBounds(bounds);
        }
        super.onBoundsChange(bounds);
    }
}

I use this with a custom call at the moment, not ideal but it works. If someone could help me implement this in the Glide code I would be thankful.

@softvision-mariusduna
Copy link

the ideal fix would be not a workaround

@kevinvanmierlo
Copy link

@softvision-mariusduna My code is not a workaround, it should be implemented instead of a TransitionDrawable (which screws up the aspect ratio). I use it in my app and it works great!

@sjudd
Copy link
Collaborator

sjudd commented Sep 16, 2015

@kevinvanmierlo
Copy link

@sjudd Ahh thanks! I'll make a pull request when I have the time! Which is probably the next couple days or the beginning of next week.

@kevinvanmierlo
Copy link

@sjudd It's going to take a little while longer. I don't have much time and I can't use BitmapDrawable, so I have to find another solution using Drawable.

@kevinvanmierlo
Copy link

@sjudd I found some time again, sorry that it takes so long. I wanted to test some options, but the library doesn't have .placeholder or GlideDrawable. Did I do something wrong?

@TWiStErRob
Copy link
Collaborator Author

master is 4.0, which has breaking changes and simplifications

.apply(RequestOptions.placeholderOf(drawable)) //=.placeholder(drawable)

You don't need GlideDrawable, crossFade has to work between any two Drawables. Sam's link is still valid though.

@kevinvanmierlo
Copy link

@TWiStErRob thanks for the quick response! Didn't know that. Do you perhaps know if the new Drawable is always a BitmapDrawable? Cause if it is a BitmapDrawable you can do more calculations on the image. If it is not I'll continue to look for a solution using Drawable

@Edijae
Copy link

Edijae commented Mar 20, 2017

@TWiStErRob This is the answer that i used to overcome this bug. For placeholders that are smaller than the full image. http://stackoverflow.com/a/41459535/3671509

@sheaam30
Copy link

I'm still seeing this issue on 3.7.0 and the only workaround for me are either .dontAnimate or .animate() but I would rather have a crossfade. Any suggestions?

@TWiStErRob
Copy link
Collaborator Author

@sheaam30 the issue is not closed; the simplest workaround is to have your placeholder and image aspect ratio match

@louis993546
Copy link

I guess this is no longer relevant in Glide v4 since there is no longer any transformation (and it seems fine to me as well)

@haniha
Copy link

haniha commented Sep 9, 2017

@louistsaitszho
no difference here , DrawableTransitionOptions.withCrossFade() cause same problem in glide4

@sjudd sjudd removed the bug label Sep 14, 2017
@sjudd
Copy link
Collaborator

sjudd commented Sep 14, 2017

I'm still open to pull requests to improve the cross fade behavior, but I think the limitations are reasonably well understood and documented, so I'm going to remove the bug label.

@LOG-TAG
Copy link

LOG-TAG commented Feb 1, 2018

Glide.with(mContext).load(item.getImage()).apply(new RequestOptions().placeholder(R.drawable.image_loading)).into((ImageView) helper.getView(R.id.media_image));

above code works fine, trouble starts when we use transition(new DrawableTransitionOptions().crossFade()

@LOG-TAG
Copy link

LOG-TAG commented Feb 1, 2018

@TWiStErRob how to handle your imageview is inisde ConstraintLayout with

 <android.support.constraint.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <android.support.v7.widget.AppCompatImageView
            android:id="@+id/media_image"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:foreground="@drawable/gradient"
            app:layout_constraintDimensionRatio="16:9"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.0"
            app:srcCompat="@android:color/darker_gray"






            android:visibility="visible"



            app:layout_constraintBottom_toTopOf="@+id/share_button"
            app:layout_constraintDimensionRatio="16:9"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.0"
            app:srcCompat="@android:color/darker_gray" />

how do I generate the placeholder and image aspect ratio match?

@TWiStErRob
Copy link
Collaborator Author

@LOG-TAG https://stackoverflow.com/a/36944010/253468 ?

@jemshit
Copy link

jemshit commented Jun 1, 2018

Using

.thumbnail(GlideApp.with(this).load(R.drawable.placeholder))
.transition(withCrossFade())

instead of .placeholder() works fine

@lawonga
Copy link

lawonga commented Dec 28, 2018

Having the same issue, however I find that even with the fix here: https://stackoverflow.com/a/36944010/253468 it still messes up. I do notice that targetHeight < drawable.getIntrinsicHeight in getCurrentDrawable. Any ideas?

@kevinvanmierlo
Copy link

Okay guys, sorry for the delay, but had some time again. I'm going to create a pull request, but before it gets accepted here is a gist with the code you can use in the meantime: https://gist.github.com/kevinvanmierlo/c46f66027e3ae37ebea85a8d2e12aaba.

ravindragv added a commit to ravindragv/NASA-Pictures that referenced this issue Oct 13, 2021
+ In the ImageDetailAdapter load the HD URL of the image while showing the thumbnail image that we got while displaying the grid view
+ Implement the new function in GlideImageLoader
  - Not enabling transition in the new method because of an existing bug in Glide - bumptech/glide#363
+ Enable crossfade for loadImage function. This will be used for the ImageGridFragment, fade out the progressDrawable and fadein the image
@Sternbach-Software
Copy link

Where is this holding?

@kevinvanmierlo
Copy link

I don't know, haven't gotten any response anymore. I created the pull request. But Travis keeps failing and I don't really know why. I fixed all formatting issues I could find, but it still fails. I asked for help, but no response.

TWiStErRob added a commit to TWiStErRob/net.twisterrob.inventory that referenced this issue Feb 24, 2023
[MOD] Cleaner svg/image loading without thumbnail/placeholder (see bumptech/glide#363)
@J6ey
Copy link

J6ey commented Oct 4, 2023

I ended up using a vector over PNG as the placeholder and haven't seen any stretching

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests