При сохранении одноцветного растрового изображения с альфа-каналом в Windows Forms сохраняется другой (неправильный) цвет - PullRequest
3 голосов
/ 14 декабря 2011

В C #, .NET 2.0, Windows Forms, Visual Studio Express 2010 я сохраняю изображение, выполненное в том же цвете:

  Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
  using (Graphics graphics = Graphics.FromImage(bitmap))
  {
      Brush brush = new SolidBrush(color);
      graphics.FillRectangle(brush, 0, 0, width, height);
      brush.Dispose();
  }

  bitmap.Save("test.png");
  bitmap.Save("test.bmp");

Если я использую, например,

Цвет [A = 153, R = 193, G = 204, B = 17] или # C1CC11

после сохранения изображения и открытия его во внешнем средстве просмотра, например Paint.NET,IrfanView, XNView и т. Д. Мне говорят, что цвет изображения на самом деле:

Цвет [A = 153, R = 193, G = 203, B = 16] или # C1CB10

так что это похожий цвет, но не тот же самый!

Я пробовал с сохранением как PNG, так и BMP.

Когда используется прозрачность (альфа), .NET сохраняет другой цвет!Когда альфа 255 (без прозрачности), он сохраняет цвет corrent.

Ответы [ 2 ]

2 голосов
/ 04 июля 2013

Просто хотел добавить к этому немного. По моему опыту, последний фрагмент кода, вероятно, выдаст ошибку времени выполнения. Это связано с тем, что массив значений argb в растровом изображении хранится так: [a1, b1, g1, r1, a2, b2, g2, r2, a3, ...] и т. Д., Где a, r , г, б являются инт. Вы должны выполнить несколько тестов на своих изображениях, чтобы увидеть, где именно находятся значения a, r, g, b в массиве. Растровые изображения не обязательно гарантируют эту ординальность (да, даже в .Net).

Чтобы переписать это немного:

   byte[] rgbValues1 = new byte[4];

                            System.Drawing.Imaging.BitmapData bpData =
                            bm.LockBits(new Rectangle(0,0,bm.Width,bm.Height),System.Drawing.Imaging.ImageLockMode.ReadWrite,
                            bm.PixelFormat);

                        int thisPixel = ptStart.X * 4 + ptStart.Y * bpData.Stride;

                        IntPtr px = bpData.Scan0 + thisPixel;

                        System.Runtime.InteropServices.Marshal.Copy(px, rgbValues1, 0, rgbValues1.Length);


                        rgbValues1[0] = (byte)(255);//blue channel

                        rgbValues1[1] = (byte)(0);//green channel

                        rgbValues1[2] = (byte)(0);//red channel

                        rgbValues1[3] = (byte)(127)//should be alpha channel

                        System.Runtime.InteropServices.Marshal.Copy(rgbValues1, 0, px, 4);

                        bm.UnlockBits(bpData);

Это позволит установить один пиксель в синий цвет с прозрачностью 50% (или это должно ) ... Я все еще работаю над этим сам ...

2 голосов
/ 14 декабря 2011

Спасибо, Джо и Ханс Пассант за ваши комментарии.

Да, как сказал Джо, проблема на линии:

graphics.FillRectangle(brush, 0, 0, width, height);

Здесь GDI + изменяет цвет аналогичным цветом, но не точным.

Похоже, что решение состоит в том, чтобы записать значения цвета непосредственно в пикселях, используя Bitmap.LockBits и Marshal.Copy:

        Bitmap bitmap = new Bitmap(this.currentSampleWidth, this.currentSampleHeight, PixelFormat.Format32bppArgb);

        // Lock the bitmap's bits.  
        Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
        BitmapData bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);

        // Get the address of the first line.
        IntPtr ptr = bmpData.Scan0;

        // Declare an array to hold the bytes of the bitmap (32 bits per pixel)
        int pixelsCount = bitmap.Width * bitmap.Height;
        int[] argbValues = new int[pixelsCount];

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptr, argbValues, 0, pixelsCount);

        // Set the color value for each pixel.
        for (int counter = 0; counter < argbValues.Length; counter++)
            argbValues[counter] = color.ToArgb();

        // Copy the RGB values back to the bitmap
        System.Runtime.InteropServices.Marshal.Copy(argbValues, 0, ptr, pixelsCount);

        // Unlock the bits.
        bitmap.UnlockBits(bmpData);

        return bitmap;
...