【问题标题】:"Bitmap too large to be uploaded into a texture"“位图太大,无法上传到纹理中”
【发布时间】:2012-05-03 12:03:30
【问题描述】:

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

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

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

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

【问题讨论】:

  • 对于任何看这里的人,如果您希望它可滚动,请不要忘记将您的图像放在滚动视图中。这将摆脱错误。我浪费了一些时间才意识到这是我的问题。
  • 对于希望显示大图像仍保持图像质量的任何人,请参阅下面@stoefln 答案中的库。我用过,值得一试。绝对比inSampleSize 方法好。
  • @Joe Blow 当前答案在您的情况下不起作用?如果不是,请详细说明您在此问题的上下文中遇到的问题?
  • 嘿@VC.One - 为我的男人生气是一件奇怪的事情。我应该点击“奖励现有答案”。没有人会费心在那个 SO 弹出窗口上繁琐地按下“最佳”按钮,因为这很愚蠢 :) 当我点击赏金时,现在一切都解决了。干杯!!
  • 我尝试总是支付赏金(我不喜欢收集积分)。我想,如果你点击我的个人资料,然后是赏金,@VC.One,你会看到这些年来来自 SO 的许多非常棒的 QA !!!!!!!!!!!!!!!

标签: android android-imageview android-image android-bitmap


【解决方案1】:

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

在我的情况下,图像的两个尺寸都小于 2048(准确地说是 1280x727),并且在 Galaxy Nexus 上特别遇到了这个问题。该图像位于drawable 文件夹中,并且没有任何合格的文件夹。 Android 假定没有密度限定符的可绘制对象是 mdpi,并为其他密度放大或缩小它们,在这种情况下为 xhdpi 放大 2 倍。将罪魁祸首图像移动到drawable-nodpi 以防止缩放解决了问题。

【讨论】:

  • 我在尝试加载绘图时遇到了内存问题。花了 2 小时试图了解为什么会发生这种情况。感谢您的间接回答。
  • 这是正确答案,应该这样标记。
  • 大约三个半月以来,我几乎全职在 Android 上进行编程,我现在才意识到这一点。使drawable 本质上是一个隐藏的drawable-mdpi 文件夹似乎很愚蠢。这也解释了为什么我的自定义地图标记看起来很糟糕(它们被放大,然后被缩小)。
  • 是的,到底是什么!我认为 99% 的 Android 程序员认为“可绘制”意味着“不要缩放”。
  • 这是我在任何地方见过的最有帮助的答案。我只能说我正在发送赏金!谢谢。
【解决方案2】:

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

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);

【讨论】:

  • 谢谢,createScaledBitmap 对我能够显示太大的位图很有帮助
  • 解决了我的问题.. 谢谢。实际上我是从相机拍摄图像并在三星 Galaxy S4 的 ImageView 中显示 4.4.2
  • 抱歉,我无法检测到“w”是什么
  • 抱歉,ctx 是什么意思?
  • @AmeerSabith 看起来 ctx 是一个 Context 对象(例如你正在运行的 Activity)
【解决方案3】:

所有的渲染都是基于 OpenGL,所以你不能超过这个限制(GL_MAX_TEXTURE_SIZE 取决于设备,但最小值是 2048x2048,所以任何低于 2048x2048 的图像都可以适应)。

对于如此大的图像,如果您想在手机中放大,您应该设置一个类似于您在谷歌地图中看到的系统。将图像分成几部分和几个定义。

或者您可以在显示之前缩小图像(请参阅此问题的user1352407's answer)。

另外,请注意您将图像放入哪个文件夹,Android 会自动放大图像。看看下面这个问题的Pilot_51's answer

【讨论】:

  • 如果是这种情况,Gallery 应用程序如何允许显示和处理使用相机拍摄的图像? 2048x2048 只是 4MP 的图像,很多 Android 手机拍摄的照片比这大得多,Gallery 应用似乎没有问题。
  • 因为 GL_MAX_TEXTURE_SIZE 取决于设备。
  • 这真的没有任何意义。我现在遇到了同样的问题 - 使用 1286x835 像素的图像。并且:仅在 Galaxy Nexus 上,我收到此错误消息且没有图像!顶级智能手机无法显示如此小的图像,这似乎很荒谬!我的 HTC Hero 能够显示!我能做什么?
  • 在此处查看 Romain 的答案:stackoverflow.com/questions/7428996/…
  • @OllieC 我也想知道画廊应用程序是如何做到的。因此,如果有人知道,或者有显示大图像的示例,那就太好了。
【解决方案4】:

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

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

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

【讨论】:

  • 使用 centerCrop() 而不调用 resize() 将导致 IllegalStateException。使用中心裁剪时库强制调用调整大小。
  • 这很有道理,因为裁剪意味着您正在调整图像的大小。
  • 快速、简单和简单的解决方案。不确定它是否是最好的,但我得到了我想要的。非常感谢
  • 在使用毕加索的目标时仍然会出现同样的错误,对于更大尺寸的图像:(
  • 尝试使用 2.5.3-SNAPSHOT 版本,现在似乎可以正常处理大图像了
【解决方案5】:

在 (AndroidManifest.xml) 中添加以下 2 个属性对我有用:

android:largeHeap="true"
android:hardwareAccelerated="false"

【讨论】:

  • 这解决了我在三星设备上的问题。谢谢
【解决方案6】:

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

【讨论】:

  • 这将阻止 Android 尝试根据设备的尺寸和屏幕密度自动缩放图像;这就是该解决方案有效的原因。 Android 将尝试自动缩放 drawable 文件夹中的任何内容。
【解决方案7】:

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

    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;
}

并使用此方法加载毕加索的图像:

    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);
}

【讨论】:

    【解决方案8】:

    BitmapRegionDecoder 可以解决问题。

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

    【讨论】:

      【解决方案9】:

      正如 Larcho 所指出的,从 API 级别 10 开始,您可以使用 BitmapRegionDecoder 从图像中加载特定区域,然后,您可以通过在内存中仅分配所需区域来完成以高分辨率显示大图像.我最近开发了一个库,它通过触摸手势处理提供大图像的可视化。 The source code and samples are available here.

      【讨论】:

        【解决方案10】:

        查看级别

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

        myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        【讨论】:

          【解决方案11】:

          我遇到了同样的问题,这是我的解决方案。将图像的宽度设置为与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);
          

          这对于任何尺寸的安卓屏幕都更好。让我知道它是否适合你。

          【讨论】:

            【解决方案12】:

            缩小图片:

            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?我的意思是我们需要将其修复为静态值吗?或者我们可以改变那些 w.r.t 设备?
            【解决方案13】:

            使用 Glide 库而不是直接加载到 imageview 中

            滑行:https://github.com/bumptech/glide

            Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
            

            【讨论】:

            • 使用 Glide 代替 Picasso 有帮助。似乎 Glide 默认会处理此类问题
            【解决方案14】:

            我一个接一个地尝试了上述所有解决方案,持续了好几个小时,但似乎都没有奏效!最后,我决定四处寻找有关使用 Android 相机捕获图像并显示它们的官方示例。官方的例子(here),终于给了我唯一可行的方法。下面我介绍我在该示例应用中找到的解决方案:

            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);
            }
            

            【讨论】:

              【解决方案15】:

              对于那些想要放置小尺寸图像的人的注意事项:

              Pilot_51 的解决方案(将您的图像移动到drawable-nodpi 文件夹)有效,但还有另一个问题: 它会使图像在屏幕上太小,除非图像被调整到非常大(如 2000 x 3800)的分辨率以适合屏幕 - 然后它会使您的应用程序更重。

              解决方案:将您的图像文件放入drawable-hdpi——这对我来说就像一个魅力。

              【讨论】:

              • 您只是在掩盖图像密度问题。在低密度硬件上,您的图像看起来会更大。
              • 我也不认为 Pilot_51 的解决方案有任何问题,您应该根据需要使用适当的图像尺寸
              【解决方案16】:

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

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

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

              【讨论】:

                【解决方案17】:
                @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);
                
                    }
                }
                

                此代码有效

                【讨论】:

                  猜你喜欢
                  • 2015-04-05
                  • 2016-11-20
                  • 2014-05-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多