【问题标题】:Why i'm getting exception when using SetPixel: SetPixel is not supported for images with indexed pixel formats?为什么使用 SetPixel 时出现异常:索引像素格式的图像不支持 SetPixel?
【发布时间】:2014-09-23 07:47:45
【问题描述】:

在我正在做的一门课的顶部:

private static Bitmap bmp2 = new Bitmap(@"C:\Temp\New folder (17)\radar001486.GIF");

然后在我正在做的一个方法中:

private void test()
   {
    int current_list_length = pointtocolor.Count;
                for (int kk=0;kk<current_list_length;kk++)
                {

                    PointF pt = pointtocolor[kk];
                    e.FillEllipse(cloudColors[cloudColorIndex], pt.X * (float)currentFactor, pt.Y * (float)currentFactor, radius, radius);
                    bmp2.SetPixel((int)pt.X * (int)currentFactor, (int)pt.Y * (int)currentFactor, Color.Yellow);

                }
                bmp2.Save(@"c:\temp\yellowbmpcolor.bmp");
   }

一旦进入循环,它就会产生异常:

bmp2.SetPixel((int)pt.X * (int)currentFactor, (int)pt.Y * (int)currentFactor, Color.Yellow);

如果我将 bmp2 的实例更改为:

private static Bitmap bmp2 = new Bitmap(@"C:\Temp\New folder (17)\radar001486.GIF");

private static Bitmap bmp2 = new Bitmap(512,512);

然后它会工作,但我想 SetPixel 原始雷达001486.GIF 上的像素,而不是新的空位图。

【问题讨论】:

标签: c# .net winforms


【解决方案1】:

问题在于您使用的是 GIF,因为它具有索引像素。如果可以,请尝试将其转换为 png;或者如果你不能,使用以下方法将其转换为非索引图像:

public Bitmap CreateNonIndexedImage(Image src)
{
    Bitmap newBmp = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    using (Graphics gfx = Graphics.FromImage(newBmp)) {
        gfx.DrawImage(src, 0, 0);
    }

    return newBmp;
}

注意:如果您可以选择这样做(即未下载的图像,或者您可以访问服务器),请务必将这些图像转换为 PNG。

【讨论】:

  • Dracor 这行得通。我正在下载的网站上的文件是 GIF 格式的。我应该将它们保存为 png 吗?或者他们我现在正在使用您的解决方案执行此操作,新位图 newBmp 是否丢失了一些数据(像素)?
  • 不,您只需使用手机的 GPU 将图像渲染成另一个图像。我说如果你不需要就不要使用它,因为它有点资源密集,但对于一些图像来说,它是一个可行的解决方案。
  • Dracor,如果我将来想自动插入许多图像,不仅很少,比如 3-5 或 10 张图像,而且很多很多,我该怎么做才能不使用手机的GPU这么多?如果您有任何想法和代码。谢谢。
  • 从技术上讲,如果您同时使用手机下载数百张可能较大的图像,那么您就做错了。如果你真的想这样做,无论如何你都必须将图像下载放到后台线程,但不要忘记加载图像需要时间。
【解决方案2】:

您尝试更改的图像是索引 GIF。这意味着图像不包含一系列具有各自颜色值的像素(就像您的新 Bitmap 那样);相反,它包含一个调色板,其中包含一系列像素以及它们在调色板中的索引值。从磁盘加载的图像的像素格式可能类似于Format8bppIndexed

你不能在这种图像上使用SetPixel,因为SetPixel想直接设置像素的R、G和B值。这不是索引图像的工作方式。

要更改这种图像,您有几个选择:

  • 最好的办法是使用 WPF,它有 GifBitmapEncoderGifBitmapDecoder。这使您可以将 GIF 数据解码为 WPF 可以绘制的内容,然后将其转换回来。因为它使用 DirectX 而不是 GDI+,所以它没有像 SetPixel 这样的限制。如果可以的话,我真的,真的建议你走这条路,但如果不是:

  • 使用 GDI+ 将Convert the image 转换为非索引类型的图像,更改它,然后convert it back。这通常是一个糟糕的想法:GDI+ 和索引格式不兼容,这包括将位图编码为索引 GIF。图像质量可能很糟糕。

  • 直接编辑字节数据。为此,您需要将 GIF 数据提取到一个数组中,并将像素设置为正确的索引值。这里的诀窍是找出正确的索引值;或者,如果调色板中恰好有一个空的,您可以添加另一个。您需要深入研究 GIF 格式才能弄清楚这一点,尽管它在空间和速度方面可能是最有效的,而不会降低图像质量。一旦你知道要写什么索引值,你就可以这样做:

    var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), 
        ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
    
    var bytes = new byte[data.Height * data.Stride];
    Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
    
    bytes[5 * data.Stride + 5] = 1; // Set the pixel at (5, 5) to the color #1
    
    Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
    
    image.UnlockBits(data);
    

【讨论】:

    【解决方案3】:

    问题在于您的图像类型,因为它适用于 .jpeg。您可以从Image 获得Bitmap

    试着改成这样:

    private static Bitmap bmp2 = new Bitmap(Image.FromFile(@"C:\Temp\New folder (17)\radar001486.GIF"));
    

    这是我的完整测试代码:

        private Bitmap bmp2 = new Bitmap(Image.FromFile(@"e:\temp\temp\yourGIF.gif"));
        public Form1()
        {
            InitializeComponent();
            pictureBox1.Image = bmp2;
        }
    
        private void button2_Click(object sender, EventArgs e)
        {
            var sz = bmp2.Size;
            for(int x= 0; x<sz.Width; x++)
            {
                for(int y=0; y<sz.Height; y++)
                {
                    bmp2.SetPixel(x, y, Color.Yellow);
                }
            }
            pictureBox1.Refresh();
        }
    

    【讨论】:

      猜你喜欢
      • 2020-04-11
      • 2019-11-19
      • 2016-10-13
      • 1970-01-01
      • 2011-07-07
      • 2020-11-06
      • 2020-04-15
      • 2014-04-12
      • 1970-01-01
      相关资源
      最近更新 更多