bpp = 每像素位数,因此 32bpp 表示 R/G/B/A 为 8/8/8/8。
就像 .NET 对这些“System.Drawing.Imaging.PixelFormat”有一个枚举。
现在,一旦我的图形有一个位图或图像对象,我将如何将其保存到文件/我将使用什么格式?
什么图像文件格式(JPEG/GIF/PNG)支持低位深度,如 16bpp 或 8bpp (而不是通常的 32bpp 或 24bpp)
我不认为其他人的回答测试了他们的代码,因为 GDI+ PNG 不支持 Encoder.BitDepth EncoderParameter。事实上,唯一能做到的编解码器是 TIFF。
您需要在保存之前更改图像的 PixelFormat 才能对输出产生任何影响。这不会总是产生您期望的 PixelFormat。有关PixelFormats 变成什么的更多信息,请参阅我的帖子。
至于 PixelFormat 转换,如下所示:
private Bitmap ChangePixelFormat(Bitmap inputImage, PixelFormat newFormat)
{
Bitmap bmp = new Bitmap(inputImage.Width, inputImage.Height, newFormat);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(inputImage, 0, 0);
}
return bmp;
}
不幸的是,这些转换会产生一些非常糟糕的输出。在执行有损转换(从高位深度到较低位深度)的情况下尤其如此。
这仅从 .Net 生成最小的可能 PNG。请注意,它是黑白的 - 甚至不是灰度。对文档很有用。
消费者代码:
Dim src = (the original bitmap)
Using img = New Bitmap(src.Width, src.Height, PixelFormat.Format16bppRgb555) ' Provided Make1bpp function requires this
img.SetResolution(src.HorizontalResolution, src.VerticalResolution)
Using g = Graphics.FromImage(img)
g.Clear(Color.White) ' remove transparancy
g.DrawImage(src, 0, 0, src.Width, src.Height)
End Using
Using img2 As Bitmap = Make1bpp(img)
img2.SetResolution(src.HorizontalResolution, src.VerticalResolution)
Dim myencoder = (From parm In ImageCodecInfo.GetImageEncoders() Where parm.MimeType = "image/png").First()
Dim encoderParams = New EncoderParameters(1)
encoderParams.Param(0) = New EncoderParameter(Encoder.ColorDepth, 8L)
If IO.File.Exists(pngName) Then
IO.File.Delete(pngName)
End If
img2.Save(pngName, myencoder, encoderParams)
End Using
End Using
这就是 PNG 编码器所关心的
Function Make1bpp(ByVal bmpIN As Bitmap) As Bitmap
Dim bmpOUT As Bitmap
bmpOUT = NewBitmap(bmpIN.Width, bmpIN.Height, PixelFormat.Format1bppIndexed)
bmpOUT.SetResolution(bmpIN.HorizontalResolution, bmpIN.VerticalResolution)
' seems like I've got this crap in this program about 100x.
If bmpIN.PixelFormat <> PixelFormat.Format16bppRgb555 Then
Throw New ApplicationException("hand-coded routine can only understand image format of Format16bppRgb555 but this image is " & _
bmpIN.PixelFormat.ToString & ". Either change the format or code this sub to handle that format, too.")
End If
' lock image bytes
Dim bmdIN As BitmapData = bmpIN.LockBits(New Rectangle(0, 0, bmpIN.Width, bmpIN.Height), _
Imaging.ImageLockMode.ReadWrite, bmpIN.PixelFormat)
' lock image bytes
Dim bmdOUT As BitmapData = bmpOUT.LockBits(New Rectangle(0, 0, bmpOUT.Width, bmpOUT.Height), _
Imaging.ImageLockMode.ReadWrite, bmpOUT.PixelFormat)
' Allocate room for the data.
Dim bytesIN(bmdIN.Stride * bmdIN.Height) As Byte
Dim bytesOUT(bmdOUT.Stride * bmdOUT.Height) As Byte
' Copy the data into the PixBytes array.
Marshal.Copy(bmdIN.Scan0, bytesIN, 0, CInt(bmdIN.Stride * bmpIN.Height))
' > this val = white pix. (each of the 3 pix in the rgb555 can hold 32 levels... 2^5 huh.)
Dim bThresh As Byte = CByte((32 * 3) * 0.66)
' transfer the pixels
For y As Integer = 0 To bmpIN.Height - 1
Dim outpos As Integer = y * bmdOUT.Stride
Dim instart As Integer = y * bmdIN.Stride
Dim byteval As Byte = 0
Dim bitpos As Byte = 128
Dim pixval As Integer
Dim pixgraylevel As Integer
For inpos As Integer = instart To instart + bmdIN.Stride - 1 Step 2
pixval = 256 * bytesIN(inpos + 1) + bytesIN(inpos) ' DEPENDANT ON Format16bppRgb555
pixgraylevel = ((pixval) And 31) + ((pixval >> 5) And 31) + ((pixval >> 10) And 31)
If pixgraylevel > bThresh Then ' DEPENDANT ON Format16bppRgb555
byteval = byteval Or bitpos
End If
bitpos = bitpos >> 1
If bitpos = 0 Then
bytesOUT(outpos) = byteval
byteval = 0
bitpos = 128
outpos += 1
End If
Next
If bitpos <> 0 Then ' stick a fork in any unfinished busines.
bytesOUT(outpos) = byteval
End If
Next
' unlock image bytes
' Copy the data back into the bitmap.
Marshal.Copy(bytesOUT, 0, _
bmdOUT.Scan0, bmdOUT.Stride * bmdOUT.Height)
' Unlock the bitmap.
bmpIN.UnlockBits(bmdIN)
bmpOUT.UnlockBits(bmdOUT)
' futile attempt to free memory.
ReDim bytesIN(0)
ReDim bytesOUT(0)
' return new bmp.
Return bmpOUT
End Function
试试这个:
ImageCodecInfo pngCodec = ImageCodecInfo.GetImageEncoders().Where(codec => codec.FormatID.Equals(ImageFormat.Png.Guid)).FirstOrDefault();
if (pngCodec != null)
{
EncoderParameters parameters = new EncoderParameters();
parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);
myImage.Save(myStream, pngCodec, parameters);
}
所有图像格式都有效地支持低位深度。如果不需要,您只需保留最后几位未使用。GIF 只支持低色;你被限制为 256 种颜色。
未经测试的代码 -
Image myImage = new Image();
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);
myImage.Save(somestream, ImageFormat.Png, parameters);
查看 System.Drawing.Imaging 命名空间,玩转 Encoder.xxx 参数设置和 image.Save 方法。HTH。
更新 还值得注意的是,如果您想要一个小图像(低字节数),您可以尝试使用 Encoder.Compression 压缩保存为 JPEG,但会以图像质量为代价。