Una manera mucho más fácil es hacer sólo la imagen con el (width, height, pixelformat)
constructor. Entonces se ocupa de la zancada en sí misma.
Luego, puede usar LockBits
para copiar los datos de su imagen en él, línea por línea, sin molestarse con la materia de Stride usted mismo; simplemente puede solicitarlo desde el objeto BitmapData
. Para la operación de copia real, para cada línea de exploración, simplemente aumenta el puntero de destino por la zancada, y el puntero de fuente por el ancho de los datos de su línea.
Aquí hay un ejemplo en el que obtuve los datos de la imagen en una matriz de bytes. Si se trata de datos completamente compactos, la zancada de entrada normalmente es solo el ancho de la imagen multiplicado por la cantidad de bytes por píxel. Si se trata de datos paletizados de 8 bits, es simplemente el ancho exacto.
Si los datos de la imagen se extrajeron de un objeto de imagen, debe haber almacenado la zancada original de ese proceso de extracción exactamente de la misma manera, sacándolo del objeto BitmapData
.
/// <summary>
/// Creates a bitmap based on data, width, height, stride and pixel format.
/// </summary>
/// <param name="sourceData">Byte array of raw source data</param>
/// <param name="width">Width of the image</param>
/// <param name="height">Height of the image</param>
/// <param name="stride">Scanline length inside the data</param>
/// <param name="pixelFormat">Pixel format</param>
/// <param name="palette">Color palette</param>
/// <param name="defaultColor">Default color to fill in on the palette if the given colors don't fully fill it.</param>
/// <returns>The new image</returns>
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
{
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7)/8;
// Compensate for possible negative stride on BMP format.
Boolean isFlipped = stride < 0;
stride = Math.Abs(stride);
// Cache these to avoid unnecessary getter calls.
Int32 targetStride = targetData.Stride;
Int64 scan0 = targetData.Scan0.ToInt64();
for (Int32 y = 0; y < height; y++)
Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
newImage.UnlockBits(targetData);
// Fix negative stride on BMP format.
if (isFlipped)
newImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, set the palette.
if ((pixelFormat & PixelFormat.Indexed) != 0 && palette != null)
{
ColorPalette pal = newImage.Palette;
for (Int32 i = 0; i < pal.Entries.Length; i++)
{
if (i < palette.Length)
pal.Entries[i] = palette[i];
else if (defaultColor.HasValue)
pal.Entries[i] = defaultColor.Value;
else
break;
}
newImage.Palette = pal;
}
return newImage;
}
Esta es la respuesta correcta; más uno todo el día. Aquí hay más sobre el cálculo de 'zancada': http://stackoverflow.com/questions/1983781/why-does-bitmapsource-create-throw-an-argumentexception/1983886#1983886. – jason
Por lo tanto, probablemente debería intentar cambiar el formato de mi imagen. Ahora estoy usando algo que almacena cada píxel como un solo byte. Habiendo dicho eso, supongo que no puedo usar ese constructor de mapa de bits ya que no hay ningún tipo de imagen que solo tome un solo byte y no dependa de un mapa de color wonky. –
Correcto, 8bpp requiere una paleta. GDI + no los admite bien, le proporcionará un sinfín de problemas. Puede comenzar a cargar una imagen de 8bpp existente para comenzar. Puede robar sus entradas de paleta o LockBits directamente si tiene el tamaño correcto. O sintetiza uno en un MemoryStream. –