简体   繁体   English

滑入SimpleTarget <Bitmap> 不尊重指定的宽度和高度

[英]Glide load into SimpleTarget<Bitmap> not honoring the specified width and height

I'm using Glide to load an image, resize it and save it to a file by means of a SimpleTarget<Bitmap> . 我正在使用Glide加载图像,调整大小并通过SimpleTarget<Bitmap>将其保存到文件中。 These images will be uploaded to Amazon S3, but that's besides the point. 这些图像将上传到Amazon S3,但除此之外。 I'm resizing the images prior to uploading as to save as much user's bandwidth as possible. 我正在上传之前调整图像大小以节省尽可能多的用户带宽。 For my app needs a 1024 pixel wide image is more than enough, so I'm using the following code to accomplish that: 对于我的应用程序需要一个1024像素宽的图像绰绰有余,所以我使用以下代码来实现这一点:

final String to = getMyImageUrl();
final Context appCtx = context.getApplicationContext();

Glide.with(appCtx)
        .load(sourceImageUri)
        .asBitmap()
        .into(new SimpleTarget<Bitmap>(1024, 768) {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                try {
                    FileOutputStream out = new FileOutputStream(to);
                    resource.compress(Bitmap.CompressFormat.JPEG, 70, out);
                    out.flush();
                    out.close();
                    MediaScannerConnection.scanFile(appCtx, new String[]{to}, null, null);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

It works almost perfectly, but the size of the resulting image is not 1024 pixels wide. 它几乎完美地工作,但生成的图像的大小不是1024像素宽。 Testing it with a source image with dimensions of 4160 x 2340 pixels the dimensions of the resulting saved image is 2080 x 1170 pixels. 使用尺寸为4160 x 2340像素的源图像对其进行测试,得到的保存图像的尺寸为2080 x 1170像素。

I've tried playing with the width and height parameters passed to new SimpleTarget<Bitmap>(350, 350) and with these parameters the resulting image dimensions are 1040 x 585 pixels. 我尝试使用传递给new SimpleTarget<Bitmap>(350, 350)widthheight参数new SimpleTarget<Bitmap>(350, 350)并使用这些参数得到的图像尺寸为1040 x 585像素。

I really don't know what to do to make Glide respect the passed dimensions. 我真的不知道如何使Glide尊重传递的尺寸。 In fact I'd like to resize the image proportionally so that the bigger dimension (either width or height) will be restricted to 1024 pixels and the smaller one resized accordingly (I believe I'll have to find a way to get the original image dimensions and then pass the width and height to SimpleTarget , but to do that I need Glide to respect the passed width and height!). 事实上,我想按比例调整图像大小,以便将较大的尺寸(宽度或高度)限制为1024像素,较小的尺寸相应调整大小(我相信我必须找到一种方法来获取原始图像尺寸,然后将宽度和高度传递给SimpleTarget ,但要做到这一点,我需要Glide尊重传递的宽度和高度!)。

Does anyone have a clue what's going on? 有没有人知道发生了什么? I'm using Glide 3.7.0. 我正在使用Glide 3.7.0。


Since this question itself may be useful for people trying to use Glide to resize and save images I believe it is in everyone's interest to provide my actual "solution" which relies on a new SimpleTarget implementation that automatically saves the resized image: 由于这个问题本身可能对尝试使用Glide调整大小和保存图像的人有用,我相信提供我的实际“解决方案”符合每个人的兴趣,该解决方案依赖于一个新的SimpleTarget实现,它自动保存调整后的图像:

import android.graphics.Bitmap;

import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;

import java.io.FileOutputStream;
import java.io.IOException;

public class FileTarget extends SimpleTarget<Bitmap> {
    public FileTarget(String fileName, int width, int height) {
        this(fileName, width, height, Bitmap.CompressFormat.JPEG, 70);
    }
    public FileTarget(String fileName, int width, int height, Bitmap.CompressFormat format, int quality) {
        super(width, height);
        this.fileName = fileName;
        this.format = format;
        this.quality = quality;
    }
    String fileName;
    Bitmap.CompressFormat format;
    int quality;
    public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
            try {
                FileOutputStream out = new FileOutputStream(fileName);
                bitmap.compress(format, quality, out);
                out.flush();
                out.close();
                onFileSaved();
            } catch (IOException e) {
                e.printStackTrace();
                onSaveException(e);
            }
    }
    public void onFileSaved() {
        // do nothing, should be overriden (optional)
    }
    public void onSaveException(Exception e) {
        // do nothing, should be overriden (optional)
    }

}

Using it is as simple as: 使用它很简单:

Glide.with(appCtx)
        .load(sourceImageUri)
        .asBitmap()
        .into(new FileTarget(to, 1024, 768) {
            @Override
            public void onFileSaved() {
                // do anything, or omit this override if you want
            }
        });

After a good night's sleep I just figured it out! 经过一夜好眠,我才明白了! I had stumbled on an issue in Glide's github page which had the answer but I didn't realize it: I missed something in the explanation, which I now fully understood after resting for 10 hours. 我在Glide的github页面上偶然发现了一个问题,但我没有意识到这一点:我在解释中遗漏了一些东西,我现在在休息了10个小时后完全理解了。 You should never underestimate the power of sleeping! 你永远不应该低估睡眠的力量! But I digress. 但我离题了。 Here is the answer found on Glide's Github issue tracker : 以下是Glide的Github问题跟踪器上的答案:

Sizing the image usually has two phases: 调整图像大小通常有两个阶段:

  • Decoding/Downsampler read image from stream with inSampleSize 解码/下采样器使用inSampleSize从流中读取图像
  • Transforming/BitmapTransformation take the Bitmap and match the exact target size Transforming / BitmapTransformation采用位图并匹配确切的目标大小

The decoding is always needed and is included in the flow, the default case is to match the target size with the "at least" downsampler, so when it comes to the transformation the image can be downsized more without quality loss (each pixel in the source will match at least 1.0 pixels and at most ~1.999 pixels) this can be controlled by asBitmap().at least|atMost|asIs|decoder(with downsampler) 总是需要解码并且包含在流程中,默认情况是使目标大小与“至少”下采样器匹配,因此当涉及到变换时,图像可以更小而不会有质量损失(每个像素在源将匹配至少1.0像素,最多约1.999像素)这可以通过asBitmap()控制。至少| atMost | asIs |解码器(带下采样器)

The transformation and target size is automatic by default, but only when using a ViewTarget. 默认情况下,转换和目标大小是自动的,但仅限于使用ViewTarget时。 When you load into an ImageView the size of that will be detected even when it has match_parent. 当您加载到ImageView时,即使它具有match_parent,也会检测到它的大小。 Also if there's no explicit transformation there'll be one applied from scaleType. 此外,如果没有明确的转换,那么将从scaleType应用一个。 Thus results in a pixel perfect Bitmap for that image, meaning 1 pixel in Bitmap = 1 pixel on screen resulting in the best possible quality with the best memory usage and fast rendering (because there's no pixel mapping needed when drawing the image). 因此,为该图像生成像素完美位图,意味着Bitmap中的1个像素=屏幕上的1个像素,从而以最佳的内存使用和快速渲染获得最佳质量(因为绘制图像时不需要像素映射)。

With a SimpleTarget you take on these responsibilities by providing a size on the constructor or via override() or implementing getSize if the sizing info is async-ly available only. 使用SimpleTarget,您可以通过在构造函数上提供大小或通过override()或实现getSize(如果大小调整信息仅为异步可用)来承担这些职责。

To fix your load add a transformation: .fitCenter|centerCrop(), your current applied transformation is .dontTransform() (Answer by Róbert Papp ) 要解决你的负载增加转换:.fitCenter | centerCrop(),您的当前应用的变换是.dontTransform()(由回答罗伯特·帕普

I got confused by this answer because of this: 我对此答案感到困惑,因为:

With a SimpleTarget you take on these responsibilities by providing a size on the constructor or via override() or implementing getSize if the sizing info is async-ly available only. 使用SimpleTarget,您可以通过在构造函数上提供大小或通过override()或实现getSize(如果大小调整信息仅为异步可用)来承担这些职责。

Since I was passing the size I thought I got this covered already and such size should have been respected. 由于我通过了尺寸,我认为我已经覆盖了这个尺寸,这样的尺寸应该得到尊重。 I missed this important concept: 我错过了这个重要的概念:

  • Decoding/Downsampler read image from stream with inSampleSize 解码/下采样器使用inSampleSize从流中读取图像
  • Transforming/BitmapTransformation take the Bitmap and match the exact target size Transforming / BitmapTransformation采用位图并匹配确切的目标大小

And this: 和这个:

To fix your load add a transformation: .fitCenter|centerCrop(), your current applied transformation is .dontTransform() 要修复你的负载,添加一个转换:.fitCenter | centerCrop(),你当前应用的转换是.dontTransform()

Now that I pieced it together it makes sense. 现在,我将它拼凑在一起是有道理的。 Glide was only downsampling the image (first step in the sizing image flow as explained by Róbert) which gives an image with approximated dimensions. Glide只是对图像进行下采样(如Róbert所解释的尺寸图像流程中的第一步),它给出了具有近似尺寸的图像。 Let me say that Glide is very smart in this aspect. 让我说Glide在这方面非常聪明。 By using the downsampling method prior to resizing it avoids working with unnecessarily large bitmaps in memory and improves the resizing quality because downsampling to the exact size would compromise too many "important" pixels! 通过在调整大小之前使用下采样方法,可以避免在内存中处理不必要的大位图并提高调整大小的质量,因为缩减到精确的大小会损害太多“重要”像素!

Since I didn't have any transformation applied to this loading pipeline, the sizing flow stopped in this first step (downsampling), and the resulting image had only approximate dimensions to my expected target size. 由于我没有对此加载管道应用任何转换,因此在第一步(下采样)中停止了大小调整流程,并且生成的图像仅具有与我的预期目标大小相近的大小。

To solve it I just applied a .fitCenter() transform as shown bellow: 为了解决这个问题,我刚刚应用了一个.fitCenter()变换,如下所示:

Glide.with(appCtx)
        .load(sourceImageUri)
        .asBitmap()
        .fitCenter()
        .into(new FileTarget(to, 1024, 768) {
            @Override
            public void onFileSaved() {
                // do anything, or omit this override if you want
            }
        });

The resulting image now have dimensions of 1024 x 576 pixels, which is exactly what I expected. 生成的图像现在具有1024 x 576像素的尺寸,这正是我所期望的。

Glide is a very cool library! Glide是一个非常酷的图书馆!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM