“位图太大,无法上传到纹理中”


154

我正在将位图加载到ImageView中,并看到此错误。我收集到此限制与OpenGL硬件纹理(2048x2048)的大小限制有关。我需要加载的图像是约4000像素高的捏缩放图像。

我尝试关闭清单中的硬件加速,但没有任何乐趣。

    <application
        android:hardwareAccelerated="false"
        ....
        >

是否可以将大于2048像素的图像加载到ImageView中?


1
对于在这里看的任何人,如果您希望图像可滚动,请不要忘记将其放在滚动视图中。那将消除错误。我浪费了一些时间才意识到这是我的问题。
杰森·里奇

对于希望显示大图像但仍保持图像质量的任何人,请参考下面@stoefln答案中的库。我用过,值得一试。绝对比inSampleSize方法更好。
Mahendra Liya 2015年

1
@Joe Blow当前答案在您的情况下不起作用?如果否,那么请详细说明您在此问题中遇到的问题?
Pravin Divraniya '16

1
嘿@ VC.One-对我的男人有所了解是一件奇怪的事。我应该单击“奖励现有答案”。没有人会费力地按该SO弹出窗口上的“最佳”按钮,因为它很愚蠢:)现在,当我单击赏金时,所有问题都解决了。干杯!!
Fattie

1
我总是尝试支付赏金(我不喜欢累积积分)。我想,如果您单击我的个人资料,然后是@VC,那么这些年来,您将看到许多非常出色的质量检查!!!!!!!!!!!!!!!
Fattie

Answers:


48

所有渲染均基于OpenGL,因此,您不能超过此限制(GL_MAX_TEXTURE_SIZE取决于设备,但最小值为2048x2048,因此任何低于2048x2048的图像都可以容纳)。

有了如此大的图像,如果您想在移动设备上进行放大,则应设置一个类似于在Google地图中看到的系统。图像分为几个部分和几个定义。

或者您可以在显示图像之前按比例缩小图像(请参阅user1352407的答案关于此问题)。

另外,请注意将图片放入哪个文件夹,Android会自动放大图片。看看下面关于这个问题的Pilot_51的答案


23
在这种情况下,Gallery应用程序如何允许显示和操纵相机拍摄的图像?2048x2048只是4MP图像,许多Android手机拍摄的照片都比这个大得多,Gallery应用似乎没有问题。
Ollie C

3
因为GL_MAX_TEXTURE_SIZE取决于设备。
jptsetung 2012年

24
这确实没有任何意义。我现在遇到了相同的问题-图像为1286x835像素。AND:仅在Galaxy Nexus上,我收到此错误消息,但没有图像!顶级智能手机无法显示这么小的图像似乎很荒谬!我的HTC Hero可以显示该信息!我能做什么?
Zordid

1
在这里看到罗曼的回答是:stackoverflow.com/questions/7428996/...
李本

3
@OllieC我也想知道图库应用程序是如何做到的。因此,如果有人知道或有显示大图像的示例,那就太好了。
Innova

408

这不是问题的直接答案(加载大于2048的图像),但是对于任何遇到此错误的人来说,这都是可能的解决方案。

就我而言,图像的两个尺寸均小于2048(准确地说是1280x727),并且该问题在Galaxy Nexus上特别明显。该图像在该drawable文件夹中,没有合格的文件夹。Android假定不具有密度限定符的可绘制对象为mdpi,并针对其他密度将其放大或缩小,在本例中,对于xhdpi,其放大两倍。移动罪魁祸首图像以drawable-nodpi防止缩放可解决此问题。


3
我在尝试加载可绘制对象时遇到内存问题。花了2个小时试图了解为什么会这样。感谢您的间接答复。
TrueCoke

16
这是正确的答案,应如此标记。
wblaschko 2014年

3
现在大约三个半月以来,我几乎一直在Android上进行编程,而我刚刚意识到了这一点。似乎很笨拙,drawable实际上使它成为一个隐藏drawable-mdpi文件夹。这也解释了为什么我的自定义地图标记看起来很糟糕(先放大,然后缩小)。
theblang 2014年

10
是的,什么鬼!我认为99%的Android程序员认为“ drawable”的意思是“不要缩放”。
马特·洛根

3
这是我在任何地方见过的最有用的答案。我只能说是要发送赏金!谢谢。
Fattie

105

我以这种方式缩小了图像:

ImageView iv  = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);

2
谢谢,createScaledBitmap对我能够显示太大的位图很有帮助
Boy

解决了我的问题..谢谢。实际上,我是从相机拍摄图像并在三星Galaxy S4的ImageView中以4.4.2显示的
Mitesh Shah

抱歉,但我无法检测到“ w”
Bobbelinio

1
对不起,这是什么ctx意思?
Ameer Sabith '18

1
@AmeerSabith看起来ctx是一个Context对象(例如您正在运行的Activity)
Matt

34

为何不花大量时间尝试手动编写/调试所有这些下采样代码,为什么不使用Picasso?它是为处理bitmaps所有类型和/或大小而设计的。

我已经使用这一行代码来消除“位图太大...”的问题:

Picasso.load(resourceId).fit().centerCrop().into(imageView);

在不调用resize()的情况下使用centerCrop()将导致IllegalStateException。使用中心裁切时,库会强制调用调整大小。
ZsoltBoldizsár2015年

这很合情合理,因为裁剪意味着您正在调整图像的大小。
Phileo99

2
快速,简单的解决方案。不知道这是不是最好的,但是我得到了想要的东西。非常感谢
Nabin

对于更大尺寸的图像,将目标与毕加索一起使用时仍会遇到相同的错误:(
Narendra Singh

尝试使用2.5.3-SNAPSHOT版本,似乎现在可以正常使用大图像
Anton Malmygin


16

将图像文件drawable-nodpidrawable文件夹更改为文件夹对我有用。


这样可以防止Android尝试根据设备的尺寸和屏幕密度自动缩放图像;这就是为什么该解决方案有效的原因。Android会尝试自动缩放drawable文件夹中的任何内容。
克里斯·西里菲斯

10

我使用了毕加索,也遇到了同样的问题。图片太大,至少在大小,宽度或高度上太大。终于我在这里找到了解决方案。您可以根据显示尺寸缩小大图像,并保持宽高比:

    public Point getDisplaySize(Display display) {
    Point size = new Point();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        display.getSize(size);
    } else {
        int width = display.getWidth();
        int height = display.getHeight();
        size = new Point(width, height);
    }

    return size;
}

并使用此方法通过Picasso加载图像:

    final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
        final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
        Picasso.with(this)
                .load(urlSource)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

同样,为了获得更好的性能,您可以根据显示屏的宽度和高度下载图像,而不是整个图像:

    public String reviseImageUrl(final Integer displayWidth,     final Integer displayHeight,
        final String originalImageUrl) {
    final String revisedImageUrl;

    if (displayWidth == null && displayHeight == null) {
        revisedImageUrl = originalImageUrl;
    } else {
        final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();

        if (displayWidth != null && displayWidth > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
        }

        if (displayHeight != null && displayHeight > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
        }

        revisedImageUrl = uriBuilder.toString();
    }

    return revisedImageUrl;
}

    final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);

然后:

    Picasso.with(this)
                .load(newImageUlr)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

编辑:getDisplaySize()

display.getWidth()/getHeight()不推荐使用。代替Display使用DisplayMetrics

public Point getDisplaySize(DisplayMetrics displayMetrics) {
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        return new Point(width, height);
}

6

BitmapRegionDecoder 绝招。

您可以覆盖onDraw(Canvas canvas),启动新线程并解码用户可见的区域。


5

正如Larcho所指出的那样,从API级别10开始,您可以用来BitmapRegionDecoder从图像中加载特定区域,并且可以通过仅在内存中分配所需区域来完成以高分辨率显示大图像的操作。我最近开发了一个可通过触摸手势处理提供大图像可视化的库。源代码和示例可在此处获得


3

查看水平

您可以使用以下代码在运行时为单个视图禁用硬件加速:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);


3

我遇到了同样的问题,这是我的解决方案。设置图像的宽度与android屏幕宽度相同,然后缩放高度

Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);

这对于任何尺寸的android屏幕都更好。请让我知道这对你有没有用。


2

缩小图像:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);

int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
    pow += 1;
options.inSampleSize = 1 << pow; 
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);

图像将按reqHeight和reqWidth的尺寸缩​​小。据我了解,inSampleSize仅接受2值的幂。


如何知道reqHeight和reqWidth?我的意思是我们需要将其固定为静态值吗?或者我们可以更改那些wrt设备?
Prashanth Debbadwar '16


1

我尝试了上述所有解决方案,一个接一个,花了很多小时,而且似乎都没有用!最后,我决定环顾四周,以获取有关使用Android相机捕获图像并进行显示的官方示例。官方示例(在此)最终给了我唯一可行的方法。下面,我介绍在该示例应用程序中找到的解决方案:

public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {

            /* There isn't enough memory to open up more than a couple camera photos */
    /* So pre-scale the target bitmap into which the file is decoded */

    /* Get the size of the ImageView */
    int targetW = imgView.getWidth();
    int targetH = imgView.getHeight();

    /* Get the size of the image */
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    /* Figure out which way needs to be reduced less */
    int scaleFactor = 1;
    if ((targetW > 0) || (targetH > 0)) {
        scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    }

    /* Set bitmap options to scale the image decode target */
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    /* Decode the JPEG file into a Bitmap */
    Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);

    /* Associate the Bitmap to the ImageView */
    imgView.setImageBitmap(bitmap);
    imgView.setVisibility(View.VISIBLE);
}

0

想要放置小尺寸图像的注意事项:

Pilot_51的解决方案(将图像移动到drawable-nodpi文件夹中)可以工作,但是还有另一个问题:图像太小了屏幕上,除非将图像调整到一个非常大的(如2000×3800)分辨率,以适应屏幕-那么它使你的应用更重。

解决方案:将您的图像文件放入drawable-hdpi-对我来说,它就像是一种魅力。


1
您只是掩盖了图像密度问题。在低密度的硬件上,您的图像看起来会更大。
rupps

我也不认为Pilot_51的解决方案有任何问题,您应该根据需要使用适当的图像大小
Nilabja

0

使用正确的可绘制子文件夹为我解决了此问题。我的解决方案是将全分辨率图像(1920x1200)放入drawable-xhdpi文件夹,而不是drawable文件夹。

我还将缩小的图像(1280x800)放入了drawable-hdpi文件夹中。

这两个分辨率与我正在编程的2013和2012 Nexus 7平板电脑匹配。我还在其他平板电脑上测试了该解决方案。


0
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);
    ///*
    if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){



        uri = data.getData();

        String[] prjection ={MediaStore.Images.Media.DATA};

        Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);

        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(prjection[0]);

        ImagePath = cursor.getString(columnIndex);

        cursor.close();

        FixBitmap = BitmapFactory.decodeFile(ImagePath);

        ShowSelectedImage = (ImageView)findViewById(R.id.imageView);

      //  FixBitmap = new BitmapDrawable(ImagePath);
        int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
        FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);

       // ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));

        ShowSelectedImage.setImageBitmap(FixBitmap);

    }
}

此代码是有效的

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.